summaryrefslogtreecommitdiff
path: root/src/cpu/o3
diff options
context:
space:
mode:
authorGabe Black <gblack@eecs.umich.edu>2006-06-12 00:49:24 -0400
committerGabe Black <gblack@eecs.umich.edu>2006-06-12 00:49:24 -0400
commit15a8f050605919579e81b6abb98a0b596334216d (patch)
tree583b0b30f13d9874a69015d58d041ce2d7352960 /src/cpu/o3
parent60a734e1750f3c051fe92fea6f12158db6e2dcb9 (diff)
parentdf4b4f001e4db902297acf3b75480e4886e4e882 (diff)
downloadgem5-15a8f050605919579e81b6abb98a0b596334216d.tar.xz
Merge m5.eecs.umich.edu:/bk/newmem
into ewok.(none):/home/gblack/m5/newmem src/arch/sparc/regfile.hh: Hand Merge --HG-- extra : convert_revision : c47202689202069892524a7d71962082469996ee
Diffstat (limited to 'src/cpu/o3')
-rw-r--r--src/cpu/o3/2bit_local_pred.cc57
-rw-r--r--src/cpu/o3/2bit_local_pred.hh49
-rw-r--r--src/cpu/o3/alpha_cpu.cc2
-rw-r--r--src/cpu/o3/alpha_cpu.hh521
-rw-r--r--src/cpu/o3/alpha_cpu_builder.cc304
-rw-r--r--src/cpu/o3/alpha_cpu_impl.hh857
-rw-r--r--src/cpu/o3/alpha_dyn_inst.cc2
-rw-r--r--src/cpu/o3/alpha_dyn_inst.hh101
-rw-r--r--src/cpu/o3/alpha_dyn_inst_impl.hh110
-rw-r--r--src/cpu/o3/alpha_impl.hh19
-rw-r--r--src/cpu/o3/alpha_params.hh75
-rw-r--r--src/cpu/o3/bpred_unit.cc10
-rw-r--r--src/cpu/o3/bpred_unit.hh197
-rw-r--r--src/cpu/o3/bpred_unit_impl.hh306
-rw-r--r--src/cpu/o3/btb.cc36
-rw-r--r--src/cpu/o3/btb.hh68
-rw-r--r--src/cpu/o3/comm.hh111
-rw-r--r--src/cpu/o3/commit.cc4
-rw-r--r--src/cpu/o3/commit.hh358
-rw-r--r--src/cpu/o3/commit_impl.hh1251
-rw-r--r--src/cpu/o3/cpu.cc998
-rw-r--r--src/cpu/o3/cpu.hh412
-rw-r--r--src/cpu/o3/cpu_policy.hh63
-rw-r--r--src/cpu/o3/decode.cc4
-rw-r--r--src/cpu/o3/decode.hh180
-rw-r--r--src/cpu/o3/decode_impl.hh728
-rw-r--r--src/cpu/o3/dep_graph.hh264
-rw-r--r--src/cpu/o3/fetch.cc4
-rw-r--r--src/cpu/o3/fetch.hh324
-rw-r--r--src/cpu/o3/fetch_impl.hh1200
-rw-r--r--src/cpu/o3/free_list.cc38
-rw-r--r--src/cpu/o3/free_list.hh85
-rw-r--r--src/cpu/o3/fu_pool.cc299
-rw-r--r--src/cpu/o3/fu_pool.hh167
-rw-r--r--src/cpu/o3/iew.cc4
-rw-r--r--src/cpu/o3/iew.hh381
-rw-r--r--src/cpu/o3/iew_impl.hh1575
-rw-r--r--src/cpu/o3/inst_queue.cc6
-rw-r--r--src/cpu/o3/inst_queue.hh453
-rw-r--r--src/cpu/o3/inst_queue_impl.hh1503
-rw-r--r--src/cpu/o3/lsq.cc38
-rw-r--r--src/cpu/o3/lsq.hh318
-rw-r--r--src/cpu/o3/lsq_impl.hh529
-rw-r--r--src/cpu/o3/lsq_unit.cc39
-rw-r--r--src/cpu/o3/lsq_unit.hh711
-rw-r--r--src/cpu/o3/lsq_unit_impl.hh929
-rw-r--r--src/cpu/o3/mem_dep_unit.cc14
-rw-r--r--src/cpu/o3/mem_dep_unit.hh240
-rw-r--r--src/cpu/o3/mem_dep_unit_impl.hh570
-rw-r--r--src/cpu/o3/ras.cc25
-rw-r--r--src/cpu/o3/ras.hh39
-rw-r--r--src/cpu/o3/regfile.hh157
-rw-r--r--src/cpu/o3/rename.cc4
-rw-r--r--src/cpu/o3/rename.hh365
-rw-r--r--src/cpu/o3/rename_impl.hh1432
-rw-r--r--src/cpu/o3/rename_map.cc281
-rw-r--r--src/cpu/o3/rename_map.hh69
-rw-r--r--src/cpu/o3/rob.cc3
-rw-r--r--src/cpu/o3/rob.hh237
-rw-r--r--src/cpu/o3/rob_impl.hh623
-rw-r--r--src/cpu/o3/sat_counter.cc28
-rw-r--r--src/cpu/o3/sat_counter.hh52
-rw-r--r--src/cpu/o3/scoreboard.cc109
-rw-r--r--src/cpu/o3/scoreboard.hh117
-rw-r--r--src/cpu/o3/store_set.cc180
-rw-r--r--src/cpu/o3/store_set.hh89
-rw-r--r--src/cpu/o3/thread_state.hh102
-rw-r--r--src/cpu/o3/tournament_pred.cc279
-rw-r--r--src/cpu/o3/tournament_pred.hh127
69 files changed, 16066 insertions, 4766 deletions
diff --git a/src/cpu/o3/2bit_local_pred.cc b/src/cpu/o3/2bit_local_pred.cc
index d9744eec7..77a45ea26 100644
--- a/src/cpu/o3/2bit_local_pred.cc
+++ b/src/cpu/o3/2bit_local_pred.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,29 +24,41 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
+#include "base/intmath.hh"
+#include "base/misc.hh"
#include "base/trace.hh"
#include "cpu/o3/2bit_local_pred.hh"
-DefaultBP::DefaultBP(unsigned _localPredictorSize,
- unsigned _localCtrBits,
- unsigned _instShiftAmt)
+LocalBP::LocalBP(unsigned _localPredictorSize,
+ unsigned _localCtrBits,
+ unsigned _instShiftAmt)
: localPredictorSize(_localPredictorSize),
localCtrBits(_localCtrBits),
instShiftAmt(_instShiftAmt)
{
- // Should do checks here to make sure sizes are correct (powers of 2).
+ if (!isPowerOf2(localPredictorSize)) {
+ fatal("Invalid local predictor size!\n");
+ }
+
+ localPredictorSets = localPredictorSize / localCtrBits;
+
+ if (!isPowerOf2(localPredictorSets)) {
+ fatal("Invalid number of local predictor sets! Check localCtrBits.\n");
+ }
// Setup the index mask.
- indexMask = localPredictorSize - 1;
+ indexMask = localPredictorSets - 1;
DPRINTF(Fetch, "Branch predictor: index mask: %#x\n", indexMask);
// Setup the array of counters for the local predictor.
- localCtrs = new SatCounter[localPredictorSize];
+ localCtrs.resize(localPredictorSets);
- for (int i = 0; i < localPredictorSize; ++i)
+ for (int i = 0; i < localPredictorSets; ++i)
localCtrs[i].setBits(_localCtrBits);
DPRINTF(Fetch, "Branch predictor: local predictor size: %i\n",
@@ -58,24 +70,30 @@ DefaultBP::DefaultBP(unsigned _localPredictorSize,
instShiftAmt);
}
+void
+LocalBP::reset()
+{
+ for (int i = 0; i < localPredictorSets; ++i) {
+ localCtrs[i].reset();
+ }
+}
+
bool
-DefaultBP::lookup(Addr &branch_addr)
+LocalBP::lookup(Addr &branch_addr, void * &bp_history)
{
bool taken;
- uint8_t local_prediction;
+ uint8_t counter_val;
unsigned local_predictor_idx = getLocalIndex(branch_addr);
DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n",
local_predictor_idx);
- assert(local_predictor_idx < localPredictorSize);
-
- local_prediction = localCtrs[local_predictor_idx].read();
+ counter_val = localCtrs[local_predictor_idx].read();
DPRINTF(Fetch, "Branch predictor: prediction is %i.\n",
- (int)local_prediction);
+ (int)counter_val);
- taken = getPrediction(local_prediction);
+ taken = getPrediction(counter_val);
#if 0
// Speculative update.
@@ -92,8 +110,9 @@ DefaultBP::lookup(Addr &branch_addr)
}
void
-DefaultBP::update(Addr &branch_addr, bool taken)
+LocalBP::update(Addr &branch_addr, bool taken, void *bp_history)
{
+ assert(bp_history == NULL);
unsigned local_predictor_idx;
// Update the local predictor.
@@ -102,8 +121,6 @@ DefaultBP::update(Addr &branch_addr, bool taken)
DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n",
local_predictor_idx);
- assert(local_predictor_idx < localPredictorSize);
-
if (taken) {
DPRINTF(Fetch, "Branch predictor: Branch updated as taken.\n");
localCtrs[local_predictor_idx].increment();
@@ -115,7 +132,7 @@ DefaultBP::update(Addr &branch_addr, bool taken)
inline
bool
-DefaultBP::getPrediction(uint8_t &count)
+LocalBP::getPrediction(uint8_t &count)
{
// Get the MSB of the count
return (count >> (localCtrBits - 1));
@@ -123,7 +140,7 @@ DefaultBP::getPrediction(uint8_t &count)
inline
unsigned
-DefaultBP::getLocalIndex(Addr &branch_addr)
+LocalBP::getLocalIndex(Addr &branch_addr)
{
return (branch_addr >> instShiftAmt) & indexMask;
}
diff --git a/src/cpu/o3/2bit_local_pred.hh b/src/cpu/o3/2bit_local_pred.hh
index 97433e542..0a2a71d3e 100644
--- a/src/cpu/o3/2bit_local_pred.hh
+++ b/src/cpu/o3/2bit_local_pred.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,43 +24,65 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__
-#define __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__
+#ifndef __CPU_O3_2BIT_LOCAL_PRED_HH__
+#define __CPU_O3_2BIT_LOCAL_PRED_HH__
// For Addr type.
#include "arch/isa_traits.hh"
#include "cpu/o3/sat_counter.hh"
-class DefaultBP
+#include <vector>
+
+/**
+ * Implements a local predictor that uses the PC to index into a table of
+ * counters. Note that any time a pointer to the bp_history is given, it
+ * should be NULL using this predictor because it does not have any branch
+ * predictor state that needs to be recorded or updated; the update can be
+ * determined solely by the branch being taken or not taken.
+ */
+class LocalBP
{
public:
/**
* Default branch predictor constructor.
+ * @param localPredictorSize Size of the local predictor.
+ * @param localCtrBits Number of bits per counter.
+ * @param instShiftAmt Offset amount for instructions to ignore alignment.
*/
- DefaultBP(unsigned localPredictorSize, unsigned localCtrBits,
- unsigned instShiftAmt);
+ LocalBP(unsigned localPredictorSize, unsigned localCtrBits,
+ unsigned instShiftAmt);
/**
* Looks up the given address in the branch predictor and returns
* a true/false value as to whether it is taken.
* @param branch_addr The address of the branch to look up.
+ * @param bp_history Pointer to any bp history state.
* @return Whether or not the branch is taken.
*/
- bool lookup(Addr &branch_addr);
+ bool lookup(Addr &branch_addr, void * &bp_history);
/**
* Updates the branch predictor with the actual result of a branch.
* @param branch_addr The address of the branch to update.
* @param taken Whether or not the branch was taken.
*/
- void update(Addr &branch_addr, bool taken);
+ void update(Addr &branch_addr, bool taken, void *bp_history);
- private:
+ void squash(void *bp_history)
+ { assert(bp_history == NULL); }
- /** Returns the taken/not taken prediction given the value of the
+ void reset();
+
+ private:
+ /**
+ * Returns the taken/not taken prediction given the value of the
* counter.
+ * @param count The value of the counter.
+ * @return The prediction based on the counter value.
*/
inline bool getPrediction(uint8_t &count);
@@ -68,11 +90,14 @@ class DefaultBP
inline unsigned getLocalIndex(Addr &PC);
/** Array of counters that make up the local predictor. */
- SatCounter *localCtrs;
+ std::vector<SatCounter> localCtrs;
/** Size of the local predictor. */
unsigned localPredictorSize;
+ /** Number of sets. */
+ unsigned localPredictorSets;
+
/** Number of bits of the local predictor's counters. */
unsigned localCtrBits;
@@ -83,4 +108,4 @@ class DefaultBP
unsigned indexMask;
};
-#endif // __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__
+#endif // __CPU_O3_2BIT_LOCAL_PRED_HH__
diff --git a/src/cpu/o3/alpha_cpu.cc b/src/cpu/o3/alpha_cpu.cc
index 7bc90dae6..39cae696b 100644
--- a/src/cpu/o3/alpha_cpu.cc
+++ b/src/cpu/o3/alpha_cpu.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_impl.hh"
diff --git a/src/cpu/o3/alpha_cpu.hh b/src/cpu/o3/alpha_cpu.hh
index 8e1e0f42a..f81837f3c 100644
--- a/src/cpu/o3/alpha_cpu.hh
+++ b/src/cpu/o3/alpha_cpu.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,268 +24,411 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Todo: Find all the stuff in ExecContext and ev5 that needs to be
-// specifically designed for this CPU.
-
-#ifndef __CPU_O3_CPU_ALPHA_FULL_CPU_HH__
-#define __CPU_O3_CPU_ALPHA_FULL_CPU_HH__
+#ifndef __CPU_O3_ALPHA_FULL_CPU_HH__
+#define __CPU_O3_ALPHA_FULL_CPU_HH__
-#include "cpu/o3/cpu.hh"
#include "arch/isa_traits.hh"
+#include "cpu/thread_context.hh"
+#include "cpu/o3/cpu.hh"
#include "sim/byteswap.hh"
+class EndQuiesceEvent;
+namespace Kernel {
+ class Statistics;
+};
+
+class TranslatingPort;
+
+/**
+ * AlphaFullCPU class. Derives from the FullO3CPU class, and
+ * implements all ISA and implementation specific functions of the
+ * CPU. This is the CPU class that is used for the SimObjects, and is
+ * what is given to the DynInsts. Most of its state exists in the
+ * FullO3CPU; the state is has is mainly for ISA specific
+ * functionality.
+ */
template <class Impl>
class AlphaFullCPU : public FullO3CPU<Impl>
{
protected:
typedef TheISA::IntReg IntReg;
+ typedef TheISA::FloatReg FloatReg;
+ typedef TheISA::FloatRegBits FloatRegBits;
typedef TheISA::MiscReg MiscReg;
typedef TheISA::RegFile RegFile;
typedef TheISA::MiscRegFile MiscRegFile;
public:
+ typedef O3ThreadState<Impl> ImplState;
+ typedef O3ThreadState<Impl> Thread;
typedef typename Impl::Params Params;
- public:
- AlphaFullCPU(Params &params);
+ /** Constructs an AlphaFullCPU with the given parameters. */
+ AlphaFullCPU(Params *params);
+
+ /**
+ * Derived ThreadContext class for use with the AlphaFullCPU. It
+ * provides the interface for any external objects to access a
+ * single thread's state and some general CPU state. Any time
+ * external objects try to update state through this interface,
+ * the CPU will create an event to squash all in-flight
+ * instructions in order to ensure state is maintained correctly.
+ * It must be defined specifically for the AlphaFullCPU because
+ * not all architectural state is located within the O3ThreadState
+ * (such as the commit PC, and registers), and specific actions
+ * must be taken when using this interface (such as squashing all
+ * in-flight instructions when doing a write to this interface).
+ */
+ class AlphaTC : public ThreadContext
+ {
+ public:
+ /** Pointer to the CPU. */
+ AlphaFullCPU<Impl> *cpu;
+
+ /** Pointer to the thread state that this TC corrseponds to. */
+ O3ThreadState<Impl> *thread;
+
+ /** Returns a pointer to this CPU. */
+ virtual BaseCPU *getCpuPtr() { return cpu; }
+
+ /** Sets this CPU's ID. */
+ virtual void setCpuId(int id) { cpu->cpu_id = id; }
+
+ /** Reads this CPU's ID. */
+ virtual int readCpuId() { return cpu->cpu_id; }
+
+#if FULL_SYSTEM
+ /** Returns a pointer to the system. */
+ virtual System *getSystemPtr() { return cpu->system; }
+
+ /** Returns a pointer to physical memory. */
+ virtual PhysicalMemory *getPhysMemPtr() { return cpu->physmem; }
+
+ /** Returns a pointer to the ITB. */
+ virtual AlphaITB *getITBPtr() { return cpu->itb; }
+
+ /** Returns a pointer to the DTB. */
+ virtual AlphaDTB *getDTBPtr() { return cpu->dtb; }
+
+ /** Returns a pointer to this thread's kernel statistics. */
+ virtual Kernel::Statistics *getKernelStats()
+ { return thread->kernelStats; }
+
+ virtual FunctionalPort *getPhysPort() { return thread->getPhysPort(); }
+
+ virtual VirtualPort *getVirtPort(ThreadContext *src_tc = NULL);
+
+ void delVirtPort(VirtualPort *vp);
+#else
+ virtual TranslatingPort *getMemPort() { return thread->getMemPort(); }
+
+ /** Returns a pointer to this thread's process. */
+ virtual Process *getProcessPtr() { return thread->getProcessPtr(); }
+#endif
+ /** Returns this thread's status. */
+ virtual Status status() const { return thread->status(); }
+
+ /** Sets this thread's status. */
+ virtual void setStatus(Status new_status)
+ { thread->setStatus(new_status); }
+
+ /** Set the status to Active. Optional delay indicates number of
+ * cycles to wait before beginning execution. */
+ virtual void activate(int delay = 1);
+
+ /** Set the status to Suspended. */
+ virtual void suspend();
+
+ /** Set the status to Unallocated. */
+ virtual void deallocate();
+
+ /** Set the status to Halted. */
+ virtual void halt();
#if FULL_SYSTEM
+ /** Dumps the function profiling information.
+ * @todo: Implement.
+ */
+ virtual void dumpFuncProfile();
+#endif
+ /** Takes over execution of a thread from another CPU. */
+ virtual void takeOverFrom(ThreadContext *old_context);
+
+ /** Registers statistics associated with this TC. */
+ virtual void regStats(const std::string &name);
+
+ /** Serializes state. */
+ virtual void serialize(std::ostream &os);
+ /** Unserializes state. */
+ virtual void unserialize(Checkpoint *cp, const std::string &section);
+
+#if FULL_SYSTEM
+ /** Returns pointer to the quiesce event. */
+ virtual EndQuiesceEvent *getQuiesceEvent();
+
+ /** Reads the last tick that this thread was activated on. */
+ virtual Tick readLastActivate();
+ /** Reads the last tick that this thread was suspended on. */
+ virtual Tick readLastSuspend();
+
+ /** Clears the function profiling information. */
+ virtual void profileClear();
+ /** Samples the function profiling information. */
+ virtual void profileSample();
+#endif
+ /** Returns this thread's ID number. */
+ virtual int getThreadNum() { return thread->readTid(); }
+
+ /** Returns the instruction this thread is currently committing.
+ * Only used when an instruction faults.
+ */
+ virtual TheISA::MachInst getInst();
+
+ /** Copies the architectural registers from another TC into this TC. */
+ virtual void copyArchRegs(ThreadContext *tc);
+
+ /** Resets all architectural registers to 0. */
+ virtual void clearArchRegs();
+
+ /** Reads an integer register. */
+ virtual uint64_t readIntReg(int reg_idx);
+
+ virtual FloatReg readFloatReg(int reg_idx, int width);
+
+ virtual FloatReg readFloatReg(int reg_idx);
+
+ virtual FloatRegBits readFloatRegBits(int reg_idx, int width);
+
+ virtual FloatRegBits readFloatRegBits(int reg_idx);
+
+ /** Sets an integer register to a value. */
+ virtual void setIntReg(int reg_idx, uint64_t val);
+
+ virtual void setFloatReg(int reg_idx, FloatReg val, int width);
+
+ virtual void setFloatReg(int reg_idx, FloatReg val);
+
+ virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width);
+
+ virtual void setFloatRegBits(int reg_idx, FloatRegBits val);
+
+ /** Reads this thread's PC. */
+ virtual uint64_t readPC()
+ { return cpu->readPC(thread->readTid()); }
+
+ /** Sets this thread's PC. */
+ virtual void setPC(uint64_t val);
+
+ /** Reads this thread's next PC. */
+ virtual uint64_t readNextPC()
+ { return cpu->readNextPC(thread->readTid()); }
+
+ /** Sets this thread's next PC. */
+ virtual void setNextPC(uint64_t val);
+
+ virtual uint64_t readNextNPC()
+ {
+ panic("Alpha has no NextNPC!");
+ return 0;
+ }
+
+ virtual void setNextNPC(uint64_t val)
+ { }
+
+ /** Reads a miscellaneous register. */
+ virtual MiscReg readMiscReg(int misc_reg)
+ { return cpu->readMiscReg(misc_reg, thread->readTid()); }
+
+ /** Reads a misc. register, including any side-effects the
+ * read might have as defined by the architecture. */
+ virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault)
+ { return cpu->readMiscRegWithEffect(misc_reg, fault, thread->readTid()); }
+
+ /** Sets a misc. register. */
+ virtual Fault setMiscReg(int misc_reg, const MiscReg &val);
+
+ /** Sets a misc. register, including any side-effects the
+ * write might have as defined by the architecture. */
+ virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val);
+
+ /** Returns the number of consecutive store conditional failures. */
+ // @todo: Figure out where these store cond failures should go.
+ virtual unsigned readStCondFailures()
+ { return thread->storeCondFailures; }
+
+ /** Sets the number of consecutive store conditional failures. */
+ virtual void setStCondFailures(unsigned sc_failures)
+ { thread->storeCondFailures = sc_failures; }
+
+#if FULL_SYSTEM
+ /** Returns if the thread is currently in PAL mode, based on
+ * the PC's value. */
+ virtual bool inPalMode()
+ { return TheISA::PcPAL(cpu->readPC(thread->readTid())); }
+#endif
+ // Only really makes sense for old CPU model. Lots of code
+ // outside the CPU still checks this function, so it will
+ // always return false to keep everything working.
+ /** Checks if the thread is misspeculating. Because it is
+ * very difficult to determine if the thread is
+ * misspeculating, this is set as false. */
+ virtual bool misspeculating() { return false; }
+
+#if !FULL_SYSTEM
+ /** Gets a syscall argument by index. */
+ virtual IntReg getSyscallArg(int i);
+
+ /** Sets a syscall argument. */
+ virtual void setSyscallArg(int i, IntReg val);
+
+ /** Sets the syscall return value. */
+ virtual void setSyscallReturn(SyscallReturn return_value);
+
+ /** Executes a syscall in SE mode. */
+ virtual void syscall(int64_t callnum)
+ { return cpu->syscall(callnum, thread->readTid()); }
+
+ /** Reads the funcExeInst counter. */
+ virtual Counter readFuncExeInst() { return thread->funcExeInst; }
+#endif
+ virtual void changeRegFileContext(TheISA::RegFile::ContextParam param,
+ TheISA::RegFile::ContextVal val)
+ { panic("Not supported on Alpha!"); }
+ };
+
+#if FULL_SYSTEM
+ /** ITB pointer. */
AlphaITB *itb;
+ /** DTB pointer. */
AlphaDTB *dtb;
#endif
- public:
+ /** Registers statistics. */
void regStats();
#if FULL_SYSTEM
- //Note that the interrupt stuff from the base CPU might be somewhat
- //ISA specific (ie NumInterruptLevels). These functions might not
- //be needed in FullCPU though.
-// void post_interrupt(int int_num, int index);
-// void clear_interrupt(int int_num, int index);
-// void clear_interrupts();
-
- Fault translateInstReq(MemReqPtr &req)
+ /** Translates instruction requestion. */
+ Fault translateInstReq(RequestPtr &req, Thread *thread)
{
- return itb->translate(req);
+ return itb->translate(req, thread->getTC());
}
- Fault translateDataReadReq(MemReqPtr &req)
+ /** Translates data read request. */
+ Fault translateDataReadReq(RequestPtr &req, Thread *thread)
{
- return dtb->translate(req, false);
+ return dtb->translate(req, thread->getTC(), false);
}
- Fault translateDataWriteReq(MemReqPtr &req)
+ /** Translates data write request. */
+ Fault translateDataWriteReq(RequestPtr &req, Thread *thread)
{
- return dtb->translate(req, true);
+ return dtb->translate(req, thread->getTC(), true);
}
#else
- Fault dummyTranslation(MemReqPtr &req)
+ /** Translates instruction requestion in syscall emulation mode. */
+ Fault translateInstReq(RequestPtr &req, Thread *thread)
{
-#if 0
- assert((req->vaddr >> 48 & 0xffff) == 0);
-#endif
-
- // put the asid in the upper 16 bits of the paddr
- req->paddr = req->vaddr & ~((Addr)0xffff << sizeof(Addr) * 8 - 16);
- req->paddr = req->paddr | (Addr)req->asid << sizeof(Addr) * 8 - 16;
- return NoFault;
+ return thread->getProcessPtr()->pTable->translate(req);
}
- Fault translateInstReq(MemReqPtr &req)
+ /** Translates data read request in syscall emulation mode. */
+ Fault translateDataReadReq(RequestPtr &req, Thread *thread)
{
- return dummyTranslation(req);
+ return thread->getProcessPtr()->pTable->translate(req);
}
- Fault translateDataReadReq(MemReqPtr &req)
+ /** Translates data write request in syscall emulation mode. */
+ Fault translateDataWriteReq(RequestPtr &req, Thread *thread)
{
- return dummyTranslation(req);
- }
-
- Fault translateDataWriteReq(MemReqPtr &req)
- {
- return dummyTranslation(req);
+ return thread->getProcessPtr()->pTable->translate(req);
}
#endif
+ /** Reads a miscellaneous register. */
+ MiscReg readMiscReg(int misc_reg, unsigned tid);
- // Later on may want to remove this misc stuff from the regfile and
- // have it handled at this level. Might prove to be an issue when
- // trying to rename source/destination registers...
- MiscReg readMiscReg(int misc_reg)
- {
- // Dummy function for now.
- // @todo: Fix this once reg file gets fixed.
- return 0;
- }
+ /** Reads a misc. register, including any side effects the read
+ * might have as defined by the architecture.
+ */
+ MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, unsigned tid);
- Fault setMiscReg(int misc_reg, const MiscReg &val)
- {
- // Dummy function for now.
- // @todo: Fix this once reg file gets fixed.
- return NoFault;
- }
+ /** Sets a miscellaneous register. */
+ Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned tid);
+
+ /** Sets a misc. register, including any side effects the write
+ * might have as defined by the architecture.
+ */
+ Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, unsigned tid);
+
+ /** Initiates a squash of all in-flight instructions for a given
+ * thread. The source of the squash is an external update of
+ * state through the TC.
+ */
+ void squashFromTC(unsigned tid);
- // Most of the full system code and syscall emulation is not yet
- // implemented. These functions do show what the final interface will
- // look like.
#if FULL_SYSTEM
+ /** Posts an interrupt. */
+ void post_interrupt(int int_num, int index);
+ /** Reads the interrupt flag. */
int readIntrFlag();
+ /** Sets the interrupt flags. */
void setIntrFlag(int val);
- Fault hwrei();
- bool inPalMode() { return AlphaISA::PcPAL(this->regFile.readPC()); }
+ /** HW return from error interrupt. */
+ Fault hwrei(unsigned tid);
+ /** Returns if a specific PC is a PAL mode PC. */
bool inPalMode(uint64_t PC)
{ return AlphaISA::PcPAL(PC); }
- void trap(Fault fault);
- bool simPalCheck(int palFunc);
+ /** Traps to handle given fault. */
+ void trap(Fault fault, unsigned tid);
+ bool simPalCheck(int palFunc, unsigned tid);
+ /** Processes any interrupts. */
void processInterrupts();
-#endif
-
-
-#if !FULL_SYSTEM
- // Need to change these into regfile calls that directly set a certain
- // register. Actually, these functions should handle most of this
- // functionality by themselves; should look up the rename and then
- // set the register.
- IntReg getSyscallArg(int i)
- {
- return this->cpuXC->readIntReg(AlphaISA::ArgumentReg0 + i);
- }
-
- // used to shift args for indirect syscall
- void setSyscallArg(int i, IntReg val)
- {
- this->cpuXC->setIntReg(AlphaISA::ArgumentReg0 + i, val);
- }
-
- void setSyscallReturn(int64_t return_value)
- {
- // check for error condition. Alpha syscall convention is to
- // indicate success/failure in reg a3 (r19) and put the
- // return value itself in the standard return value reg (v0).
- const int RegA3 = 19; // only place this is used
- if (return_value >= 0) {
- // no error
- this->cpuXC->setIntReg(RegA3, 0);
- this->cpuXC->setIntReg(AlphaISA::ReturnValueReg, return_value);
- } else {
- // got an error, return details
- this->cpuXC->setIntReg(RegA3, (IntReg) -1);
- this->cpuXC->setIntReg(AlphaISA::ReturnValueReg, -return_value);
- }
- }
-
- void syscall(short thread_num);
- void squashStages();
-
-#endif
-
- void copyToXC();
- void copyFromXC();
-
- public:
-#if FULL_SYSTEM
- bool palShadowEnabled;
-
- // Not sure this is used anywhere.
- void intr_post(RegFile *regs, Fault fault, Addr pc);
- // Actually used within exec files. Implement properly.
- void swapPALShadow(bool use_shadow);
- // Called by CPU constructor. Can implement as I please.
- void initCPU(RegFile *regs);
- // Called by initCPU. Implement as I please.
- void initIPRs(RegFile *regs);
+ /** Halts the CPU. */
void halt() { panic("Halt not implemented!\n"); }
#endif
- template <class T>
- Fault read(MemReqPtr &req, T &data)
- {
-#if FULL_SYSTEM && THE_ISA == ALPHA_ISA
- if (req->flags & LOCKED) {
- req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr);
- req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true);
- }
+#if !FULL_SYSTEM
+ /** Executes a syscall.
+ * @todo: Determine if this needs to be virtual.
+ */
+ void syscall(int64_t callnum, int tid);
+ /** Gets a syscall argument. */
+ IntReg getSyscallArg(int i, int tid);
+
+ /** Used to shift args for indirect syscall. */
+ void setSyscallArg(int i, IntReg val, int tid);
+
+ /** Sets the return value of a syscall. */
+ void setSyscallReturn(SyscallReturn return_value, int tid);
#endif
- Fault error;
- error = this->mem->read(req, data);
- data = gtoh(data);
- return error;
- }
-
+ /** CPU read function, forwards read to LSQ. */
template <class T>
- Fault read(MemReqPtr &req, T &data, int load_idx)
+ Fault read(RequestPtr &req, T &data, int load_idx)
{
return this->iew.ldstQueue.read(req, data, load_idx);
}
+ /** CPU write function, forwards write to LSQ. */
template <class T>
- Fault write(MemReqPtr &req, T &data)
- {
-#if FULL_SYSTEM && THE_ISA == ALPHA_ISA
- ExecContext *xc;
-
- // If this is a store conditional, act appropriately
- if (req->flags & LOCKED) {
- xc = req->xc;
-
- if (req->flags & UNCACHEABLE) {
- // Don't update result register (see stq_c in isa_desc)
- req->result = 2;
- xc->setStCondFailures(0);//Needed? [RGD]
- } else {
- bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag);
- Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag);
- req->result = lock_flag;
- if (!lock_flag ||
- ((lock_addr & ~0xf) != (req->paddr & ~0xf))) {
- xc->setMiscReg(TheISA::Lock_Flag_DepTag, false);
- xc->setStCondFailures(xc->readStCondFailures() + 1);
- if (((xc->readStCondFailures()) % 100000) == 0) {
- std::cerr << "Warning: "
- << xc->readStCondFailures()
- << " consecutive store conditional failures "
- << "on cpu " << req->xc->readCpuId()
- << std::endl;
- }
- return NoFault;
- }
- else xc->setStCondFailures(0);
- }
- }
-
- // Need to clear any locked flags on other proccessors for
- // this address. Only do this for succsful Store Conditionals
- // and all other stores (WH64?). Unsuccessful Store
- // Conditionals would have returned above, and wouldn't fall
- // through.
- for (int i = 0; i < this->system->execContexts.size(); i++){
- xc = this->system->execContexts[i];
- if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) ==
- (req->paddr & ~0xf)) {
- xc->setMiscReg(TheISA::Lock_Flag_DepTag, false);
- }
- }
-
-#endif
-
- return this->mem->write(req, (T)htog(data));
- }
-
- template <class T>
- Fault write(MemReqPtr &req, T &data, int store_idx)
+ Fault write(RequestPtr &req, T &data, int store_idx)
{
return this->iew.ldstQueue.write(req, data, store_idx);
}
+ Addr lockAddr;
+
+ /** Temporary fix for the lock flag, works in the UP case. */
+ bool lockFlag;
};
-#endif // __CPU_O3_CPU_ALPHA_FULL_CPU_HH__
+#endif // __CPU_O3_ALPHA_FULL_CPU_HH__
diff --git a/src/cpu/o3/alpha_cpu_builder.cc b/src/cpu/o3/alpha_cpu_builder.cc
index 6025b8ef2..828977ccb 100644
--- a/src/cpu/o3/alpha_cpu_builder.cc
+++ b/src/cpu/o3/alpha_cpu_builder.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,41 +24,23 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#include "base/inifile.hh"
-#include "base/loader/symtab.hh"
-#include "base/misc.hh"
+#include <string>
+
#include "cpu/base.hh"
-#include "cpu/exetrace.hh"
#include "cpu/o3/alpha_cpu.hh"
#include "cpu/o3/alpha_impl.hh"
-#include "mem/base_mem.hh"
-#include "mem/cache/base_cache.hh"
-#include "mem/mem_interface.hh"
+#include "cpu/o3/alpha_params.hh"
+#include "cpu/o3/fu_pool.hh"
#include "sim/builder.hh"
-#include "sim/debug.hh"
-#include "sim/host.hh"
-#include "sim/process.hh"
-#include "sim/sim_events.hh"
-#include "sim/sim_object.hh"
-#include "sim/stats.hh"
-
-#if FULL_SYSTEM
-#include "base/remote_gdb.hh"
-#include "mem/functional/memory_control.hh"
-#include "mem/functional/physical.hh"
-#include "sim/system.hh"
-#include "arch/tlb.hh"
-#include "arch/vtophys.hh"
-#else // !FULL_SYSTEM
-#include "mem/functional/functional.hh"
-#endif // FULL_SYSTEM
class DerivAlphaFullCPU : public AlphaFullCPU<AlphaSimpleImpl>
{
public:
- DerivAlphaFullCPU(AlphaSimpleParams p)
+ DerivAlphaFullCPU(AlphaSimpleParams *p)
: AlphaFullCPU<AlphaSimpleImpl>(p)
{ }
};
@@ -67,6 +49,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
Param<int> clock;
Param<int> numThreads;
+Param<int> activity;
#if FULL_SYSTEM
SimObjectParam<System *> system;
@@ -76,15 +59,17 @@ SimObjectParam<AlphaDTB *> dtb;
#else
SimObjectVectorParam<Process *> workload;
#endif // FULL_SYSTEM
-SimObjectParam<FunctionalMemory *> mem;
+
+SimObjectParam<MemObject *> mem;
+
+SimObjectParam<BaseCPU *> checker;
Param<Counter> max_insts_any_thread;
Param<Counter> max_insts_all_threads;
Param<Counter> max_loads_any_thread;
Param<Counter> max_loads_all_threads;
-SimObjectParam<BaseCache *> icache;
-SimObjectParam<BaseCache *> dcache;
+Param<unsigned> cachePorts;
Param<unsigned> decodeToFetchDelay;
Param<unsigned> renameToFetchDelay;
@@ -112,25 +97,25 @@ Param<unsigned> executeIntWidth;
Param<unsigned> executeFloatWidth;
Param<unsigned> executeBranchWidth;
Param<unsigned> executeMemoryWidth;
+SimObjectParam<FUPool *> fuPool;
Param<unsigned> iewToCommitDelay;
Param<unsigned> renameToROBDelay;
Param<unsigned> commitWidth;
Param<unsigned> squashWidth;
+Param<Tick> trapLatency;
+Param<Tick> fetchTrapLatency;
-#if 0
+Param<std::string> predType;
Param<unsigned> localPredictorSize;
-Param<unsigned> localPredictorCtrBits;
-#endif
-Param<unsigned> local_predictor_size;
-Param<unsigned> local_ctr_bits;
-Param<unsigned> local_history_table_size;
-Param<unsigned> local_history_bits;
-Param<unsigned> global_predictor_size;
-Param<unsigned> global_ctr_bits;
-Param<unsigned> global_history_bits;
-Param<unsigned> choice_predictor_size;
-Param<unsigned> choice_ctr_bits;
+Param<unsigned> localCtrBits;
+Param<unsigned> localHistoryTableSize;
+Param<unsigned> localHistoryBits;
+Param<unsigned> globalPredictorSize;
+Param<unsigned> globalCtrBits;
+Param<unsigned> globalHistoryBits;
+Param<unsigned> choicePredictorSize;
+Param<unsigned> choiceCtrBits;
Param<unsigned> BTBEntries;
Param<unsigned> BTBTagSize;
@@ -147,6 +132,16 @@ Param<unsigned> numPhysFloatRegs;
Param<unsigned> numIQEntries;
Param<unsigned> numROBEntries;
+Param<unsigned> smtNumFetchingThreads;
+Param<std::string> smtFetchPolicy;
+Param<std::string> smtLSQPolicy;
+Param<unsigned> smtLSQThreshold;
+Param<std::string> smtIQPolicy;
+Param<unsigned> smtIQThreshold;
+Param<std::string> smtROBPolicy;
+Param<unsigned> smtROBThreshold;
+Param<std::string> smtCommitPolicy;
+
Param<unsigned> instShiftAmt;
Param<bool> defer_registration;
@@ -160,6 +155,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
INIT_PARAM(clock, "clock speed"),
INIT_PARAM(numThreads, "number of HW thread contexts"),
+ INIT_PARAM_DFLT(activity, "Initial activity count", 0),
#if FULL_SYSTEM
INIT_PARAM(system, "System object"),
@@ -170,7 +166,9 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
INIT_PARAM(workload, "Processes to run"),
#endif // FULL_SYSTEM
- INIT_PARAM_DFLT(mem, "Memory", NULL),
+ INIT_PARAM(mem, "Memory"),
+
+ INIT_PARAM_DFLT(checker, "Checker CPU", NULL),
INIT_PARAM_DFLT(max_insts_any_thread,
"Terminate when any thread reaches this inst count",
@@ -187,8 +185,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
"count",
0),
- INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL),
- INIT_PARAM_DFLT(dcache, "L1 data cache", NULL),
+ INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200),
INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"),
INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"),
@@ -196,7 +193,6 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
"delay"),
INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"),
INIT_PARAM(fetchWidth, "Fetch width"),
-
INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"),
INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode"
"delay"),
@@ -222,27 +218,26 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
INIT_PARAM(executeFloatWidth, "Floating point execute width"),
INIT_PARAM(executeBranchWidth, "Branch execute width"),
INIT_PARAM(executeMemoryWidth, "Memory execute width"),
+ INIT_PARAM_DFLT(fuPool, "Functional unit pool", NULL),
INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit "
"delay"),
INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"),
INIT_PARAM(commitWidth, "Commit width"),
INIT_PARAM(squashWidth, "Squash width"),
-
-#if 0
- INIT_PARAM(localPredictorSize, "Size of the local predictor in entries. "
- "Must be a power of 2."),
- INIT_PARAM(localPredictorCtrBits, "Number of bits per counter for bpred"),
-#endif
- INIT_PARAM(local_predictor_size, "Size of local predictor"),
- INIT_PARAM(local_ctr_bits, "Bits per counter"),
- INIT_PARAM(local_history_table_size, "Size of local history table"),
- INIT_PARAM(local_history_bits, "Bits for the local history"),
- INIT_PARAM(global_predictor_size, "Size of global predictor"),
- INIT_PARAM(global_ctr_bits, "Bits per counter"),
- INIT_PARAM(global_history_bits, "Bits of history"),
- INIT_PARAM(choice_predictor_size, "Size of choice predictor"),
- INIT_PARAM(choice_ctr_bits, "Bits of choice counters"),
+ INIT_PARAM_DFLT(trapLatency, "Number of cycles before the trap is handled", 6),
+ INIT_PARAM_DFLT(fetchTrapLatency, "Number of cycles before the fetch trap is handled", 12),
+
+ INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"),
+ INIT_PARAM(localPredictorSize, "Size of local predictor"),
+ INIT_PARAM(localCtrBits, "Bits per counter"),
+ INIT_PARAM(localHistoryTableSize, "Size of local history table"),
+ INIT_PARAM(localHistoryBits, "Bits for the local history"),
+ INIT_PARAM(globalPredictorSize, "Size of global predictor"),
+ INIT_PARAM(globalCtrBits, "Bits per counter"),
+ INIT_PARAM(globalHistoryBits, "Bits of history"),
+ INIT_PARAM(choicePredictorSize, "Size of choice predictor"),
+ INIT_PARAM(choiceCtrBits, "Bits of choice counters"),
INIT_PARAM(BTBEntries, "Number of BTB entries"),
INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"),
@@ -260,6 +255,16 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU)
INIT_PARAM(numIQEntries, "Number of instruction queue entries"),
INIT_PARAM(numROBEntries, "Number of reorder buffer entries"),
+ INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1),
+ INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"),
+ INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"),
+ INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100),
+ INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"),
+ INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100),
+ INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"),
+ INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100),
+ INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"),
+
INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"),
INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
@@ -287,101 +292,114 @@ CREATE_SIM_OBJECT(DerivAlphaFullCPU)
#endif
- AlphaSimpleParams params;
+ AlphaSimpleParams *params = new AlphaSimpleParams;
- params.clock = clock;
+ params->clock = clock;
- params.name = getInstanceName();
- params.numberOfThreads = actual_num_threads;
+ params->name = getInstanceName();
+ params->numberOfThreads = actual_num_threads;
+ params->activity = activity;
#if FULL_SYSTEM
- params.system = system;
- params.cpu_id = cpu_id;
- params.itb = itb;
- params.dtb = dtb;
+ params->system = system;
+ params->cpu_id = cpu_id;
+ params->itb = itb;
+ params->dtb = dtb;
#else
- params.workload = workload;
+ params->workload = workload;
#endif // FULL_SYSTEM
- params.mem = mem;
+ params->mem = mem;
+
+ params->checker = checker;
- params.max_insts_any_thread = max_insts_any_thread;
- params.max_insts_all_threads = max_insts_all_threads;
- params.max_loads_any_thread = max_loads_any_thread;
- params.max_loads_all_threads = max_loads_all_threads;
+ params->max_insts_any_thread = max_insts_any_thread;
+ params->max_insts_all_threads = max_insts_all_threads;
+ params->max_loads_any_thread = max_loads_any_thread;
+ params->max_loads_all_threads = max_loads_all_threads;
//
// Caches
//
- params.icacheInterface = icache ? icache->getInterface() : NULL;
- params.dcacheInterface = dcache ? dcache->getInterface() : NULL;
-
- params.decodeToFetchDelay = decodeToFetchDelay;
- params.renameToFetchDelay = renameToFetchDelay;
- params.iewToFetchDelay = iewToFetchDelay;
- params.commitToFetchDelay = commitToFetchDelay;
- params.fetchWidth = fetchWidth;
-
- params.renameToDecodeDelay = renameToDecodeDelay;
- params.iewToDecodeDelay = iewToDecodeDelay;
- params.commitToDecodeDelay = commitToDecodeDelay;
- params.fetchToDecodeDelay = fetchToDecodeDelay;
- params.decodeWidth = decodeWidth;
-
- params.iewToRenameDelay = iewToRenameDelay;
- params.commitToRenameDelay = commitToRenameDelay;
- params.decodeToRenameDelay = decodeToRenameDelay;
- params.renameWidth = renameWidth;
-
- params.commitToIEWDelay = commitToIEWDelay;
- params.renameToIEWDelay = renameToIEWDelay;
- params.issueToExecuteDelay = issueToExecuteDelay;
- params.issueWidth = issueWidth;
- params.executeWidth = executeWidth;
- params.executeIntWidth = executeIntWidth;
- params.executeFloatWidth = executeFloatWidth;
- params.executeBranchWidth = executeBranchWidth;
- params.executeMemoryWidth = executeMemoryWidth;
-
- params.iewToCommitDelay = iewToCommitDelay;
- params.renameToROBDelay = renameToROBDelay;
- params.commitWidth = commitWidth;
- params.squashWidth = squashWidth;
-#if 0
- params.localPredictorSize = localPredictorSize;
- params.localPredictorCtrBits = localPredictorCtrBits;
-#endif
- params.local_predictor_size = local_predictor_size;
- params.local_ctr_bits = local_ctr_bits;
- params.local_history_table_size = local_history_table_size;
- params.local_history_bits = local_history_bits;
- params.global_predictor_size = global_predictor_size;
- params.global_ctr_bits = global_ctr_bits;
- params.global_history_bits = global_history_bits;
- params.choice_predictor_size = choice_predictor_size;
- params.choice_ctr_bits = choice_ctr_bits;
-
- params.BTBEntries = BTBEntries;
- params.BTBTagSize = BTBTagSize;
-
- params.RASSize = RASSize;
-
- params.LQEntries = LQEntries;
- params.SQEntries = SQEntries;
- params.SSITSize = SSITSize;
- params.LFSTSize = LFSTSize;
-
- params.numPhysIntRegs = numPhysIntRegs;
- params.numPhysFloatRegs = numPhysFloatRegs;
- params.numIQEntries = numIQEntries;
- params.numROBEntries = numROBEntries;
-
- params.instShiftAmt = 2;
-
- params.defReg = defer_registration;
-
- params.functionTrace = function_trace;
- params.functionTraceStart = function_trace_start;
+ params->cachePorts = cachePorts;
+
+ params->decodeToFetchDelay = decodeToFetchDelay;
+ params->renameToFetchDelay = renameToFetchDelay;
+ params->iewToFetchDelay = iewToFetchDelay;
+ params->commitToFetchDelay = commitToFetchDelay;
+ params->fetchWidth = fetchWidth;
+
+ params->renameToDecodeDelay = renameToDecodeDelay;
+ params->iewToDecodeDelay = iewToDecodeDelay;
+ params->commitToDecodeDelay = commitToDecodeDelay;
+ params->fetchToDecodeDelay = fetchToDecodeDelay;
+ params->decodeWidth = decodeWidth;
+
+ params->iewToRenameDelay = iewToRenameDelay;
+ params->commitToRenameDelay = commitToRenameDelay;
+ params->decodeToRenameDelay = decodeToRenameDelay;
+ params->renameWidth = renameWidth;
+
+ params->commitToIEWDelay = commitToIEWDelay;
+ params->renameToIEWDelay = renameToIEWDelay;
+ params->issueToExecuteDelay = issueToExecuteDelay;
+ params->issueWidth = issueWidth;
+ params->executeWidth = executeWidth;
+ params->executeIntWidth = executeIntWidth;
+ params->executeFloatWidth = executeFloatWidth;
+ params->executeBranchWidth = executeBranchWidth;
+ params->executeMemoryWidth = executeMemoryWidth;
+ params->fuPool = fuPool;
+
+ params->iewToCommitDelay = iewToCommitDelay;
+ params->renameToROBDelay = renameToROBDelay;
+ params->commitWidth = commitWidth;
+ params->squashWidth = squashWidth;
+ params->trapLatency = trapLatency;
+ params->fetchTrapLatency = fetchTrapLatency;
+
+ params->predType = predType;
+ params->localPredictorSize = localPredictorSize;
+ params->localCtrBits = localCtrBits;
+ params->localHistoryTableSize = localHistoryTableSize;
+ params->localHistoryBits = localHistoryBits;
+ params->globalPredictorSize = globalPredictorSize;
+ params->globalCtrBits = globalCtrBits;
+ params->globalHistoryBits = globalHistoryBits;
+ params->choicePredictorSize = choicePredictorSize;
+ params->choiceCtrBits = choiceCtrBits;
+
+ params->BTBEntries = BTBEntries;
+ params->BTBTagSize = BTBTagSize;
+
+ params->RASSize = RASSize;
+
+ params->LQEntries = LQEntries;
+ params->SQEntries = SQEntries;
+
+ params->SSITSize = SSITSize;
+ params->LFSTSize = LFSTSize;
+
+ params->numPhysIntRegs = numPhysIntRegs;
+ params->numPhysFloatRegs = numPhysFloatRegs;
+ params->numIQEntries = numIQEntries;
+ params->numROBEntries = numROBEntries;
+
+ params->smtNumFetchingThreads = smtNumFetchingThreads;
+ params->smtFetchPolicy = smtFetchPolicy;
+ params->smtIQPolicy = smtIQPolicy;
+ params->smtLSQPolicy = smtLSQPolicy;
+ params->smtLSQThreshold = smtLSQThreshold;
+ params->smtROBPolicy = smtROBPolicy;
+ params->smtROBThreshold = smtROBThreshold;
+ params->smtCommitPolicy = smtCommitPolicy;
+
+ params->instShiftAmt = 2;
+
+ params->deferRegistration = defer_registration;
+
+ params->functionTrace = function_trace;
+ params->functionTraceStart = function_trace_start;
cpu = new DerivAlphaFullCPU(params);
diff --git a/src/cpu/o3/alpha_cpu_impl.hh b/src/cpu/o3/alpha_cpu_impl.hh
index 7c4c2b969..98290e57f 100644
--- a/src/cpu/o3/alpha_cpu_impl.hh
+++ b/src/cpu/o3/alpha_cpu_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,33 +24,138 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "arch/alpha/faults.hh"
#include "base/cprintf.hh"
#include "base/statistics.hh"
#include "base/timebuf.hh"
-#include "mem/cache/cache.hh" // for dynamic cast
-#include "mem/mem_interface.hh"
-#include "sim/builder.hh"
+#include "cpu/checker/thread_context.hh"
#include "sim/sim_events.hh"
#include "sim/stats.hh"
#include "cpu/o3/alpha_cpu.hh"
#include "cpu/o3/alpha_params.hh"
#include "cpu/o3/comm.hh"
+#include "cpu/o3/thread_state.hh"
#if FULL_SYSTEM
#include "arch/alpha/osfpal.hh"
-#include "arch/alpha/isa_traits.hh"
+#include "arch/isa_traits.hh"
+#include "cpu/quiesce_event.hh"
+#include "kern/kernel_stats.hh"
+#include "sim/system.hh"
#endif
+using namespace TheISA;
+
template <class Impl>
-AlphaFullCPU<Impl>::AlphaFullCPU(Params &params)
+AlphaFullCPU<Impl>::AlphaFullCPU(Params *params)
+#if FULL_SYSTEM
+ : FullO3CPU<Impl>(params), itb(params->itb), dtb(params->dtb)
+#else
: FullO3CPU<Impl>(params)
+#endif
{
DPRINTF(FullCPU, "AlphaFullCPU: Creating AlphaFullCPU object.\n");
+ // Setup any thread state.
+ this->thread.resize(this->numThreads);
+
+ for (int i = 0; i < this->numThreads; ++i) {
+#if FULL_SYSTEM
+ // SMT is not supported in FS mode yet.
+ assert(this->numThreads == 1);
+ this->thread[i] = new Thread(this, 0);
+ this->thread[i]->setStatus(ThreadContext::Suspended);
+#else
+ if (i < params->workload.size()) {
+ DPRINTF(FullCPU, "FullCPU: Workload[%i] process is %#x",
+ i, this->thread[i]);
+ this->thread[i] = new Thread(this, i, params->workload[i],
+ i, params->mem);
+
+ this->thread[i]->setStatus(ThreadContext::Suspended);
+
+#if !FULL_SYSTEM
+ /* Use this port to for syscall emulation writes to memory. */
+ Port *mem_port;
+ TranslatingPort *trans_port;
+ trans_port = new TranslatingPort(csprintf("%s-%d-funcport",
+ name(), i),
+ params->workload[i]->pTable,
+ false);
+ mem_port = params->mem->getPort("functional");
+ mem_port->setPeer(trans_port);
+ trans_port->setPeer(mem_port);
+ this->thread[i]->setMemPort(trans_port);
+#endif
+ //usedTids[i] = true;
+ //threadMap[i] = i;
+ } else {
+ //Allocate Empty thread so M5 can use later
+ //when scheduling threads to CPU
+ Process* dummy_proc = NULL;
+
+ this->thread[i] = new Thread(this, i, dummy_proc, i, params->mem);
+ //usedTids[i] = false;
+ }
+#endif // !FULL_SYSTEM
+
+ ThreadContext *tc;
+
+ // Setup the TC that will serve as the interface to the threads/CPU.
+ AlphaTC *alpha_tc = new AlphaTC;
+
+ // If we're using a checker, then the TC should be the
+ // CheckerThreadContext.
+ if (params->checker) {
+ tc = new CheckerThreadContext<AlphaTC>(
+ alpha_tc, this->checker);
+ } else {
+ tc = alpha_tc;
+ }
+
+ alpha_tc->cpu = this;
+ alpha_tc->thread = this->thread[i];
+
+#if FULL_SYSTEM
+ // Setup quiesce event.
+ this->thread[i]->quiesceEvent = new EndQuiesceEvent(tc);
+
+ Port *mem_port;
+ FunctionalPort *phys_port;
+ VirtualPort *virt_port;
+ phys_port = new FunctionalPort(csprintf("%s-%d-funcport",
+ name(), i));
+ mem_port = this->system->physmem->getPort("functional");
+ mem_port->setPeer(phys_port);
+ phys_port->setPeer(mem_port);
+
+ virt_port = new VirtualPort(csprintf("%s-%d-vport",
+ name(), i));
+ mem_port = this->system->physmem->getPort("functional");
+ mem_port->setPeer(virt_port);
+ virt_port->setPeer(mem_port);
+
+ this->thread[i]->setPhysPort(phys_port);
+ this->thread[i]->setVirtPort(virt_port);
+#endif
+ // Give the thread the TC.
+ this->thread[i]->tc = tc;
+
+ // Add the TC to the CPU's list of TC's.
+ this->threadContexts.push_back(tc);
+ }
+
+ for (int i=0; i < this->numThreads; i++) {
+ this->thread[i]->setFuncExeInst(0);
+ }
+
+ // Sets CPU pointers. These must be set at this level because the CPU
+ // pointers are defined to be the highest level of CPU class.
this->fetch.setCPU(this);
this->decode.setCPU(this);
this->rename.setCPU(this);
@@ -58,6 +163,10 @@ AlphaFullCPU<Impl>::AlphaFullCPU(Params &params)
this->commit.setCPU(this);
this->rob.setCPU(this);
+ this->regFile.setCPU(this);
+
+ lockAddr = 0;
+ lockFlag = false;
}
template <class Impl>
@@ -73,179 +182,519 @@ AlphaFullCPU<Impl>::regStats()
this->commit.regStats();
}
-#if !FULL_SYSTEM
+#if FULL_SYSTEM
+template <class Impl>
+VirtualPort *
+AlphaFullCPU<Impl>::AlphaTC::getVirtPort(ThreadContext *src_tc)
+{
+ if (!src_tc)
+ return thread->getVirtPort();
+
+ VirtualPort *vp;
+ Port *mem_port;
+
+ vp = new VirtualPort("tc-vport", src_tc);
+ mem_port = cpu->system->physmem->getPort("functional");
+ mem_port->setPeer(vp);
+ vp->setPeer(mem_port);
+ return vp;
+}
-// Will probably need to know which thread is calling syscall
-// Will need to pass that information in to the DynInst when it is constructed,
-// so that this call can be made with the proper thread number.
template <class Impl>
void
-AlphaFullCPU<Impl>::syscall(short thread_num)
+AlphaFullCPU<Impl>::AlphaTC::dumpFuncProfile()
{
- DPRINTF(FullCPU, "AlphaFullCPU: Syscall() called.\n\n");
+ // Currently not supported
+}
+#endif
- // Commit stage needs to run as well.
- this->commit.tick();
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::takeOverFrom(ThreadContext *old_context)
+{
+ // some things should already be set up
+#if FULL_SYSTEM
+ assert(getSystemPtr() == old_context->getSystemPtr());
+#else
+ assert(getProcessPtr() == old_context->getProcessPtr());
+#endif
- squashStages();
+ // copy over functional state
+ setStatus(old_context->status());
+ copyArchRegs(old_context);
+ setCpuId(old_context->readCpuId());
- // Temporarily increase this by one to account for the syscall
- // instruction.
- ++(this->funcExeInst);
+#if !FULL_SYSTEM
+ thread->funcExeInst = old_context->readFuncExeInst();
+#else
+ EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent();
+ if (other_quiesce) {
+ // Point the quiesce event's TC at this TC so that it wakes up
+ // the proper CPU.
+ other_quiesce->tc = this;
+ }
+ if (thread->quiesceEvent) {
+ thread->quiesceEvent->tc = this;
+ }
- // Copy over all important state to xc once all the unrolling is done.
- copyToXC();
+ // Transfer kernel stats from one CPU to the other.
+ thread->kernelStats = old_context->getKernelStats();
+// storeCondFailures = 0;
+ cpu->lockFlag = false;
+#endif
- // This is hardcoded to thread 0 while the CPU is only single threaded.
- this->thread[0]->syscall();
+ old_context->setStatus(ThreadContext::Unallocated);
- // Copy over all important state back to CPU.
- copyFromXC();
+ thread->inSyscall = false;
+ thread->trapPending = false;
+}
- // Decrease funcExeInst by one as the normal commit will handle
- // incrememnting it.
- --(this->funcExeInst);
+#if FULL_SYSTEM
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::delVirtPort(VirtualPort *vp)
+{
+ delete vp->getPeer();
+ delete vp;
}
+#endif
-// This is not a pretty function, and should only be used if it is necessary
-// to fake having everything squash all at once (ie for non-full system
-// syscalls). Maybe put this at the FullCPU level?
template <class Impl>
void
-AlphaFullCPU<Impl>::squashStages()
+AlphaFullCPU<Impl>::AlphaTC::activate(int delay)
{
- InstSeqNum rob_head = this->rob.readHeadSeqNum();
+ DPRINTF(FullCPU, "Calling activate on AlphaTC\n");
+
+ if (thread->status() == ThreadContext::Active)
+ return;
- // Now hack the time buffer to put this sequence number in the places
- // where the stages might read it.
- for (int i = 0; i < 5; ++i)
- {
- this->timeBuffer.access(-i)->commitInfo.doneSeqNum = rob_head;
+#if FULL_SYSTEM
+ thread->lastActivate = curTick;
+#endif
+
+ if (thread->status() == ThreadContext::Unallocated) {
+ cpu->activateWhenReady(thread->readTid());
+ return;
}
- this->fetch.squash(this->rob.readHeadNextPC());
- this->fetchQueue.advance();
-
- this->decode.squash();
- this->decodeQueue.advance();
-
- this->rename.squash();
- this->renameQueue.advance();
- this->renameQueue.advance();
-
- // Be sure to advance the IEW queues so that the commit stage doesn't
- // try to set an instruction as completed at the same time that it
- // might be deleting it.
- this->iew.squash();
- this->iewQueue.advance();
- this->iewQueue.advance();
- // Needs to tell the LSQ to write back all of its data
- this->iew.lsqWriteback();
-
- this->rob.squash(rob_head);
- this->commit.setSquashing();
-
- // Now hack the time buffer to clear the sequence numbers in the places
- // where the stages might read it.?
- for (int i = 0; i < 5; ++i)
- {
- this->timeBuffer.access(-i)->commitInfo.doneSeqNum = 0;
+ thread->setStatus(ThreadContext::Active);
+
+ // status() == Suspended
+ cpu->activateContext(thread->readTid(), delay);
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::suspend()
+{
+ DPRINTF(FullCPU, "Calling suspend on AlphaTC\n");
+
+ if (thread->status() == ThreadContext::Suspended)
+ return;
+
+#if FULL_SYSTEM
+ thread->lastActivate = curTick;
+ thread->lastSuspend = curTick;
+#endif
+/*
+#if FULL_SYSTEM
+ // Don't change the status from active if there are pending interrupts
+ if (cpu->check_interrupts()) {
+ assert(status() == ThreadContext::Active);
+ return;
}
+#endif
+*/
+ thread->setStatus(ThreadContext::Suspended);
+ cpu->suspendContext(thread->readTid());
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::deallocate()
+{
+ DPRINTF(FullCPU, "Calling deallocate on AlphaTC\n");
+ if (thread->status() == ThreadContext::Unallocated)
+ return;
+
+ thread->setStatus(ThreadContext::Unallocated);
+ cpu->deallocateContext(thread->readTid());
}
-#endif // FULL_SYSTEM
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::halt()
+{
+ DPRINTF(FullCPU, "Calling halt on AlphaTC\n");
+
+ if (thread->status() == ThreadContext::Halted)
+ return;
+
+ thread->setStatus(ThreadContext::Halted);
+ cpu->haltContext(thread->readTid());
+}
template <class Impl>
void
-AlphaFullCPU<Impl>::copyToXC()
+AlphaFullCPU<Impl>::AlphaTC::regStats(const std::string &name)
{
- PhysRegIndex renamed_reg;
+#if FULL_SYSTEM
+ thread->kernelStats = new Kernel::Statistics(cpu->system);
+ thread->kernelStats->regStats(name + ".kern");
+#endif
+}
- // First loop through the integer registers.
- for (int i = 0; i < AlphaISA::NumIntRegs; ++i)
- {
- renamed_reg = this->renameMap.lookup(i);
- this->cpuXC->setIntReg(i, this->regFile.readIntReg(renamed_reg));
- DPRINTF(FullCPU, "FullCPU: Copying register %i, has data %lli.\n",
- renamed_reg, this->regFile.intRegFile[renamed_reg]);
- }
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::serialize(std::ostream &os)
+{
+#if FULL_SYSTEM
+ if (thread->kernelStats)
+ thread->kernelStats->serialize(os);
+#endif
- // Then loop through the floating point registers.
- for (int i = 0; i < AlphaISA::NumFloatRegs; ++i)
- {
- renamed_reg = this->renameMap.lookup(i + AlphaISA::FP_Base_DepTag);
- this->cpuXC->setFloatRegBits(i,
- this->regFile.readFloatRegBits(renamed_reg));
- }
+}
- this->cpuXC->setMiscReg(AlphaISA::Fpcr_DepTag,
- this->regFile.readMiscReg(AlphaISA::Fpcr_DepTag));
- this->cpuXC->setMiscReg(AlphaISA::Uniq_DepTag,
- this->regFile.readMiscReg(AlphaISA::Uniq_DepTag));
- this->cpuXC->setMiscReg(AlphaISA::Lock_Flag_DepTag,
- this->regFile.readMiscReg(AlphaISA::Lock_Flag_DepTag));
- this->cpuXC->setMiscReg(AlphaISA::Lock_Addr_DepTag,
- this->regFile.readMiscReg(AlphaISA::Lock_Addr_DepTag));
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::unserialize(Checkpoint *cp, const std::string &section)
+{
+#if FULL_SYSTEM
+ if (thread->kernelStats)
+ thread->kernelStats->unserialize(cp, section);
+#endif
- this->cpuXC->setPC(this->rob.readHeadPC());
- this->cpuXC->setNextPC(this->cpuXC->readPC()+4);
+}
-#if !FULL_SYSTEM
- this->cpuXC->setFuncExeInst(this->funcExeInst);
+#if FULL_SYSTEM
+template <class Impl>
+EndQuiesceEvent *
+AlphaFullCPU<Impl>::AlphaTC::getQuiesceEvent()
+{
+ return thread->quiesceEvent;
+}
+
+template <class Impl>
+Tick
+AlphaFullCPU<Impl>::AlphaTC::readLastActivate()
+{
+ return thread->lastActivate;
+}
+
+template <class Impl>
+Tick
+AlphaFullCPU<Impl>::AlphaTC::readLastSuspend()
+{
+ return thread->lastSuspend;
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::profileClear()
+{}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::profileSample()
+{}
#endif
+
+template <class Impl>
+TheISA::MachInst
+AlphaFullCPU<Impl>::AlphaTC:: getInst()
+{
+ return thread->getInst();
}
-// This function will probably mess things up unless the ROB is empty and
-// there are no instructions in the pipeline.
template <class Impl>
void
-AlphaFullCPU<Impl>::copyFromXC()
+AlphaFullCPU<Impl>::AlphaTC::copyArchRegs(ThreadContext *tc)
{
+ // This function will mess things up unless the ROB is empty and
+ // there are no instructions in the pipeline.
+ unsigned tid = thread->readTid();
PhysRegIndex renamed_reg;
// First loop through the integer registers.
- for (int i = 0; i < AlphaISA::NumIntRegs; ++i)
- {
- renamed_reg = this->renameMap.lookup(i);
+ for (int i = 0; i < AlphaISA::NumIntRegs; ++i) {
+ renamed_reg = cpu->renameMap[tid].lookup(i);
DPRINTF(FullCPU, "FullCPU: Copying over register %i, had data %lli, "
"now has data %lli.\n",
- renamed_reg, this->regFile.intRegFile[renamed_reg],
- this->cpuXC->readIntReg(i));
+ renamed_reg, cpu->readIntReg(renamed_reg),
+ tc->readIntReg(i));
- this->regFile.setIntReg(renamed_reg, this->cpuXC->readIntReg(i));
+ cpu->setIntReg(renamed_reg, tc->readIntReg(i));
}
// Then loop through the floating point registers.
- for (int i = 0; i < AlphaISA::NumFloatRegs; ++i)
- {
- renamed_reg = this->renameMap.lookup(i + AlphaISA::FP_Base_DepTag);
- this->regFile.setFloatRegBits(renamed_reg,
- this->cpuXC->readFloatRegBits(i));
+ for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) {
+ renamed_reg = cpu->renameMap[tid].lookup(i + AlphaISA::FP_Base_DepTag);
+ cpu->setFloatRegBits(renamed_reg,
+ tc->readFloatRegBits(i));
}
- // Then loop through the misc registers.
- this->regFile.setMiscReg(AlphaISA::Fpcr_DepTag,
- this->cpuXC->readMiscReg(AlphaISA::Fpcr_DepTag));
- this->regFile.setMiscReg(AlphaISA::Uniq_DepTag,
- this->cpuXC->readMiscReg(AlphaISA::Uniq_DepTag));
- this->regFile.setMiscReg(AlphaISA::Lock_Flag_DepTag,
- this->cpuXC->readMiscReg(AlphaISA::Lock_Flag_DepTag));
- this->regFile.setMiscReg(AlphaISA::Lock_Addr_DepTag,
- this->cpuXC->readMiscReg(AlphaISA::Lock_Addr_DepTag));
+ // Copy the misc regs.
+ copyMiscRegs(tc, this);
// Then finally set the PC and the next PC.
-// regFile.pc = cpuXC->regs.pc;
-// regFile.npc = cpuXC->regs.npc;
+ cpu->setPC(tc->readPC(), tid);
+ cpu->setNextPC(tc->readNextPC(), tid);
#if !FULL_SYSTEM
- this->funcExeInst = this->cpuXC->readFuncExeInst();
+ this->thread->funcExeInst = tc->readFuncExeInst();
#endif
}
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::clearArchRegs()
+{}
+
+template <class Impl>
+uint64_t
+AlphaFullCPU<Impl>::AlphaTC::readIntReg(int reg_idx)
+{
+ return cpu->readArchIntReg(reg_idx, thread->readTid());
+}
+
+template <class Impl>
+FloatReg
+AlphaFullCPU<Impl>::AlphaTC::readFloatReg(int reg_idx, int width)
+{
+ switch(width) {
+ case 32:
+ return cpu->readArchFloatRegSingle(reg_idx, thread->readTid());
+ case 64:
+ return cpu->readArchFloatRegDouble(reg_idx, thread->readTid());
+ default:
+ panic("Unsupported width!");
+ return 0;
+ }
+}
+
+template <class Impl>
+FloatReg
+AlphaFullCPU<Impl>::AlphaTC::readFloatReg(int reg_idx)
+{
+ return cpu->readArchFloatRegSingle(reg_idx, thread->readTid());
+}
+
+template <class Impl>
+FloatRegBits
+AlphaFullCPU<Impl>::AlphaTC::readFloatRegBits(int reg_idx, int width)
+{
+ DPRINTF(Fault, "Reading floatint register through the TC!\n");
+ return cpu->readArchFloatRegInt(reg_idx, thread->readTid());
+}
+
+template <class Impl>
+FloatRegBits
+AlphaFullCPU<Impl>::AlphaTC::readFloatRegBits(int reg_idx)
+{
+ return cpu->readArchFloatRegInt(reg_idx, thread->readTid());
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setIntReg(int reg_idx, uint64_t val)
+{
+ cpu->setArchIntReg(reg_idx, val, thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setFloatReg(int reg_idx, FloatReg val, int width)
+{
+ switch(width) {
+ case 32:
+ cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid());
+ break;
+ case 64:
+ cpu->setArchFloatRegDouble(reg_idx, val, thread->readTid());
+ break;
+ }
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setFloatReg(int reg_idx, FloatReg val)
+{
+ cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid());
+
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setFloatRegBits(int reg_idx, FloatRegBits val,
+ int width)
+{
+ DPRINTF(Fault, "Setting floatint register through the TC!\n");
+ cpu->setArchFloatRegInt(reg_idx, val, thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setFloatRegBits(int reg_idx, FloatRegBits val)
+{
+ cpu->setArchFloatRegInt(reg_idx, val, thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setPC(uint64_t val)
+{
+ cpu->setPC(val, thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setNextPC(uint64_t val)
+{
+ cpu->setNextPC(val, thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+}
+
+template <class Impl>
+Fault
+AlphaFullCPU<Impl>::AlphaTC::setMiscReg(int misc_reg, const MiscReg &val)
+{
+ Fault ret_fault = cpu->setMiscReg(misc_reg, val, thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+
+ return ret_fault;
+}
+
+template <class Impl>
+Fault
+AlphaFullCPU<Impl>::AlphaTC::setMiscRegWithEffect(int misc_reg,
+ const MiscReg &val)
+{
+ Fault ret_fault = cpu->setMiscRegWithEffect(misc_reg, val,
+ thread->readTid());
+
+ // Squash if we're not already in a state update mode.
+ if (!thread->trapPending && !thread->inSyscall) {
+ cpu->squashFromTC(thread->readTid());
+ }
+
+ return ret_fault;
+}
+
+#if !FULL_SYSTEM
+
+template <class Impl>
+TheISA::IntReg
+AlphaFullCPU<Impl>::AlphaTC::getSyscallArg(int i)
+{
+ return cpu->getSyscallArg(i, thread->readTid());
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setSyscallArg(int i, IntReg val)
+{
+ cpu->setSyscallArg(i, val, thread->readTid());
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::AlphaTC::setSyscallReturn(SyscallReturn return_value)
+{
+ cpu->setSyscallReturn(return_value, thread->readTid());
+}
+
+#endif // FULL_SYSTEM
+
+template <class Impl>
+MiscReg
+AlphaFullCPU<Impl>::readMiscReg(int misc_reg, unsigned tid)
+{
+ return this->regFile.readMiscReg(misc_reg, tid);
+}
+
+template <class Impl>
+MiscReg
+AlphaFullCPU<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault,
+ unsigned tid)
+{
+ return this->regFile.readMiscRegWithEffect(misc_reg, fault, tid);
+}
+
+template <class Impl>
+Fault
+AlphaFullCPU<Impl>::setMiscReg(int misc_reg, const MiscReg &val, unsigned tid)
+{
+ return this->regFile.setMiscReg(misc_reg, val, tid);
+}
+
+template <class Impl>
+Fault
+AlphaFullCPU<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val,
+ unsigned tid)
+{
+ return this->regFile.setMiscRegWithEffect(misc_reg, val, tid);
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::squashFromTC(unsigned tid)
+{
+ this->thread[tid]->inSyscall = true;
+ this->commit.generateTCEvent(tid);
+}
+
#if FULL_SYSTEM
template <class Impl>
+void
+AlphaFullCPU<Impl>::post_interrupt(int int_num, int index)
+{
+ BaseCPU::post_interrupt(int_num, index);
+
+ if (this->thread[0]->status() == ThreadContext::Suspended) {
+ DPRINTF(IPI,"Suspended Processor awoke\n");
+ this->threadContexts[0]->activate();
+ }
+}
+
+template <class Impl>
int
AlphaFullCPU<Impl>::readIntrFlag()
{
@@ -259,20 +708,14 @@ AlphaFullCPU<Impl>::setIntrFlag(int val)
this->regFile.setIntrFlag(val);
}
-// Can force commit stage to squash and stuff.
template <class Impl>
Fault
-AlphaFullCPU<Impl>::hwrei()
+AlphaFullCPU<Impl>::hwrei(unsigned tid)
{
- if (!inPalMode())
- return new AlphaISA::UnimplementedOpcodeFault;
-
- this->setNextPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR));
+ // Need to clear the lock flag upon returning from an interrupt.
+ this->lockFlag = false;
-// kernelStats.hwrei();
-
- if ((this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR) & 1) == 0)
-// AlphaISA::swap_palshadow(&regs, false);
+ this->thread[tid]->kernelStats->hwrei();
this->checkInterrupts = true;
@@ -282,15 +725,17 @@ AlphaFullCPU<Impl>::hwrei()
template <class Impl>
bool
-AlphaFullCPU<Impl>::simPalCheck(int palFunc)
+AlphaFullCPU<Impl>::simPalCheck(int palFunc, unsigned tid)
{
-// kernelStats.callpal(palFunc);
+ if (this->thread[tid]->kernelStats)
+ this->thread[tid]->kernelStats->callpal(palFunc,
+ this->threadContexts[tid]);
switch (palFunc) {
case PAL::halt:
halt();
if (--System::numSystemsRunning == 0)
- new SimExitEvent("all cpus halted");
+ exitSimLoop("all cpus halted");
break;
case PAL::bpt:
@@ -303,69 +748,125 @@ AlphaFullCPU<Impl>::simPalCheck(int palFunc)
return true;
}
-// Probably shouldn't be able to switch to the trap handler as quickly as
-// this. Also needs to get the exception restart address from the commit
-// stage.
template <class Impl>
void
-AlphaFullCPU<Impl>::trap(Fault fault)
+AlphaFullCPU<Impl>::trap(Fault fault, unsigned tid)
+{
+ // Pass the thread's TC into the invoke method.
+ fault->invoke(this->threadContexts[tid]);
+}
+
+template <class Impl>
+void
+AlphaFullCPU<Impl>::processInterrupts()
{
-/* // Keep in mind that a trap may be initiated by fetch if there's a TLB
- // miss
- uint64_t PC = this->commit.readCommitPC();
+ // Check for interrupts here. For now can copy the code that
+ // exists within isa_fullsys_traits.hh. Also assume that thread 0
+ // is the one that handles the interrupts.
+ // @todo: Possibly consolidate the interrupt checking code.
+ // @todo: Allow other threads to handle interrupts.
+
+ // Check if there are any outstanding interrupts
+ //Handle the interrupts
+ int ipl = 0;
+ int summary = 0;
+
+ this->checkInterrupts = false;
+
+ if (this->readMiscReg(IPR_ASTRR, 0))
+ panic("asynchronous traps not implemented\n");
+
+ if (this->readMiscReg(IPR_SIRR, 0)) {
+ for (int i = INTLEVEL_SOFTWARE_MIN;
+ i < INTLEVEL_SOFTWARE_MAX; i++) {
+ if (this->readMiscReg(IPR_SIRR, 0) & (ULL(1) << i)) {
+ // See table 4-19 of the 21164 hardware reference
+ ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1;
+ summary |= (ULL(1) << i);
+ }
+ }
+ }
- DPRINTF(Fault, "Fault %s\n", fault->name());
- this->recordEvent(csprintf("Fault %s", fault->name()));
+ uint64_t interrupts = this->intr_status();
+
+ if (interrupts) {
+ for (int i = INTLEVEL_EXTERNAL_MIN;
+ i < INTLEVEL_EXTERNAL_MAX; i++) {
+ if (interrupts & (ULL(1) << i)) {
+ // See table 4-19 of the 21164 hardware reference
+ ipl = i;
+ summary |= (ULL(1) << i);
+ }
+ }
+ }
- //kernelStats.fault(fault);
+ if (ipl && ipl > this->readMiscReg(IPR_IPLR, 0)) {
+ this->setMiscReg(IPR_ISR, summary, 0);
+ this->setMiscReg(IPR_INTID, ipl, 0);
+ // Checker needs to know these two registers were updated.
+ if (this->checker) {
+ this->checker->threadBase()->setMiscReg(IPR_ISR, summary);
+ this->checker->threadBase()->setMiscReg(IPR_INTID, ipl);
+ }
+ this->trap(Fault(new InterruptFault), 0);
+ DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
+ this->readMiscReg(IPR_IPLR, 0), ipl, summary);
+ }
+}
- if (fault->isA<ArithmeticFault>())
- panic("Arithmetic traps are unimplemented!");
+#endif // FULL_SYSTEM
- // exception restart address - Get the commit PC
- if (!fault->isA<InterruptFault>() || !inPalMode(PC))
- this->regFile.miscRegs.setReg(AlphaISA::IPR_EXC_ADDR, PC);
+#if !FULL_SYSTEM
- if (fault->isA<PalFault>() || fault->isA<ArithmeticFault>())
- // || fault == InterruptFault && !PC_PAL(regs.pc)
- {
- // traps... skip faulting instruction
- AlphaISA::MiscReg ipr_exc_addr =
- this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR);
- this->regFile.miscRegs.setReg(AlphaISA::IPR_EXC_ADDR,
- ipr_exc_addr + 4);
- }
+template <class Impl>
+void
+AlphaFullCPU<Impl>::syscall(int64_t callnum, int tid)
+{
+ DPRINTF(FullCPU, "AlphaFullCPU: [tid:%i] Executing syscall().\n\n", tid);
+
+ DPRINTF(Activity,"Activity: syscall() called.\n");
- if (!inPalMode(PC))
- swapPALShadow(true);
+ // Temporarily increase this by one to account for the syscall
+ // instruction.
+ ++(this->thread[tid]->funcExeInst);
- this->regFile.setPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_PAL_BASE) +
- (dynamic_cast<AlphaFault *>(fault.get()))->vect());
- this->regFile.setNextPC(PC + sizeof(MachInst));*/
+ // Execute the actual syscall.
+ this->thread[tid]->syscall(callnum);
+
+ // Decrease funcExeInst by one as the normal commit will handle
+ // incrementing it.
+ --(this->thread[tid]->funcExeInst);
}
template <class Impl>
-void
-AlphaFullCPU<Impl>::processInterrupts()
+TheISA::IntReg
+AlphaFullCPU<Impl>::getSyscallArg(int i, int tid)
{
- // Check for interrupts here. For now can copy the code that exists
- // within isa_fullsys_traits.hh.
+ return this->readArchIntReg(AlphaISA::ArgumentReg0 + i, tid);
}
-// swap_palshadow swaps in the values of the shadow registers and
-// swaps them with the values of the physical registers that map to the
-// same logical index.
template <class Impl>
void
-AlphaFullCPU<Impl>::swapPALShadow(bool use_shadow)
+AlphaFullCPU<Impl>::setSyscallArg(int i, IntReg val, int tid)
{
- if (palShadowEnabled == use_shadow)
- panic("swap_palshadow: wrong PAL shadow state");
-
- palShadowEnabled = use_shadow;
-
- // Will have to lookup in rename map to get physical registers, then
- // swap.
+ this->setArchIntReg(AlphaISA::ArgumentReg0 + i, val, tid);
}
-#endif // FULL_SYSTEM
+template <class Impl>
+void
+AlphaFullCPU<Impl>::setSyscallReturn(SyscallReturn return_value, int tid)
+{
+ // check for error condition. Alpha syscall convention is to
+ // indicate success/failure in reg a3 (r19) and put the
+ // return value itself in the standard return value reg (v0).
+ if (return_value.successful()) {
+ // no error
+ this->setArchIntReg(SyscallSuccessReg, 0, tid);
+ this->setArchIntReg(ReturnValueReg, return_value.value(), tid);
+ } else {
+ // got an error, return details
+ this->setArchIntReg(SyscallSuccessReg, (IntReg) -1, tid);
+ this->setArchIntReg(ReturnValueReg, -return_value.value(), tid);
+ }
+}
+#endif
diff --git a/src/cpu/o3/alpha_dyn_inst.cc b/src/cpu/o3/alpha_dyn_inst.cc
index 72ac77d95..0c1723eec 100644
--- a/src/cpu/o3/alpha_dyn_inst.cc
+++ b/src/cpu/o3/alpha_dyn_inst.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst_impl.hh"
diff --git a/src/cpu/o3/alpha_dyn_inst.hh b/src/cpu/o3/alpha_dyn_inst.hh
index 5b8a05e5c..36a08c4a7 100644
--- a/src/cpu/o3/alpha_dyn_inst.hh
+++ b/src/cpu/o3/alpha_dyn_inst.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,23 +24,28 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_ALPHA_DYN_INST_HH__
-#define __CPU_O3_CPU_ALPHA_DYN_INST_HH__
+#ifndef __CPU_O3_ALPHA_DYN_INST_HH__
+#define __CPU_O3_ALPHA_DYN_INST_HH__
+#include "arch/isa_traits.hh"
#include "cpu/base_dyn_inst.hh"
+#include "cpu/inst_seq.hh"
#include "cpu/o3/alpha_cpu.hh"
#include "cpu/o3/alpha_impl.hh"
-#include "cpu/inst_seq.hh"
+
+class Packet;
/**
- * Mostly implementation specific AlphaDynInst. It is templated in case there
- * are other implementations that are similar enough to be able to use this
- * class without changes. This is mainly useful if there are multiple similar
- * CPU implementations of the same ISA.
+ * Mostly implementation & ISA specific AlphaDynInst. As with most
+ * other classes in the new CPU model, it is templated on the Impl to
+ * allow for passing in of all types, such as the CPU type and the ISA
+ * type. The AlphaDynInst serves as the primary interface to the CPU
+ * for instructions that are executing.
*/
-
template <class Impl>
class AlphaDynInst : public BaseDynInst<Impl>
{
@@ -50,10 +55,14 @@ class AlphaDynInst : public BaseDynInst<Impl>
/** Binary machine instruction type. */
typedef TheISA::MachInst MachInst;
+ /** Extended machine instruction type. */
+ typedef TheISA::ExtMachInst ExtMachInst;
/** Logical register index type. */
typedef TheISA::RegIndex RegIndex;
/** Integer register index type. */
typedef TheISA::IntReg IntReg;
+ typedef TheISA::FloatReg FloatReg;
+ typedef TheISA::FloatRegBits FloatRegBits;
/** Misc register index type. */
typedef TheISA::MiscReg MiscReg;
@@ -64,60 +73,74 @@ class AlphaDynInst : public BaseDynInst<Impl>
public:
/** BaseDynInst constructor given a binary instruction. */
- AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num,
+ AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num,
FullCPU *cpu);
/** BaseDynInst constructor given a static inst pointer. */
AlphaDynInst(StaticInstPtr &_staticInst);
/** Executes the instruction.*/
- Fault execute()
- {
- return this->fault = this->staticInst->execute(this, this->traceData);
- }
+ Fault execute();
+
+ /** Initiates the access. Only valid for memory operations. */
+ Fault initiateAcc();
+
+ /** Completes the access. Only valid for memory operations. */
+ Fault completeAcc(Packet *pkt);
+
+ private:
+ /** Initializes variables. */
+ void initVars();
public:
+ /** Reads a miscellaneous register. */
MiscReg readMiscReg(int misc_reg)
{
- // Dummy function for now.
- // @todo: Fix this once reg file gets fixed.
- return 0;
+ return this->cpu->readMiscReg(misc_reg, this->threadNumber);
}
+ /** Reads a misc. register, including any side-effects the read
+ * might have as defined by the architecture.
+ */
MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault)
{
- // Dummy function for now.
- // @todo: Fix this once reg file gets fixed.
- return 0;
+ return this->cpu->readMiscRegWithEffect(misc_reg, fault,
+ this->threadNumber);
}
+ /** Sets a misc. register. */
Fault setMiscReg(int misc_reg, const MiscReg &val)
{
- // Dummy function for now.
- // @todo: Fix this once reg file gets fixed.
- return NoFault;
+ this->instResult.integer = val;
+ return this->cpu->setMiscReg(misc_reg, val, this->threadNumber);
}
+ /** Sets a misc. register, including any side-effects the write
+ * might have as defined by the architecture.
+ */
Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val)
{
- // Dummy function for now.
- // @todo: Fix this once reg file gets fixed.
- return NoFault;
+ return this->cpu->setMiscRegWithEffect(misc_reg, val,
+ this->threadNumber);
}
#if FULL_SYSTEM
+ /** Calls hardware return from error interrupt. */
Fault hwrei();
+ /** Reads interrupt flag. */
int readIntrFlag();
+ /** Sets interrupt flag. */
void setIntrFlag(int val);
+ /** Checks if system is in PAL mode. */
bool inPalMode();
+ /** Traps to handle specified fault. */
void trap(Fault fault);
bool simPalCheck(int palFunc);
#else
- void syscall();
+ /** Calls a syscall. */
+ void syscall(int64_t callnum);
#endif
-
-
private:
/** Physical register index of the destination registers of this
* instruction.
@@ -178,32 +201,32 @@ class AlphaDynInst : public BaseDynInst<Impl>
void setIntReg(const StaticInst *si, int idx, uint64_t val)
{
this->cpu->setIntReg(_destRegIdx[idx], val);
- this->instResult.integer = val;
+ BaseDynInst<Impl>::setIntReg(si, idx, val);
}
void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width)
{
this->cpu->setFloatReg(_destRegIdx[idx], val, width);
- this->instResult.fp = val;
+ BaseDynInst<Impl>::setFloatReg(si, idx, val, width);
}
void setFloatReg(const StaticInst *si, int idx, FloatReg val)
{
this->cpu->setFloatReg(_destRegIdx[idx], val);
- this->instResult.dbl = val;
+ BaseDynInst<Impl>::setFloatReg(si, idx, val);
}
void setFloatRegBits(const StaticInst *si, int idx,
FloatRegBits val, int width)
{
this->cpu->setFloatRegBits(_destRegIdx[idx], val, width);
- this->instResult.integer = val;
+ BaseDynInst<Impl>::setFloatRegBits(si, idx, val);
}
void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val)
{
this->cpu->setFloatRegBits(_destRegIdx[idx], val);
- this->instResult.integer = val;
+ BaseDynInst<Impl>::setFloatRegBits(si, idx, val);
}
/** Returns the physical register index of the i'th destination
@@ -249,16 +272,24 @@ class AlphaDynInst : public BaseDynInst<Impl>
}
public:
+ /** Calculates EA part of a memory instruction. Currently unused,
+ * though it may be useful in the future if we want to split
+ * memory operations into EA calculation and memory access parts.
+ */
Fault calcEA()
{
return this->staticInst->eaCompInst()->execute(this, this->traceData);
}
+ /** Does the memory access part of a memory instruction. Currently unused,
+ * though it may be useful in the future if we want to split
+ * memory operations into EA calculation and memory access parts.
+ */
Fault memAccess()
{
return this->staticInst->memAccInst()->execute(this, this->traceData);
}
};
-#endif // __CPU_O3_CPU_ALPHA_DYN_INST_HH__
+#endif // __CPU_O3_ALPHA_DYN_INST_HH__
diff --git a/src/cpu/o3/alpha_dyn_inst_impl.hh b/src/cpu/o3/alpha_dyn_inst_impl.hh
index 96b7d3430..a73cf4a7d 100644
--- a/src/cpu/o3/alpha_dyn_inst_impl.hh
+++ b/src/cpu/o3/alpha_dyn_inst_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,47 +24,95 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
template <class Impl>
-AlphaDynInst<Impl>::AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC,
+AlphaDynInst<Impl>::AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC,
InstSeqNum seq_num, FullCPU *cpu)
: BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu)
{
+ initVars();
+}
+
+template <class Impl>
+AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst)
+ : BaseDynInst<Impl>(_staticInst)
+{
+ initVars();
+}
+
+template <class Impl>
+void
+AlphaDynInst<Impl>::initVars()
+{
// Make sure to have the renamed register entries set to the same
// as the normal register entries. It will allow the IQ to work
// without any modifications.
- for (int i = 0; i < this->staticInst->numDestRegs(); i++)
- {
+ for (int i = 0; i < this->staticInst->numDestRegs(); i++) {
_destRegIdx[i] = this->staticInst->destRegIdx(i);
}
- for (int i = 0; i < this->staticInst->numSrcRegs(); i++)
- {
+ for (int i = 0; i < this->staticInst->numSrcRegs(); i++) {
_srcRegIdx[i] = this->staticInst->srcRegIdx(i);
this->_readySrcRegIdx[i] = 0;
}
+}
+
+template <class Impl>
+Fault
+AlphaDynInst<Impl>::execute()
+{
+ // @todo: Pretty convoluted way to avoid squashing from happening
+ // when using the TC during an instruction's execution
+ // (specifically for instructions that have side-effects that use
+ // the TC). Fix this.
+ bool in_syscall = this->thread->inSyscall;
+ this->thread->inSyscall = true;
+
+ this->fault = this->staticInst->execute(this, this->traceData);
+
+ this->thread->inSyscall = in_syscall;
+ return this->fault;
}
template <class Impl>
-AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst)
- : BaseDynInst<Impl>(_staticInst)
+Fault
+AlphaDynInst<Impl>::initiateAcc()
{
- // Make sure to have the renamed register entries set to the same
- // as the normal register entries. It will allow the IQ to work
- // without any modifications.
- for (int i = 0; i < _staticInst->numDestRegs(); i++)
- {
- _destRegIdx[i] = _staticInst->destRegIdx(i);
- }
+ // @todo: Pretty convoluted way to avoid squashing from happening
+ // when using the TC during an instruction's execution
+ // (specifically for instructions that have side-effects that use
+ // the TC). Fix this.
+ bool in_syscall = this->thread->inSyscall;
+ this->thread->inSyscall = true;
+
+ this->fault = this->staticInst->initiateAcc(this, this->traceData);
+
+ this->thread->inSyscall = in_syscall;
- for (int i = 0; i < _staticInst->numSrcRegs(); i++)
- {
- _srcRegIdx[i] = _staticInst->srcRegIdx(i);
+ return this->fault;
+}
+
+template <class Impl>
+Fault
+AlphaDynInst<Impl>::completeAcc(Packet *pkt)
+{
+ if (this->isLoad()) {
+ this->fault = this->staticInst->completeAcc(pkt, this,
+ this->traceData);
+ } else if (this->isStore()) {
+ this->fault = this->staticInst->completeAcc(pkt, this,
+ this->traceData);
+ } else {
+ panic("Unknown type!");
}
+
+ return this->fault;
}
#if FULL_SYSTEM
@@ -72,14 +120,26 @@ template <class Impl>
Fault
AlphaDynInst<Impl>::hwrei()
{
- return this->cpu->hwrei();
+ // Can only do a hwrei when in pal mode.
+ if (!this->cpu->inPalMode(this->readPC()))
+ return new AlphaISA::UnimplementedOpcodeFault;
+
+ // Set the next PC based on the value of the EXC_ADDR IPR.
+ this->setNextPC(this->cpu->readMiscReg(AlphaISA::IPR_EXC_ADDR,
+ this->threadNumber));
+
+ // Tell CPU to clear any state it needs to if a hwrei is taken.
+ this->cpu->hwrei(this->threadNumber);
+
+ // FIXME: XXX check for interrupts? XXX
+ return NoFault;
}
template <class Impl>
int
AlphaDynInst<Impl>::readIntrFlag()
{
-return this->cpu->readIntrFlag();
+ return this->cpu->readIntrFlag();
}
template <class Impl>
@@ -93,28 +153,28 @@ template <class Impl>
bool
AlphaDynInst<Impl>::inPalMode()
{
- return this->cpu->inPalMode();
+ return this->cpu->inPalMode(this->PC);
}
template <class Impl>
void
AlphaDynInst<Impl>::trap(Fault fault)
{
- this->cpu->trap(fault);
+ this->cpu->trap(fault, this->threadNumber);
}
template <class Impl>
bool
AlphaDynInst<Impl>::simPalCheck(int palFunc)
{
- return this->cpu->simPalCheck(palFunc);
+ return this->cpu->simPalCheck(palFunc, this->threadNumber);
}
#else
template <class Impl>
void
-AlphaDynInst<Impl>::syscall()
+AlphaDynInst<Impl>::syscall(int64_t callnum)
{
- this->cpu->syscall(this->threadNumber);
+ this->cpu->syscall(callnum, this->threadNumber);
}
#endif
diff --git a/src/cpu/o3/alpha_impl.hh b/src/cpu/o3/alpha_impl.hh
index 5e39fcb37..52f7c2394 100644
--- a/src/cpu/o3/alpha_impl.hh
+++ b/src/cpu/o3/alpha_impl.hh
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_ALPHA_IMPL_HH__
-#define __CPU_O3_CPU_ALPHA_IMPL_HH__
+#ifndef __CPU_O3_ALPHA_IMPL_HH__
+#define __CPU_O3_ALPHA_IMPL_HH__
#include "arch/alpha/isa_traits.hh"
@@ -41,7 +43,7 @@ class AlphaDynInst;
template <class Impl>
class AlphaFullCPU;
-/** Implementation specific struct that defines several key things to the
+/** Implementation specific struct that defines several key types to the
* CPU, the stages within the CPU, the time buffers, and the DynInst.
* The struct defines the ISA, the CPU policy, the specific DynInst, the
* specific FullCPU, and all of the structs from the time buffers to do
@@ -54,10 +56,10 @@ struct AlphaSimpleImpl
/** The type of MachInst. */
typedef TheISA::MachInst MachInst;
- /** The CPU policy to be used (ie fetch, decode, etc.). */
+ /** The CPU policy to be used, which defines all of the CPU stages. */
typedef SimpleCPUPolicy<AlphaSimpleImpl> CPUPol;
- /** The DynInst to be used. */
+ /** The DynInst type to be used. */
typedef AlphaDynInst<AlphaSimpleImpl> DynInst;
/** The refcounted DynInst pointer to be used. In most cases this is
@@ -65,15 +67,16 @@ struct AlphaSimpleImpl
*/
typedef RefCountingPtr<DynInst> DynInstPtr;
- /** The FullCPU to be used. */
+ /** The FullCPU type to be used. */
typedef AlphaFullCPU<AlphaSimpleImpl> FullCPU;
/** The Params to be passed to each stage. */
typedef AlphaSimpleParams Params;
enum {
- MaxWidth = 8
+ MaxWidth = 8,
+ MaxThreads = 4
};
};
-#endif // __CPU_O3_CPU_ALPHA_IMPL_HH__
+#endif // __CPU_O3_ALPHA_IMPL_HH__
diff --git a/src/cpu/o3/alpha_params.hh b/src/cpu/o3/alpha_params.hh
index 79b0937e3..f3cf36887 100644
--- a/src/cpu/o3/alpha_params.hh
+++ b/src/cpu/o3/alpha_params.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,20 +24,22 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_ALPHA_SIMPLE_PARAMS_HH__
-#define __CPU_O3_CPU_ALPHA_SIMPLE_PARAMS_HH__
+#ifndef __CPU_O3_ALPHA_PARAMS_HH__
+#define __CPU_O3_ALPHA_PARAMS_HH__
#include "cpu/o3/cpu.hh"
//Forward declarations
-class System;
-class AlphaITB;
class AlphaDTB;
-class FunctionalMemory;
+class AlphaITB;
+class FUPool;
+class MemObject;
class Process;
-class MemInterface;
+class System;
/**
* This file defines the parameters that will be used for the AlphaFullCPU.
@@ -56,13 +58,19 @@ class AlphaSimpleParams : public BaseFullCPU::Params
Process *process;
#endif // FULL_SYSTEM
- FunctionalMemory *mem;
+ MemObject *mem;
+
+ BaseCPU *checker;
+
+ unsigned activity;
//
// Caches
//
- MemInterface *icacheInterface;
- MemInterface *dcacheInterface;
+// MemInterface *icacheInterface;
+// MemInterface *dcacheInterface;
+
+ unsigned cachePorts;
//
// Fetch
@@ -102,6 +110,7 @@ class AlphaSimpleParams : public BaseFullCPU::Params
unsigned executeFloatWidth;
unsigned executeBranchWidth;
unsigned executeMemoryWidth;
+ FUPool *fuPool;
//
// Commit
@@ -110,24 +119,22 @@ class AlphaSimpleParams : public BaseFullCPU::Params
unsigned renameToROBDelay;
unsigned commitWidth;
unsigned squashWidth;
+ Tick trapLatency;
+ Tick fetchTrapLatency;
//
- // Branch predictor (BP & BTB)
+ // Branch predictor (BP, BTB, RAS)
//
-/*
+ std::string predType;
unsigned localPredictorSize;
- unsigned localPredictorCtrBits;
-*/
-
- unsigned local_predictor_size;
- unsigned local_ctr_bits;
- unsigned local_history_table_size;
- unsigned local_history_bits;
- unsigned global_predictor_size;
- unsigned global_ctr_bits;
- unsigned global_history_bits;
- unsigned choice_predictor_size;
- unsigned choice_ctr_bits;
+ unsigned localCtrBits;
+ unsigned localHistoryTableSize;
+ unsigned localHistoryBits;
+ unsigned globalPredictorSize;
+ unsigned globalCtrBits;
+ unsigned globalHistoryBits;
+ unsigned choicePredictorSize;
+ unsigned choiceCtrBits;
unsigned BTBEntries;
unsigned BTBTagSize;
@@ -154,10 +161,24 @@ class AlphaSimpleParams : public BaseFullCPU::Params
unsigned numIQEntries;
unsigned numROBEntries;
+ //SMT Parameters
+ unsigned smtNumFetchingThreads;
+
+ std::string smtFetchPolicy;
+
+ std::string smtIQPolicy;
+ unsigned smtIQThreshold;
+
+ std::string smtLSQPolicy;
+ unsigned smtLSQThreshold;
+
+ std::string smtCommitPolicy;
+
+ std::string smtROBPolicy;
+ unsigned smtROBThreshold;
+
// Probably can get this from somewhere.
unsigned instShiftAmt;
-
- bool defReg;
};
-#endif // __CPU_O3_CPU_ALPHA_PARAMS_HH__
+#endif // __CPU_O3_ALPHA_PARAMS_HH__
diff --git a/src/cpu/o3/bpred_unit.cc b/src/cpu/o3/bpred_unit.cc
index 85bd6f0a6..b33543bdc 100644
--- a/src/cpu/o3/bpred_unit.cc
+++ b/src/cpu/o3/bpred_unit.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,10 +24,16 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/bpred_unit_impl.hh"
#include "cpu/o3/alpha_impl.hh"
#include "cpu/o3/alpha_dyn_inst.hh"
+#include "cpu/ozone/ozone_impl.hh"
+//#include "cpu/ozone/simple_impl.hh"
-template class TwobitBPredUnit<AlphaSimpleImpl>;
+template class BPredUnit<AlphaSimpleImpl>;
+template class BPredUnit<OzoneImpl>;
+//template class BPredUnit<SimpleImpl>;
diff --git a/src/cpu/o3/bpred_unit.hh b/src/cpu/o3/bpred_unit.hh
index 2725684f7..2c0a39565 100644
--- a/src/cpu/o3/bpred_unit.hh
+++ b/src/cpu/o3/bpred_unit.hh
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __BPRED_UNIT_HH__
-#define __BPRED_UNIT_HH__
+#ifndef __CPU_O3_BPRED_UNIT_HH__
+#define __CPU_O3_BPRED_UNIT_HH__
// For Addr type.
#include "arch/isa_traits.hh"
@@ -35,99 +37,220 @@
#include "cpu/inst_seq.hh"
#include "cpu/o3/2bit_local_pred.hh"
-#include "cpu/o3/tournament_pred.hh"
#include "cpu/o3/btb.hh"
#include "cpu/o3/ras.hh"
+#include "cpu/o3/tournament_pred.hh"
#include <list>
/**
* Basically a wrapper class to hold both the branch predictor
- * and the BTB. Right now I'm unsure of the implementation; it would
- * be nicer to have something closer to the CPUPolicy or the Impl where
- * this is just typedefs, but it forces the upper level stages to be
- * aware of the constructors of the BP and the BTB. The nicer thing
- * to do is have this templated on the Impl, accept the usual Params
- * object, and be able to call the constructors on the BP and BTB.
+ * and the BTB.
*/
template<class Impl>
-class TwobitBPredUnit
+class BPredUnit
{
- public:
+ private:
typedef typename Impl::Params Params;
typedef typename Impl::DynInstPtr DynInstPtr;
- TwobitBPredUnit(Params &params);
+ enum PredType {
+ Local,
+ Tournament
+ };
+
+ PredType predictor;
+ public:
+
+ /**
+ * @param params The params object, that has the size of the BP and BTB.
+ */
+ BPredUnit(Params *params);
+
+ /**
+ * Registers statistics.
+ */
void regStats();
- bool predict(DynInstPtr &inst, Addr &PC);
+ void switchOut();
+
+ void takeOverFrom();
+
+ /**
+ * Predicts whether or not the instruction is a taken branch, and the
+ * target of the branch if it is taken.
+ * @param inst The branch instruction.
+ * @param PC The predicted PC is passed back through this parameter.
+ * @param tid The thread id.
+ * @return Returns if the branch is taken or not.
+ */
+ bool predict(DynInstPtr &inst, Addr &PC, unsigned tid);
+
+ // @todo: Rename this function.
+ void BPUncond(void * &bp_history);
- void update(const InstSeqNum &done_sn);
+ /**
+ * Tells the branch predictor to commit any updates until the given
+ * sequence number.
+ * @param done_sn The sequence number to commit any older updates up until.
+ * @param tid The thread id.
+ */
+ void update(const InstSeqNum &done_sn, unsigned tid);
- void squash(const InstSeqNum &squashed_sn);
+ /**
+ * Squashes all outstanding updates until a given sequence number.
+ * @param squashed_sn The sequence number to squash any younger updates up
+ * until.
+ * @param tid The thread id.
+ */
+ void squash(const InstSeqNum &squashed_sn, unsigned tid);
+ /**
+ * Squashes all outstanding updates until a given sequence number, and
+ * corrects that sn's update with the proper address and taken/not taken.
+ * @param squashed_sn The sequence number to squash any younger updates up
+ * until.
+ * @param corr_target The correct branch target.
+ * @param actually_taken The correct branch direction.
+ * @param tid The thread id.
+ */
void squash(const InstSeqNum &squashed_sn, const Addr &corr_target,
- bool actually_taken);
+ bool actually_taken, unsigned tid);
- bool BPLookup(Addr &inst_PC)
- { return BP.lookup(inst_PC); }
+ /**
+ * @param bp_history Pointer to the history object. The predictor
+ * will need to update any state and delete the object.
+ */
+ void BPSquash(void *bp_history);
+ /**
+ * Looks up a given PC in the BP to see if it is taken or not taken.
+ * @param inst_PC The PC to look up.
+ * @param bp_history Pointer that will be set to an object that
+ * has the branch predictor state associated with the lookup.
+ * @return Whether the branch is taken or not taken.
+ */
+ bool BPLookup(Addr &inst_PC, void * &bp_history);
+
+ /**
+ * Looks up a given PC in the BTB to see if a matching entry exists.
+ * @param inst_PC The PC to look up.
+ * @return Whether the BTB contains the given PC.
+ */
bool BTBValid(Addr &inst_PC)
- { return BTB.valid(inst_PC); }
+ { return BTB.valid(inst_PC, 0); }
+ /**
+ * Looks up a given PC in the BTB to get the predicted target.
+ * @param inst_PC The PC to look up.
+ * @return The address of the target of the branch.
+ */
Addr BTBLookup(Addr &inst_PC)
- { return BTB.lookup(inst_PC); }
+ { return BTB.lookup(inst_PC, 0); }
- // Will want to include global history.
- void BPUpdate(Addr &inst_PC, bool taken)
- { BP.update(inst_PC, taken); }
+ /**
+ * Updates the BP with taken/not taken information.
+ * @param inst_PC The branch's PC that will be updated.
+ * @param taken Whether the branch was taken or not taken.
+ * @param bp_history Pointer to the branch predictor state that is
+ * associated with the branch lookup that is being updated.
+ * @todo Make this update flexible enough to handle a global predictor.
+ */
+ void BPUpdate(Addr &inst_PC, bool taken, void *bp_history);
+ /**
+ * Updates the BTB with the target of a branch.
+ * @param inst_PC The branch's PC that will be updated.
+ * @param target_PC The branch's target that will be added to the BTB.
+ */
void BTBUpdate(Addr &inst_PC, Addr &target_PC)
- { BTB.update(inst_PC, target_PC); }
+ { BTB.update(inst_PC, target_PC,0); }
+
+ void dump();
private:
struct PredictorHistory {
+ /**
+ * Makes a predictor history struct that contains any
+ * information needed to update the predictor, BTB, and RAS.
+ */
PredictorHistory(const InstSeqNum &seq_num, const Addr &inst_PC,
- const bool pred_taken)
- : seqNum(seq_num), PC(inst_PC), predTaken(pred_taken),
- globalHistory(0), usedRAS(0), wasCall(0), RASIndex(0),
- RASTarget(0)
+ const bool pred_taken, void *bp_history,
+ const unsigned _tid)
+ : seqNum(seq_num), PC(inst_PC), RASTarget(0),
+ RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0),
+ wasCall(0), bpHistory(bp_history)
{ }
+ /** The sequence number for the predictor history entry. */
InstSeqNum seqNum;
+ /** The PC associated with the sequence number. */
Addr PC;
- bool predTaken;
+ /** The RAS target (only valid if a return). */
+ Addr RASTarget;
- unsigned globalHistory;
+ /** The RAS index of the instruction (only valid if a call). */
+ unsigned RASIndex;
+
+ /** The thread id. */
+ unsigned tid;
+ /** Whether or not it was predicted taken. */
+ bool predTaken;
+
+ /** Whether or not the RAS was used. */
bool usedRAS;
+ /** Whether or not the instruction was a call. */
bool wasCall;
- unsigned RASIndex;
-
- Addr RASTarget;
+ /** Pointer to the history object passed back from the branch
+ * predictor. It is used to update or restore state of the
+ * branch predictor.
+ */
+ void *bpHistory;
};
- std::list<PredictorHistory> predHist;
+ typedef std::list<PredictorHistory> History;
+
+ /**
+ * The per-thread predictor history. This is used to update the predictor
+ * as instructions are committed, or restore it to the proper state after
+ * a squash.
+ */
+ History predHist[Impl::MaxThreads];
+
+ /** The local branch predictor. */
+ LocalBP *localBP;
- DefaultBP BP;
+ /** The tournament branch predictor. */
+ TournamentBP *tournamentBP;
+ /** The BTB. */
DefaultBTB BTB;
- ReturnAddrStack RAS;
+ /** The per-thread return address stack. */
+ ReturnAddrStack RAS[Impl::MaxThreads];
+ /** Stat for number of BP lookups. */
Stats::Scalar<> lookups;
+ /** Stat for number of conditional branches predicted. */
Stats::Scalar<> condPredicted;
+ /** Stat for number of conditional branches predicted incorrectly. */
Stats::Scalar<> condIncorrect;
+ /** Stat for number of BTB lookups. */
Stats::Scalar<> BTBLookups;
+ /** Stat for number of BTB hits. */
Stats::Scalar<> BTBHits;
+ /** Stat for number of times the BTB is correct. */
Stats::Scalar<> BTBCorrect;
+ /** Stat for number of times the RAS is used to get a target. */
Stats::Scalar<> usedRAS;
+ /** Stat for number of times the RAS is incorrect. */
Stats::Scalar<> RASIncorrect;
};
-#endif // __BPRED_UNIT_HH__
+#endif // __CPU_O3_BPRED_UNIT_HH__
diff --git a/src/cpu/o3/bpred_unit_impl.hh b/src/cpu/o3/bpred_unit_impl.hh
index 8d16a0cdf..0da02145b 100644
--- a/src/cpu/o3/bpred_unit_impl.hh
+++ b/src/cpu/o3/bpred_unit_impl.hh
@@ -24,27 +24,54 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
+#include <list>
+#include <vector>
+
#include "base/trace.hh"
#include "base/traceflags.hh"
#include "cpu/o3/bpred_unit.hh"
+using namespace std;
+
template<class Impl>
-TwobitBPredUnit<Impl>::TwobitBPredUnit(Params &params)
- : BP(params.local_predictor_size,
- params.local_ctr_bits,
- params.instShiftAmt),
- BTB(params.BTBEntries,
- params.BTBTagSize,
- params.instShiftAmt),
- RAS(params.RASSize)
+BPredUnit<Impl>::BPredUnit(Params *params)
+ : BTB(params->BTBEntries,
+ params->BTBTagSize,
+ params->instShiftAmt)
{
+ // Setup the selected predictor.
+ if (params->predType == "local") {
+ localBP = new LocalBP(params->localPredictorSize,
+ params->localCtrBits,
+ params->instShiftAmt);
+ predictor = Local;
+ } else if (params->predType == "tournament") {
+ tournamentBP = new TournamentBP(params->localPredictorSize,
+ params->localCtrBits,
+ params->localHistoryTableSize,
+ params->localHistoryBits,
+ params->globalPredictorSize,
+ params->globalHistoryBits,
+ params->globalCtrBits,
+ params->choicePredictorSize,
+ params->choiceCtrBits,
+ params->instShiftAmt);
+ predictor = Tournament;
+ } else {
+ fatal("Invalid BP selected!");
+ }
+
+ for (int i=0; i < Impl::MaxThreads; i++)
+ RAS[i].init(params->RASSize);
}
template <class Impl>
void
-TwobitBPredUnit<Impl>::regStats()
+BPredUnit<Impl>::regStats()
{
lookups
.name(name() + ".BPredUnit.lookups")
@@ -79,7 +106,7 @@ TwobitBPredUnit<Impl>::regStats()
usedRAS
.name(name() + ".BPredUnit.usedRAS")
- .desc("Number of times the RAS was used.")
+ .desc("Number of times the RAS was used to get a target.")
;
RASIncorrect
@@ -89,12 +116,36 @@ TwobitBPredUnit<Impl>::regStats()
}
template <class Impl>
+void
+BPredUnit<Impl>::switchOut()
+{
+ // Clear any state upon switch out.
+ for (int i = 0; i < Impl::MaxThreads; ++i) {
+ squash(0, i);
+ }
+}
+
+template <class Impl>
+void
+BPredUnit<Impl>::takeOverFrom()
+{
+ // Can reset all predictor state, but it's not necessarily better
+ // than leaving it be.
+/*
+ for (int i = 0; i < Impl::MaxThreads; ++i)
+ RAS[i].reset();
+
+ BP.reset();
+ BTB.reset();
+*/
+}
+
+template <class Impl>
bool
-TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC)
+BPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC, unsigned tid)
{
// See if branch predictor predicts taken.
// If so, get its target addr either from the BTB or the RAS.
- // Once that's done, speculatively update the predictor?
// Save off record of branch stuff so the RAS can be fixed
// up once it's done.
@@ -105,19 +156,25 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC)
++lookups;
+ void *bp_history = NULL;
+
if (inst->isUncondCtrl()) {
- DPRINTF(Fetch, "BranchPred: Unconditional control.\n");
+ DPRINTF(Fetch, "BranchPred: [tid:%i] Unconditional control.\n", tid);
pred_taken = true;
+ // Tell the BP there was an unconditional branch.
+ BPUncond(bp_history);
} else {
++condPredicted;
- pred_taken = BPLookup(PC);
+ pred_taken = BPLookup(PC, bp_history);
- DPRINTF(Fetch, "BranchPred: Branch predictor predicted %i for PC %#x"
- "\n", pred_taken, inst->readPC());
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Branch predictor predicted %i "
+ "for PC %#x\n",
+ tid, pred_taken, inst->readPC());
}
- PredictorHistory predict_record(inst->seqNum, PC, pred_taken);
+ PredictorHistory predict_record(inst->seqNum, PC, pred_taken,
+ bp_history, tid);
// Now lookup in the BTB or RAS.
if (pred_taken) {
@@ -126,45 +183,48 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC)
// If it's a function return call, then look up the address
// in the RAS.
- target = RAS.top();
+ target = RAS[tid].top();
// Record the top entry of the RAS, and its index.
predict_record.usedRAS = true;
- predict_record.RASIndex = RAS.topIdx();
+ predict_record.RASIndex = RAS[tid].topIdx();
predict_record.RASTarget = target;
- RAS.pop();
+ assert(predict_record.RASIndex < 16);
- DPRINTF(Fetch, "BranchPred: Instruction %#x is a return, RAS "
- "predicted target: %#x, RAS index: %i.\n",
- inst->readPC(), target, predict_record.RASIndex);
+ RAS[tid].pop();
+
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x is a return, "
+ "RAS predicted target: %#x, RAS index: %i.\n",
+ tid, inst->readPC(), target, predict_record.RASIndex);
} else {
++BTBLookups;
if (inst->isCall()) {
- RAS.push(PC+sizeof(MachInst));
+ RAS[tid].push(PC + sizeof(MachInst));
// Record that it was a call so that the top RAS entry can
// be popped off if the speculation is incorrect.
predict_record.wasCall = true;
- DPRINTF(Fetch, "BranchPred: Instruction %#x was a call, "
- "adding %#x to the RAS.\n",
- inst->readPC(), PC+sizeof(MachInst));
+ DPRINTF(Fetch, "BranchPred: [tid:%i] Instruction %#x was a call"
+ ", adding %#x to the RAS.\n",
+ tid, inst->readPC(), PC + sizeof(MachInst));
}
- if (BTB.valid(PC)) {
+ if (BTB.valid(PC, tid)) {
++BTBHits;
- //If it's anything else, use the BTB to get the target addr.
- target = BTB.lookup(PC);
+ // If it's not a return, use the BTB to get the target addr.
+ target = BTB.lookup(PC, tid);
- DPRINTF(Fetch, "BranchPred: Instruction %#x predicted target "
- "is %#x.\n", inst->readPC(), target);
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x predicted"
+ " target is %#x.\n",
+ tid, inst->readPC(), target);
} else {
- DPRINTF(Fetch, "BranchPred: BTB doesn't have a valid entry."
- "\n");
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: BTB doesn't have a "
+ "valid entry.\n",tid);
pred_taken = false;
}
@@ -180,97 +240,173 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC)
inst->setPredTarg(PC);
}
- predHist.push_front(predict_record);
+ predHist[tid].push_front(predict_record);
- assert(!predHist.empty());
+ DPRINTF(Fetch, "[tid:%i] predHist.size(): %i\n", tid, predHist[tid].size());
return pred_taken;
}
template <class Impl>
void
-TwobitBPredUnit<Impl>::update(const InstSeqNum &done_sn)
+BPredUnit<Impl>::update(const InstSeqNum &done_sn, unsigned tid)
{
- DPRINTF(Fetch, "BranchPred: Commiting branches until sequence number "
- "%i.\n", done_sn);
-
- while (!predHist.empty() && predHist.back().seqNum <= done_sn) {
- assert(!predHist.empty());
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Commiting branches until sequence"
+ "number %lli.\n", tid, done_sn);
- // Update the branch predictor with the correct results of branches.
- BP.update(predHist.back().PC, predHist.back().predTaken);
+ while (!predHist[tid].empty() &&
+ predHist[tid].back().seqNum <= done_sn) {
+ // Update the branch predictor with the correct results.
+ BPUpdate(predHist[tid].back().PC,
+ predHist[tid].back().predTaken,
+ predHist[tid].back().bpHistory);
- predHist.pop_back();
+ predHist[tid].pop_back();
}
}
template <class Impl>
void
-TwobitBPredUnit<Impl>::squash(const InstSeqNum &squashed_sn)
+BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, unsigned tid)
{
- while (!predHist.empty() && predHist.front().seqNum > squashed_sn) {
- if (predHist.front().usedRAS) {
- DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, "
- "target: %#x.\n",
- predHist.front().RASIndex,
- predHist.front().RASTarget);
-
- RAS.restore(predHist.front().RASIndex,
- predHist.front().RASTarget);
- } else if (predHist.front().wasCall) {
- DPRINTF(Fetch, "BranchPred: Removing speculative entry added "
- "to the RAS.\n");
-
- RAS.pop();
+ History &pred_hist = predHist[tid];
+
+ while (!pred_hist.empty() &&
+ pred_hist.front().seqNum > squashed_sn) {
+ if (pred_hist.front().usedRAS) {
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Restoring top of RAS to: %i,"
+ " target: %#x.\n",
+ tid,
+ pred_hist.front().RASIndex,
+ pred_hist.front().RASTarget);
+
+ RAS[tid].restore(pred_hist.front().RASIndex,
+ pred_hist.front().RASTarget);
+
+ } else if (pred_hist.front().wasCall) {
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Removing speculative entry "
+ "added to the RAS.\n",tid);
+
+ RAS[tid].pop();
}
- predHist.pop_front();
+ // This call should delete the bpHistory.
+ BPSquash(pred_hist.front().bpHistory);
+
+ pred_hist.pop_front();
}
+
}
template <class Impl>
void
-TwobitBPredUnit<Impl>::squash(const InstSeqNum &squashed_sn,
- const Addr &corr_target,
- const bool actually_taken)
+BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn,
+ const Addr &corr_target,
+ const bool actually_taken,
+ unsigned tid)
{
// Now that we know that a branch was mispredicted, we need to undo
// all the branches that have been seen up until this branch and
// fix up everything.
+ History &pred_hist = predHist[tid];
+
++condIncorrect;
- DPRINTF(Fetch, "BranchPred: Squashing from sequence number %i, "
+ DPRINTF(Fetch, "BranchPred: [tid:%i]: Squashing from sequence number %i, "
"setting target to %#x.\n",
- squashed_sn, corr_target);
+ tid, squashed_sn, corr_target);
- while (!predHist.empty() && predHist.front().seqNum > squashed_sn) {
+ squash(squashed_sn, tid);
- if (predHist.front().usedRAS) {
- DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, "
- "target: %#x.\n",
- predHist.front().RASIndex,
- predHist.front().RASTarget);
+ // If there's a squash due to a syscall, there may not be an entry
+ // corresponding to the squash. In that case, don't bother trying to
+ // fix up the entry.
+ if (!pred_hist.empty()) {
+ assert(pred_hist.front().seqNum == squashed_sn);
+ if (pred_hist.front().usedRAS) {
+ ++RASIncorrect;
+ }
- RAS.restore(predHist.front().RASIndex,
- predHist.front().RASTarget);
- } else if (predHist.front().wasCall) {
- DPRINTF(Fetch, "BranchPred: Removing speculative entry added "
- "to the RAS.\n");
+ BPUpdate(pred_hist.front().PC, actually_taken,
+ pred_hist.front().bpHistory);
- RAS.pop();
- }
+ BTB.update(pred_hist.front().PC, corr_target, tid);
+ pred_hist.pop_front();
+ }
+}
+
+template <class Impl>
+void
+BPredUnit<Impl>::BPUncond(void * &bp_history)
+{
+ // Only the tournament predictor cares about unconditional branches.
+ if (predictor == Tournament) {
+ tournamentBP->uncondBr(bp_history);
+ }
+}
- predHist.pop_front();
+template <class Impl>
+void
+BPredUnit<Impl>::BPSquash(void *bp_history)
+{
+ if (predictor == Local) {
+ localBP->squash(bp_history);
+ } else if (predictor == Tournament) {
+ tournamentBP->squash(bp_history);
+ } else {
+ panic("Predictor type is unexpected value!");
}
+}
- predHist.front().predTaken = actually_taken;
+template <class Impl>
+bool
+BPredUnit<Impl>::BPLookup(Addr &inst_PC, void * &bp_history)
+{
+ if (predictor == Local) {
+ return localBP->lookup(inst_PC, bp_history);
+ } else if (predictor == Tournament) {
+ return tournamentBP->lookup(inst_PC, bp_history);
+ } else {
+ panic("Predictor type is unexpected value!");
+ }
+}
- if (predHist.front().usedRAS) {
- ++RASIncorrect;
+template <class Impl>
+void
+BPredUnit<Impl>::BPUpdate(Addr &inst_PC, bool taken, void *bp_history)
+{
+ if (predictor == Local) {
+ localBP->update(inst_PC, taken, bp_history);
+ } else if (predictor == Tournament) {
+ tournamentBP->update(inst_PC, taken, bp_history);
+ } else {
+ panic("Predictor type is unexpected value!");
}
+}
+
+template <class Impl>
+void
+BPredUnit<Impl>::dump()
+{
+ typename History::iterator pred_hist_it;
+
+ for (int i = 0; i < Impl::MaxThreads; ++i) {
+ if (!predHist[i].empty()) {
+ pred_hist_it = predHist[i].begin();
- BP.update(predHist.front().PC, actually_taken);
+ cprintf("predHist[%i].size(): %i\n", i, predHist[i].size());
- BTB.update(predHist.front().PC, corr_target);
+ while (pred_hist_it != predHist[i].end()) {
+ cprintf("[sn:%lli], PC:%#x, tid:%i, predTaken:%i, "
+ "bpHistory:%#x\n",
+ (*pred_hist_it).seqNum, (*pred_hist_it).PC,
+ (*pred_hist_it).tid, (*pred_hist_it).predTaken,
+ (*pred_hist_it).bpHistory);
+ pred_hist_it++;
+ }
+
+ cprintf("\n");
+ }
+ }
}
diff --git a/src/cpu/o3/btb.cc b/src/cpu/o3/btb.cc
index 2d39c3856..01640f4d1 100644
--- a/src/cpu/o3/btb.cc
+++ b/src/cpu/o3/btb.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "base/intmath.hh"
@@ -39,14 +41,15 @@ DefaultBTB::DefaultBTB(unsigned _numEntries,
tagBits(_tagBits),
instShiftAmt(_instShiftAmt)
{
- // @todo Check to make sure num_entries is valid (a power of 2)
-
DPRINTF(Fetch, "BTB: Creating BTB object.\n");
- btb = new BTBEntry[numEntries];
+ if (!isPowerOf2(numEntries)) {
+ fatal("BTB entries is not a power of 2!");
+ }
- for (int i = 0; i < numEntries; ++i)
- {
+ btb.resize(numEntries);
+
+ for (int i = 0; i < numEntries; ++i) {
btb[i].valid = false;
}
@@ -57,6 +60,14 @@ DefaultBTB::DefaultBTB(unsigned _numEntries,
tagShiftAmt = instShiftAmt + floorLog2(numEntries);
}
+void
+DefaultBTB::reset()
+{
+ for (int i = 0; i < numEntries; ++i) {
+ btb[i].valid = false;
+ }
+}
+
inline
unsigned
DefaultBTB::getIndex(const Addr &inst_PC)
@@ -73,7 +84,7 @@ DefaultBTB::getTag(const Addr &inst_PC)
}
bool
-DefaultBTB::valid(const Addr &inst_PC)
+DefaultBTB::valid(const Addr &inst_PC, unsigned tid)
{
unsigned btb_idx = getIndex(inst_PC);
@@ -81,7 +92,9 @@ DefaultBTB::valid(const Addr &inst_PC)
assert(btb_idx < numEntries);
- if (btb[btb_idx].valid && inst_tag == btb[btb_idx].tag) {
+ if (btb[btb_idx].valid
+ && inst_tag == btb[btb_idx].tag
+ && btb[btb_idx].tid == tid) {
return true;
} else {
return false;
@@ -92,7 +105,7 @@ DefaultBTB::valid(const Addr &inst_PC)
// address is valid, and also the address. For now will just use addr = 0 to
// represent invalid entry.
Addr
-DefaultBTB::lookup(const Addr &inst_PC)
+DefaultBTB::lookup(const Addr &inst_PC, unsigned tid)
{
unsigned btb_idx = getIndex(inst_PC);
@@ -100,7 +113,9 @@ DefaultBTB::lookup(const Addr &inst_PC)
assert(btb_idx < numEntries);
- if (btb[btb_idx].valid && inst_tag == btb[btb_idx].tag) {
+ if (btb[btb_idx].valid
+ && inst_tag == btb[btb_idx].tag
+ && btb[btb_idx].tid == tid) {
return btb[btb_idx].target;
} else {
return 0;
@@ -108,12 +123,13 @@ DefaultBTB::lookup(const Addr &inst_PC)
}
void
-DefaultBTB::update(const Addr &inst_PC, const Addr &target)
+DefaultBTB::update(const Addr &inst_PC, const Addr &target, unsigned tid)
{
unsigned btb_idx = getIndex(inst_PC);
assert(btb_idx < numEntries);
+ btb[btb_idx].tid = tid;
btb[btb_idx].valid = true;
btb[btb_idx].target = target;
btb[btb_idx].tag = getTag(inst_PC);
diff --git a/src/cpu/o3/btb.hh b/src/cpu/o3/btb.hh
index 77bdc32ea..dfa3b7b06 100644
--- a/src/cpu/o3/btb.hh
+++ b/src/cpu/o3/btb.hh
@@ -24,13 +24,16 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_BTB_HH__
-#define __CPU_O3_CPU_BTB_HH__
+#ifndef __CPU_O3_BTB_HH__
+#define __CPU_O3_BTB_HH__
// For Addr type.
#include "arch/isa_traits.hh"
+#include "base/misc.hh"
class DefaultBTB
{
@@ -42,39 +45,86 @@ class DefaultBTB
{
}
+ /** The entry's tag. */
Addr tag;
+
+ /** The entry's target. */
Addr target;
+
+ /** The entry's thread id. */
+ unsigned tid;
+
+ /** Whether or not the entry is valid. */
bool valid;
};
public:
+ /** Creates a BTB with the given number of entries, number of bits per
+ * tag, and instruction offset amount.
+ * @param numEntries Number of entries for the BTB.
+ * @param tagBits Number of bits for each tag in the BTB.
+ * @param instShiftAmt Offset amount for instructions to ignore alignment.
+ */
DefaultBTB(unsigned numEntries, unsigned tagBits,
unsigned instShiftAmt);
- Addr lookup(const Addr &inst_PC);
-
- bool valid(const Addr &inst_PC);
-
- void update(const Addr &inst_PC, const Addr &target_PC);
+ void reset();
+
+ /** Looks up an address in the BTB. Must call valid() first on the address.
+ * @param inst_PC The address of the branch to look up.
+ * @param tid The thread id.
+ * @return Returns the target of the branch.
+ */
+ Addr lookup(const Addr &inst_PC, unsigned tid);
+
+ /** Checks if a branch is in the BTB.
+ * @param inst_PC The address of the branch to look up.
+ * @param tid The thread id.
+ * @return Whether or not the branch exists in the BTB.
+ */
+ bool valid(const Addr &inst_PC, unsigned tid);
+
+ /** Updates the BTB with the target of a branch.
+ * @param inst_PC The address of the branch being updated.
+ * @param target_PC The target address of the branch.
+ * @param tid The thread id.
+ */
+ void update(const Addr &inst_PC, const Addr &target_PC,
+ unsigned tid);
private:
+ /** Returns the index into the BTB, based on the branch's PC.
+ * @param inst_PC The branch to look up.
+ * @return Returns the index into the BTB.
+ */
inline unsigned getIndex(const Addr &inst_PC);
+ /** Returns the tag bits of a given address.
+ * @param inst_PC The branch's address.
+ * @return Returns the tag bits.
+ */
inline Addr getTag(const Addr &inst_PC);
- BTBEntry *btb;
+ /** The actual BTB. */
+ std::vector<BTBEntry> btb;
+ /** The number of entries in the BTB. */
unsigned numEntries;
+ /** The index mask. */
unsigned idxMask;
+ /** The number of tag bits per entry. */
unsigned tagBits;
+ /** The tag mask. */
unsigned tagMask;
+ /** Number of bits to shift PC when calculating index. */
unsigned instShiftAmt;
+ /** Number of bits to shift PC when calculating tag. */
unsigned tagShiftAmt;
};
-#endif // __CPU_O3_CPU_BTB_HH__
+#endif // __CPU_O3_BTB_HH__
diff --git a/src/cpu/o3/comm.hh b/src/cpu/o3/comm.hh
index c74c77ddf..bf1bd08e8 100644
--- a/src/cpu/o3/comm.hh
+++ b/src/cpu/o3/comm.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,32 +24,41 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_COMM_HH__
-#define __CPU_O3_CPU_COMM_HH__
+#ifndef __CPU_O3_COMM_HH__
+#define __CPU_O3_COMM_HH__
#include <vector>
+#include "arch/faults.hh"
#include "arch/isa_traits.hh"
#include "cpu/inst_seq.hh"
#include "sim/host.hh"
-// Find better place to put this typedef.
-// The impl might be the best place for this.
+// Typedef for physical register index type. Although the Impl would be the
+// most likely location for this, there are a few classes that need this
+// typedef yet are not templated on the Impl. For now it will be defined here.
typedef short int PhysRegIndex;
+/** Struct that defines the information passed from fetch to decode. */
template<class Impl>
-struct SimpleFetchSimpleDecode {
+struct DefaultFetchDefaultDecode {
typedef typename Impl::DynInstPtr DynInstPtr;
int size;
DynInstPtr insts[Impl::MaxWidth];
+ Fault fetchFault;
+ InstSeqNum fetchFaultSN;
+ bool clearFetchFault;
};
+/** Struct that defines the information passed from decode to rename. */
template<class Impl>
-struct SimpleDecodeSimpleRename {
+struct DefaultDecodeDefaultRename {
typedef typename Impl::DynInstPtr DynInstPtr;
int size;
@@ -57,8 +66,9 @@ struct SimpleDecodeSimpleRename {
DynInstPtr insts[Impl::MaxWidth];
};
+/** Struct that defines the information passed from rename to IEW. */
template<class Impl>
-struct SimpleRenameSimpleIEW {
+struct DefaultRenameDefaultIEW {
typedef typename Impl::DynInstPtr DynInstPtr;
int size;
@@ -66,20 +76,23 @@ struct SimpleRenameSimpleIEW {
DynInstPtr insts[Impl::MaxWidth];
};
+/** Struct that defines the information passed from IEW to commit. */
template<class Impl>
-struct SimpleIEWSimpleCommit {
+struct DefaultIEWDefaultCommit {
typedef typename Impl::DynInstPtr DynInstPtr;
int size;
DynInstPtr insts[Impl::MaxWidth];
- bool squash;
- bool branchMispredict;
- bool branchTaken;
- uint64_t mispredPC;
- uint64_t nextPC;
- InstSeqNum squashedSeqNum;
+ bool squash[Impl::MaxThreads];
+ bool branchMispredict[Impl::MaxThreads];
+ bool branchTaken[Impl::MaxThreads];
+ uint64_t mispredPC[Impl::MaxThreads];
+ uint64_t nextPC[Impl::MaxThreads];
+ InstSeqNum squashedSeqNum[Impl::MaxThreads];
+
+ bool includeSquashInst[Impl::MaxThreads];
};
template<class Impl>
@@ -91,73 +104,95 @@ struct IssueStruct {
DynInstPtr insts[Impl::MaxWidth];
};
+/** Struct that defines all backwards communication. */
+template<class Impl>
struct TimeBufStruct {
struct decodeComm {
bool squash;
- bool stall;
bool predIncorrect;
uint64_t branchAddr;
InstSeqNum doneSeqNum;
- // Might want to package this kind of branch stuff into a single
+ // @todo: Might want to package this kind of branch stuff into a single
// struct as it is used pretty frequently.
bool branchMispredict;
bool branchTaken;
uint64_t mispredPC;
uint64_t nextPC;
+
+ unsigned branchCount;
};
- decodeComm decodeInfo;
+ decodeComm decodeInfo[Impl::MaxThreads];
- // Rename can't actually tell anything to squash or send a new PC back
- // because it doesn't do anything along those lines. But maybe leave
- // these fields in here to keep the stages mostly orthagonal.
struct renameComm {
- bool squash;
- bool stall;
-
- uint64_t nextPC;
};
- renameComm renameInfo;
+ renameComm renameInfo[Impl::MaxThreads];
struct iewComm {
- bool stall;
-
// Also eventually include skid buffer space.
+ bool usedIQ;
unsigned freeIQEntries;
+ bool usedLSQ;
+ unsigned freeLSQEntries;
+
+ unsigned iqCount;
+ unsigned ldstqCount;
+
+ unsigned dispatched;
+ unsigned dispatchedToLSQ;
};
- iewComm iewInfo;
+ iewComm iewInfo[Impl::MaxThreads];
struct commitComm {
- bool squash;
- bool stall;
+ bool usedROB;
unsigned freeROBEntries;
+ bool emptyROB;
+
+ bool squash;
+ bool robSquashing;
bool branchMispredict;
bool branchTaken;
uint64_t mispredPC;
uint64_t nextPC;
- bool robSquashing;
-
// Represents the instruction that has either been retired or
// squashed. Similar to having a single bus that broadcasts the
// retired or squashed sequence number.
InstSeqNum doneSeqNum;
- // Extra bit of information so that the LDSTQ only updates when it
- // needs to.
- bool commitIsLoad;
+ //Just in case we want to do a commit/squash on a cycle
+ //(necessary for multiple ROBs?)
+ bool commitInsts;
+ InstSeqNum squashSeqNum;
// Communication specifically to the IQ to tell the IQ that it can
// schedule a non-speculative instruction.
InstSeqNum nonSpecSeqNum;
+
+ // Hack for now to send back an uncached access to the IEW stage.
+ typedef typename Impl::DynInstPtr DynInstPtr;
+ bool uncached;
+ DynInstPtr uncachedLoad;
+
+ bool interruptPending;
+ bool clearInterrupt;
};
- commitComm commitInfo;
+ commitComm commitInfo[Impl::MaxThreads];
+
+ bool decodeBlock[Impl::MaxThreads];
+ bool decodeUnblock[Impl::MaxThreads];
+ bool renameBlock[Impl::MaxThreads];
+ bool renameUnblock[Impl::MaxThreads];
+ bool iewBlock[Impl::MaxThreads];
+ bool iewUnblock[Impl::MaxThreads];
+ bool commitBlock[Impl::MaxThreads];
+ bool commitUnblock[Impl::MaxThreads];
};
-#endif //__CPU_O3_CPU_COMM_HH__
+#endif //__CPU_O3_COMM_HH__
diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc
index cf33d7f8b..770008a33 100644
--- a/src/cpu/o3/commit.cc
+++ b/src/cpu/o3/commit.cc
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
#include "cpu/o3/alpha_impl.hh"
#include "cpu/o3/commit_impl.hh"
-template class SimpleCommit<AlphaSimpleImpl>;
+template class DefaultCommit<AlphaSimpleImpl>;
diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh
index 580c1a316..b7404c488 100644
--- a/src/cpu/o3/commit.hh
+++ b/src/cpu/o3/commit.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,31 +24,46 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Todo: Maybe have a special method for handling interrupts/traps.
-//
-// Traps: Have IEW send a signal to commit saying that there's a trap to
-// be handled. Have commit send the PC back to the fetch stage, along
-// with the current commit PC. Fetch will directly access the IPR and save
-// off all the proper stuff. Commit can send out a squash, or something
-// close to it.
-// Do the same for hwrei(). However, requires that commit be specifically
-// built to support that kind of stuff. Probably not horrible to have
-// commit support having the CPU tell it to squash the other stages and
-// restart at a given address. The IPR register does become an issue.
-// Probably not a big deal if the IPR stuff isn't cycle accurate. Can just
-// have the original function handle writing to the IPR register.
-
-#ifndef __CPU_O3_CPU_SIMPLE_COMMIT_HH__
-#define __CPU_O3_CPU_SIMPLE_COMMIT_HH__
+#ifndef __CPU_O3_COMMIT_HH__
+#define __CPU_O3_COMMIT_HH__
+#include "arch/faults.hh"
#include "base/statistics.hh"
#include "base/timebuf.hh"
-#include "mem/memory_interface.hh"
-
+#include "cpu/exetrace.hh"
+#include "cpu/inst_seq.hh"
+
+template <class>
+class O3ThreadState;
+
+/**
+ * DefaultCommit handles single threaded and SMT commit. Its width is
+ * specified by the parameters; each cycle it tries to commit that
+ * many instructions. The SMT policy decides which thread it tries to
+ * commit instructions from. Non- speculative instructions must reach
+ * the head of the ROB before they are ready to execute; once they
+ * reach the head, commit will broadcast the instruction's sequence
+ * number to the previous stages so that they can issue/ execute the
+ * instruction. Only one non-speculative instruction is handled per
+ * cycle. Commit is responsible for handling all back-end initiated
+ * redirects. It receives the redirect, and then broadcasts it to all
+ * stages, indicating the sequence number they should squash until,
+ * and any necessary branch misprediction information as well. It
+ * priortizes redirects by instruction's age, only broadcasting a
+ * redirect if it corresponds to an instruction that should currently
+ * be in the ROB. This is done by tracking the sequence number of the
+ * youngest instruction in the ROB, which gets updated to any
+ * squashing instruction's sequence number, and only broadcasting a
+ * redirect if it corresponds to an older instruction. Commit also
+ * supports multiple cycle squashing, to model a ROB that can only
+ * remove a certain number of instructions per cycle.
+ */
template<class Impl>
-class SimpleCommit
+class DefaultCommit
{
public:
// Typedefs from the Impl.
@@ -57,62 +72,219 @@ class SimpleCommit
typedef typename Impl::Params Params;
typedef typename Impl::CPUPol CPUPol;
+ typedef typename CPUPol::RenameMap RenameMap;
typedef typename CPUPol::ROB ROB;
typedef typename CPUPol::TimeStruct TimeStruct;
+ typedef typename CPUPol::FetchStruct FetchStruct;
typedef typename CPUPol::IEWStruct IEWStruct;
typedef typename CPUPol::RenameStruct RenameStruct;
- public:
- // I don't believe commit can block, so it will only have two
- // statuses for now.
- // Actually if there's a cache access that needs to block (ie
- // uncachable load or just a mem access in commit) then the stage
- // may have to wait.
- enum Status {
+ typedef typename CPUPol::Fetch Fetch;
+ typedef typename CPUPol::IEW IEW;
+
+ typedef O3ThreadState<Impl> Thread;
+
+ /** Event class used to schedule a squash due to a trap (fault or
+ * interrupt) to happen on a specific cycle.
+ */
+ class TrapEvent : public Event {
+ private:
+ DefaultCommit<Impl> *commit;
+ unsigned tid;
+
+ public:
+ TrapEvent(DefaultCommit<Impl> *_commit, unsigned _tid);
+
+ void process();
+ const char *description();
+ };
+
+ /** Overall commit status. Used to determine if the CPU can deschedule
+ * itself due to a lack of activity.
+ */
+ enum CommitStatus{
+ Active,
+ Inactive
+ };
+
+ /** Individual thread status. */
+ enum ThreadStatus {
Running,
Idle,
ROBSquashing,
- DcacheMissStall,
- DcacheMissComplete
+ TrapPending,
+ FetchTrapPending
+ };
+
+ /** Commit policy for SMT mode. */
+ enum CommitPolicy {
+ Aggressive,
+ RoundRobin,
+ OldestReady
};
private:
- Status _status;
+ /** Overall commit status. */
+ CommitStatus _status;
+ /** Next commit status, to be set at the end of the cycle. */
+ CommitStatus _nextStatus;
+ /** Per-thread status. */
+ ThreadStatus commitStatus[Impl::MaxThreads];
+ /** Commit policy used in SMT mode. */
+ CommitPolicy commitPolicy;
public:
- SimpleCommit(Params &params);
+ /** Construct a DefaultCommit with the given parameters. */
+ DefaultCommit(Params *params);
+
+ /** Returns the name of the DefaultCommit. */
+ std::string name() const;
+ /** Registers statistics. */
void regStats();
+ /** Sets the CPU pointer. */
void setCPU(FullCPU *cpu_ptr);
+ /** Sets the list of threads. */
+ void setThreads(std::vector<Thread *> &threads);
+
+ /** Sets the main time buffer pointer, used for backwards communication. */
void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);
+ void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr);
+
+ /** Sets the pointer to the queue coming from rename. */
void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr);
+ /** Sets the pointer to the queue coming from IEW. */
void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr);
+ void setFetchStage(Fetch *fetch_stage);
+
+ Fetch *fetchStage;
+
+ /** Sets the pointer to the IEW stage. */
+ void setIEWStage(IEW *iew_stage);
+
+ /** The pointer to the IEW stage. Used solely to ensure that
+ * various events (traps, interrupts, syscalls) do not occur until
+ * all stores have written back.
+ */
+ IEW *iewStage;
+
+ /** Sets pointer to list of active threads. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
+
+ /** Sets pointer to the commited state rename map. */
+ void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]);
+
+ /** Sets pointer to the ROB. */
void setROB(ROB *rob_ptr);
+ /** Initializes stage by sending back the number of free entries. */
+ void initStage();
+
+ /** Initializes the switching out of commit. */
+ void switchOut();
+
+ /** Completes the switch out of commit. */
+ void doSwitchOut();
+
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
+
+ /** Ticks the commit stage, which tries to commit instructions. */
void tick();
+ /** Handles any squashes that are sent from IEW, and adds instructions
+ * to the ROB and tries to commit instructions.
+ */
void commit();
+ /** Returns the number of free ROB entries for a specific thread. */
+ unsigned numROBFreeEntries(unsigned tid);
+
+ /** Generates an event to schedule a squash due to a trap. */
+ void generateTrapEvent(unsigned tid);
+
+ /** Records that commit needs to initiate a squash due to an
+ * external state update through the TC.
+ */
+ void generateTCEvent(unsigned tid);
+
private:
+ /** Updates the overall status of commit with the nextStatus, and
+ * tell the CPU if commit is active/inactive.
+ */
+ void updateStatus();
+
+ /** Sets the next status based on threads' statuses, which becomes the
+ * current status at the end of the cycle.
+ */
+ void setNextStatus();
+
+ /** Checks if the ROB is completed with squashing. This is for the case
+ * where the ROB can take multiple cycles to complete squashing.
+ */
+ bool robDoneSquashing();
+
+ /** Returns if any of the threads have the number of ROB entries changed
+ * on this cycle. Used to determine if the number of free ROB entries needs
+ * to be sent back to previous stages.
+ */
+ bool changedROBEntries();
+
+ /** Squashes all in flight instructions. */
+ void squashAll(unsigned tid);
+
+ /** Handles squashing due to a trap. */
+ void squashFromTrap(unsigned tid);
+
+ /** Handles squashing due to an TC write. */
+ void squashFromTC(unsigned tid);
+ /** Commits as many instructions as possible. */
void commitInsts();
+ /** Tries to commit the head ROB instruction passed in.
+ * @param head_inst The instruction to be committed.
+ */
bool commitHead(DynInstPtr &head_inst, unsigned inst_num);
+ /** Gets instructions from rename and inserts them into the ROB. */
void getInsts();
+ /** Marks completed instructions using information sent from IEW. */
void markCompletedInsts();
+ /** Gets the thread to commit, based on the SMT policy. */
+ int getCommittingThread();
+
+ /** Returns the thread ID to use based on a round robin policy. */
+ int roundRobin();
+
+ /** Returns the thread ID to use based on an oldest instruction policy. */
+ int oldestReady();
+
public:
- uint64_t readCommitPC();
+ /** Returns the PC of the head instruction of the ROB.
+ * @todo: Probably remove this function as it returns only thread 0.
+ */
+ uint64_t readPC() { return PC[0]; }
+
+ /** Returns the PC of a specific thread. */
+ uint64_t readPC(unsigned tid) { return PC[tid]; }
- void setSquashing() { _status = ROBSquashing; }
+ /** Sets the PC of a specific thread. */
+ void setPC(uint64_t val, unsigned tid) { PC[tid] = val; }
+
+ /** Reads the PC of a specific thread. */
+ uint64_t readNextPC(unsigned tid) { return nextPC[tid]; }
+
+ /** Sets the next PC of a specific thread. */
+ void setNextPC(uint64_t val, unsigned tid) { nextPC[tid] = val; }
private:
/** Time buffer interface. */
@@ -124,6 +296,10 @@ class SimpleCommit
/** Wire to read information from IEW (for ROB). */
typename TimeBuffer<TimeStruct>::wire robInfoFromIEW;
+ TimeBuffer<FetchStruct> *fetchQueue;
+
+ typename TimeBuffer<FetchStruct>::wire fromFetch;
+
/** IEW instruction queue interface. */
TimeBuffer<IEWStruct> *iewQueue;
@@ -136,22 +312,54 @@ class SimpleCommit
/** Wire to read information from rename queue. */
typename TimeBuffer<RenameStruct>::wire fromRename;
+ public:
/** ROB interface. */
ROB *rob;
+ private:
/** Pointer to FullCPU. */
FullCPU *cpu;
- /** Memory interface. Used for d-cache accesses. */
- MemInterface *dcacheInterface;
+ /** Vector of all of the threads. */
+ std::vector<Thread *> thread;
+
+ Fault fetchFault;
+
+ int fetchTrapWait;
+
+ /** Records that commit has written to the time buffer this cycle. Used for
+ * the CPU to determine if it can deschedule itself if there is no activity.
+ */
+ bool wroteToTimeBuffer;
+
+ /** Records if the number of ROB entries has changed this cycle. If it has,
+ * then the number of free entries must be re-broadcast.
+ */
+ bool changedROBNumEntries[Impl::MaxThreads];
+
+ /** A counter of how many threads are currently squashing. */
+ int squashCounter;
+
+ /** Records if a thread has to squash this cycle due to a trap. */
+ bool trapSquash[Impl::MaxThreads];
+
+ /** Records if a thread has to squash this cycle due to an XC write. */
+ bool tcSquash[Impl::MaxThreads];
+
+ /** Priority List used for Commit Policy */
+ std::list<unsigned> priority_list;
- private:
/** IEW to Commit delay, in ticks. */
unsigned iewToCommitDelay;
+ /** Commit to IEW delay, in ticks. */
+ unsigned commitToIEWDelay;
+
/** Rename to ROB delay, in ticks. */
unsigned renameToROBDelay;
+ unsigned fetchToCommitDelay;
+
/** Rename width, in instructions. Used so ROB knows how many
* instructions to get from the rename instruction queue.
*/
@@ -165,16 +373,82 @@ class SimpleCommit
/** Commit width, in instructions. */
unsigned commitWidth;
+ /** Number of Reorder Buffers */
+ unsigned numRobs;
+
+ /** Number of Active Threads */
+ unsigned numThreads;
+
+ /** Is a switch out pending. */
+ bool switchPending;
+
+ /** Is commit switched out. */
+ bool switchedOut;
+
+ /** The latency to handle a trap. Used when scheduling trap
+ * squash event.
+ */
+ Tick trapLatency;
+
+ Tick fetchTrapLatency;
+
+ Tick fetchFaultTick;
+
+ /** The commit PC of each thread. Refers to the instruction that
+ * is currently being processed/committed.
+ */
+ Addr PC[Impl::MaxThreads];
+
+ /** The next PC of each thread. */
+ Addr nextPC[Impl::MaxThreads];
+
+ /** The sequence number of the youngest valid instruction in the ROB. */
+ InstSeqNum youngestSeqNum[Impl::MaxThreads];
+
+ /** Pointer to the list of active threads. */
+ std::list<unsigned> *activeThreads;
+
+ /** Rename map interface. */
+ RenameMap *renameMap[Impl::MaxThreads];
+
+ /** Updates commit stats based on this instruction. */
+ void updateComInstStats(DynInstPtr &inst);
+
+ /** Stat for the total number of committed instructions. */
Stats::Scalar<> commitCommittedInsts;
+ /** Stat for the total number of squashed instructions discarded by commit.
+ */
Stats::Scalar<> commitSquashedInsts;
+ /** Stat for the total number of times commit is told to squash.
+ * @todo: Actually increment this stat.
+ */
Stats::Scalar<> commitSquashEvents;
+ /** Stat for the total number of times commit has had to stall due to a non-
+ * speculative instruction reaching the head of the ROB.
+ */
Stats::Scalar<> commitNonSpecStalls;
- Stats::Scalar<> commitCommittedBranches;
- Stats::Scalar<> commitCommittedLoads;
- Stats::Scalar<> commitCommittedMemRefs;
+ /** Stat for the total number of branch mispredicts that caused a squash. */
Stats::Scalar<> branchMispredicts;
-
- Stats::Distribution<> n_committed_dist;
+ /** Distribution of the number of committed instructions each cycle. */
+ Stats::Distribution<> numCommittedDist;
+
+ /** Total number of instructions committed. */
+ Stats::Vector<> statComInst;
+ /** Total number of software prefetches committed. */
+ Stats::Vector<> statComSwp;
+ /** Stat for the total number of committed memory references. */
+ Stats::Vector<> statComRefs;
+ /** Stat for the total number of committed loads. */
+ Stats::Vector<> statComLoads;
+ /** Total number of committed memory barriers. */
+ Stats::Vector<> statComMembars;
+ /** Total number of committed branches. */
+ Stats::Vector<> statComBranches;
+
+ /** Number of cycles where the commit bandwidth limit is reached. */
+ Stats::Scalar<> commitEligibleSamples;
+ /** Number of instructions not committed due to bandwidth limits. */
+ Stats::Vector<> commitEligible;
};
-#endif // __CPU_O3_CPU_SIMPLE_COMMIT_HH__
+#endif // __CPU_O3_COMMIT_HH__
diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh
index e289bc0c0..ceb2918e0 100644
--- a/src/cpu/o3/commit_impl.hh
+++ b/src/cpu/o3/commit_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,28 +24,117 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
+#include <algorithm>
+#include <string>
+
+#include "base/loader/symtab.hh"
#include "base/timebuf.hh"
-#include "cpu/o3/commit.hh"
+#include "cpu/checker/cpu.hh"
#include "cpu/exetrace.hh"
+#include "cpu/o3/commit.hh"
+#include "cpu/o3/thread_state.hh"
+
+using namespace std;
template <class Impl>
-SimpleCommit<Impl>::SimpleCommit(Params &params)
- : dcacheInterface(params.dcacheInterface),
- iewToCommitDelay(params.iewToCommitDelay),
- renameToROBDelay(params.renameToROBDelay),
- renameWidth(params.renameWidth),
- iewWidth(params.executeWidth),
- commitWidth(params.commitWidth)
+DefaultCommit<Impl>::TrapEvent::TrapEvent(DefaultCommit<Impl> *_commit,
+ unsigned _tid)
+ : Event(&mainEventQueue, CPU_Tick_Pri), commit(_commit), tid(_tid)
{
- _status = Idle;
+ this->setFlags(Event::AutoDelete);
}
template <class Impl>
void
-SimpleCommit<Impl>::regStats()
+DefaultCommit<Impl>::TrapEvent::process()
+{
+ // This will get reset by commit if it was switched out at the
+ // time of this event processing.
+ commit->trapSquash[tid] = true;
+}
+
+template <class Impl>
+const char *
+DefaultCommit<Impl>::TrapEvent::description()
+{
+ return "Trap event";
+}
+
+template <class Impl>
+DefaultCommit<Impl>::DefaultCommit(Params *params)
+ : squashCounter(0),
+ iewToCommitDelay(params->iewToCommitDelay),
+ commitToIEWDelay(params->commitToIEWDelay),
+ renameToROBDelay(params->renameToROBDelay),
+ fetchToCommitDelay(params->commitToFetchDelay),
+ renameWidth(params->renameWidth),
+ iewWidth(params->executeWidth),
+ commitWidth(params->commitWidth),
+ numThreads(params->numberOfThreads),
+ switchPending(false),
+ switchedOut(false),
+ trapLatency(params->trapLatency),
+ fetchTrapLatency(params->fetchTrapLatency)
{
+ _status = Active;
+ _nextStatus = Inactive;
+ string policy = params->smtCommitPolicy;
+
+ //Convert string to lowercase
+ std::transform(policy.begin(), policy.end(), policy.begin(),
+ (int(*)(int)) tolower);
+
+ //Assign commit policy
+ if (policy == "aggressive"){
+ commitPolicy = Aggressive;
+
+ DPRINTF(Commit,"Commit Policy set to Aggressive.");
+ } else if (policy == "roundrobin"){
+ commitPolicy = RoundRobin;
+
+ //Set-Up Priority List
+ for (int tid=0; tid < numThreads; tid++) {
+ priority_list.push_back(tid);
+ }
+
+ DPRINTF(Commit,"Commit Policy set to Round Robin.");
+ } else if (policy == "oldestready"){
+ commitPolicy = OldestReady;
+
+ DPRINTF(Commit,"Commit Policy set to Oldest Ready.");
+ } else {
+ assert(0 && "Invalid SMT Commit Policy. Options Are: {Aggressive,"
+ "RoundRobin,OldestReady}");
+ }
+
+ for (int i=0; i < numThreads; i++) {
+ commitStatus[i] = Idle;
+ changedROBNumEntries[i] = false;
+ trapSquash[i] = false;
+ tcSquash[i] = false;
+ PC[i] = nextPC[i] = 0;
+ }
+
+ fetchFaultTick = 0;
+ fetchTrapWait = 0;
+}
+
+template <class Impl>
+std::string
+DefaultCommit<Impl>::name() const
+{
+ return cpu->name() + ".commit";
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::regStats()
+{
+ using namespace Stats;
commitCommittedInsts
.name(name() + ".commitCommittedInsts")
.desc("The number of committed instructions")
@@ -63,41 +152,110 @@ SimpleCommit<Impl>::regStats()
.desc("The number of times commit has been forced to stall to "
"communicate backwards")
.prereq(commitNonSpecStalls);
- commitCommittedBranches
- .name(name() + ".commitCommittedBranches")
- .desc("The number of committed branches")
- .prereq(commitCommittedBranches);
- commitCommittedLoads
- .name(name() + ".commitCommittedLoads")
- .desc("The number of committed loads")
- .prereq(commitCommittedLoads);
- commitCommittedMemRefs
- .name(name() + ".commitCommittedMemRefs")
- .desc("The number of committed memory references")
- .prereq(commitCommittedMemRefs);
branchMispredicts
.name(name() + ".branchMispredicts")
.desc("The number of times a branch was mispredicted")
.prereq(branchMispredicts);
- n_committed_dist
+ numCommittedDist
.init(0,commitWidth,1)
.name(name() + ".COM:committed_per_cycle")
.desc("Number of insts commited each cycle")
.flags(Stats::pdf)
;
+
+ statComInst
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:count")
+ .desc("Number of instructions committed")
+ .flags(total)
+ ;
+
+ statComSwp
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:swp_count")
+ .desc("Number of s/w prefetches committed")
+ .flags(total)
+ ;
+
+ statComRefs
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:refs")
+ .desc("Number of memory references committed")
+ .flags(total)
+ ;
+
+ statComLoads
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:loads")
+ .desc("Number of loads committed")
+ .flags(total)
+ ;
+
+ statComMembars
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:membars")
+ .desc("Number of memory barriers committed")
+ .flags(total)
+ ;
+
+ statComBranches
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:branches")
+ .desc("Number of branches committed")
+ .flags(total)
+ ;
+
+ //
+ // Commit-Eligible instructions...
+ //
+ // -> The number of instructions eligible to commit in those
+ // cycles where we reached our commit BW limit (less the number
+ // actually committed)
+ //
+ // -> The average value is computed over ALL CYCLES... not just
+ // the BW limited cycles
+ //
+ // -> The standard deviation is computed only over cycles where
+ // we reached the BW limit
+ //
+ commitEligible
+ .init(cpu->number_of_threads)
+ .name(name() + ".COM:bw_limited")
+ .desc("number of insts not committed due to BW limits")
+ .flags(total)
+ ;
+
+ commitEligibleSamples
+ .name(name() + ".COM:bw_lim_events")
+ .desc("number cycles where commit BW limit reached")
+ ;
}
template <class Impl>
void
-SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr)
+DefaultCommit<Impl>::setCPU(FullCPU *cpu_ptr)
{
DPRINTF(Commit, "Commit: Setting CPU pointer.\n");
cpu = cpu_ptr;
+
+ // Commit must broadcast the number of free entries it has at the start of
+ // the simulation, so it starts as active.
+ cpu->activateStage(FullCPU::CommitIdx);
+
+ trapLatency = cpu->cycles(trapLatency);
+ fetchTrapLatency = cpu->cycles(fetchTrapLatency);
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::setThreads(vector<Thread *> &threads)
+{
+ thread = threads;
}
template <class Impl>
void
-SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
+DefaultCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
DPRINTF(Commit, "Commit: Setting time buffer pointer.\n");
timeBuffer = tb_ptr;
@@ -111,7 +269,18 @@ SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
template <class Impl>
void
-SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
+DefaultCommit<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
+{
+ DPRINTF(Commit, "Commit: Setting fetch queue pointer.\n");
+ fetchQueue = fq_ptr;
+
+ // Setup wire to get instructions from rename (for the ROB).
+ fromFetch = fetchQueue->getWire(-fetchToCommitDelay);
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
{
DPRINTF(Commit, "Commit: Setting rename queue pointer.\n");
renameQueue = rq_ptr;
@@ -122,7 +291,7 @@ SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
template <class Impl>
void
-SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
+DefaultCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
{
DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n");
iewQueue = iq_ptr;
@@ -133,7 +302,40 @@ SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
template <class Impl>
void
-SimpleCommit<Impl>::setROB(ROB *rob_ptr)
+DefaultCommit<Impl>::setFetchStage(Fetch *fetch_stage)
+{
+ fetchStage = fetch_stage;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::setIEWStage(IEW *iew_stage)
+{
+ iewStage = iew_stage;
+}
+
+template<class Impl>
+void
+DefaultCommit<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ DPRINTF(Commit, "Commit: Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::setRenameMap(RenameMap rm_ptr[])
+{
+ DPRINTF(Commit, "Setting rename map pointers.\n");
+
+ for (int i=0; i < numThreads; i++) {
+ renameMap[i] = &rm_ptr[i];
+ }
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::setROB(ROB *rob_ptr)
{
DPRINTF(Commit, "Commit: Setting ROB pointer.\n");
rob = rob_ptr;
@@ -141,107 +343,451 @@ SimpleCommit<Impl>::setROB(ROB *rob_ptr)
template <class Impl>
void
-SimpleCommit<Impl>::tick()
-{
- // If the ROB is currently in its squash sequence, then continue
- // to squash. In this case, commit does not do anything. Otherwise
- // run commit.
- if (_status == ROBSquashing) {
- if (rob->isDoneSquashing()) {
- _status = Running;
- } else {
- rob->doSquash();
-
- // Send back sequence number of tail of ROB, so other stages
- // can squash younger instructions. Note that really the only
- // stage that this is important for is the IEW stage; other
- // stages can just clear all their state as long as selective
- // replay isn't used.
- toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum();
- toIEW->commitInfo.robSquashing = true;
+DefaultCommit<Impl>::initStage()
+{
+ rob->setActiveThreads(activeThreads);
+ rob->resetEntries();
+
+ // Broadcast the number of free entries.
+ for (int i=0; i < numThreads; i++) {
+ toIEW->commitInfo[i].usedROB = true;
+ toIEW->commitInfo[i].freeROBEntries = rob->numFreeEntries(i);
+ }
+
+ cpu->activityThisCycle();
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::switchOut()
+{
+ switchPending = true;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::doSwitchOut()
+{
+ switchedOut = true;
+ switchPending = false;
+ rob->switchOut();
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::takeOverFrom()
+{
+ switchedOut = false;
+ _status = Active;
+ _nextStatus = Inactive;
+ for (int i=0; i < numThreads; i++) {
+ commitStatus[i] = Idle;
+ changedROBNumEntries[i] = false;
+ trapSquash[i] = false;
+ tcSquash[i] = false;
+ }
+ squashCounter = 0;
+ rob->takeOverFrom();
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::updateStatus()
+{
+ // reset ROB changed variable
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+ changedROBNumEntries[tid] = false;
+
+ // Also check if any of the threads has a trap pending
+ if (commitStatus[tid] == TrapPending ||
+ commitStatus[tid] == FetchTrapPending) {
+ _nextStatus = Active;
}
- } else {
- commit();
}
+ if (_nextStatus == Inactive && _status == Active) {
+ DPRINTF(Activity, "Deactivating stage.\n");
+ cpu->deactivateStage(FullCPU::CommitIdx);
+ } else if (_nextStatus == Active && _status == Inactive) {
+ DPRINTF(Activity, "Activating stage.\n");
+ cpu->activateStage(FullCPU::CommitIdx);
+ }
+
+ _status = _nextStatus;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::setNextStatus()
+{
+ int squashes = 0;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (commitStatus[tid] == ROBSquashing) {
+ squashes++;
+ }
+ }
+
+ assert(squashes == squashCounter);
+
+ // If commit is currently squashing, then it will have activity for the
+ // next cycle. Set its next status as active.
+ if (squashCounter) {
+ _nextStatus = Active;
+ }
+}
+
+template <class Impl>
+bool
+DefaultCommit<Impl>::changedROBEntries()
+{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (changedROBNumEntries[tid]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <class Impl>
+unsigned
+DefaultCommit<Impl>::numROBFreeEntries(unsigned tid)
+{
+ return rob->numFreeEntries(tid);
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::generateTrapEvent(unsigned tid)
+{
+ DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid);
+
+ TrapEvent *trap = new TrapEvent(this, tid);
+
+ trap->schedule(curTick + trapLatency);
+
+ thread[tid]->trapPending = true;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::generateTCEvent(unsigned tid)
+{
+ DPRINTF(Commit, "Generating TC squash event for [tid:%i]\n", tid);
+
+ tcSquash[tid] = true;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::squashAll(unsigned tid)
+{
+ // If we want to include the squashing instruction in the squash,
+ // then use one older sequence number.
+ // Hopefully this doesn't mess things up. Basically I want to squash
+ // all instructions of this thread.
+ InstSeqNum squashed_inst = rob->isEmpty() ?
+ 0 : rob->readHeadInst(tid)->seqNum - 1;;
+
+ // All younger instructions will be squashed. Set the sequence
+ // number as the youngest instruction in the ROB (0 in this case.
+ // Hopefully nothing breaks.)
+ youngestSeqNum[tid] = 0;
+
+ rob->squash(squashed_inst, tid);
+ changedROBNumEntries[tid] = true;
+
+ // Send back the sequence number of the squashed instruction.
+ toIEW->commitInfo[tid].doneSeqNum = squashed_inst;
+
+ // Send back the squash signal to tell stages that they should
+ // squash.
+ toIEW->commitInfo[tid].squash = true;
+
+ // Send back the rob squashing signal so other stages know that
+ // the ROB is in the process of squashing.
+ toIEW->commitInfo[tid].robSquashing = true;
+
+ toIEW->commitInfo[tid].branchMispredict = false;
+
+ toIEW->commitInfo[tid].nextPC = PC[tid];
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::squashFromTrap(unsigned tid)
+{
+ squashAll(tid);
+
+ DPRINTF(Commit, "Squashing from trap, restarting at PC %#x\n", PC[tid]);
+
+ thread[tid]->trapPending = false;
+ thread[tid]->inSyscall = false;
+
+ trapSquash[tid] = false;
+
+ commitStatus[tid] = ROBSquashing;
+ cpu->activityThisCycle();
+
+ ++squashCounter;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::squashFromTC(unsigned tid)
+{
+ squashAll(tid);
+
+ DPRINTF(Commit, "Squashing from TC, restarting at PC %#x\n", PC[tid]);
+
+ thread[tid]->inSyscall = false;
+ assert(!thread[tid]->trapPending);
+
+ commitStatus[tid] = ROBSquashing;
+ cpu->activityThisCycle();
+
+ tcSquash[tid] = false;
+
+ ++squashCounter;
+}
+
+template <class Impl>
+void
+DefaultCommit<Impl>::tick()
+{
+ wroteToTimeBuffer = false;
+ _nextStatus = Inactive;
+
+ if (switchPending && rob->isEmpty() && !iewStage->hasStoresToWB()) {
+ cpu->signalSwitched();
+ return;
+ }
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ // Check if any of the threads are done squashing. Change the
+ // status if they are done.
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (commitStatus[tid] == ROBSquashing) {
+
+ if (rob->isDoneSquashing(tid)) {
+ commitStatus[tid] = Running;
+ --squashCounter;
+ } else {
+ DPRINTF(Commit,"[tid:%u]: Still Squashing, cannot commit any"
+ "insts this cycle.\n", tid);
+ }
+ }
+ }
+
+ commit();
+
markCompletedInsts();
- // Writeback number of free ROB entries here.
- DPRINTF(Commit, "Commit: ROB has %d free entries.\n",
- rob->numFreeEntries());
- toIEW->commitInfo.freeROBEntries = rob->numFreeEntries();
+ threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (!rob->isEmpty(tid) && rob->readHeadInst(tid)->readyToCommit()) {
+ // The ROB has more instructions it can commit. Its next status
+ // will be active.
+ _nextStatus = Active;
+
+ DynInstPtr inst = rob->readHeadInst(tid);
+
+ DPRINTF(Commit,"[tid:%i]: Instruction [sn:%lli] PC %#x is head of"
+ " ROB and ready to commit\n",
+ tid, inst->seqNum, inst->readPC());
+
+ } else if (!rob->isEmpty(tid)) {
+ DynInstPtr inst = rob->readHeadInst(tid);
+
+ DPRINTF(Commit,"[tid:%i]: Can't commit, Instruction [sn:%lli] PC "
+ "%#x is head of ROB and not ready\n",
+ tid, inst->seqNum, inst->readPC());
+ }
+
+ DPRINTF(Commit, "[tid:%i]: ROB has %d insts & %d free entries.\n",
+ tid, rob->countInsts(tid), rob->numFreeEntries(tid));
+ }
+
+
+ if (wroteToTimeBuffer) {
+ DPRINTF(Activity, "Activity This Cycle.\n");
+ cpu->activityThisCycle();
+ }
+
+ updateStatus();
}
template <class Impl>
void
-SimpleCommit<Impl>::commit()
+DefaultCommit<Impl>::commit()
{
+
//////////////////////////////////////
// Check for interrupts
//////////////////////////////////////
- // Process interrupts if interrupts are enabled and not in PAL mode.
- // Take the PC from commit and write it to the IPR, then squash. The
- // interrupt completing will take care of restoring the PC from that value
- // in the IPR. Look at IPR[EXC_ADDR];
- // hwrei() is what resets the PC to the place where instruction execution
- // beings again.
#if FULL_SYSTEM
- if (//checkInterrupts &&
+ // Process interrupts if interrupts are enabled, not in PAL mode,
+ // and no other traps or external squashes are currently pending.
+ // @todo: Allow other threads to handle interrupts.
+ if (cpu->checkInterrupts &&
cpu->check_interrupts() &&
- !cpu->inPalMode(readCommitPC())) {
- // Will need to squash all instructions currently in flight and have
- // the interrupt handler restart at the last non-committed inst.
- // Most of that can be handled through the trap() function. The
- // processInterrupts() function really just checks for interrupts
- // and then calls trap() if there is an interrupt present.
+ !cpu->inPalMode(readPC()) &&
+ !trapSquash[0] &&
+ !tcSquash[0]) {
+ // Tell fetch that there is an interrupt pending. This will
+ // make fetch wait until it sees a non PAL-mode PC, at which
+ // point it stops fetching instructions.
+ toIEW->commitInfo[0].interruptPending = true;
+
+ // Wait until the ROB is empty and all stores have drained in
+ // order to enter the interrupt.
+ if (rob->isEmpty() && !iewStage->hasStoresToWB()) {
+ // Not sure which thread should be the one to interrupt. For now
+ // always do thread 0.
+ assert(!thread[0]->inSyscall);
+ thread[0]->inSyscall = true;
+
+ // CPU will handle implementation of the interrupt.
+ cpu->processInterrupts();
+
+ // Now squash or record that I need to squash this cycle.
+ commitStatus[0] = TrapPending;
+
+ // Exit state update mode to avoid accidental updating.
+ thread[0]->inSyscall = false;
+
+ // Generate trap squash event.
+ generateTrapEvent(0);
- // CPU will handle implementation of the interrupt.
- cpu->processInterrupts();
+ toIEW->commitInfo[0].clearInterrupt = true;
+
+ DPRINTF(Commit, "Interrupt detected.\n");
+ } else {
+ DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n");
+ }
}
#endif // FULL_SYSTEM
////////////////////////////////////
- // Check for squash signal, handle that first
+ // Check for any possible squashes, handle them first
////////////////////////////////////
- // Want to mainly check if the IEW stage is telling the ROB to squash.
- // Should I also check if the commit stage is telling the ROB to squah?
- // This might be necessary to keep the same timing between the IQ and
- // the ROB...
- if (fromIEW->squash) {
- DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n");
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+/*
+ if (fromFetch->fetchFault && commitStatus[0] != TrapPending) {
+ // Record the fault. Wait until it's empty in the ROB.
+ // Then handle the trap. Ignore it if there's already a
+ // trap pending as fetch will be redirected.
+ fetchFault = fromFetch->fetchFault;
+ fetchFaultTick = curTick + fetchTrapLatency;
+ commitStatus[0] = FetchTrapPending;
+ DPRINTF(Commit, "Fault from fetch recorded. Will trap if the "
+ "ROB empties without squashing the fault.\n");
+ fetchTrapWait = 0;
+ }
+
+ // Fetch may tell commit to clear the trap if it's been squashed.
+ if (fromFetch->clearFetchFault) {
+ DPRINTF(Commit, "Received clear fetch fault signal\n");
+ fetchTrapWait = 0;
+ if (commitStatus[0] == FetchTrapPending) {
+ DPRINTF(Commit, "Clearing fault from fetch\n");
+ commitStatus[0] = Running;
+ }
+ }
+*/
+ // Not sure which one takes priority. I think if we have
+ // both, that's a bad sign.
+ if (trapSquash[tid] == true) {
+ assert(!tcSquash[tid]);
+ squashFromTrap(tid);
+ } else if (tcSquash[tid] == true) {
+ squashFromTC(tid);
+ }
+
+ // Squashed sequence number must be older than youngest valid
+ // instruction in the ROB. This prevents squashes from younger
+ // instructions overriding squashes from older instructions.
+ if (fromIEW->squash[tid] &&
+ commitStatus[tid] != TrapPending &&
+ fromIEW->squashedSeqNum[tid] <= youngestSeqNum[tid]) {
- _status = ROBSquashing;
+ DPRINTF(Commit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n",
+ tid,
+ fromIEW->mispredPC[tid],
+ fromIEW->squashedSeqNum[tid]);
- InstSeqNum squashed_inst = fromIEW->squashedSeqNum;
+ DPRINTF(Commit, "[tid:%i]: Redirecting to PC %#x\n",
+ tid,
+ fromIEW->nextPC[tid]);
- rob->squash(squashed_inst);
+ commitStatus[tid] = ROBSquashing;
- // Send back the sequence number of the squashed instruction.
- toIEW->commitInfo.doneSeqNum = squashed_inst;
+ ++squashCounter;
- // Send back the squash signal to tell stages that they should squash.
- toIEW->commitInfo.squash = true;
+ // If we want to include the squashing instruction in the squash,
+ // then use one older sequence number.
+ InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid];
- // Send back the rob squashing signal so other stages know that the
- // ROB is in the process of squashing.
- toIEW->commitInfo.robSquashing = true;
+ if (fromIEW->includeSquashInst[tid] == true)
+ squashed_inst--;
- toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict;
+ // All younger instructions will be squashed. Set the sequence
+ // number as the youngest instruction in the ROB.
+ youngestSeqNum[tid] = squashed_inst;
- toIEW->commitInfo.branchTaken = fromIEW->branchTaken;
+ rob->squash(squashed_inst, tid);
+ changedROBNumEntries[tid] = true;
- toIEW->commitInfo.nextPC = fromIEW->nextPC;
+ toIEW->commitInfo[tid].doneSeqNum = squashed_inst;
- toIEW->commitInfo.mispredPC = fromIEW->mispredPC;
+ toIEW->commitInfo[tid].squash = true;
- if (toIEW->commitInfo.branchMispredict) {
- ++branchMispredicts;
+ // Send back the rob squashing signal so other stages know that
+ // the ROB is in the process of squashing.
+ toIEW->commitInfo[tid].robSquashing = true;
+
+ toIEW->commitInfo[tid].branchMispredict =
+ fromIEW->branchMispredict[tid];
+
+ toIEW->commitInfo[tid].branchTaken =
+ fromIEW->branchTaken[tid];
+
+ toIEW->commitInfo[tid].nextPC = fromIEW->nextPC[tid];
+
+ toIEW->commitInfo[tid].mispredPC = fromIEW->mispredPC[tid];
+
+ if (toIEW->commitInfo[tid].branchMispredict) {
+ ++branchMispredicts;
+ }
}
+
}
- if (_status != ROBSquashing) {
+ setNextStatus();
+
+ if (squashCounter != numThreads) {
// If we're not currently squashing, then get instructions.
getInsts();
@@ -249,121 +795,186 @@ SimpleCommit<Impl>::commit()
commitInsts();
}
- // If the ROB is empty, we can set this stage to idle. Use this
- // in the future when the Idle status will actually be utilized.
-#if 0
- if (rob->isEmpty()) {
- DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n");
- _status = Idle;
- // Schedule an event so that commit will actually wake up
- // once something gets put in the ROB.
+ //Check for any activity
+ threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (changedROBNumEntries[tid]) {
+ toIEW->commitInfo[tid].usedROB = true;
+ toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);
+
+ if (rob->isEmpty(tid)) {
+ toIEW->commitInfo[tid].emptyROB = true;
+ }
+
+ wroteToTimeBuffer = true;
+ changedROBNumEntries[tid] = false;
+ }
}
-#endif
}
-// Loop that goes through as many instructions in the ROB as possible and
-// tries to commit them. The actual work for committing is done by the
-// commitHead() function.
template <class Impl>
void
-SimpleCommit<Impl>::commitInsts()
+DefaultCommit<Impl>::commitInsts()
{
////////////////////////////////////
// Handle commit
- // Note that commit will be handled prior to the ROB so that the ROB
- // only tries to commit instructions it has in this current cycle, and
- // not instructions it is writing in during this cycle.
- // Can't commit and squash things at the same time...
+ // Note that commit will be handled prior to putting new
+ // instructions in the ROB so that the ROB only tries to commit
+ // instructions it has in this current cycle, and not instructions
+ // it is writing in during this cycle. Can't commit and squash
+ // things at the same time...
////////////////////////////////////
- if (rob->isEmpty())
- return;
-
- DynInstPtr head_inst = rob->readHeadInst();
+ DPRINTF(Commit, "Trying to commit instructions in the ROB.\n");
unsigned num_committed = 0;
+ DynInstPtr head_inst;
+
// Commit as many instructions as possible until the commit bandwidth
// limit is reached, or it becomes impossible to commit any more.
- while (!rob->isEmpty() &&
- head_inst->readyToCommit() &&
- num_committed < commitWidth)
- {
- DPRINTF(Commit, "Commit: Trying to commit head instruction.\n");
+ while (num_committed < commitWidth) {
+ int commit_thread = getCommittingThread();
+
+ if (commit_thread == -1 || !rob->isHeadReady(commit_thread))
+ break;
+
+ head_inst = rob->readHeadInst(commit_thread);
+
+ int tid = head_inst->threadNumber;
+
+ assert(tid == commit_thread);
+
+ DPRINTF(Commit, "Trying to commit head instruction, [sn:%i] [tid:%i]\n",
+ head_inst->seqNum, tid);
- // If the head instruction is squashed, it is ready to retire at any
- // time. However, we need to avoid updating any other state
- // incorrectly if it's already been squashed.
+ // If the head instruction is squashed, it is ready to retire
+ // (be removed from the ROB) at any time.
if (head_inst->isSquashed()) {
- DPRINTF(Commit, "Commit: Retiring squashed instruction from "
+ DPRINTF(Commit, "Retiring squashed instruction from "
"ROB.\n");
- // Tell ROB to retire head instruction. This retires the head
- // inst in the ROB without affecting any other stages.
- rob->retireHead();
+ rob->retireHead(commit_thread);
++commitSquashedInsts;
+ // Record that the number of ROB entries has changed.
+ changedROBNumEntries[tid] = true;
} else {
+ PC[tid] = head_inst->readPC();
+ nextPC[tid] = head_inst->readNextPC();
+
// Increment the total number of non-speculative instructions
// executed.
// Hack for now: it really shouldn't happen until after the
// commit is deemed to be successful, but this count is needed
// for syscalls.
- cpu->funcExeInst++;
+ thread[tid]->funcExeInst++;
// Try to commit the head instruction.
bool commit_success = commitHead(head_inst, num_committed);
- // Update what instruction we are looking at if the commit worked.
if (commit_success) {
++num_committed;
- // Send back which instruction has been committed.
- // @todo: Update this later when a wider pipeline is used.
- // Hmm, can't really give a pointer here...perhaps the
- // sequence number instead (copy).
- toIEW->commitInfo.doneSeqNum = head_inst->seqNum;
+ changedROBNumEntries[tid] = true;
+
+ // Set the doneSeqNum to the youngest committed instruction.
+ toIEW->commitInfo[tid].doneSeqNum = head_inst->seqNum;
++commitCommittedInsts;
- if (!head_inst->isNop()) {
- cpu->instDone();
+ // To match the old model, don't count nops and instruction
+ // prefetches towards the total commit count.
+ if (!head_inst->isNop() && !head_inst->isInstPrefetch()) {
+ cpu->instDone(tid);
+ }
+
+ PC[tid] = nextPC[tid];
+ nextPC[tid] = nextPC[tid] + sizeof(TheISA::MachInst);
+#if FULL_SYSTEM
+ int count = 0;
+ Addr oldpc;
+ do {
+ // Debug statement. Checks to make sure we're not
+ // currently updating state while handling PC events.
+ if (count == 0)
+ assert(!thread[tid]->inSyscall &&
+ !thread[tid]->trapPending);
+ oldpc = PC[tid];
+ cpu->system->pcEventQueue.service(
+ thread[tid]->getTC());
+ count++;
+ } while (oldpc != PC[tid]);
+ if (count > 1) {
+ DPRINTF(Commit, "PC skip function event, stopping commit\n");
+ break;
}
+#endif
} else {
+ DPRINTF(Commit, "Unable to commit head instruction PC:%#x "
+ "[tid:%i] [sn:%i].\n",
+ head_inst->readPC(), tid ,head_inst->seqNum);
break;
}
}
-
- // Update the pointer to read the next instruction in the ROB.
- head_inst = rob->readHeadInst();
}
DPRINTF(CommitRate, "%i\n", num_committed);
- n_committed_dist.sample(num_committed);
+ numCommittedDist.sample(num_committed);
+
+ if (num_committed == commitWidth) {
+ commitEligibleSamples++;
+ }
}
template <class Impl>
bool
-SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
+DefaultCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
{
- // Make sure instruction is valid
assert(head_inst);
- // If the instruction is not executed yet, then it is a non-speculative
- // or store inst. Signal backwards that it should be executed.
+ int tid = head_inst->threadNumber;
+
+ // If the instruction is not executed yet, then it will need extra
+ // handling. Signal backwards that it should be executed.
if (!head_inst->isExecuted()) {
// Keep this number correct. We have not yet actually executed
// and committed this instruction.
- cpu->funcExeInst--;
-
- if (head_inst->isNonSpeculative()) {
- DPRINTF(Commit, "Commit: Encountered a store or non-speculative "
- "instruction at the head of the ROB, PC %#x.\n",
- head_inst->readPC());
+ thread[tid]->funcExeInst--;
+
+ head_inst->reachedCommit = true;
+
+ if (head_inst->isNonSpeculative() ||
+ head_inst->isStoreConditional() ||
+ head_inst->isMemBarrier() ||
+ head_inst->isWriteBarrier()) {
+
+ DPRINTF(Commit, "Encountered a barrier or non-speculative "
+ "instruction [sn:%lli] at the head of the ROB, PC %#x.\n",
+ head_inst->seqNum, head_inst->readPC());
+
+#if !FULL_SYSTEM
+ // Hack to make sure syscalls/memory barriers/quiesces
+ // aren't executed until all stores write back their data.
+ // This direct communication shouldn't be used for
+ // anything other than this.
+ if (inst_num > 0 || iewStage->hasStoresToWB())
+#else
+ if ((head_inst->isMemBarrier() || head_inst->isWriteBarrier() ||
+ head_inst->isQuiesce()) &&
+ iewStage->hasStoresToWB())
+#endif
+ {
+ DPRINTF(Commit, "Waiting for all stores to writeback.\n");
+ return false;
+ }
- toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum;
+ toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum;
// Change the instruction so it won't try to commit again until
// it is executed.
@@ -372,74 +983,111 @@ SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
++commitNonSpecStalls;
return false;
+ } else if (head_inst->isLoad()) {
+ DPRINTF(Commit, "[sn:%lli]: Uncached load, PC %#x.\n",
+ head_inst->seqNum, head_inst->readPC());
+
+ // Send back the non-speculative instruction's sequence
+ // number. Tell the lsq to re-execute the load.
+ toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum;
+ toIEW->commitInfo[tid].uncached = true;
+ toIEW->commitInfo[tid].uncachedLoad = head_inst;
+
+ head_inst->clearCanCommit();
+
+ return false;
} else {
- panic("Commit: Trying to commit un-executed instruction "
+ panic("Trying to commit un-executed instruction "
"of unknown type!\n");
}
}
- // Now check if it's one of the special trap or barrier or
- // serializing instructions.
- if (head_inst->isThreadSync() ||
- head_inst->isSerializing() ||
- head_inst->isMemBarrier() ||
- head_inst->isWriteBarrier() )
- {
- // Not handled for now. Mem barriers and write barriers are safe
- // to simply let commit as memory accesses only happen once they
- // reach the head of commit. Not sure about the other two.
- panic("Serializing or barrier instructions"
- " are not handled yet.\n");
+ if (head_inst->isThreadSync()) {
+ // Not handled for now.
+ panic("Thread sync instructions are not handled yet.\n");
+ }
+
+ // Stores mark themselves as completed.
+ if (!head_inst->isStore()) {
+ head_inst->setCompleted();
+ }
+
+ // Use checker prior to updating anything due to traps or PC
+ // based events.
+ if (cpu->checker) {
+ cpu->checker->tick(head_inst);
}
// Check if the instruction caused a fault. If so, trap.
Fault inst_fault = head_inst->getFault();
if (inst_fault != NoFault) {
- if (!head_inst->isNop()) {
+ head_inst->setCompleted();
#if FULL_SYSTEM
- cpu->trap(inst_fault);
-#else // !FULL_SYSTEM
- panic("fault (%d) detected @ PC %08p", inst_fault,
- head_inst->PC);
-#endif // FULL_SYSTEM
+ DPRINTF(Commit, "Inst [sn:%lli] PC %#x has a fault\n",
+ head_inst->seqNum, head_inst->readPC());
+
+ if (iewStage->hasStoresToWB() || inst_num > 0) {
+ DPRINTF(Commit, "Stores outstanding, fault must wait.\n");
+ return false;
}
- }
- // Check if we're really ready to commit. If not then return false.
- // I'm pretty sure all instructions should be able to commit if they've
- // reached this far. For now leave this in as a check.
- if (!rob->isHeadReady()) {
- panic("Commit: Unable to commit head instruction!\n");
- return false;
- }
+ if (cpu->checker && head_inst->isStore()) {
+ cpu->checker->tick(head_inst);
+ }
- // If it's a branch, then send back branch prediction update info
- // to the fetch stage.
- // This should be handled in the iew stage if a mispredict happens...
+ assert(!thread[tid]->inSyscall);
- if (head_inst->isControl()) {
+ // Mark that we're in state update mode so that the trap's
+ // execution doesn't generate extra squashes.
+ thread[tid]->inSyscall = true;
-#if 0
- toIEW->nextPC = head_inst->readPC();
- //Maybe switch over to BTB incorrect.
- toIEW->btbMissed = head_inst->btbMiss();
- toIEW->target = head_inst->nextPC;
- //Maybe also include global history information.
- //This simple version will have no branch prediction however.
-#endif
+ // DTB will sometimes need the machine instruction for when
+ // faults happen. So we will set it here, prior to the DTB
+ // possibly needing it for its fault.
+ thread[tid]->setInst(
+ static_cast<TheISA::MachInst>(head_inst->staticInst->machInst));
+
+ // Execute the trap. Although it's slightly unrealistic in
+ // terms of timing (as it doesn't wait for the full timing of
+ // the trap event to complete before updating state), it's
+ // needed to update the state as soon as possible. This
+ // prevents external agents from changing any specific state
+ // that the trap need.
+ cpu->trap(inst_fault, tid);
+
+ // Exit state update mode to avoid accidental updating.
+ thread[tid]->inSyscall = false;
+
+ commitStatus[tid] = TrapPending;
- ++commitCommittedBranches;
+ // Generate trap squash event.
+ generateTrapEvent(tid);
+
+ return false;
+#else // !FULL_SYSTEM
+ panic("fault (%d) detected @ PC %08p", inst_fault,
+ head_inst->PC);
+#endif // FULL_SYSTEM
}
- // Now that the instruction is going to be committed, finalize its
- // trace data.
+ updateComInstStats(head_inst);
+
if (head_inst->traceData) {
+ head_inst->traceData->setFetchSeq(head_inst->seqNum);
+ head_inst->traceData->setCPSeq(thread[tid]->numInst);
head_inst->traceData->finalize();
+ head_inst->traceData = NULL;
+ }
+
+ // Update the commit rename map
+ for (int i = 0; i < head_inst->numDestRegs(); i++) {
+ renameMap[tid]->setEntry(head_inst->destRegIdx(i),
+ head_inst->renamedDestRegIdx(i));
}
- //Finally clear the head ROB entry.
- rob->retireHead();
+ // Finally clear the head ROB entry.
+ rob->retireHead(tid);
// Return true to indicate that we have committed an instruction.
return true;
@@ -447,37 +1095,39 @@ SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
template <class Impl>
void
-SimpleCommit<Impl>::getInsts()
+DefaultCommit<Impl>::getInsts()
{
- //////////////////////////////////////
- // Handle ROB functions
- //////////////////////////////////////
-
- // Read any issued instructions and place them into the ROB. Do this
- // prior to squashing to avoid having instructions in the ROB that
- // don't get squashed properly.
+ // Read any renamed instructions and place them into the ROB.
int insts_to_process = min((int)renameWidth, fromRename->size);
- for (int inst_num = 0;
- inst_num < insts_to_process;
- ++inst_num)
+ for (int inst_num = 0; inst_num < insts_to_process; ++inst_num)
{
- if (!fromRename->insts[inst_num]->isSquashed()) {
- DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n",
- fromRename->insts[inst_num]->readPC());
- rob->insertInst(fromRename->insts[inst_num]);
+ DynInstPtr inst = fromRename->insts[inst_num];
+ int tid = inst->threadNumber;
+
+ if (!inst->isSquashed() &&
+ commitStatus[tid] != ROBSquashing) {
+ changedROBNumEntries[tid] = true;
+
+ DPRINTF(Commit, "Inserting PC %#x [sn:%i] [tid:%i] into ROB.\n",
+ inst->readPC(), inst->seqNum, tid);
+
+ rob->insertInst(inst);
+
+ assert(rob->getThreadEntries(tid) <= rob->getMaxEntries(tid));
+
+ youngestSeqNum[tid] = inst->seqNum;
} else {
- DPRINTF(Commit, "Commit: Instruction %i PC %#x was "
+ DPRINTF(Commit, "Instruction PC %#x [sn:%i] [tid:%i] was "
"squashed, skipping.\n",
- fromRename->insts[inst_num]->seqNum,
- fromRename->insts[inst_num]->readPC());
+ inst->readPC(), inst->seqNum, tid);
}
}
}
template <class Impl>
void
-SimpleCommit<Impl>::markCompletedInsts()
+DefaultCommit<Impl>::markCompletedInsts()
{
// Grab completed insts out of the IEW instruction queue, and mark
// instructions completed within the ROB.
@@ -485,18 +1135,177 @@ SimpleCommit<Impl>::markCompletedInsts()
inst_num < fromIEW->size && fromIEW->insts[inst_num];
++inst_num)
{
- DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n",
- fromIEW->insts[inst_num]->readPC(),
- fromIEW->insts[inst_num]->seqNum);
+ if (!fromIEW->insts[inst_num]->isSquashed()) {
+ DPRINTF(Commit, "[tid:%i]: Marking PC %#x, [sn:%lli] ready "
+ "within ROB.\n",
+ fromIEW->insts[inst_num]->threadNumber,
+ fromIEW->insts[inst_num]->readPC(),
+ fromIEW->insts[inst_num]->seqNum);
+
+ // Mark the instruction as ready to commit.
+ fromIEW->insts[inst_num]->setCanCommit();
+ }
+ }
+}
+
+template <class Impl>
+bool
+DefaultCommit<Impl>::robDoneSquashing()
+{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
- // Mark the instruction as ready to commit.
- fromIEW->insts[inst_num]->setCanCommit();
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (!rob->isDoneSquashing(tid))
+ return false;
}
+
+ return true;
}
template <class Impl>
-uint64_t
-SimpleCommit<Impl>::readCommitPC()
+void
+DefaultCommit<Impl>::updateComInstStats(DynInstPtr &inst)
{
- return rob->readHeadPC();
+ unsigned thread = inst->threadNumber;
+
+ //
+ // Pick off the software prefetches
+ //
+#ifdef TARGET_ALPHA
+ if (inst->isDataPrefetch()) {
+ statComSwp[thread]++;
+ } else {
+ statComInst[thread]++;
+ }
+#else
+ statComInst[thread]++;
+#endif
+
+ //
+ // Control Instructions
+ //
+ if (inst->isControl())
+ statComBranches[thread]++;
+
+ //
+ // Memory references
+ //
+ if (inst->isMemRef()) {
+ statComRefs[thread]++;
+
+ if (inst->isLoad()) {
+ statComLoads[thread]++;
+ }
+ }
+
+ if (inst->isMemBarrier()) {
+ statComMembars[thread]++;
+ }
+}
+
+////////////////////////////////////////
+// //
+// SMT COMMIT POLICY MAINTAINED HERE //
+// //
+////////////////////////////////////////
+template <class Impl>
+int
+DefaultCommit<Impl>::getCommittingThread()
+{
+ if (numThreads > 1) {
+ switch (commitPolicy) {
+
+ case Aggressive:
+ //If Policy is Aggressive, commit will call
+ //this function multiple times per
+ //cycle
+ return oldestReady();
+
+ case RoundRobin:
+ return roundRobin();
+
+ case OldestReady:
+ return oldestReady();
+
+ default:
+ return -1;
+ }
+ } else {
+ int tid = (*activeThreads).front();
+
+ if (commitStatus[tid] == Running ||
+ commitStatus[tid] == Idle ||
+ commitStatus[tid] == FetchTrapPending) {
+ return tid;
+ } else {
+ return -1;
+ }
+ }
+}
+
+template<class Impl>
+int
+DefaultCommit<Impl>::roundRobin()
+{
+ list<unsigned>::iterator pri_iter = priority_list.begin();
+ list<unsigned>::iterator end = priority_list.end();
+
+ while (pri_iter != end) {
+ unsigned tid = *pri_iter;
+
+ if (commitStatus[tid] == Running ||
+ commitStatus[tid] == Idle) {
+
+ if (rob->isHeadReady(tid)) {
+ priority_list.erase(pri_iter);
+ priority_list.push_back(tid);
+
+ return tid;
+ }
+ }
+
+ pri_iter++;
+ }
+
+ return -1;
+}
+
+template<class Impl>
+int
+DefaultCommit<Impl>::oldestReady()
+{
+ unsigned oldest = 0;
+ bool first = true;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (!rob->isEmpty(tid) &&
+ (commitStatus[tid] == Running ||
+ commitStatus[tid] == Idle ||
+ commitStatus[tid] == FetchTrapPending)) {
+
+ if (rob->isHeadReady(tid)) {
+
+ DynInstPtr head_inst = rob->readHeadInst(tid);
+
+ if (first) {
+ oldest = tid;
+ first = false;
+ } else if (head_inst->seqNum < oldest) {
+ oldest = tid;
+ }
+ }
+ }
+ }
+
+ if (!first) {
+ return oldest;
+ } else {
+ return -1;
+ }
}
diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc
index a268dbc23..788c6b164 100644
--- a/src/cpu/o3/cpu.cc
+++ b/src/cpu/o3/cpu.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "config/full_system.hh"
@@ -33,19 +35,30 @@
#else
#include "sim/process.hh"
#endif
-#include "sim/root.hh"
-#include "cpu/cpu_exec_context.hh"
-#include "cpu/exec_context.hh"
+#include "cpu/activity.hh"
+#include "cpu/checker/cpu.hh"
+#include "cpu/simple_thread.hh"
+#include "cpu/thread_context.hh"
#include "cpu/o3/alpha_dyn_inst.hh"
#include "cpu/o3/alpha_impl.hh"
#include "cpu/o3/cpu.hh"
+#include "sim/root.hh"
+#include "sim/stat_control.hh"
+
using namespace std;
+using namespace TheISA;
+
+BaseFullCPU::BaseFullCPU(Params *params)
+ : BaseCPU(params), cpu_id(0)
+{
+}
-BaseFullCPU::BaseFullCPU(Params &params)
- : BaseCPU(&params), cpu_id(0)
+void
+BaseFullCPU::regStats()
{
+ BaseCPU::regStats();
}
template <class Impl>
@@ -68,97 +81,82 @@ FullO3CPU<Impl>::TickEvent::description()
return "FullO3CPU tick event";
}
-//Call constructor to all the pipeline stages here
template <class Impl>
-FullO3CPU<Impl>::FullO3CPU(Params &params)
-#if FULL_SYSTEM
+FullO3CPU<Impl>::FullO3CPU(Params *params)
: BaseFullCPU(params),
-#else
- : BaseFullCPU(params),
-#endif // FULL_SYSTEM
tickEvent(this),
+ removeInstsThisCycle(false),
fetch(params),
decode(params),
rename(params),
iew(params),
commit(params),
- regFile(params.numPhysIntRegs, params.numPhysFloatRegs),
+ regFile(params->numPhysIntRegs, params->numPhysFloatRegs),
- freeList(TheISA::NumIntRegs, params.numPhysIntRegs,
- TheISA::NumFloatRegs, params.numPhysFloatRegs),
+ freeList(params->numberOfThreads,//number of activeThreads
+ TheISA::NumIntRegs, params->numPhysIntRegs,
+ TheISA::NumFloatRegs, params->numPhysFloatRegs),
- renameMap(TheISA::NumIntRegs, params.numPhysIntRegs,
- TheISA::NumFloatRegs, params.numPhysFloatRegs,
- TheISA::NumMiscRegs,
- TheISA::ZeroReg,
- TheISA::ZeroReg + TheISA::NumIntRegs),
+ rob(params->numROBEntries, params->squashWidth,
+ params->smtROBPolicy, params->smtROBThreshold,
+ params->numberOfThreads),
- rob(params.numROBEntries, params.squashWidth),
+ scoreboard(params->numberOfThreads,//number of activeThreads
+ TheISA::NumIntRegs, params->numPhysIntRegs,
+ TheISA::NumFloatRegs, params->numPhysFloatRegs,
+ TheISA::NumMiscRegs * number_of_threads,
+ TheISA::ZeroReg),
- // What to pass to these time buffers?
// For now just have these time buffers be pretty big.
+ // @todo: Make these time buffer sizes parameters or derived
+ // from latencies
timeBuffer(5, 5),
fetchQueue(5, 5),
decodeQueue(5, 5),
renameQueue(5, 5),
iewQueue(5, 5),
-
- cpuXC(NULL),
+ activityRec(NumStages, 10, params->activity),
globalSeqNum(1),
#if FULL_SYSTEM
- system(params.system),
- memCtrl(system->memctrl),
+ system(params->system),
physmem(system->physmem),
- itb(params.itb),
- dtb(params.dtb),
- mem(params.mem),
-#else
- // Hardcoded for a single thread!!
- mem(params.workload[0]->getMemory()),
#endif // FULL_SYSTEM
-
- icacheInterface(params.icacheInterface),
- dcacheInterface(params.dcacheInterface),
- deferRegistration(params.defReg),
- numInsts(0),
- funcExeInst(0)
+ mem(params->mem),
+ switchCount(0),
+ deferRegistration(params->deferRegistration),
+ numThreads(number_of_threads)
{
_status = Idle;
-#if !FULL_SYSTEM
- thread.resize(this->number_of_threads);
-#endif
-
- for (int i = 0; i < this->number_of_threads; ++i) {
+ if (params->checker) {
+ BaseCPU *temp_checker = params->checker;
+ checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker);
+ checker->setMemory(mem);
#if FULL_SYSTEM
- assert(i == 0);
- thread[i] = new CPUExecContext(this, 0, system, itb, dtb, mem);
- system->execContexts[i] = thread[i]->getProxy();
-
- execContexts.push_back(system->execContexts[i]);
-#else
- if (i < params.workload.size()) {
- DPRINTF(FullCPU, "FullCPU: Workload[%i]'s starting PC is %#x, "
- "process is %#x",
- i, params.workload[i]->prog_entry, thread[i]);
- thread[i] = new CPUExecContext(this, i, params.workload[i], i);
- }
- assert(params.workload[i]->getMemory() != NULL);
- assert(mem != NULL);
- execContexts.push_back(thread[i]->getProxy());
-#endif // !FULL_SYSTEM
+ checker->setSystem(params->system);
+#endif
+ } else {
+ checker = NULL;
}
- // Note that this is a hack so that my code which still uses xc-> will
- // still work. I should remove this eventually
- cpuXC = thread[0];
+#if !FULL_SYSTEM
+ thread.resize(number_of_threads);
+ tids.resize(number_of_threads);
+#endif
+
+ // The stages also need their CPU pointer setup. However this
+ // must be done at the upper level CPU because they have pointers
+ // to the upper level CPU, and not this FullO3CPU.
- // The stages also need their CPU pointer setup. However this must be
- // done at the upper level CPU because they have pointers to the upper
- // level CPU, and not this FullO3CPU.
+ // Set up Pointers to the activeThreads list for each stage
+ fetch.setActiveThreads(&activeThreads);
+ decode.setActiveThreads(&activeThreads);
+ rename.setActiveThreads(&activeThreads);
+ iew.setActiveThreads(&activeThreads);
+ commit.setActiveThreads(&activeThreads);
// Give each of the stages the time buffer they will use.
fetch.setTimeBuffer(&timeBuffer);
@@ -170,6 +168,7 @@ FullO3CPU<Impl>::FullO3CPU(Params &params)
// Also setup each of the stages' queues.
fetch.setFetchQueue(&fetchQueue);
decode.setFetchQueue(&fetchQueue);
+ commit.setFetchQueue(&fetchQueue);
decode.setDecodeQueue(&decodeQueue);
rename.setDecodeQueue(&decodeQueue);
rename.setRenameQueue(&renameQueue);
@@ -178,16 +177,79 @@ FullO3CPU<Impl>::FullO3CPU(Params &params)
commit.setIEWQueue(&iewQueue);
commit.setRenameQueue(&renameQueue);
+ commit.setFetchStage(&fetch);
+ commit.setIEWStage(&iew);
+ rename.setIEWStage(&iew);
+ rename.setCommitStage(&commit);
+
+#if !FULL_SYSTEM
+ int active_threads = params->workload.size();
+#else
+ int active_threads = 1;
+#endif
+
+ //Make Sure That this a Valid Architeture
+ assert(params->numPhysIntRegs >= numThreads * TheISA::NumIntRegs);
+ assert(params->numPhysFloatRegs >= numThreads * TheISA::NumFloatRegs);
+
+ rename.setScoreboard(&scoreboard);
+ iew.setScoreboard(&scoreboard);
+
// Setup the rename map for whichever stages need it.
- rename.setRenameMap(&renameMap);
- iew.setRenameMap(&renameMap);
+ PhysRegIndex lreg_idx = 0;
+ PhysRegIndex freg_idx = params->numPhysIntRegs; //Index to 1 after int regs
+
+ for (int tid=0; tid < numThreads; tid++) {
+ bool bindRegs = (tid <= active_threads - 1);
+
+ commitRenameMap[tid].init(TheISA::NumIntRegs,
+ params->numPhysIntRegs,
+ lreg_idx, //Index for Logical. Regs
+
+ TheISA::NumFloatRegs,
+ params->numPhysFloatRegs,
+ freg_idx, //Index for Float Regs
+
+ TheISA::NumMiscRegs,
+
+ TheISA::ZeroReg,
+ TheISA::ZeroReg,
+
+ tid,
+ false);
- // Setup the free list for whichever stages need it.
+ renameMap[tid].init(TheISA::NumIntRegs,
+ params->numPhysIntRegs,
+ lreg_idx, //Index for Logical. Regs
+
+ TheISA::NumFloatRegs,
+ params->numPhysFloatRegs,
+ freg_idx, //Index for Float Regs
+
+ TheISA::NumMiscRegs,
+
+ TheISA::ZeroReg,
+ TheISA::ZeroReg,
+
+ tid,
+ bindRegs);
+ }
+
+ rename.setRenameMap(renameMap);
+ commit.setRenameMap(commitRenameMap);
+
+ // Give renameMap & rename stage access to the freeList;
+ for (int i=0; i < numThreads; i++) {
+ renameMap[i].setFreeList(&freeList);
+ }
rename.setFreeList(&freeList);
- renameMap.setFreeList(&freeList);
// Setup the ROB for whichever stages need it.
commit.setROB(&rob);
+
+ lastRunningCycle = curTick;
+
+ contextSwitch = false;
}
template <class Impl>
@@ -199,7 +261,58 @@ template <class Impl>
void
FullO3CPU<Impl>::fullCPURegStats()
{
+ BaseFullCPU::regStats();
+
// Register any of the FullCPU's stats here.
+ timesIdled
+ .name(name() + ".timesIdled")
+ .desc("Number of times that the entire CPU went into an idle state and"
+ " unscheduled itself")
+ .prereq(timesIdled);
+
+ idleCycles
+ .name(name() + ".idleCycles")
+ .desc("Total number of cycles that the CPU has spent unscheduled due "
+ "to idling")
+ .prereq(idleCycles);
+
+ // Number of Instructions simulated
+ // --------------------------------
+ // Should probably be in Base CPU but need templated
+ // MaxThreads so put in here instead
+ committedInsts
+ .init(numThreads)
+ .name(name() + ".committedInsts")
+ .desc("Number of Instructions Simulated");
+
+ totalCommittedInsts
+ .name(name() + ".committedInsts_total")
+ .desc("Number of Instructions Simulated");
+
+ cpi
+ .name(name() + ".cpi")
+ .desc("CPI: Cycles Per Instruction")
+ .precision(6);
+ cpi = simTicks / committedInsts;
+
+ totalCpi
+ .name(name() + ".cpi_total")
+ .desc("CPI: Total CPI of All Threads")
+ .precision(6);
+ totalCpi = simTicks / totalCommittedInsts;
+
+ ipc
+ .name(name() + ".ipc")
+ .desc("IPC: Instructions Per Cycle")
+ .precision(6);
+ ipc = committedInsts / simTicks;
+
+ totalIpc
+ .name(name() + ".ipc_total")
+ .desc("IPC: Total IPC of All Threads")
+ .precision(6);
+ totalIpc = totalCommittedInsts / simTicks;
+
}
template <class Impl>
@@ -208,9 +321,11 @@ FullO3CPU<Impl>::tick()
{
DPRINTF(FullCPU, "\n\nFullCPU: Ticking main, FullO3CPU.\n");
- //Tick each of the stages if they're actually running.
- //Will want to figure out a way to unschedule itself if they're all
- //going to be idle for a long time.
+ ++numCycles;
+
+// activity = false;
+
+ //Tick each of the stages
fetch.tick();
decode.tick();
@@ -221,7 +336,11 @@ FullO3CPU<Impl>::tick()
commit.tick();
- // Now advance the time buffers, unless the stage is stalled.
+#if !FULL_SYSTEM
+ doContextSwitch();
+#endif
+
+ // Now advance the time buffers
timeBuffer.advance();
fetchQueue.advance();
@@ -229,114 +348,405 @@ FullO3CPU<Impl>::tick()
renameQueue.advance();
iewQueue.advance();
- if (_status == Running && !tickEvent.scheduled())
- tickEvent.schedule(curTick + 1);
+ activityRec.advance();
+
+ if (removeInstsThisCycle) {
+ cleanUpRemovedInsts();
+ }
+
+ if (!tickEvent.scheduled()) {
+ if (_status == SwitchedOut) {
+ // increment stat
+ lastRunningCycle = curTick;
+ } else if (!activityRec.active()) {
+ lastRunningCycle = curTick;
+ timesIdled++;
+ } else {
+ tickEvent.schedule(curTick + cycles(1));
+ }
+ }
+
+#if !FULL_SYSTEM
+ updateThreadPriority();
+#endif
+
}
template <class Impl>
void
FullO3CPU<Impl>::init()
{
- if(!deferRegistration)
- {
- this->registerExecContexts();
+ if (!deferRegistration) {
+ registerThreadContexts();
+ }
- // Need to do a copy of the xc->regs into the CPU's regfile so
- // that it can start properly.
+ // Set inSyscall so that the CPU doesn't squash when initially
+ // setting up registers.
+ for (int i = 0; i < number_of_threads; ++i)
+ thread[i]->inSyscall = true;
+
+ for (int tid=0; tid < number_of_threads; tid++) {
#if FULL_SYSTEM
- ExecContext *src_xc = system->execContexts[0];
- TheISA::initCPU(src_xc, src_xc->readCpuId());
+ ThreadContext *src_tc = threadContexts[tid];
#else
- ExecContext *src_xc = thread[0]->getProxy();
+ ThreadContext *src_tc = thread[tid]->getTC();
#endif
- // First loop through the integer registers.
- for (int i = 0; i < TheISA::NumIntRegs; ++i)
- {
- regFile.intRegFile[i] = src_xc->readIntReg(i);
+ // Threads start in the Suspended State
+ if (src_tc->status() != ThreadContext::Suspended) {
+ continue;
}
- // Then loop through the floating point registers.
- for (int i = 0; i < TheISA::NumFloatRegs; ++i)
- {
- regFile.floatRegFile.setRegBits(i, src_xc->readRegBits(i))
- }
-/*
- // Then loop through the misc registers.
- regFile.miscRegs.fpcr = src_xc->regs.miscRegs.fpcr;
- regFile.miscRegs.uniq = src_xc->regs.miscRegs.uniq;
- regFile.miscRegs.lock_flag = src_xc->regs.miscRegs.lock_flag;
- regFile.miscRegs.lock_addr = src_xc->regs.miscRegs.lock_addr;
-*/
- // Then finally set the PC and the next PC.
- regFile.pc = src_xc->readPC();
- regFile.npc = src_xc->readNextPC();
+#if FULL_SYSTEM
+ TheISA::initCPU(src_tc, src_tc->readCpuId());
+#endif
+ }
+
+ // Clear inSyscall.
+ for (int i = 0; i < number_of_threads; ++i)
+ thread[i]->inSyscall = false;
+
+ // Initialize stages.
+ fetch.initStage();
+ iew.initStage();
+ rename.initStage();
+ commit.initStage();
+
+ commit.setThreads(thread);
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::insertThread(unsigned tid)
+{
+ DPRINTF(FullCPU,"[tid:%i] Initializing thread data");
+ // Will change now that the PC and thread state is internal to the CPU
+ // and not in the ThreadContext.
+#if 0
+#if FULL_SYSTEM
+ ThreadContext *src_tc = system->threadContexts[tid];
+#else
+ ThreadContext *src_tc = thread[tid];
+#endif
+
+ //Bind Int Regs to Rename Map
+ for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) {
+ PhysRegIndex phys_reg = freeList.getIntReg();
+
+ renameMap[tid].setEntry(ireg,phys_reg);
+ scoreboard.setReg(phys_reg);
+ }
+
+ //Bind Float Regs to Rename Map
+ for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) {
+ PhysRegIndex phys_reg = freeList.getFloatReg();
+
+ renameMap[tid].setEntry(freg,phys_reg);
+ scoreboard.setReg(phys_reg);
+ }
+
+ //Copy Thread Data Into RegFile
+ this->copyFromTC(tid);
+
+ //Set PC/NPC
+ regFile.pc[tid] = src_tc->readPC();
+ regFile.npc[tid] = src_tc->readNextPC();
+
+ src_tc->setStatus(ThreadContext::Active);
+
+ activateContext(tid,1);
+
+ //Reset ROB/IQ/LSQ Entries
+ commit.rob->resetEntries();
+ iew.resetEntries();
+#endif
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::removeThread(unsigned tid)
+{
+ DPRINTF(FullCPU,"[tid:%i] Removing thread data");
+#if 0
+ //Unbind Int Regs from Rename Map
+ for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) {
+ PhysRegIndex phys_reg = renameMap[tid].lookup(ireg);
+
+ scoreboard.unsetReg(phys_reg);
+ freeList.addReg(phys_reg);
+ }
+
+ //Unbind Float Regs from Rename Map
+ for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) {
+ PhysRegIndex phys_reg = renameMap[tid].lookup(freg);
+
+ scoreboard.unsetReg(phys_reg);
+ freeList.addReg(phys_reg);
}
+
+ //Copy Thread Data From RegFile
+ /* Fix Me:
+ * Do we really need to do this if we are removing a thread
+ * in the sense that it's finished (exiting)? If the thread is just
+ * being suspended we might...
+ */
+// this->copyToTC(tid);
+
+ //Squash Throughout Pipeline
+ fetch.squash(0,tid);
+ decode.squash(tid);
+ rename.squash(tid);
+
+ assert(iew.ldstQueue.getCount(tid) == 0);
+
+ //Reset ROB/IQ/LSQ Entries
+ if (activeThreads.size() >= 1) {
+ commit.rob->resetEntries();
+ iew.resetEntries();
+ }
+#endif
}
+
template <class Impl>
void
-FullO3CPU<Impl>::activateContext(int thread_num, int delay)
+FullO3CPU<Impl>::activateWhenReady(int tid)
+{
+ DPRINTF(FullCPU,"[tid:%i]: Checking if resources are available for incoming"
+ "(e.g. PhysRegs/ROB/IQ/LSQ) \n",
+ tid);
+
+ bool ready = true;
+
+ if (freeList.numFreeIntRegs() >= TheISA::NumIntRegs) {
+ DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough "
+ "Phys. Int. Regs.\n",
+ tid);
+ ready = false;
+ } else if (freeList.numFreeFloatRegs() >= TheISA::NumFloatRegs) {
+ DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough "
+ "Phys. Float. Regs.\n",
+ tid);
+ ready = false;
+ } else if (commit.rob->numFreeEntries() >=
+ commit.rob->entryAmount(activeThreads.size() + 1)) {
+ DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough "
+ "ROB entries.\n",
+ tid);
+ ready = false;
+ } else if (iew.instQueue.numFreeEntries() >=
+ iew.instQueue.entryAmount(activeThreads.size() + 1)) {
+ DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough "
+ "IQ entries.\n",
+ tid);
+ ready = false;
+ } else if (iew.ldstQueue.numFreeEntries() >=
+ iew.ldstQueue.entryAmount(activeThreads.size() + 1)) {
+ DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough "
+ "LSQ entries.\n",
+ tid);
+ ready = false;
+ }
+
+ if (ready) {
+ insertThread(tid);
+
+ contextSwitch = false;
+
+ cpuWaitList.remove(tid);
+ } else {
+ suspendContext(tid);
+
+ //blocks fetch
+ contextSwitch = true;
+
+ //do waitlist
+ cpuWaitList.push_back(tid);
+ }
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::activateContext(int tid, int delay)
{
// Needs to set each stage to running as well.
+ list<unsigned>::iterator isActive = 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(FullCPU, "Adding Thread %i to active threads list\n",
+ tid);
+
+ activeThreads.push_back(tid);
+ }
+
+ assert(_status == Idle || _status == SwitchedOut);
scheduleTickEvent(delay);
+ // Be sure to signal that there's some activity so the CPU doesn't
+ // deschedule itself.
+ activityRec.activity();
+ fetch.wakeFromQuiesce();
+
_status = Running;
}
template <class Impl>
void
-FullO3CPU<Impl>::suspendContext(int thread_num)
+FullO3CPU<Impl>::suspendContext(int tid)
{
- panic("suspendContext unimplemented!");
+ DPRINTF(FullCPU,"[tid: %i]: Suspended ...\n", tid);
+ unscheduleTickEvent();
+ _status = Idle;
+/*
+ //Remove From Active List, if Active
+ list<unsigned>::iterator isActive = find(
+ activeThreads.begin(), activeThreads.end(), tid);
+
+ if (isActive != activeThreads.end()) {
+ DPRINTF(FullCPU,"[tid:%i]: Removing from active threads list\n",
+ tid);
+ activeThreads.erase(isActive);
+ }
+*/
}
template <class Impl>
void
-FullO3CPU<Impl>::deallocateContext(int thread_num)
+FullO3CPU<Impl>::deallocateContext(int tid)
{
- panic("deallocateContext unimplemented!");
+ DPRINTF(FullCPU,"[tid:%i]: Deallocating ...", tid);
+/*
+ //Remove From Active List, if Active
+ list<unsigned>::iterator isActive = find(
+ activeThreads.begin(), activeThreads.end(), tid);
+
+ if (isActive != activeThreads.end()) {
+ DPRINTF(FullCPU,"[tid:%i]: Removing from active threads list\n",
+ tid);
+ activeThreads.erase(isActive);
+
+ removeThread(tid);
+ }
+*/
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::haltContext(int tid)
+{
+ DPRINTF(FullCPU,"[tid:%i]: Halted ...", tid);
+/*
+ //Remove From Active List, if Active
+ list<unsigned>::iterator isActive = find(
+ activeThreads.begin(), activeThreads.end(), tid);
+
+ if (isActive != activeThreads.end()) {
+ DPRINTF(FullCPU,"[tid:%i]: Removing from active threads list\n",
+ tid);
+ activeThreads.erase(isActive);
+
+ removeThread(tid);
+ }
+*/
}
template <class Impl>
void
-FullO3CPU<Impl>::haltContext(int thread_num)
+FullO3CPU<Impl>::switchOut(Sampler *_sampler)
{
- panic("haltContext unimplemented!");
+ sampler = _sampler;
+ switchCount = 0;
+ fetch.switchOut();
+ decode.switchOut();
+ rename.switchOut();
+ iew.switchOut();
+ commit.switchOut();
+
+ // Wake the CPU and record activity so everything can drain out if
+ // the CPU is currently idle.
+ wakeCPU();
+ activityRec.activity();
}
template <class Impl>
void
-FullO3CPU<Impl>::switchOut()
+FullO3CPU<Impl>::signalSwitched()
{
- panic("FullO3CPU does not have a switch out function.\n");
+ if (++switchCount == NumStages) {
+ fetch.doSwitchOut();
+ rename.doSwitchOut();
+ commit.doSwitchOut();
+ instList.clear();
+ while (!removeList.empty()) {
+ removeList.pop();
+ }
+
+ if (checker)
+ checker->switchOut(sampler);
+
+ if (tickEvent.scheduled())
+ tickEvent.squash();
+ sampler->signalSwitched();
+ _status = SwitchedOut;
+ }
+ assert(switchCount <= 5);
}
template <class Impl>
void
FullO3CPU<Impl>::takeOverFrom(BaseCPU *oldCPU)
{
+ // Flush out any old data from the time buffers.
+ for (int i = 0; i < 10; ++i) {
+ timeBuffer.advance();
+ fetchQueue.advance();
+ decodeQueue.advance();
+ renameQueue.advance();
+ iewQueue.advance();
+ }
+
+ activityRec.reset();
+
BaseCPU::takeOverFrom(oldCPU);
+ fetch.takeOverFrom();
+ decode.takeOverFrom();
+ rename.takeOverFrom();
+ iew.takeOverFrom();
+ commit.takeOverFrom();
+
assert(!tickEvent.scheduled());
- // Set all status's to active, schedule the
- // CPU's tick event.
- for (int i = 0; i < execContexts.size(); ++i) {
- ExecContext *xc = execContexts[i];
- if (xc->status() == ExecContext::Active && _status != Running) {
+ // @todo: Figure out how to properly select the tid to put onto
+ // the active threads list.
+ int tid = 0;
+
+ list<unsigned>::iterator isActive = 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(FullCPU, "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
+ for (int i = 0; i < threadContexts.size(); ++i) {
+ ThreadContext *tc = threadContexts[i];
+ if (tc->status() == ThreadContext::Active && _status != Running) {
_status = Running;
tickEvent.schedule(curTick);
}
}
-}
-
-template <class Impl>
-InstSeqNum
-FullO3CPU<Impl>::getAndIncrementInstSeq()
-{
- // Hopefully this works right.
- return globalSeqNum++;
+ if (!tickEvent.scheduled())
+ tickEvent.schedule(curTick);
}
template <class Impl>
@@ -411,156 +821,368 @@ FullO3CPU<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val)
template <class Impl>
uint64_t
-FullO3CPU<Impl>::readPC()
+FullO3CPU<Impl>::readArchIntReg(int reg_idx, unsigned tid)
+{
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx);
+
+ return regFile.readIntReg(phys_reg);
+}
+
+template <class Impl>
+float
+FullO3CPU<Impl>::readArchFloatRegSingle(int reg_idx, unsigned tid)
+{
+ int idx = reg_idx + TheISA::FP_Base_DepTag;
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx);
+
+ return regFile.readFloatReg(phys_reg);
+}
+
+template <class Impl>
+double
+FullO3CPU<Impl>::readArchFloatRegDouble(int reg_idx, unsigned tid)
+{
+ int idx = reg_idx + TheISA::FP_Base_DepTag;
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx);
+
+ return regFile.readFloatReg(phys_reg, 64);
+}
+
+template <class Impl>
+uint64_t
+FullO3CPU<Impl>::readArchFloatRegInt(int reg_idx, unsigned tid)
{
- return regFile.readPC();
+ int idx = reg_idx + TheISA::FP_Base_DepTag;
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx);
+
+ return regFile.readFloatRegBits(phys_reg);
}
template <class Impl>
void
-FullO3CPU<Impl>::setNextPC(uint64_t val)
+FullO3CPU<Impl>::setArchIntReg(int reg_idx, uint64_t val, unsigned tid)
{
- regFile.setNextPC(val);
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx);
+
+ regFile.setIntReg(phys_reg, val);
}
template <class Impl>
void
-FullO3CPU<Impl>::setPC(Addr new_PC)
+FullO3CPU<Impl>::setArchFloatRegSingle(int reg_idx, float val, unsigned tid)
{
- regFile.setPC(new_PC);
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx);
+
+ regFile.setFloatReg(phys_reg, val);
}
template <class Impl>
void
-FullO3CPU<Impl>::addInst(DynInstPtr &inst)
+FullO3CPU<Impl>::setArchFloatRegDouble(int reg_idx, double val, unsigned tid)
{
- instList.push_back(inst);
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx);
+
+ regFile.setFloatReg(phys_reg, val, 64);
}
template <class Impl>
void
-FullO3CPU<Impl>::instDone()
+FullO3CPU<Impl>::setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid)
{
- // Keep an instruction count.
- numInsts++;
+ PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx);
- // Check for instruction-count-based events.
- comInstEventQueue[0]->serviceEvents(numInsts);
+ regFile.setFloatRegBits(phys_reg, val);
+}
+
+template <class Impl>
+uint64_t
+FullO3CPU<Impl>::readPC(unsigned tid)
+{
+ return commit.readPC(tid);
}
template <class Impl>
void
-FullO3CPU<Impl>::removeBackInst(DynInstPtr &inst)
+FullO3CPU<Impl>::setPC(Addr new_PC,unsigned tid)
{
- DynInstPtr inst_to_delete;
+ commit.setPC(new_PC, tid);
+}
- // Walk through the instruction list, removing any instructions
- // that were inserted after the given instruction, inst.
- while (instList.back() != inst)
- {
- assert(!instList.empty());
+template <class Impl>
+uint64_t
+FullO3CPU<Impl>::readNextPC(unsigned tid)
+{
+ return commit.readNextPC(tid);
+}
- // Obtain the pointer to the instruction.
- inst_to_delete = instList.back();
+template <class Impl>
+void
+FullO3CPU<Impl>::setNextPC(uint64_t val,unsigned tid)
+{
+ commit.setNextPC(val, tid);
+}
- DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n",
- inst_to_delete->seqNum, inst_to_delete->readPC());
+template <class Impl>
+typename FullO3CPU<Impl>::ListIt
+FullO3CPU<Impl>::addInst(DynInstPtr &inst)
+{
+ instList.push_back(inst);
- // Remove the instruction from the list.
- instList.pop_back();
+ return --(instList.end());
+}
- // Mark it as squashed.
- inst_to_delete->setSquashed();
- }
+template <class Impl>
+void
+FullO3CPU<Impl>::instDone(unsigned tid)
+{
+ // Keep an instruction count.
+ thread[tid]->numInst++;
+ thread[tid]->numInsts++;
+ committedInsts[tid]++;
+ totalCommittedInsts++;
+
+ // Check for instruction-count-based events.
+ comInstEventQueue[tid]->serviceEvents(thread[tid]->numInst);
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::addToRemoveList(DynInstPtr &inst)
+{
+ removeInstsThisCycle = true;
+
+ removeList.push(inst->getInstListIt());
}
template <class Impl>
void
FullO3CPU<Impl>::removeFrontInst(DynInstPtr &inst)
{
- DynInstPtr inst_to_remove;
+ DPRINTF(FullCPU, "FullCPU: Removing committed instruction [tid:%i] PC %#x "
+ "[sn:%lli]\n",
+ inst->threadNumber, inst->readPC(), inst->seqNum);
- // The front instruction should be the same one being asked to be removed.
- assert(instList.front() == inst);
+ removeInstsThisCycle = true;
// Remove the front instruction.
- inst_to_remove = inst;
- instList.pop_front();
-
- DPRINTF(FullCPU, "FullCPU: Removing committed instruction %#x, PC %#x\n",
- inst_to_remove, inst_to_remove->readPC());
+ removeList.push(inst->getInstListIt());
}
template <class Impl>
void
-FullO3CPU<Impl>::removeInstsNotInROB()
+FullO3CPU<Impl>::removeInstsNotInROB(unsigned tid)
{
- DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction "
- "list.\n");
+ DPRINTF(FullCPU, "FullCPU: Thread %i: Deleting instructions from instruction"
+ " list.\n", tid);
+
+ ListIt end_it;
+
+ bool rob_empty = false;
+
+ if (instList.empty()) {
+ return;
+ } else if (rob.isEmpty(/*tid*/)) {
+ DPRINTF(FullCPU, "FullCPU: ROB is empty, squashing all insts.\n");
+ end_it = instList.begin();
+ rob_empty = true;
+ } else {
+ end_it = (rob.readTailInst(tid))->getInstListIt();
+ DPRINTF(FullCPU, "FullCPU: ROB is not empty, squashing insts not in ROB.\n");
+ }
- DynInstPtr rob_tail = rob.readTailInst();
+ removeInstsThisCycle = true;
- removeBackInst(rob_tail);
+ ListIt inst_it = instList.end();
+
+ inst_it--;
+
+ // Walk through the instruction list, removing any instructions
+ // that were inserted after the given instruction iterator, end_it.
+ while (inst_it != end_it) {
+ assert(!instList.empty());
+
+ squashInstIt(inst_it, tid);
+
+ inst_it--;
+ }
+
+ // If the ROB was empty, then we actually need to remove the first
+ // instruction as well.
+ if (rob_empty) {
+ squashInstIt(inst_it, tid);
+ }
}
template <class Impl>
void
-FullO3CPU<Impl>::removeInstsUntil(const InstSeqNum &seq_num)
+FullO3CPU<Impl>::removeInstsUntil(const InstSeqNum &seq_num,
+ unsigned tid)
{
+ assert(!instList.empty());
+
+ removeInstsThisCycle = true;
+
+ ListIt inst_iter = instList.end();
+
+ inst_iter--;
+
DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction "
- "list.\n");
+ "list that are from [tid:%i] and above [sn:%lli] (end=%lli).\n",
+ tid, seq_num, (*inst_iter)->seqNum);
- DynInstPtr inst_to_delete;
+ while ((*inst_iter)->seqNum > seq_num) {
- while (instList.back()->seqNum > seq_num) {
- assert(!instList.empty());
+ bool break_loop = (inst_iter == instList.begin());
- // Obtain the pointer to the instruction.
- inst_to_delete = instList.back();
+ squashInstIt(inst_iter, tid);
- DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n",
- inst_to_delete->seqNum, inst_to_delete->readPC());
+ inst_iter--;
- // Remove the instruction from the list.
- instList.back() = NULL;
- instList.pop_back();
+ if (break_loop)
+ break;
+ }
+}
+
+template <class Impl>
+inline void
+FullO3CPU<Impl>::squashInstIt(const ListIt &instIt, const unsigned &tid)
+{
+ if ((*instIt)->threadNumber == tid) {
+ DPRINTF(FullCPU, "FullCPU: Squashing instruction, "
+ "[tid:%i] [sn:%lli] PC %#x\n",
+ (*instIt)->threadNumber,
+ (*instIt)->seqNum,
+ (*instIt)->readPC());
// Mark it as squashed.
- inst_to_delete->setSquashed();
- }
+ (*instIt)->setSquashed();
+ // @todo: Formulate a consistent method for deleting
+ // instructions from the instruction list
+ // Remove the instruction from the list.
+ removeList.push(instIt);
+ }
}
template <class Impl>
void
+FullO3CPU<Impl>::cleanUpRemovedInsts()
+{
+ while (!removeList.empty()) {
+ DPRINTF(FullCPU, "FullCPU: Removing instruction, "
+ "[tid:%i] [sn:%lli] PC %#x\n",
+ (*removeList.front())->threadNumber,
+ (*removeList.front())->seqNum,
+ (*removeList.front())->readPC());
+
+ instList.erase(removeList.front());
+
+ removeList.pop();
+ }
+
+ removeInstsThisCycle = false;
+}
+/*
+template <class Impl>
+void
FullO3CPU<Impl>::removeAllInsts()
{
instList.clear();
}
-
+*/
template <class Impl>
void
FullO3CPU<Impl>::dumpInsts()
{
int num = 0;
- typename list<DynInstPtr>::iterator inst_list_it = instList.begin();
- while (inst_list_it != instList.end())
- {
- cprintf("Instruction:%i\nPC:%#x\nSN:%lli\nIssued:%i\nSquashed:%i\n\n",
- num, (*inst_list_it)->readPC(), (*inst_list_it)->seqNum,
- (*inst_list_it)->isIssued(), (*inst_list_it)->isSquashed());
+ ListIt inst_list_it = instList.begin();
+
+ cprintf("Dumping Instruction List\n");
+
+ while (inst_list_it != instList.end()) {
+ cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n"
+ "Squashed:%i\n\n",
+ num, (*inst_list_it)->readPC(), (*inst_list_it)->threadNumber,
+ (*inst_list_it)->seqNum, (*inst_list_it)->isIssued(),
+ (*inst_list_it)->isSquashed());
inst_list_it++;
++num;
}
}
-
+/*
template <class Impl>
void
FullO3CPU<Impl>::wakeDependents(DynInstPtr &inst)
{
iew.wakeDependents(inst);
}
+*/
+template <class Impl>
+void
+FullO3CPU<Impl>::wakeCPU()
+{
+ if (activityRec.active() || tickEvent.scheduled()) {
+ DPRINTF(Activity, "CPU already running.\n");
+ return;
+ }
+
+ DPRINTF(Activity, "Waking up CPU\n");
+
+ idleCycles += (curTick - 1) - lastRunningCycle;
+
+ tickEvent.schedule(curTick);
+}
+
+template <class Impl>
+int
+FullO3CPU<Impl>::getFreeTid()
+{
+ for (int i=0; i < numThreads; i++) {
+ if (!tids[i]) {
+ tids[i] = true;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::doContextSwitch()
+{
+ if (contextSwitch) {
+
+ //ADD CODE TO DEACTIVE THREAD HERE (???)
+
+ for (int tid=0; tid < cpuWaitList.size(); tid++) {
+ activateWhenReady(tid);
+ }
+
+ if (cpuWaitList.size() == 0)
+ contextSwitch = true;
+ }
+}
+
+template <class Impl>
+void
+FullO3CPU<Impl>::updateThreadPriority()
+{
+ if (activeThreads.size() > 1)
+ {
+ //DEFAULT TO ROUND ROBIN SCHEME
+ //e.g. Move highest priority to end of thread list
+ list<unsigned>::iterator list_begin = activeThreads.begin();
+ list<unsigned>::iterator list_end = activeThreads.end();
+
+ unsigned high_thread = *list_begin;
+
+ activeThreads.erase(list_begin);
+
+ activeThreads.push_back(high_thread);
+ }
+}
// Forward declaration of FullO3CPU.
template class FullO3CPU<AlphaSimpleImpl>;
diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh
index f7c80e8a1..ff41a3306 100644
--- a/src/cpu/o3/cpu.hh
+++ b/src/cpu/o3/cpu.hh
@@ -24,33 +24,36 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-//Todo: Add in a lot of the functions that are ISA specific. Also define
-//the functions that currently exist within the base cpu class. Define
-//everything for the simobject stuff so it can be serialized and
-//instantiated, add in debugging statements everywhere. Have CPU schedule
-//itself properly. Threads!
-// Avoid running stages and advancing queues if idle/stalled.
-
-#ifndef __CPU_O3_CPU_FULL_CPU_HH__
-#define __CPU_O3_CPU_FULL_CPU_HH__
+#ifndef __CPU_O3_CPU_HH__
+#define __CPU_O3_CPU_HH__
#include <iostream>
#include <list>
+#include <queue>
+#include <set>
#include <vector>
+#include "arch/isa_traits.hh"
#include "base/statistics.hh"
#include "base/timebuf.hh"
#include "config/full_system.hh"
+#include "cpu/activity.hh"
#include "cpu/base.hh"
-#include "cpu/cpu_exec_context.hh"
+#include "cpu/simple_thread.hh"
#include "cpu/o3/comm.hh"
#include "cpu/o3/cpu_policy.hh"
+#include "cpu/o3/scoreboard.hh"
+#include "cpu/o3/thread_state.hh"
#include "sim/process.hh"
-class ExecContext;
-class FunctionalMemory;
+template <class>
+class Checker;
+class ThreadContext;
+class MemObject;
class Process;
class BaseFullCPU : public BaseCPU
@@ -59,59 +62,79 @@ class BaseFullCPU : public BaseCPU
public:
typedef BaseCPU::Params Params;
-#if FULL_SYSTEM
- BaseFullCPU(Params &params);
-#else
- BaseFullCPU(Params &params);
-#endif // FULL_SYSTEM
+ BaseFullCPU(Params *params);
+
+ void regStats();
+
+ int readCpuId() { return cpu_id; }
protected:
int cpu_id;
};
+/**
+ * FullO3CPU class, has each of the stages (fetch through commit)
+ * within it, as well as all of the time buffers between stages. The
+ * tick() function for the CPU is defined here.
+ */
template <class Impl>
class FullO3CPU : public BaseFullCPU
{
public:
- //Put typedefs from the Impl here.
+ typedef TheISA::FloatReg FloatReg;
+ typedef TheISA::FloatRegBits FloatRegBits;
+
+ // Typedefs from the Impl here.
typedef typename Impl::CPUPol CPUPolicy;
typedef typename Impl::Params Params;
typedef typename Impl::DynInstPtr DynInstPtr;
+ typedef O3ThreadState<Impl> Thread;
+
+ typedef typename std::list<DynInstPtr>::iterator ListIt;
+
public:
enum Status {
Running,
Idle,
Halted,
- Blocked // ?
+ Blocked,
+ SwitchedOut
};
+ /** Overall CPU status. */
Status _status;
private:
class TickEvent : public Event
{
private:
+ /** Pointer to the CPU. */
FullO3CPU<Impl> *cpu;
public:
+ /** Constructs a tick event. */
TickEvent(FullO3CPU<Impl> *c);
+
+ /** Processes a tick event, calling tick() on the CPU. */
void process();
+ /** Returns the description of the tick event. */
const char *description();
};
+ /** The tick event used for scheduling CPU ticks. */
TickEvent tickEvent;
- /// Schedule tick event, regardless of its current state.
+ /** Schedule tick event, regardless of its current state. */
void scheduleTickEvent(int delay)
{
if (tickEvent.squashed())
- tickEvent.reschedule(curTick + delay);
+ tickEvent.reschedule(curTick + cycles(delay));
else if (!tickEvent.scheduled())
- tickEvent.schedule(curTick + delay);
+ tickEvent.schedule(curTick + cycles(delay));
}
- /// Unschedule tick event, regardless of its current state.
+ /** Unschedule tick event, regardless of its current state. */
void unscheduleTickEvent()
{
if (tickEvent.scheduled())
@@ -119,25 +142,83 @@ class FullO3CPU : public BaseFullCPU
}
public:
- FullO3CPU(Params &params);
+ /** Constructs a CPU with the given parameters. */
+ FullO3CPU(Params *params);
+ /** Destructor. */
~FullO3CPU();
+ /** Registers statistics. */
void fullCPURegStats();
+ /** Ticks CPU, calling tick() on each stage, and checking the overall
+ * activity to see if the CPU should deschedule itself.
+ */
void tick();
+ /** Initialize the CPU */
void init();
- void activateContext(int thread_num, int delay);
- void suspendContext(int thread_num);
- void deallocateContext(int thread_num);
- void haltContext(int thread_num);
+ /** Setup CPU to insert a thread's context */
+ void insertThread(unsigned tid);
+
+ /** Remove all of a thread's context from CPU */
+ void removeThread(unsigned tid);
+
+ /** Count the Total Instructions Committed in the CPU. */
+ virtual Counter totalInstructions() const
+ {
+ Counter total(0);
+
+ for (int i=0; i < thread.size(); i++)
+ total += thread[i]->numInst;
+
+ return total;
+ }
+
+ /** Add Thread to Active Threads List. */
+ void activateContext(int tid, int delay);
+
+ /** Remove Thread from Active Threads List */
+ void suspendContext(int tid);
+
+ /** Remove Thread from Active Threads List &&
+ * Remove Thread Context from CPU.
+ */
+ void deallocateContext(int tid);
- void switchOut();
+ /** Remove Thread from Active Threads List &&
+ * Remove Thread Context from CPU.
+ */
+ void haltContext(int tid);
+
+ /** Activate a Thread When CPU Resources are Available. */
+ void activateWhenReady(int tid);
+
+ /** Add or Remove a Thread Context in the CPU. */
+ void doContextSwitch();
+
+ /** Update The Order In Which We Process Threads. */
+ void updateThreadPriority();
+
+ /** Executes a syscall on this cycle.
+ * ---------------------------------------
+ * Note: this is a virtual function. CPU-Specific
+ * functionality defined in derived classes
+ */
+ virtual void syscall(int tid) { panic("Unimplemented!"); }
+
+ /** Switches out this CPU. */
+ void switchOut(Sampler *sampler);
+
+ /** Signals to this CPU that a stage has completed switching out. */
+ void signalSwitched();
+
+ /** Takes over from another CPU. */
void takeOverFrom(BaseCPU *oldCPU);
/** Get the current instruction sequence number, and increment it. */
- InstSeqNum getAndIncrementInstSeq();
+ InstSeqNum getAndIncrementInstSeq()
+ { return globalSeqNum++; }
#if FULL_SYSTEM
/** Check if this address is a valid instruction address. */
@@ -147,27 +228,24 @@ class FullO3CPU : public BaseFullCPU
bool validDataAddr(Addr addr) { return true; }
/** Get instruction asid. */
- int getInstAsid()
- { return regFile.miscRegs.getInstAsid(); }
+ int getInstAsid(unsigned tid)
+ { return regFile.miscRegs[tid].getInstAsid(); }
/** Get data asid. */
- int getDataAsid()
- { return regFile.miscRegs.getDataAsid(); }
+ int getDataAsid(unsigned tid)
+ { return regFile.miscRegs[tid].getDataAsid(); }
#else
- bool validInstAddr(Addr addr)
- { return thread[0]->validInstAddr(addr); }
-
- bool validDataAddr(Addr addr)
- { return thread[0]->validDataAddr(addr); }
+ /** Get instruction asid. */
+ int getInstAsid(unsigned tid)
+ { return thread[tid]->getInstAsid(); }
- int getInstAsid() { return thread[0]->getInstAsid(); }
- int getDataAsid() { return thread[0]->getDataAsid(); }
+ /** Get data asid. */
+ int getDataAsid(unsigned tid)
+ { return thread[tid]->getDataAsid(); }
#endif
- //
- // New accessors for new decoder.
- //
+ /** Register accessors. Index refers to the physical register index. */
uint64_t readIntReg(int reg_idx);
FloatReg readFloatReg(int reg_idx);
@@ -180,103 +258,115 @@ class FullO3CPU : public BaseFullCPU
void setIntReg(int reg_idx, uint64_t val);
- void setFloatReg(int reg_idx, FloatReg val, int width);
+ void setFloatReg(int reg_idx, FloatReg val);
void setFloatReg(int reg_idx, FloatReg val, int width);
void setFloatRegBits(int reg_idx, FloatRegBits val);
- void setFloatRegBits(int reg_idx, FloatRegBits val);
+ void setFloatRegBits(int reg_idx, FloatRegBits val, int width);
+
+ uint64_t readArchIntReg(int reg_idx, unsigned tid);
+
+ float readArchFloatRegSingle(int reg_idx, unsigned tid);
+
+ double readArchFloatRegDouble(int reg_idx, unsigned tid);
+
+ uint64_t readArchFloatRegInt(int reg_idx, unsigned tid);
+
+ /** Architectural register accessors. Looks up in the commit
+ * rename table to obtain the true physical index of the
+ * architected register first, then accesses that physical
+ * register.
+ */
+ void setArchIntReg(int reg_idx, uint64_t val, unsigned tid);
+
+ void setArchFloatRegSingle(int reg_idx, float val, unsigned tid);
- uint64_t readPC();
+ void setArchFloatRegDouble(int reg_idx, double val, unsigned tid);
- void setNextPC(uint64_t val);
+ void setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid);
- void setPC(Addr new_PC);
+ /** Reads the commit PC of a specific thread. */
+ uint64_t readPC(unsigned tid);
+
+ /** Sets the commit PC of a specific thread. */
+ void setPC(Addr new_PC, unsigned tid);
+
+ /** Reads the next PC of a specific thread. */
+ uint64_t readNextPC(unsigned tid);
+
+ /** Sets the next PC of a specific thread. */
+ void setNextPC(uint64_t val, unsigned tid);
/** Function to add instruction onto the head of the list of the
* instructions. Used when new instructions are fetched.
*/
- void addInst(DynInstPtr &inst);
+ ListIt addInst(DynInstPtr &inst);
/** Function to tell the CPU that an instruction has completed. */
- void instDone();
-
- /** Remove all instructions in back of the given instruction, but leave
- * that instruction in the list. This is useful in a squash, when there
- * are instructions in this list that don't exist in structures such as
- * the ROB. The instruction doesn't have to be the last instruction in
- * the list, but will be once this function completes.
- * @todo: Remove only up until that inst? Squashed inst is most likely
- * valid.
- */
- void removeBackInst(DynInstPtr &inst);
-
- /** Remove an instruction from the front of the list. It is expected
- * that there are no instructions in front of it (that is, none are older
- * than the instruction being removed). Used when retiring instructions.
- * @todo: Remove the argument to this function, and just have it remove
- * last instruction once it's verified that commit has the same ordering
- * as the instruction list.
+ void instDone(unsigned tid);
+
+ /** Add Instructions to the CPU Remove List*/
+ void addToRemoveList(DynInstPtr &inst);
+
+ /** Remove an instruction from the front end of the list. There's
+ * no restriction on location of the instruction.
*/
void removeFrontInst(DynInstPtr &inst);
/** Remove all instructions that are not currently in the ROB. */
- void removeInstsNotInROB();
+ void removeInstsNotInROB(unsigned tid);
/** Remove all instructions younger than the given sequence number. */
- void removeInstsUntil(const InstSeqNum &seq_num);
+ void removeInstsUntil(const InstSeqNum &seq_num,unsigned tid);
- /** Remove all instructions from the list. */
- void removeAllInsts();
+ /** Removes the instruction pointed to by the iterator. */
+ inline void squashInstIt(const ListIt &instIt, const unsigned &tid);
- void dumpInsts();
+ /** Cleans up all instructions on the remove list. */
+ void cleanUpRemovedInsts();
- /** Basically a wrapper function so that instructions executed at
- * commit can tell the instruction queue that they have completed.
- * Eventually this hack should be removed.
- */
- void wakeDependents(DynInstPtr &inst);
+ /** Debug function to print all instructions on the list. */
+ void dumpInsts();
public:
/** List of all the instructions in flight. */
- list<DynInstPtr> instList;
+ std::list<DynInstPtr> instList;
+
+ /** List of all the instructions that will be removed at the end of this
+ * cycle.
+ */
+ std::queue<ListIt> removeList;
+
+#ifdef DEBUG
+ /** Debug structure to keep track of the sequence numbers still in
+ * flight.
+ */
+ std::set<InstSeqNum> snList;
+#endif
+
+ /** Records if instructions need to be removed this cycle due to
+ * being retired or squashed.
+ */
+ bool removeInstsThisCycle;
- //not sure these should be private.
protected:
/** The fetch stage. */
typename CPUPolicy::Fetch fetch;
- /** The fetch stage's status. */
- typename CPUPolicy::Fetch::Status fetchStatus;
-
/** The decode stage. */
typename CPUPolicy::Decode decode;
- /** The decode stage's status. */
- typename CPUPolicy::Decode::Status decodeStatus;
-
/** The dispatch stage. */
typename CPUPolicy::Rename rename;
- /** The dispatch stage's status. */
- typename CPUPolicy::Rename::Status renameStatus;
-
/** The issue/execute/writeback stages. */
typename CPUPolicy::IEW iew;
- /** The issue/execute/writeback stage's status. */
- typename CPUPolicy::IEW::Status iewStatus;
-
/** The commit stage. */
typename CPUPolicy::Commit commit;
- /** The fetch stage's status. */
- typename CPUPolicy::Commit::Status commitStatus;
-
- //Might want to just pass these objects in to the constructors of the
- //appropriate stage. regFile is in iew, freeList in dispatch, renameMap
- //in dispatch, and the rob in commit.
/** The register file. */
typename CPUPolicy::RegFile regFile;
@@ -284,12 +374,33 @@ class FullO3CPU : public BaseFullCPU
typename CPUPolicy::FreeList freeList;
/** The rename map. */
- typename CPUPolicy::RenameMap renameMap;
+ typename CPUPolicy::RenameMap renameMap[Impl::MaxThreads];
+
+ /** The commit rename map. */
+ typename CPUPolicy::RenameMap commitRenameMap[Impl::MaxThreads];
/** The re-order buffer. */
typename CPUPolicy::ROB rob;
+ /** Active Threads List */
+ std::list<unsigned> activeThreads;
+
+ /** Integer Register Scoreboard */
+ Scoreboard scoreboard;
+
public:
+ /** Enum to give each stage a specific index, so when calling
+ * activateStage() or deactivateStage(), they can specify which stage
+ * is being activated/deactivated.
+ */
+ enum StageIdx {
+ FetchIdx,
+ DecodeIdx,
+ RenameIdx,
+ IEWIdx,
+ CommitIdx,
+ NumStages };
+
/** Typedefs from the Impl to get the structs that each of the
* time buffers should use.
*/
@@ -318,46 +429,109 @@ class FullO3CPU : public BaseFullCPU
/** The IEW stage's instruction queue. */
TimeBuffer<IEWStruct> iewQueue;
+ private:
+ /** The activity recorder; used to tell if the CPU has any
+ * activity remaining or if it can go to idle and deschedule
+ * itself.
+ */
+ ActivityRecorder activityRec;
+
public:
- /** The temporary exec context to support older accessors. */
- CPUExecContext *cpuXC;
+ /** Records that there was time buffer activity this cycle. */
+ void activityThisCycle() { activityRec.activity(); }
- /** Temporary function to get pointer to exec context. */
- ExecContext *xcBase()
- {
- return thread[0]->getProxy();
- }
+ /** Changes a stage's status to active within the activity recorder. */
+ void activateStage(const StageIdx idx)
+ { activityRec.activateStage(idx); }
+
+ /** Changes a stage's status to inactive within the activity recorder. */
+ void deactivateStage(const StageIdx idx)
+ { activityRec.deactivateStage(idx); }
- CPUExecContext *cpuXCBase()
+ /** Wakes the CPU, rescheduling the CPU if it's not already active. */
+ void wakeCPU();
+
+ /** Gets a free thread id. Use if thread ids change across system. */
+ int getFreeTid();
+
+ public:
+ /** Returns a pointer to a thread context. */
+ ThreadContext *tcBase(unsigned tid)
{
- return thread[0];
+ return thread[tid]->getTC();
}
+ /** The global sequence number counter. */
InstSeqNum globalSeqNum;
+ /** Pointer to the checker, which can dynamically verify
+ * instruction results at run time. This can be set to NULL if it
+ * is not being used.
+ */
+ Checker<DynInstPtr> *checker;
+
#if FULL_SYSTEM
+ /** Pointer to the system. */
System *system;
- MemoryController *memCtrl;
+ /** Pointer to physical memory. */
PhysicalMemory *physmem;
+#endif
- AlphaITB *itb;
- AlphaDTB *dtb;
+ /** Pointer to memory. */
+ MemObject *mem;
-// SWContext *swCtx;
-#endif
- std::vector<CPUExecContext *> thread;
+ /** Pointer to the sampler */
+ Sampler *sampler;
+
+ /** Counter of how many stages have completed switching out. */
+ int switchCount;
- FunctionalMemory *mem;
+ /** Pointers to all of the threads in the CPU. */
+ std::vector<Thread *> thread;
+ /** Pointer to the icache interface. */
MemInterface *icacheInterface;
+ /** Pointer to the dcache interface. */
MemInterface *dcacheInterface;
+ /** Whether or not the CPU should defer its registration. */
bool deferRegistration;
- Counter numInsts;
-
- Counter funcExeInst;
+ /** Is there a context switch pending? */
+ bool contextSwitch;
+
+ /** Threads Scheduled to Enter CPU */
+ std::list<int> cpuWaitList;
+
+ /** The cycle that the CPU was last running, used for statistics. */
+ Tick lastRunningCycle;
+
+ /** Number of Threads CPU can process */
+ unsigned numThreads;
+
+ /** Mapping for system thread id to cpu id */
+ std::map<unsigned,unsigned> threadMap;
+
+ /** Available thread ids in the cpu*/
+ std::vector<unsigned> tids;
+
+ /** Stat for total number of times the CPU is descheduled. */
+ Stats::Scalar<> timesIdled;
+ /** Stat for total number of cycles the CPU spends descheduled. */
+ Stats::Scalar<> idleCycles;
+ /** Stat for the number of committed instructions per thread. */
+ Stats::Vector<> committedInsts;
+ /** Stat for the total number of committed instructions. */
+ Stats::Scalar<> totalCommittedInsts;
+ /** Stat for the CPI per thread. */
+ Stats::Formula cpi;
+ /** Stat for the total CPI. */
+ Stats::Formula totalCpi;
+ /** Stat for the IPC per thread. */
+ Stats::Formula ipc;
+ /** Stat for the total IPC. */
+ Stats::Formula totalIpc;
};
-#endif
+#endif // __CPU_O3_CPU_HH__
diff --git a/src/cpu/o3/cpu_policy.hh b/src/cpu/o3/cpu_policy.hh
index 41f06f81b..32a0adcf1 100644
--- a/src/cpu/o3/cpu_policy.hh
+++ b/src/cpu/o3/cpu_policy.hh
@@ -24,15 +24,18 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_CPU_POLICY_HH__
-#define __CPU_O3_CPU_CPU_POLICY_HH__
+#ifndef __CPU_O3_CPU_POLICY_HH__
+#define __CPU_O3_CPU_POLICY_HH__
#include "cpu/o3/bpred_unit.hh"
#include "cpu/o3/free_list.hh"
#include "cpu/o3/inst_queue.hh"
-#include "cpu/o3/ldstq.hh"
+#include "cpu/o3/lsq.hh"
+#include "cpu/o3/lsq_unit.hh"
#include "cpu/o3/mem_dep_unit.hh"
#include "cpu/o3/regfile.hh"
#include "cpu/o3/rename_map.hh"
@@ -47,42 +50,70 @@
#include "cpu/o3/comm.hh"
+/**
+ * Struct that defines the key classes to be used by the CPU. All
+ * classes use the typedefs defined here to determine what are the
+ * classes of the other stages and communication buffers. In order to
+ * change a structure such as the IQ, simply change the typedef here
+ * to use the desired class instead, and recompile. In order to
+ * create a different CPU to be used simultaneously with this one, see
+ * the alpha_impl.hh file for instructions.
+ */
template<class Impl>
struct SimpleCPUPolicy
{
- typedef TwobitBPredUnit<Impl> BPredUnit;
+ /** Typedef for the branch prediction unit (which includes the BP,
+ * RAS, and BTB).
+ */
+ typedef BPredUnit<Impl> BPredUnit;
+ /** Typedef for the register file. Most classes assume a unified
+ * physical register file.
+ */
typedef PhysRegFile<Impl> RegFile;
+ /** Typedef for the freelist of registers. */
typedef SimpleFreeList FreeList;
+ /** Typedef for the rename map. */
typedef SimpleRenameMap RenameMap;
+ /** Typedef for the ROB. */
typedef ROB<Impl> ROB;
+ /** Typedef for the instruction queue/scheduler. */
typedef InstructionQueue<Impl> IQ;
+ /** Typedef for the memory dependence unit. */
typedef MemDepUnit<StoreSet, Impl> MemDepUnit;
- typedef LDSTQ<Impl> LDSTQ;
+ /** Typedef for the LSQ. */
+ typedef LSQ<Impl> LSQ;
+ /** Typedef for the thread-specific LSQ units. */
+ typedef LSQUnit<Impl> LSQUnit;
- typedef SimpleFetch<Impl> Fetch;
- typedef SimpleDecode<Impl> Decode;
- typedef SimpleRename<Impl> Rename;
- typedef SimpleIEW<Impl> IEW;
- typedef SimpleCommit<Impl> Commit;
+ /** Typedef for fetch. */
+ typedef DefaultFetch<Impl> Fetch;
+ /** Typedef for decode. */
+ typedef DefaultDecode<Impl> Decode;
+ /** Typedef for rename. */
+ typedef DefaultRename<Impl> Rename;
+ /** Typedef for Issue/Execute/Writeback. */
+ typedef DefaultIEW<Impl> IEW;
+ /** Typedef for commit. */
+ typedef DefaultCommit<Impl> Commit;
/** The struct for communication between fetch and decode. */
- typedef SimpleFetchSimpleDecode<Impl> FetchStruct;
+ typedef DefaultFetchDefaultDecode<Impl> FetchStruct;
/** The struct for communication between decode and rename. */
- typedef SimpleDecodeSimpleRename<Impl> DecodeStruct;
+ typedef DefaultDecodeDefaultRename<Impl> DecodeStruct;
/** The struct for communication between rename and IEW. */
- typedef SimpleRenameSimpleIEW<Impl> RenameStruct;
+ typedef DefaultRenameDefaultIEW<Impl> RenameStruct;
/** The struct for communication between IEW and commit. */
- typedef SimpleIEWSimpleCommit<Impl> IEWStruct;
+ typedef DefaultIEWDefaultCommit<Impl> IEWStruct;
/** The struct for communication within the IEW stage. */
typedef IssueStruct<Impl> IssueStruct;
/** The struct for all backwards communication. */
- typedef TimeBufStruct TimeStruct;
+ typedef TimeBufStruct<Impl> TimeStruct;
};
-#endif //__CPU_O3_CPU_CPU_POLICY_HH__
+#endif //__CPU_O3_CPU_POLICY_HH__
diff --git a/src/cpu/o3/decode.cc b/src/cpu/o3/decode.cc
index 290648318..4924f018a 100644
--- a/src/cpu/o3/decode.cc
+++ b/src/cpu/o3/decode.cc
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
#include "cpu/o3/alpha_impl.hh"
#include "cpu/o3/decode_impl.hh"
-template class SimpleDecode<AlphaSimpleImpl>;
+template class DefaultDecode<AlphaSimpleImpl>;
diff --git a/src/cpu/o3/decode.hh b/src/cpu/o3/decode.hh
index 5b9a0f822..ff88358d6 100644
--- a/src/cpu/o3/decode.hh
+++ b/src/cpu/o3/decode.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,18 +24,27 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_SIMPLE_DECODE_HH__
-#define __CPU_O3_CPU_SIMPLE_DECODE_HH__
+#ifndef __CPU_O3_DECODE_HH__
+#define __CPU_O3_DECODE_HH__
#include <queue>
#include "base/statistics.hh"
#include "base/timebuf.hh"
+/**
+ * DefaultDecode class handles both single threaded and SMT
+ * decode. Its width is specified by the parameters; each cycles it
+ * tries to decode that many instructions. Because instructions are
+ * actually decoded when the StaticInst is created, this stage does
+ * not do much other than check any PC-relative branches.
+ */
template<class Impl>
-class SimpleDecode
+class DefaultDecode
{
private:
// Typedefs from the Impl.
@@ -50,49 +59,132 @@ class SimpleDecode
typedef typename CPUPol::TimeStruct TimeStruct;
public:
- // The only time decode will become blocked is if dispatch becomes
- // blocked, which means IQ or ROB is probably full.
- enum Status {
+ /** Overall decode stage status. Used to determine if the CPU can
+ * deschedule itself due to a lack of activity.
+ */
+ enum DecodeStatus {
+ Active,
+ Inactive
+ };
+
+ /** Individual thread status. */
+ enum ThreadStatus {
Running,
Idle,
+ StartSquash,
Squashing,
Blocked,
Unblocking
};
private:
- // May eventually need statuses on a per thread basis.
- Status _status;
+ /** Decode status. */
+ DecodeStatus _status;
+
+ /** Per-thread status. */
+ ThreadStatus decodeStatus[Impl::MaxThreads];
public:
- SimpleDecode(Params &params);
+ /** DefaultDecode constructor. */
+ DefaultDecode(Params *params);
+
+ /** Returns the name of decode. */
+ std::string name() const;
+ /** Registers statistics. */
void regStats();
+ /** Sets CPU pointer. */
void setCPU(FullCPU *cpu_ptr);
+ /** Sets the main backwards communication time buffer pointer. */
void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);
+ /** Sets pointer to time buffer used to communicate to the next stage. */
void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr);
+ /** Sets pointer to time buffer coming from fetch. */
void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr);
+ /** Sets pointer to list of active threads. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
+
+ /** Switches out the decode stage. */
+ void switchOut();
+
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
+
+ /** Ticks decode, processing all input signals and decoding as many
+ * instructions as possible.
+ */
void tick();
- void decode();
+ /** Determines what to do based on decode's current status.
+ * @param status_change decode() sets this variable if there was a status
+ * change (ie switching from from blocking to unblocking).
+ * @param tid Thread id to decode instructions from.
+ */
+ void decode(bool &status_change, unsigned tid);
+
+ /** Processes instructions from fetch and passes them on to rename.
+ * Decoding of instructions actually happens when they are created in
+ * fetch, so this function mostly checks if PC-relative branches are
+ * correct.
+ */
+ void decodeInsts(unsigned tid);
private:
+ /** Inserts a thread's instructions into the skid buffer, to be decoded
+ * once decode unblocks.
+ */
+ void skidInsert(unsigned tid);
+
+ /** Returns if all of the skid buffers are empty. */
+ bool skidsEmpty();
+
+ /** Updates overall decode status based on all of the threads' statuses. */
+ void updateStatus();
+
+ /** Separates instructions from fetch into individual lists of instructions
+ * sorted by thread.
+ */
+ void sortInsts();
+
+ /** Reads all stall signals from the backwards communication timebuffer. */
+ void readStallSignals(unsigned tid);
+
+ /** Checks all input signals and updates decode's status appropriately. */
+ bool checkSignalsAndUpdate(unsigned tid);
+
+ /** Checks all stall signals, and returns if any are true. */
+ bool checkStall(unsigned tid) const;
+
+ /** Returns if there any instructions from fetch on this cycle. */
inline bool fetchInstsValid();
- void block();
+ /** Switches decode to blocking, and signals back that decode has
+ * become blocked.
+ * @return Returns true if there is a status change.
+ */
+ bool block(unsigned tid);
- inline void unblock();
+ /** Switches decode to unblocking if the skid buffer is empty, and
+ * signals back that decode has unblocked.
+ * @return Returns true if there is a status change.
+ */
+ bool unblock(unsigned tid);
- void squash(DynInstPtr &inst);
+ /** Squashes if there is a PC-relative branch that was predicted
+ * incorrectly. Sends squash information back to fetch.
+ */
+ void squash(DynInstPtr &inst, unsigned tid);
public:
- // Might want to make squash a friend function.
- void squash();
+ /** Squashes due to commit signalling a squash. Changes status to
+ * squashing and clears block/unblock signals as needed.
+ */
+ unsigned squash(unsigned tid);
private:
// Interfaces to objects outside of decode.
@@ -127,10 +219,27 @@ class SimpleDecode
/** Wire to get fetch's output from fetch queue. */
typename TimeBuffer<FetchStruct>::wire fromFetch;
+ /** Queue of all instructions coming from fetch this cycle. */
+ std::queue<DynInstPtr> insts[Impl::MaxThreads];
+
/** Skid buffer between fetch and decode. */
- std::queue<FetchStruct> skidBuffer;
+ std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads];
+
+ /** Variable that tracks if decode has written to the time buffer this
+ * cycle. Used to tell CPU if there is activity this cycle.
+ */
+ bool wroteToTimeBuffer;
+
+ /** Source of possible stalls. */
+ struct Stalls {
+ bool rename;
+ bool iew;
+ bool commit;
+ };
+
+ /** Tracks which stages are telling decode to stall. */
+ Stalls stalls[Impl::MaxThreads];
- //Consider making these unsigned to avoid any confusion.
/** Rename to decode delay, in ticks. */
unsigned renameToDecodeDelay;
@@ -146,20 +255,43 @@ class SimpleDecode
/** The width of decode, in instructions. */
unsigned decodeWidth;
- /** The instruction that decode is currently on. It needs to have
- * persistent state so that when a stall occurs in the middle of a
- * group of instructions, it can restart at the proper instruction.
- */
- unsigned numInst;
+ /** Index of instructions being sent to rename. */
+ unsigned toRenameIndex;
+
+ /** number of Active Threads*/
+ unsigned numThreads;
+ /** List of active thread ids */
+ std::list<unsigned> *activeThreads;
+
+ /** Number of branches in flight. */
+ unsigned branchCount[Impl::MaxThreads];
+
+ /** Maximum size of the skid buffer. */
+ unsigned skidBufferMax;
+
+ /** Stat for total number of idle cycles. */
Stats::Scalar<> decodeIdleCycles;
+ /** Stat for total number of blocked cycles. */
Stats::Scalar<> decodeBlockedCycles;
+ /** Stat for total number of normal running cycles. */
+ Stats::Scalar<> decodeRunCycles;
+ /** Stat for total number of unblocking cycles. */
Stats::Scalar<> decodeUnblockCycles;
+ /** Stat for total number of squashing cycles. */
Stats::Scalar<> decodeSquashCycles;
+ /** Stat for number of times a branch is resolved at decode. */
+ Stats::Scalar<> decodeBranchResolved;
+ /** Stat for number of times a branch mispredict is detected. */
Stats::Scalar<> decodeBranchMispred;
+ /** Stat for number of times decode detected a non-control instruction
+ * incorrectly predicted as a branch.
+ */
Stats::Scalar<> decodeControlMispred;
+ /** Stat for total number of decoded instructions. */
Stats::Scalar<> decodeDecodedInsts;
+ /** Stat for total number of squashed instructions. */
Stats::Scalar<> decodeSquashedInsts;
};
-#endif // __CPU_O3_CPU_SIMPLE_DECODE_HH__
+#endif // __CPU_O3_DECODE_HH__
diff --git a/src/cpu/o3/decode_impl.hh b/src/cpu/o3/decode_impl.hh
index 463f0ddac..8a6ea6626 100644
--- a/src/cpu/o3/decode_impl.hh
+++ b/src/cpu/o3/decode_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,75 +24,105 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/decode.hh"
+using namespace std;
+
template<class Impl>
-SimpleDecode<Impl>::SimpleDecode(Params &params)
- : renameToDecodeDelay(params.renameToDecodeDelay),
- iewToDecodeDelay(params.iewToDecodeDelay),
- commitToDecodeDelay(params.commitToDecodeDelay),
- fetchToDecodeDelay(params.fetchToDecodeDelay),
- decodeWidth(params.decodeWidth),
- numInst(0)
+DefaultDecode<Impl>::DefaultDecode(Params *params)
+ : renameToDecodeDelay(params->renameToDecodeDelay),
+ iewToDecodeDelay(params->iewToDecodeDelay),
+ commitToDecodeDelay(params->commitToDecodeDelay),
+ fetchToDecodeDelay(params->fetchToDecodeDelay),
+ decodeWidth(params->decodeWidth),
+ numThreads(params->numberOfThreads)
{
- DPRINTF(Decode, "Decode: decodeWidth=%i.\n", decodeWidth);
- _status = Idle;
+ _status = Inactive;
+
+ // Setup status, make sure stall signals are clear.
+ for (int i = 0; i < numThreads; ++i) {
+ decodeStatus[i] = Idle;
+
+ stalls[i].rename = false;
+ stalls[i].iew = false;
+ stalls[i].commit = false;
+ }
+
+ // @todo: Make into a parameter
+ skidBufferMax = (fetchToDecodeDelay * params->fetchWidth) + decodeWidth;
+}
+
+template <class Impl>
+std::string
+DefaultDecode<Impl>::name() const
+{
+ return cpu->name() + ".decode";
}
template <class Impl>
void
-SimpleDecode<Impl>::regStats()
+DefaultDecode<Impl>::regStats()
{
decodeIdleCycles
- .name(name() + ".decodeIdleCycles")
+ .name(name() + ".DECODE:IdleCycles")
.desc("Number of cycles decode is idle")
.prereq(decodeIdleCycles);
decodeBlockedCycles
- .name(name() + ".decodeBlockedCycles")
+ .name(name() + ".DECODE:BlockedCycles")
.desc("Number of cycles decode is blocked")
.prereq(decodeBlockedCycles);
+ decodeRunCycles
+ .name(name() + ".DECODE:RunCycles")
+ .desc("Number of cycles decode is running")
+ .prereq(decodeRunCycles);
decodeUnblockCycles
- .name(name() + ".decodeUnblockCycles")
+ .name(name() + ".DECODE:UnblockCycles")
.desc("Number of cycles decode is unblocking")
.prereq(decodeUnblockCycles);
decodeSquashCycles
- .name(name() + ".decodeSquashCycles")
+ .name(name() + ".DECODE:SquashCycles")
.desc("Number of cycles decode is squashing")
.prereq(decodeSquashCycles);
+ decodeBranchResolved
+ .name(name() + ".DECODE:BranchResolved")
+ .desc("Number of times decode resolved a branch")
+ .prereq(decodeBranchResolved);
decodeBranchMispred
- .name(name() + ".decodeBranchMispred")
+ .name(name() + ".DECODE:BranchMispred")
.desc("Number of times decode detected a branch misprediction")
.prereq(decodeBranchMispred);
decodeControlMispred
- .name(name() + ".decodeControlMispred")
+ .name(name() + ".DECODE:ControlMispred")
.desc("Number of times decode detected an instruction incorrectly"
" predicted as a control")
.prereq(decodeControlMispred);
decodeDecodedInsts
- .name(name() + ".decodeDecodedInsts")
+ .name(name() + ".DECODE:DecodedInsts")
.desc("Number of instructions handled by decode")
.prereq(decodeDecodedInsts);
decodeSquashedInsts
- .name(name() + ".decodeSquashedInsts")
+ .name(name() + ".DECODE:SquashedInsts")
.desc("Number of squashed instructions handled by decode")
.prereq(decodeSquashedInsts);
}
template<class Impl>
void
-SimpleDecode<Impl>::setCPU(FullCPU *cpu_ptr)
+DefaultDecode<Impl>::setCPU(FullCPU *cpu_ptr)
{
- DPRINTF(Decode, "Decode: Setting CPU pointer.\n");
+ DPRINTF(Decode, "Setting CPU pointer.\n");
cpu = cpu_ptr;
}
template<class Impl>
void
-SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
+DefaultDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
- DPRINTF(Decode, "Decode: Setting time buffer pointer.\n");
+ DPRINTF(Decode, "Setting time buffer pointer.\n");
timeBuffer = tb_ptr;
// Setup wire to write information back to fetch.
@@ -106,9 +136,9 @@ SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
template<class Impl>
void
-SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
+DefaultDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
{
- DPRINTF(Decode, "Decode: Setting decode queue pointer.\n");
+ DPRINTF(Decode, "Setting decode queue pointer.\n");
decodeQueue = dq_ptr;
// Setup wire to write information to proper place in decode queue.
@@ -117,9 +147,9 @@ SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
template<class Impl>
void
-SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
+DefaultDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
{
- DPRINTF(Decode, "Decode: Setting fetch queue pointer.\n");
+ DPRINTF(Decode, "Setting fetch queue pointer.\n");
fetchQueue = fq_ptr;
// Setup wire to read information from fetch queue.
@@ -127,250 +157,541 @@ SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
}
template<class Impl>
+void
+DefaultDecode<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ DPRINTF(Decode, "Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
+}
+
+template <class Impl>
+void
+DefaultDecode<Impl>::switchOut()
+{
+ // Decode can immediately switch out.
+ cpu->signalSwitched();
+}
+
+template <class Impl>
+void
+DefaultDecode<Impl>::takeOverFrom()
+{
+ _status = Inactive;
+
+ // Be sure to reset state and clear out any old instructions.
+ for (int i = 0; i < numThreads; ++i) {
+ decodeStatus[i] = Idle;
+
+ stalls[i].rename = false;
+ stalls[i].iew = false;
+ stalls[i].commit = false;
+ while (!insts[i].empty())
+ insts[i].pop();
+ while (!skidBuffer[i].empty())
+ skidBuffer[i].pop();
+ branchCount[i] = 0;
+ }
+ wroteToTimeBuffer = false;
+}
+
+template<class Impl>
+bool
+DefaultDecode<Impl>::checkStall(unsigned tid) const
+{
+ bool ret_val = false;
+
+ if (stalls[tid].rename) {
+ DPRINTF(Decode,"[tid:%i]: Stall fom Rename stage detected.\n", tid);
+ ret_val = true;
+ } else if (stalls[tid].iew) {
+ DPRINTF(Decode,"[tid:%i]: Stall fom IEW stage detected.\n", tid);
+ ret_val = true;
+ } else if (stalls[tid].commit) {
+ DPRINTF(Decode,"[tid:%i]: Stall fom Commit stage detected.\n", tid);
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+template<class Impl>
inline bool
-SimpleDecode<Impl>::fetchInstsValid()
+DefaultDecode<Impl>::fetchInstsValid()
{
return fromFetch->size > 0;
}
template<class Impl>
-void
-SimpleDecode<Impl>::block()
+bool
+DefaultDecode<Impl>::block(unsigned tid)
{
- DPRINTF(Decode, "Decode: Blocking.\n");
-
- // Set the status to Blocked.
- _status = Blocked;
+ DPRINTF(Decode, "[tid:%u]: Blocking.\n", tid);
// Add the current inputs to the skid buffer so they can be
// reprocessed when this stage unblocks.
- skidBuffer.push(*fromFetch);
+ skidInsert(tid);
+
+ // If the decode status is blocked or unblocking then decode has not yet
+ // signalled fetch to unblock. In that case, there is no need to tell
+ // fetch to block.
+ if (decodeStatus[tid] != Blocked) {
+ // Set the status to Blocked.
+ decodeStatus[tid] = Blocked;
+
+ if (decodeStatus[tid] != Unblocking) {
+ toFetch->decodeBlock[tid] = true;
+ wroteToTimeBuffer = true;
+ }
- // Note that this stage only signals previous stages to stall when
- // it is the cause of the stall originates at this stage. Otherwise
- // the previous stages are expected to check all possible stall signals.
+ return true;
+ }
+
+ return false;
}
template<class Impl>
-inline void
-SimpleDecode<Impl>::unblock()
+bool
+DefaultDecode<Impl>::unblock(unsigned tid)
{
- DPRINTF(Decode, "Decode: Unblocking, going to remove "
- "instructions from skid buffer.\n");
- // Remove the now processed instructions from the skid buffer.
- skidBuffer.pop();
-
- // If there's still information in the skid buffer, then
- // continue to tell previous stages to stall. They will be
- // able to restart once the skid buffer is empty.
- if (!skidBuffer.empty()) {
- toFetch->decodeInfo.stall = true;
- } else {
- DPRINTF(Decode, "Decode: Finished unblocking.\n");
- _status = Running;
+ // Decode is done unblocking only if the skid buffer is empty.
+ if (skidBuffer[tid].empty()) {
+ DPRINTF(Decode, "[tid:%u]: Done unblocking.\n", tid);
+ toFetch->decodeUnblock[tid] = true;
+ wroteToTimeBuffer = true;
+
+ decodeStatus[tid] = Running;
+ return true;
}
+
+ DPRINTF(Decode, "[tid:%u]: Currently unblocking.\n", tid);
+
+ return false;
}
-// This squash is specifically for when Decode detects a PC-relative branch
-// was predicted incorrectly.
template<class Impl>
void
-SimpleDecode<Impl>::squash(DynInstPtr &inst)
+DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid)
{
- DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction "
- "detected at decode.\n");
- Addr new_PC = inst->readNextPC();
-
- toFetch->decodeInfo.branchMispredict = true;
- toFetch->decodeInfo.doneSeqNum = inst->seqNum;
- toFetch->decodeInfo.predIncorrect = true;
- toFetch->decodeInfo.squash = true;
- toFetch->decodeInfo.nextPC = new_PC;
- toFetch->decodeInfo.branchTaken = true;
+ DPRINTF(Decode, "[tid:%i]: Squashing due to incorrect branch prediction "
+ "detected at decode.\n", tid);
+
+ // Send back mispredict information.
+ toFetch->decodeInfo[tid].branchMispredict = true;
+ toFetch->decodeInfo[tid].doneSeqNum = inst->seqNum;
+ toFetch->decodeInfo[tid].predIncorrect = true;
+ toFetch->decodeInfo[tid].squash = true;
+ toFetch->decodeInfo[tid].nextPC = inst->branchTarget();
+ toFetch->decodeInfo[tid].branchTaken =
+ inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst));
+
+ // Might have to tell fetch to unblock.
+ if (decodeStatus[tid] == Blocked ||
+ decodeStatus[tid] == Unblocking) {
+ toFetch->decodeUnblock[tid] = 1;
+ }
// Set status to squashing.
- _status = Squashing;
+ decodeStatus[tid] = Squashing;
+
+ for (int i=0; i<fromFetch->size; i++) {
+ if (fromFetch->insts[i]->threadNumber == tid &&
+ fromFetch->insts[i]->seqNum > inst->seqNum) {
+ fromFetch->insts[i]->squashed = true;
+ }
+ }
+
+ // Clear the instruction list and skid buffer in case they have any
+ // insts in them.
+ while (!insts[tid].empty()) {
+ insts[tid].pop();
+ }
- // Clear the skid buffer in case it has any data in it.
- while (!skidBuffer.empty()) {
- skidBuffer.pop();
+ while (!skidBuffer[tid].empty()) {
+ skidBuffer[tid].pop();
}
// Squash instructions up until this one
- // Slightly unrealistic!
- cpu->removeInstsUntil(inst->seqNum);
+ cpu->removeInstsUntil(inst->seqNum, tid);
}
template<class Impl>
-void
-SimpleDecode<Impl>::squash()
+unsigned
+DefaultDecode<Impl>::squash(unsigned tid)
{
- DPRINTF(Decode, "Decode: Squashing.\n");
+ DPRINTF(Decode, "[tid:%i]: Squashing.\n",tid);
+
+ if (decodeStatus[tid] == Blocked ||
+ decodeStatus[tid] == Unblocking) {
+#if !FULL_SYSTEM
+ // In syscall emulation, we can have both a block and a squash due
+ // to a syscall in the same cycle. This would cause both signals to
+ // be high. This shouldn't happen in full system.
+ // @todo: Determine if this still happens.
+ if (toFetch->decodeBlock[tid]) {
+ toFetch->decodeBlock[tid] = 0;
+ } else {
+ toFetch->decodeUnblock[tid] = 1;
+ }
+#else
+ toFetch->decodeUnblock[tid] = 1;
+#endif
+ }
+
// Set status to squashing.
- _status = Squashing;
+ decodeStatus[tid] = Squashing;
- // Maybe advance the time buffer? Not sure what to do in the normal
- // case.
+ // Go through incoming instructions from fetch and squash them.
+ unsigned squash_count = 0;
- // Clear the skid buffer in case it has any data in it.
- while (!skidBuffer.empty())
- {
- skidBuffer.pop();
+ for (int i=0; i<fromFetch->size; i++) {
+ if (fromFetch->insts[i]->threadNumber == tid) {
+ fromFetch->insts[i]->squashed = true;
+ squash_count++;
+ }
}
+
+ // Clear the instruction list and skid buffer in case they have any
+ // insts in them.
+ while (!insts[tid].empty()) {
+ insts[tid].pop();
+ }
+
+ while (!skidBuffer[tid].empty()) {
+ skidBuffer[tid].pop();
+ }
+
+ return squash_count;
}
template<class Impl>
void
-SimpleDecode<Impl>::tick()
+DefaultDecode<Impl>::skidInsert(unsigned tid)
{
- // Decode should try to execute as many instructions as its bandwidth
- // will allow, as long as it is not currently blocked.
- if (_status != Blocked && _status != Squashing) {
- DPRINTF(Decode, "Decode: Not blocked, so attempting to run "
- "stage.\n");
- // Make sure that the skid buffer has something in it if the
- // status is unblocking.
- assert(_status == Unblocking ? !skidBuffer.empty() : 1);
+ DynInstPtr inst = NULL;
- decode();
+ while (!insts[tid].empty()) {
+ inst = insts[tid].front();
- // If the status was unblocking, then instructions from the skid
- // buffer were used. Remove those instructions and handle
- // the rest of unblocking.
- if (_status == Unblocking) {
- ++decodeUnblockCycles;
+ insts[tid].pop();
- if (fetchInstsValid()) {
- // Add the current inputs to the skid buffer so they can be
- // reprocessed when this stage unblocks.
- skidBuffer.push(*fromFetch);
- }
+ assert(tid == inst->threadNumber);
- unblock();
- }
- } else if (_status == Blocked) {
- ++decodeBlockedCycles;
+ DPRINTF(Decode,"Inserting [sn:%lli] PC:%#x into decode skidBuffer %i\n",
+ inst->seqNum, inst->readPC(), inst->threadNumber);
- if (fetchInstsValid()) {
- block();
- }
+ skidBuffer[tid].push(inst);
+ }
- if (!fromRename->renameInfo.stall &&
- !fromIEW->iewInfo.stall &&
- !fromCommit->commitInfo.stall) {
- DPRINTF(Decode, "Decode: Stall signals cleared, going to "
- "unblock.\n");
- _status = Unblocking;
+ // @todo: Eventually need to enforce this by not letting a thread
+ // fetch past its skidbuffer
+ assert(skidBuffer[tid].size() <= skidBufferMax);
+}
- // Continue to tell previous stage to block until this
- // stage is done unblocking.
- toFetch->decodeInfo.stall = true;
- } else {
- DPRINTF(Decode, "Decode: Still blocked.\n");
- toFetch->decodeInfo.stall = true;
+template<class Impl>
+bool
+DefaultDecode<Impl>::skidsEmpty()
+{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ if (!skidBuffer[*threads++].empty())
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+void
+DefaultDecode<Impl>::updateStatus()
+{
+ bool any_unblocking = false;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (decodeStatus[tid] == Unblocking) {
+ any_unblocking = true;
+ break;
}
+ }
+
+ // Decode will have activity if it's unblocking.
+ if (any_unblocking) {
+ if (_status == Inactive) {
+ _status = Active;
+
+ DPRINTF(Activity, "Activating stage.\n");
- if (fromCommit->commitInfo.squash ||
- fromCommit->commitInfo.robSquashing) {
- squash();
+ cpu->activateStage(FullCPU::DecodeIdx);
}
- } else if (_status == Squashing) {
- if (!fromCommit->commitInfo.squash &&
- !fromCommit->commitInfo.robSquashing) {
- _status = Running;
- } else if (fromCommit->commitInfo.squash) {
- ++decodeSquashCycles;
-
- squash();
+ } else {
+ // If it's not unblocking, then decode will not have any internal
+ // activity. Switch it to inactive.
+ if (_status == Active) {
+ _status = Inactive;
+ DPRINTF(Activity, "Deactivating stage.\n");
+
+ cpu->deactivateStage(FullCPU::DecodeIdx);
}
}
}
+template <class Impl>
+void
+DefaultDecode<Impl>::sortInsts()
+{
+ int insts_from_fetch = fromFetch->size;
+#ifdef DEBUG
+ for (int i=0; i < numThreads; i++)
+ assert(insts[i].empty());
+#endif
+ for (int i = 0; i < insts_from_fetch; ++i) {
+ insts[fromFetch->insts[i]->threadNumber].push(fromFetch->insts[i]);
+ }
+}
+
template<class Impl>
void
-SimpleDecode<Impl>::decode()
+DefaultDecode<Impl>::readStallSignals(unsigned tid)
{
- // Check time buffer if being told to squash.
- if (fromCommit->commitInfo.squash) {
- squash();
- return;
+ if (fromRename->renameBlock[tid]) {
+ stalls[tid].rename = true;
}
- // Check time buffer if being told to stall.
- if (fromRename->renameInfo.stall ||
- fromIEW->iewInfo.stall ||
- fromCommit->commitInfo.stall) {
- block();
- return;
+ if (fromRename->renameUnblock[tid]) {
+ assert(stalls[tid].rename);
+ stalls[tid].rename = false;
+ }
+
+ if (fromIEW->iewBlock[tid]) {
+ stalls[tid].iew = true;
+ }
+
+ if (fromIEW->iewUnblock[tid]) {
+ assert(stalls[tid].iew);
+ stalls[tid].iew = false;
+ }
+
+ if (fromCommit->commitBlock[tid]) {
+ stalls[tid].commit = true;
+ }
+
+ if (fromCommit->commitUnblock[tid]) {
+ assert(stalls[tid].commit);
+ stalls[tid].commit = false;
+ }
+}
+
+template <class Impl>
+bool
+DefaultDecode<Impl>::checkSignalsAndUpdate(unsigned tid)
+{
+ // Check if there's a squash signal, squash if there is.
+ // Check stall signals, block if necessary.
+ // If status was blocked
+ // Check if stall conditions have passed
+ // if so then go to unblocking
+ // If status was Squashing
+ // check if squashing is not high. Switch to running this cycle.
+
+ // Update the per thread stall statuses.
+ readStallSignals(tid);
+
+ // Check squash signals from commit.
+ if (fromCommit->commitInfo[tid].squash) {
+
+ DPRINTF(Decode, "[tid:%u]: Squashing instructions due to squash "
+ "from commit.\n", tid);
+
+ squash(tid);
+
+ return true;
+ }
+
+ // Check ROB squash signals from commit.
+ if (fromCommit->commitInfo[tid].robSquashing) {
+ DPRINTF(Decode, "[tid:%]: ROB is still squashing.\n",tid);
+
+ // Continue to squash.
+ decodeStatus[tid] = Squashing;
+
+ return true;
+ }
+
+ if (checkStall(tid)) {
+ return block(tid);
}
- // Check fetch queue to see if instructions are available.
- // If no available instructions, do nothing, unless this stage is
- // currently unblocking.
- if (!fetchInstsValid() && _status != Unblocking) {
- DPRINTF(Decode, "Decode: Nothing to do, breaking out early.\n");
+ if (decodeStatus[tid] == Blocked) {
+ DPRINTF(Decode, "[tid:%u]: Done blocking, switching to unblocking.\n",
+ tid);
+
+ decodeStatus[tid] = Unblocking;
+
+ unblock(tid);
+
+ return true;
+ }
+
+ if (decodeStatus[tid] == Squashing) {
+ // Switch status to running if decode isn't being told to block or
+ // squash this cycle.
+ DPRINTF(Decode, "[tid:%u]: Done squashing, switching to running.\n",
+ tid);
+
+ decodeStatus[tid] = Running;
+
+ return false;
+ }
+
+ // If we've reached this point, we have not gotten any signals that
+ // cause decode to change its status. Decode remains the same as before.
+ return false;
+}
+
+template<class Impl>
+void
+DefaultDecode<Impl>::tick()
+{
+ wroteToTimeBuffer = false;
+
+ bool status_change = false;
+
+ toRenameIndex = 0;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ sortInsts();
+
+ //Check stall and squash signals.
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ DPRINTF(Decode,"Processing [tid:%i]\n",tid);
+ status_change = checkSignalsAndUpdate(tid) || status_change;
+
+ decode(status_change, tid);
+ }
+
+ if (status_change) {
+ updateStatus();
+ }
+
+ if (wroteToTimeBuffer) {
+ DPRINTF(Activity, "Activity this cycle.\n");
+
+ cpu->activityThisCycle();
+ }
+}
+
+template<class Impl>
+void
+DefaultDecode<Impl>::decode(bool &status_change, unsigned tid)
+{
+ // If status is Running or idle,
+ // call decodeInsts()
+ // If status is Unblocking,
+ // buffer any instructions coming from fetch
+ // continue trying to empty skid buffer
+ // check if stall conditions have passed
+
+ if (decodeStatus[tid] == Blocked) {
+ ++decodeBlockedCycles;
+ } else if (decodeStatus[tid] == Squashing) {
+ ++decodeSquashCycles;
+ }
+
+ // Decode should try to decode as many instructions as its bandwidth
+ // will allow, as long as it is not currently blocked.
+ if (decodeStatus[tid] == Running ||
+ decodeStatus[tid] == Idle) {
+ DPRINTF(Decode, "[tid:%u] Not blocked, so attempting to run "
+ "stage.\n",tid);
+
+ decodeInsts(tid);
+ } else if (decodeStatus[tid] == Unblocking) {
+ // Make sure that the skid buffer has something in it if the
+ // status is unblocking.
+ assert(!skidsEmpty());
+
+ // If the status was unblocking, then instructions from the skid
+ // buffer were used. Remove those instructions and handle
+ // the rest of unblocking.
+ decodeInsts(tid);
+
+ if (fetchInstsValid()) {
+ // Add the current inputs to the skid buffer so they can be
+ // reprocessed when this stage unblocks.
+ skidInsert(tid);
+ }
+
+ status_change = unblock(tid) || status_change;
+ }
+}
+
+template <class Impl>
+void
+DefaultDecode<Impl>::decodeInsts(unsigned tid)
+{
+ // Instructions can come either from the skid buffer or the list of
+ // instructions coming from fetch, depending on decode's status.
+ int insts_available = decodeStatus[tid] == Unblocking ?
+ skidBuffer[tid].size() : insts[tid].size();
+
+ if (insts_available == 0) {
+ DPRINTF(Decode, "[tid:%u] Nothing to do, breaking out"
+ " early.\n",tid);
// Should I change the status to idle?
++decodeIdleCycles;
return;
+ } else if (decodeStatus[tid] == Unblocking) {
+ DPRINTF(Decode, "[tid:%u] Unblocking, removing insts from skid "
+ "buffer.\n",tid);
+ ++decodeUnblockCycles;
+ } else if (decodeStatus[tid] == Running) {
+ ++decodeRunCycles;
}
- // Might be better to use a base DynInst * instead?
DynInstPtr inst;
- unsigned to_rename_index = 0;
+ std::queue<DynInstPtr>
+ &insts_to_decode = decodeStatus[tid] == Unblocking ?
+ skidBuffer[tid] : insts[tid];
- int insts_available = _status == Unblocking ?
- skidBuffer.front().size - numInst :
- fromFetch->size;
+ DPRINTF(Decode, "[tid:%u]: Sending instruction to rename.\n",tid);
- // Debug block...
-#if 0
- if (insts_available) {
- DPRINTF(Decode, "Decode: Instructions available.\n");
- } else {
- if (_status == Unblocking && skidBuffer.empty()) {
- DPRINTF(Decode, "Decode: No instructions available, skid buffer "
- "empty.\n");
- } else if (_status != Unblocking &&
- !fromFetch->insts[0]) {
- DPRINTF(Decode, "Decode: No instructions available, fetch queue "
- "empty.\n");
- } else {
- panic("Decode: No instructions available, unexpected condition!"
- "\n");
- }
- }
-#endif
+ while (insts_available > 0 && toRenameIndex < decodeWidth) {
+ assert(!insts_to_decode.empty());
- while (insts_available > 0)
- {
- DPRINTF(Decode, "Decode: Sending instruction to rename.\n");
+ inst = insts_to_decode.front();
- inst = _status == Unblocking ? skidBuffer.front().insts[numInst] :
- fromFetch->insts[numInst];
+ insts_to_decode.pop();
- DPRINTF(Decode, "Decode: Processing instruction %i with PC %#x\n",
- inst->seqNum, inst->readPC());
+ DPRINTF(Decode, "[tid:%u]: Processing instruction [sn:%lli] with "
+ "PC %#x\n",
+ tid, inst->seqNum, inst->readPC());
if (inst->isSquashed()) {
- DPRINTF(Decode, "Decode: Instruction %i with PC %#x is "
+ DPRINTF(Decode, "[tid:%u]: Instruction %i with PC %#x is "
"squashed, skipping.\n",
- inst->seqNum, inst->readPC());
+ tid, inst->seqNum, inst->readPC());
++decodeSquashedInsts;
- ++numInst;
--insts_available;
continue;
}
-
// Also check if instructions have no source registers. Mark
// them as ready to issue at any time. Not sure if this check
// should exist here or at a later stage; however it doesn't matter
// too much for function correctness.
- // Isn't this handled by the inst queue?
if (inst->numSrcRegs() == 0) {
inst->setCanIssue();
}
@@ -378,9 +699,12 @@ SimpleDecode<Impl>::decode()
// This current instruction is valid, so add it into the decode
// queue. The next instruction may not be valid, so check to
// see if branches were predicted correctly.
- toRename->insts[to_rename_index] = inst;
+ toRename->insts[toRenameIndex] = inst;
++(toRename->size);
+ ++toRenameIndex;
+ ++decodeDecodedInsts;
+ --insts_available;
// Ensure that if it was predicted as a branch, it really is a
// branch.
@@ -388,38 +712,40 @@ SimpleDecode<Impl>::decode()
panic("Instruction predicted as a branch!");
++decodeControlMispred;
+
// Might want to set some sort of boolean and just do
// a check at the end
- squash(inst);
+ squash(inst, inst->threadNumber);
+
break;
}
// Go ahead and compute any PC-relative branches.
-
if (inst->isDirectCtrl() && inst->isUncondCtrl()) {
+ ++decodeBranchResolved;
- inst->setNextPC(inst->branchTarget());
-
- if (inst->mispredicted()) {
+ if (inst->branchTarget() != inst->readPredTarg()) {
++decodeBranchMispred;
+
// Might want to set some sort of boolean and just do
// a check at the end
- squash(inst);
+ squash(inst, inst->threadNumber);
+ inst->setPredTarg(inst->branchTarget());
+
break;
}
}
+ }
- // Normally can check if a direct branch has the right target
- // addr (either the immediate, or the branch PC + 4) and redirect
- // fetch if it's incorrect.
-
- // Increment which instruction we're looking at.
- ++numInst;
- ++to_rename_index;
- ++decodeDecodedInsts;
-
- --insts_available;
+ // If we didn't process all instructions, then we will need to block
+ // and put all those instructions into the skid buffer.
+ if (!insts_to_decode.empty()) {
+ block(tid);
}
- numInst = 0;
+ // Record that decode has written to the time buffer for activity
+ // tracking.
+ if (toRenameIndex) {
+ wroteToTimeBuffer = true;
+ }
}
diff --git a/src/cpu/o3/dep_graph.hh b/src/cpu/o3/dep_graph.hh
new file mode 100644
index 000000000..3659b1a37
--- /dev/null
+++ b/src/cpu/o3/dep_graph.hh
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#ifndef __CPU_O3_DEP_GRAPH_HH__
+#define __CPU_O3_DEP_GRAPH_HH__
+
+#include "cpu/o3/comm.hh"
+
+/** Node in a linked list. */
+template <class DynInstPtr>
+class DependencyEntry
+{
+ public:
+ DependencyEntry()
+ : inst(NULL), next(NULL)
+ { }
+
+ DynInstPtr inst;
+ //Might want to include data about what arch. register the
+ //dependence is waiting on.
+ DependencyEntry<DynInstPtr> *next;
+};
+
+/** Array of linked list that maintains the dependencies between
+ * producing instructions and consuming instructions. Each linked
+ * list represents a single physical register, having the future
+ * producer of the register's value, and all consumers waiting on that
+ * value on the list. The head node of each linked list represents
+ * the producing instruction of that register. Instructions are put
+ * on the list upon reaching the IQ, and are removed from the list
+ * either when the producer completes, or the instruction is squashed.
+*/
+template <class DynInstPtr>
+class DependencyGraph
+{
+ public:
+ typedef DependencyEntry<DynInstPtr> DepEntry;
+
+ /** Default construction. Must call resize() prior to use. */
+ DependencyGraph()
+ : numEntries(0), memAllocCounter(0), nodesTraversed(0), nodesRemoved(0)
+ { }
+
+ /** Resize the dependency graph to have num_entries registers. */
+ void resize(int num_entries);
+
+ /** Clears all of the linked lists. */
+ void reset();
+
+ /** Inserts an instruction to be dependent on the given index. */
+ void insert(PhysRegIndex idx, DynInstPtr &new_inst);
+
+ /** Sets the producing instruction of a given register. */
+ void setInst(PhysRegIndex idx, DynInstPtr &new_inst)
+ { dependGraph[idx].inst = new_inst; }
+
+ /** Clears the producing instruction. */
+ void clearInst(PhysRegIndex idx)
+ { dependGraph[idx].inst = NULL; }
+
+ /** Removes an instruction from a single linked list. */
+ void remove(PhysRegIndex idx, DynInstPtr &inst_to_remove);
+
+ /** Removes and returns the newest dependent of a specific register. */
+ DynInstPtr pop(PhysRegIndex idx);
+
+ /** Checks if there are any dependents on a specific register. */
+ bool empty(PhysRegIndex idx) { return !dependGraph[idx].next; }
+
+ /** Debugging function to dump out the dependency graph.
+ */
+ void dump();
+
+ private:
+ /** Array of linked lists. Each linked list is a list of all the
+ * instructions that depend upon a given register. The actual
+ * register's index is used to index into the graph; ie all
+ * instructions in flight that are dependent upon r34 will be
+ * in the linked list of dependGraph[34].
+ */
+ DepEntry *dependGraph;
+
+ /** Number of linked lists; identical to the number of registers. */
+ int numEntries;
+
+ // Debug variable, remove when done testing.
+ unsigned memAllocCounter;
+
+ public:
+ // Debug variable, remove when done testing.
+ uint64_t nodesTraversed;
+ // Debug variable, remove when done testing.
+ uint64_t nodesRemoved;
+};
+
+template <class DynInstPtr>
+void
+DependencyGraph<DynInstPtr>::resize(int num_entries)
+{
+ numEntries = num_entries;
+ dependGraph = new DepEntry[numEntries];
+}
+
+template <class DynInstPtr>
+void
+DependencyGraph<DynInstPtr>::reset()
+{
+ // Clear the dependency graph
+ DepEntry *curr;
+ DepEntry *prev;
+
+ for (int i = 0; i < numEntries; ++i) {
+ curr = dependGraph[i].next;
+
+ while (curr) {
+ memAllocCounter--;
+
+ prev = curr;
+ curr = prev->next;
+ prev->inst = NULL;
+
+ delete prev;
+ }
+
+ if (dependGraph[i].inst) {
+ dependGraph[i].inst = NULL;
+ }
+
+ dependGraph[i].next = NULL;
+ }
+}
+
+template <class DynInstPtr>
+void
+DependencyGraph<DynInstPtr>::insert(PhysRegIndex idx, DynInstPtr &new_inst)
+{
+ //Add this new, dependent instruction at the head of the dependency
+ //chain.
+
+ // First create the entry that will be added to the head of the
+ // dependency chain.
+ DepEntry *new_entry = new DepEntry;
+ new_entry->next = dependGraph[idx].next;
+ new_entry->inst = new_inst;
+
+ // Then actually add it to the chain.
+ dependGraph[idx].next = new_entry;
+
+ ++memAllocCounter;
+}
+
+
+template <class DynInstPtr>
+void
+DependencyGraph<DynInstPtr>::remove(PhysRegIndex idx,
+ DynInstPtr &inst_to_remove)
+{
+ DepEntry *prev = &dependGraph[idx];
+ DepEntry *curr = dependGraph[idx].next;
+
+ // Make sure curr isn't NULL. Because this instruction is being
+ // removed from a dependency list, it must have been placed there at
+ // an earlier time. The dependency chain should not be empty,
+ // unless the instruction dependent upon it is already ready.
+ if (curr == NULL) {
+ return;
+ }
+
+ nodesRemoved++;
+
+ // Find the instruction to remove within the dependency linked list.
+ while (curr->inst != inst_to_remove) {
+ prev = curr;
+ curr = curr->next;
+ nodesTraversed++;
+
+ assert(curr != NULL);
+ }
+
+ // Now remove this instruction from the list.
+ prev->next = curr->next;
+
+ --memAllocCounter;
+
+ // Could push this off to the destructor of DependencyEntry
+ curr->inst = NULL;
+
+ delete curr;
+}
+
+template <class DynInstPtr>
+DynInstPtr
+DependencyGraph<DynInstPtr>::pop(PhysRegIndex idx)
+{
+ DepEntry *node;
+ node = dependGraph[idx].next;
+ DynInstPtr inst = NULL;
+ if (node) {
+ inst = node->inst;
+ dependGraph[idx].next = node->next;
+ node->inst = NULL;
+ memAllocCounter--;
+ delete node;
+ }
+ return inst;
+}
+
+template <class DynInstPtr>
+void
+DependencyGraph<DynInstPtr>::dump()
+{
+ DepEntry *curr;
+
+ for (int i = 0; i < numEntries; ++i)
+ {
+ curr = &dependGraph[i];
+
+ if (curr->inst) {
+ cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ",
+ i, curr->inst->readPC(), curr->inst->seqNum);
+ } else {
+ cprintf("dependGraph[%i]: No producer. consumer: ", i);
+ }
+
+ while (curr->next != NULL) {
+ curr = curr->next;
+
+ cprintf("%#x [sn:%lli] ",
+ curr->inst->readPC(), curr->inst->seqNum);
+ }
+
+ cprintf("\n");
+ }
+ cprintf("memAllocCounter: %i\n", memAllocCounter);
+}
+
+#endif // __CPU_O3_DEP_GRAPH_HH__
diff --git a/src/cpu/o3/fetch.cc b/src/cpu/o3/fetch.cc
index 8ad5e6565..5f52d0fca 100644
--- a/src/cpu/o3/fetch.cc
+++ b/src/cpu/o3/fetch.cc
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
#include "cpu/o3/alpha_impl.hh"
#include "cpu/o3/fetch_impl.hh"
-template class SimpleFetch<AlphaSimpleImpl>;
+template class DefaultFetch<AlphaSimpleImpl>;
diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh
index cc64800d9..76b32de68 100644
--- a/src/cpu/o3/fetch.hh
+++ b/src/cpu/o3/fetch.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,28 +24,33 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Todo: SMT fetch,
-// Add a way to get a stage's current status.
-
-#ifndef __CPU_O3_CPU_SIMPLE_FETCH_HH__
-#define __CPU_O3_CPU_SIMPLE_FETCH_HH__
+#ifndef __CPU_O3_FETCH_HH__
+#define __CPU_O3_FETCH_HH__
+#include "arch/utility.hh"
#include "base/statistics.hh"
#include "base/timebuf.hh"
#include "cpu/pc_event.hh"
-#include "mem/mem_interface.hh"
+#include "mem/packet.hh"
+#include "mem/port.hh"
#include "sim/eventq.hh"
+class Sampler;
+
/**
- * SimpleFetch class to fetch a single instruction each cycle. SimpleFetch
- * will stall if there's an Icache miss, but otherwise assumes a one cycle
- * Icache hit.
+ * DefaultFetch class handles both single threaded and SMT fetch. Its
+ * width is specified by the parameters; each cycle it tries to fetch
+ * that many instructions. It supports using a branch predictor to
+ * predict direction and targets.
+ * It supports the idling functionality of the CPU by indicating to
+ * the CPU when it is active and inactive.
*/
-
template <class Impl>
-class SimpleFetch
+class DefaultFetch
{
public:
/** Typedefs from Impl. */
@@ -55,56 +60,153 @@ class SimpleFetch
typedef typename Impl::FullCPU FullCPU;
typedef typename Impl::Params Params;
+ /** Typedefs from the CPU policy. */
typedef typename CPUPol::BPredUnit BPredUnit;
typedef typename CPUPol::FetchStruct FetchStruct;
typedef typename CPUPol::TimeStruct TimeStruct;
/** Typedefs from ISA. */
typedef TheISA::MachInst MachInst;
+ typedef TheISA::ExtMachInst ExtMachInst;
+
+ /** IcachePort class for DefaultFetch. Handles doing the
+ * communication with the cache/memory.
+ */
+ class IcachePort : public Port
+ {
+ protected:
+ /** Pointer to fetch. */
+ DefaultFetch<Impl> *fetch;
+
+ public:
+ /** Default constructor. */
+ IcachePort(DefaultFetch<Impl> *_fetch)
+ : Port(_fetch->name() + "-iport"), fetch(_fetch)
+ { }
+
+ protected:
+ /** Atomic version of receive. Panics. */
+ virtual Tick recvAtomic(PacketPtr pkt);
+
+ /** Functional version of receive. Panics. */
+ virtual void recvFunctional(PacketPtr pkt);
+
+ /** Receives status change. Other than range changing, panics. */
+ virtual void recvStatusChange(Status status);
+
+ /** Returns the address ranges of this device. */
+ virtual void getDeviceAddressRanges(AddrRangeList &resp,
+ AddrRangeList &snoop)
+ { resp.clear(); snoop.clear(); }
+
+ /** Timing version of receive. Handles setting fetch to the
+ * proper status to start fetching. */
+ virtual bool recvTiming(PacketPtr pkt);
+
+ /** Handles doing a retry of a failed fetch. */
+ virtual void recvRetry();
+ };
public:
- enum Status {
+ /** Overall fetch status. Used to determine if the CPU can
+ * deschedule itsef due to a lack of activity.
+ */
+ enum FetchStatus {
+ Active,
+ Inactive
+ };
+
+ /** Individual thread status. */
+ enum ThreadStatus {
Running,
Idle,
Squashing,
Blocked,
- IcacheMissStall,
- IcacheMissComplete
+ Fetching,
+ TrapPending,
+ QuiescePending,
+ SwitchOut,
+ IcacheWaitResponse,
+ IcacheWaitRetry,
+ IcacheAccessComplete
};
- // May eventually need statuses on a per thread basis.
- Status _status;
+ /** Fetching Policy, Add new policies here.*/
+ enum FetchPriority {
+ SingleThread,
+ RoundRobin,
+ Branch,
+ IQ,
+ LSQ
+ };
- bool stalled;
+ private:
+ /** Fetch status. */
+ FetchStatus _status;
- public:
- class CacheCompletionEvent : public Event
- {
- private:
- SimpleFetch *fetch;
+ /** Per-thread status. */
+ ThreadStatus fetchStatus[Impl::MaxThreads];
- public:
- CacheCompletionEvent(SimpleFetch *_fetch);
+ /** Fetch policy. */
+ FetchPriority fetchPolicy;
- virtual void process();
- virtual const char *description();
- };
+ /** List that has the threads organized by priority. */
+ std::list<unsigned> priorityList;
public:
- /** SimpleFetch constructor. */
- SimpleFetch(Params &params);
+ /** DefaultFetch constructor. */
+ DefaultFetch(Params *params);
+
+ /** Returns the name of fetch. */
+ std::string name() const;
+ /** Registers statistics. */
void regStats();
+ /** Sets CPU pointer. */
void setCPU(FullCPU *cpu_ptr);
+ /** Sets the main backwards communication time buffer pointer. */
void setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer);
+ /** Sets pointer to list of active threads. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
+
+ /** Sets pointer to time buffer used to communicate to the next stage. */
void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr);
- void processCacheCompletion();
+ /** Initialize stage. */
+ void initStage();
+
+ /** Processes cache completion event. */
+ void processCacheCompletion(PacketPtr pkt);
+
+ /** Begins the switch out of the fetch stage. */
+ void switchOut();
+
+ /** Completes the switch out of the fetch stage. */
+ void doSwitchOut();
+
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
+
+ /** Checks if the fetch stage is switched out. */
+ bool isSwitchedOut() { return switchedOut; }
+
+ /** Tells fetch to wake up from a quiesce instruction. */
+ void wakeFromQuiesce();
private:
+ /** Changes the status of this stage to active, and indicates this
+ * to the CPU.
+ */
+ inline void switchToActive();
+
+ /** Changes the status of this stage to inactive, and indicates
+ * this to the CPU.
+ */
+ inline void switchToInactive();
+
/**
* Looks up in the branch predictor to see if the next PC should be
* either next PC+=MachInst or a branch target.
@@ -120,24 +222,54 @@ class SimpleFetch
* fault that happened. Puts the data into the class variable
* cacheData.
* @param fetch_PC The PC address that is being fetched from.
+ * @param ret_fault The fault reference that will be set to the result of
+ * the icache access.
+ * @param tid Thread id.
* @return Any fault that occured.
*/
- Fault fetchCacheLine(Addr fetch_PC);
+ bool fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid);
- inline void doSquash(const Addr &new_PC);
+ /** Squashes a specific thread and resets the PC. */
+ inline void doSquash(const Addr &new_PC, unsigned tid);
- void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num);
+ /** Squashes a specific thread and resets the PC. Also tells the CPU to
+ * remove any instructions between fetch and decode that should be sqaushed.
+ */
+ void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num,
+ unsigned tid);
+
+ /** Checks if a thread is stalled. */
+ bool checkStall(unsigned tid) const;
+
+ /** Updates overall fetch stage status; to be called at the end of each
+ * cycle. */
+ FetchStatus updateFetchStatus();
public:
- // Figure out PC vs next PC and how it should be updated
- void squash(const Addr &new_PC);
+ /** Squashes a specific thread and resets the PC. Also tells the CPU to
+ * remove any instructions that are not in the ROB. The source of this
+ * squash should be the commit stage.
+ */
+ void squash(const Addr &new_PC, unsigned tid);
+ /** Ticks the fetch stage, processing all inputs signals and fetching
+ * as many instructions as possible.
+ */
void tick();
- void fetch();
+ /** Checks all input signals and updates the status as necessary.
+ * @return: Returns if the status has changed due to input signals.
+ */
+ bool checkSignalsAndUpdate(unsigned tid);
+
+ /** Does the actual fetching of instructions and passing them on to the
+ * next stage.
+ * @param status_change fetch() sets this variable if there was a status
+ * change (ie switching to IcacheMissStall).
+ */
+ void fetch(bool &status_change);
- // Align an address (typically a PC) to the start of an I-cache block.
- // We fold in the PISA 64- to 32-bit conversion here as well.
+ /** Align a PC to the start of an I-cache block. */
Addr icacheBlockAlignPC(Addr addr)
{
addr = TheISA::realPCToFetchPC(addr);
@@ -145,6 +277,25 @@ class SimpleFetch
}
private:
+ /** Handles retrying the fetch access. */
+ void recvRetry();
+
+ /** Returns the appropriate thread to fetch, given the fetch policy. */
+ int getFetchingThread(FetchPriority &fetch_priority);
+
+ /** Returns the appropriate thread to fetch using a round robin policy. */
+ int roundRobin();
+
+ /** Returns the appropriate thread to fetch using the IQ count policy. */
+ int iqCount();
+
+ /** Returns the appropriate thread to fetch using the LSQ count policy. */
+ int lsqCount();
+
+ /** Returns the appropriate thread to fetch using the branch count policy. */
+ int branchCount();
+
+ private:
/** Pointer to the FullCPU. */
FullCPU *cpu;
@@ -170,14 +321,41 @@ class SimpleFetch
/** Wire used to write any information heading to decode. */
typename TimeBuffer<FetchStruct>::wire toDecode;
+ MemObject *mem;
+
/** Icache interface. */
- MemInterface *icacheInterface;
+ IcachePort *icachePort;
/** BPredUnit. */
BPredUnit branchPred;
+ /** Per-thread fetch PC. */
+ Addr PC[Impl::MaxThreads];
+
+ /** Per-thread next PC. */
+ Addr nextPC[Impl::MaxThreads];
+
/** Memory request used to access cache. */
- MemReqPtr memReq;
+ RequestPtr memReq[Impl::MaxThreads];
+
+ /** Variable that tracks if fetch has written to the time buffer this
+ * cycle. Used to tell CPU if there is activity this cycle.
+ */
+ bool wroteToTimeBuffer;
+
+ /** Tracks how many instructions has been fetched this cycle. */
+ int numInst;
+
+ /** Source of possible stalls. */
+ struct Stalls {
+ bool decode;
+ bool rename;
+ bool iew;
+ bool commit;
+ };
+
+ /** Tracks which stages are telling fetch to stall. */
+ Stalls stalls[Impl::MaxThreads];
/** Decode to fetch delay, in ticks. */
unsigned decodeToFetchDelay;
@@ -194,6 +372,15 @@ class SimpleFetch
/** The width of fetch in instructions. */
unsigned fetchWidth;
+ /** Is the cache blocked? If so no threads can access it. */
+ bool cacheBlocked;
+
+ /** The packet that is waiting to be retried. */
+ PacketPtr retryPkt;
+
+ /** The thread that is waiting on the cache to tell fetch to retry. */
+ int retryTid;
+
/** Cache block size. */
int cacheBlkSize;
@@ -201,23 +388,68 @@ class SimpleFetch
Addr cacheBlkMask;
/** The cache line being fetched. */
- uint8_t *cacheData;
+ uint8_t *cacheData[Impl::MaxThreads];
/** Size of instructions. */
int instSize;
/** Icache stall statistics. */
- Counter lastIcacheStall;
+ Counter lastIcacheStall[Impl::MaxThreads];
+
+ /** List of Active Threads */
+ std::list<unsigned> *activeThreads;
+ /** Number of threads. */
+ unsigned numThreads;
+
+ /** Number of threads that are actively fetching. */
+ unsigned numFetchingThreads;
+
+ /** Thread ID being fetched. */
+ int threadFetched;
+
+ /** Checks if there is an interrupt pending. If there is, fetch
+ * must stop once it is not fetching PAL instructions.
+ */
+ bool interruptPending;
+
+ /** Records if fetch is switched out. */
+ bool switchedOut;
+
+ // @todo: Consider making these vectors and tracking on a per thread basis.
+ /** Stat for total number of cycles stalled due to an icache miss. */
Stats::Scalar<> icacheStallCycles;
+ /** Stat for total number of fetched instructions. */
Stats::Scalar<> fetchedInsts;
+ Stats::Scalar<> fetchedBranches;
+ /** Stat for total number of predicted branches. */
Stats::Scalar<> predictedBranches;
+ /** Stat for total number of cycles spent fetching. */
Stats::Scalar<> fetchCycles;
+ /** Stat for total number of cycles spent squashing. */
Stats::Scalar<> fetchSquashCycles;
+ /** Stat for total number of cycles spent blocked due to other stages in
+ * the pipeline.
+ */
+ Stats::Scalar<> fetchIdleCycles;
+ /** Total number of cycles spent blocked. */
Stats::Scalar<> fetchBlockedCycles;
+ /** Total number of cycles spent in any other state. */
+ Stats::Scalar<> fetchMiscStallCycles;
+ /** Stat for total number of fetched cache lines. */
Stats::Scalar<> fetchedCacheLines;
-
- Stats::Distribution<> fetch_nisn_dist;
+ /** Total number of outstanding icache accesses that were dropped
+ * due to a squash.
+ */
+ Stats::Scalar<> fetchIcacheSquashes;
+ /** Distribution of number of instructions fetched each cycle. */
+ Stats::Distribution<> fetchNisnDist;
+ /** Rate of how often fetch was idle. */
+ Stats::Formula idleRate;
+ /** Number of branch fetches per cycle. */
+ Stats::Formula branchRate;
+ /** Number of instruction fetched per cycle. */
+ Stats::Formula fetchRate;
};
-#endif //__CPU_O3_CPU_SIMPLE_FETCH_HH__
+#endif //__CPU_O3_FETCH_HH__
diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh
index 8029fc732..c0a2a5d09 100644
--- a/src/cpu/o3/fetch_impl.hh
+++ b/src/cpu/o3/fetch_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,85 +24,161 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Remove this later; used only for debugging.
-#define OPCODE(X) (X >> 26) & 0x3f
-
#include "arch/isa_traits.hh"
-#include "sim/byteswap.hh"
+#include "arch/utility.hh"
+#include "cpu/checker/cpu.hh"
#include "cpu/exetrace.hh"
-#include "mem/base_mem.hh"
-#include "mem/mem_interface.hh"
-#include "mem/mem_req.hh"
#include "cpu/o3/fetch.hh"
-
+#include "mem/packet.hh"
+#include "mem/request.hh"
+#include "sim/byteswap.hh"
+#include "sim/host.hh"
#include "sim/root.hh"
+#if FULL_SYSTEM
+#include "arch/tlb.hh"
+#include "arch/vtophys.hh"
+#include "base/remote_gdb.hh"
+#include "sim/system.hh"
+#endif // FULL_SYSTEM
+
+#include <algorithm>
+
+using namespace std;
+using namespace TheISA;
+
template<class Impl>
-SimpleFetch<Impl>::CacheCompletionEvent
-::CacheCompletionEvent(SimpleFetch *_fetch)
- : Event(&mainEventQueue),
- fetch(_fetch)
+Tick
+DefaultFetch<Impl>::IcachePort::recvAtomic(PacketPtr pkt)
{
+ panic("DefaultFetch doesn't expect recvAtomic callback!");
+ return curTick;
}
template<class Impl>
void
-SimpleFetch<Impl>::CacheCompletionEvent::process()
+DefaultFetch<Impl>::IcachePort::recvFunctional(PacketPtr pkt)
{
- fetch->processCacheCompletion();
+ panic("DefaultFetch doesn't expect recvFunctional callback!");
}
template<class Impl>
-const char *
-SimpleFetch<Impl>::CacheCompletionEvent::description()
+void
+DefaultFetch<Impl>::IcachePort::recvStatusChange(Status status)
+{
+ if (status == RangeChange)
+ return;
+
+ panic("DefaultFetch doesn't expect recvStatusChange callback!");
+}
+
+template<class Impl>
+bool
+DefaultFetch<Impl>::IcachePort::recvTiming(Packet *pkt)
{
- return "SimpleFetch cache completion event";
+ fetch->processCacheCompletion(pkt);
+ return true;
}
template<class Impl>
-SimpleFetch<Impl>::SimpleFetch(Params &params)
- : icacheInterface(params.icacheInterface),
+void
+DefaultFetch<Impl>::IcachePort::recvRetry()
+{
+ fetch->recvRetry();
+}
+
+template<class Impl>
+DefaultFetch<Impl>::DefaultFetch(Params *params)
+ : mem(params->mem),
branchPred(params),
- decodeToFetchDelay(params.decodeToFetchDelay),
- renameToFetchDelay(params.renameToFetchDelay),
- iewToFetchDelay(params.iewToFetchDelay),
- commitToFetchDelay(params.commitToFetchDelay),
- fetchWidth(params.fetchWidth)
-{
- DPRINTF(Fetch, "Fetch: Fetch constructor called\n");
-
- // Set status to idle.
- _status = Idle;
-
- // Create a new memory request.
- memReq = new MemReq();
- // Not sure of this parameter. I think it should be based on the
- // thread number.
-#if !FULL_SYSTEM
- memReq->asid = 0;
-#else
- memReq->asid = 0;
-#endif // FULL_SYSTEM
- memReq->data = new uint8_t[64];
+ decodeToFetchDelay(params->decodeToFetchDelay),
+ renameToFetchDelay(params->renameToFetchDelay),
+ iewToFetchDelay(params->iewToFetchDelay),
+ commitToFetchDelay(params->commitToFetchDelay),
+ fetchWidth(params->fetchWidth),
+ cacheBlocked(false),
+ retryPkt(NULL),
+ retryTid(-1),
+ numThreads(params->numberOfThreads),
+ numFetchingThreads(params->smtNumFetchingThreads),
+ interruptPending(false),
+ switchedOut(false)
+{
+ if (numThreads > Impl::MaxThreads)
+ fatal("numThreads is not a valid value\n");
+
+ DPRINTF(Fetch, "Fetch constructor called\n");
+
+ // Set fetch stage's status to inactive.
+ _status = Inactive;
+
+ string policy = params->smtFetchPolicy;
+
+ // Convert string to lowercase
+ std::transform(policy.begin(), policy.end(), policy.begin(),
+ (int(*)(int)) tolower);
+
+ // Figure out fetch policy
+ if (policy == "singlethread") {
+ fetchPolicy = SingleThread;
+ } else if (policy == "roundrobin") {
+ fetchPolicy = RoundRobin;
+ DPRINTF(Fetch, "Fetch policy set to Round Robin\n");
+ } else if (policy == "branch") {
+ fetchPolicy = Branch;
+ DPRINTF(Fetch, "Fetch policy set to Branch Count\n");
+ } else if (policy == "iqcount") {
+ fetchPolicy = IQ;
+ DPRINTF(Fetch, "Fetch policy set to IQ count\n");
+ } else if (policy == "lsqcount") {
+ fetchPolicy = LSQ;
+ DPRINTF(Fetch, "Fetch policy set to LSQ count\n");
+ } else {
+ fatal("Invalid Fetch Policy. Options Are: {SingleThread,"
+ " RoundRobin,LSQcount,IQcount}\n");
+ }
// Size of cache block.
- cacheBlkSize = icacheInterface ? icacheInterface->getBlockSize() : 64;
+ cacheBlkSize = 64;
// Create mask to get rid of offset bits.
cacheBlkMask = (cacheBlkSize - 1);
+ for (int tid=0; tid < numThreads; tid++) {
+
+ fetchStatus[tid] = Running;
+
+ priorityList.push_back(tid);
+
+ memReq[tid] = NULL;
+
+ // Create space to store a cache line.
+ cacheData[tid] = new uint8_t[cacheBlkSize];
+
+ stalls[tid].decode = 0;
+ stalls[tid].rename = 0;
+ stalls[tid].iew = 0;
+ stalls[tid].commit = 0;
+ }
+
// Get the size of an instruction.
instSize = sizeof(MachInst);
+}
- // Create space to store a cache line.
- cacheData = new uint8_t[cacheBlkSize];
+template <class Impl>
+std::string
+DefaultFetch<Impl>::name() const
+{
+ return cpu->name() + ".fetch";
}
template <class Impl>
void
-SimpleFetch<Impl>::regStats()
+DefaultFetch<Impl>::regStats()
{
icacheStallCycles
.name(name() + ".icacheStallCycles")
@@ -110,58 +186,114 @@ SimpleFetch<Impl>::regStats()
.prereq(icacheStallCycles);
fetchedInsts
- .name(name() + ".fetchedInsts")
+ .name(name() + ".Insts")
.desc("Number of instructions fetch has processed")
.prereq(fetchedInsts);
+
+ fetchedBranches
+ .name(name() + ".Branches")
+ .desc("Number of branches that fetch encountered")
+ .prereq(fetchedBranches);
+
predictedBranches
.name(name() + ".predictedBranches")
.desc("Number of branches that fetch has predicted taken")
.prereq(predictedBranches);
+
fetchCycles
- .name(name() + ".fetchCycles")
+ .name(name() + ".Cycles")
.desc("Number of cycles fetch has run and was not squashing or"
" blocked")
.prereq(fetchCycles);
+
fetchSquashCycles
- .name(name() + ".fetchSquashCycles")
+ .name(name() + ".SquashCycles")
.desc("Number of cycles fetch has spent squashing")
.prereq(fetchSquashCycles);
+
+ fetchIdleCycles
+ .name(name() + ".IdleCycles")
+ .desc("Number of cycles fetch was idle")
+ .prereq(fetchIdleCycles);
+
fetchBlockedCycles
- .name(name() + ".fetchBlockedCycles")
+ .name(name() + ".BlockedCycles")
.desc("Number of cycles fetch has spent blocked")
.prereq(fetchBlockedCycles);
+
fetchedCacheLines
- .name(name() + ".fetchedCacheLines")
+ .name(name() + ".CacheLines")
.desc("Number of cache lines fetched")
.prereq(fetchedCacheLines);
- fetch_nisn_dist
+ fetchMiscStallCycles
+ .name(name() + ".MiscStallCycles")
+ .desc("Number of cycles fetch has spent waiting on interrupts, or "
+ "bad addresses, or out of MSHRs")
+ .prereq(fetchMiscStallCycles);
+
+ fetchIcacheSquashes
+ .name(name() + ".IcacheSquashes")
+ .desc("Number of outstanding Icache misses that were squashed")
+ .prereq(fetchIcacheSquashes);
+
+ fetchNisnDist
.init(/* base value */ 0,
/* last value */ fetchWidth,
/* bucket size */ 1)
- .name(name() + ".FETCH:rate_dist")
+ .name(name() + ".rateDist")
.desc("Number of instructions fetched each cycle (Total)")
- .flags(Stats::pdf)
- ;
+ .flags(Stats::pdf);
+
+ idleRate
+ .name(name() + ".idleRate")
+ .desc("Percent of cycles fetch was idle")
+ .prereq(idleRate);
+ idleRate = fetchIdleCycles * 100 / cpu->numCycles;
+
+ branchRate
+ .name(name() + ".branchRate")
+ .desc("Number of branch fetches per cycle")
+ .flags(Stats::total);
+ branchRate = fetchedBranches / cpu->numCycles;
+
+ fetchRate
+ .name(name() + ".rate")
+ .desc("Number of inst fetches per cycle")
+ .flags(Stats::total);
+ fetchRate = fetchedInsts / cpu->numCycles;
branchPred.regStats();
}
template<class Impl>
void
-SimpleFetch<Impl>::setCPU(FullCPU *cpu_ptr)
+DefaultFetch<Impl>::setCPU(FullCPU *cpu_ptr)
{
- DPRINTF(Fetch, "Fetch: Setting the CPU pointer.\n");
+ DPRINTF(Fetch, "Setting the CPU pointer.\n");
cpu = cpu_ptr;
- // This line will be removed eventually.
- memReq->xc = cpu->xcBase();
+
+ // Name is finally available, so create the port.
+ icachePort = new IcachePort(this);
+
+ Port *mem_dport = mem->getPort("");
+ icachePort->setPeer(mem_dport);
+ mem_dport->setPeer(icachePort);
+
+ if (cpu->checker) {
+ cpu->checker->setIcachePort(icachePort);
+ }
+
+ // Fetch needs to start fetching instructions at the very beginning,
+ // so it must start up in active state.
+ switchToActive();
}
template<class Impl>
void
-SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer)
+DefaultFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer)
{
- DPRINTF(Fetch, "Fetch: Setting the time buffer pointer.\n");
+ DPRINTF(Fetch, "Setting the time buffer pointer.\n");
timeBuffer = time_buffer;
// Create wires to get information from proper places in time buffer.
@@ -173,9 +305,17 @@ SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer)
template<class Impl>
void
-SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
+DefaultFetch<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ DPRINTF(Fetch, "Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
+}
+
+template<class Impl>
+void
+DefaultFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
{
- DPRINTF(Fetch, "Fetch: Setting the fetch queue pointer.\n");
+ DPRINTF(Fetch, "Setting the fetch queue pointer.\n");
fetchQueue = fq_ptr;
// Create wire to write information to proper place in fetch queue.
@@ -184,21 +324,134 @@ SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
template<class Impl>
void
-SimpleFetch<Impl>::processCacheCompletion()
+DefaultFetch<Impl>::initStage()
{
- DPRINTF(Fetch, "Fetch: Waking up from cache miss.\n");
+ // Setup PC and nextPC with initial state.
+ for (int tid = 0; tid < numThreads; tid++) {
+ PC[tid] = cpu->readPC(tid);
+ nextPC[tid] = cpu->readNextPC(tid);
+ }
+}
+
+template<class Impl>
+void
+DefaultFetch<Impl>::processCacheCompletion(PacketPtr pkt)
+{
+ unsigned tid = pkt->req->getThreadNum();
+
+ DPRINTF(Fetch, "[tid:%u] Waking up from cache miss.\n",tid);
// Only change the status if it's still waiting on the icache access
// to return.
- // Can keep track of how many cache accesses go unused due to
- // misspeculation here.
- if (_status == IcacheMissStall)
- _status = IcacheMissComplete;
+ if (fetchStatus[tid] != IcacheWaitResponse ||
+ pkt->req != memReq[tid] ||
+ isSwitchedOut()) {
+ ++fetchIcacheSquashes;
+ delete pkt->req;
+ delete pkt;
+ memReq[tid] = NULL;
+ return;
+ }
+
+ // 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);
+
+ switchToActive();
+
+ // Only switch to IcacheAccessComplete if we're not stalled as well.
+ if (checkStall(tid)) {
+ fetchStatus[tid] = Blocked;
+ } else {
+ fetchStatus[tid] = IcacheAccessComplete;
+ }
+
+ // Reset the mem req to NULL.
+ delete pkt->req;
+ delete pkt;
+ memReq[tid] = NULL;
+}
+
+template <class Impl>
+void
+DefaultFetch<Impl>::switchOut()
+{
+ // Fetch is ready to switch out at any time.
+ switchedOut = true;
+ cpu->signalSwitched();
+}
+
+template <class Impl>
+void
+DefaultFetch<Impl>::doSwitchOut()
+{
+ // Branch predictor needs to have its state cleared.
+ branchPred.switchOut();
+}
+
+template <class Impl>
+void
+DefaultFetch<Impl>::takeOverFrom()
+{
+ // Reset all state
+ for (int 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->readPC(i);
+ nextPC[i] = cpu->readNextPC(i);
+ fetchStatus[i] = Running;
+ }
+ numInst = 0;
+ wroteToTimeBuffer = false;
+ _status = Inactive;
+ switchedOut = false;
+ branchPred.takeOverFrom();
+}
+
+template <class Impl>
+void
+DefaultFetch<Impl>::wakeFromQuiesce()
+{
+ DPRINTF(Fetch, "Waking up from quiesce\n");
+ // Hopefully this is safe
+ // @todo: Allow other threads to wake from quiesce.
+ fetchStatus[0] = Running;
+}
+
+template <class Impl>
+inline void
+DefaultFetch<Impl>::switchToActive()
+{
+ if (_status == Inactive) {
+ DPRINTF(Activity, "Activating stage.\n");
+
+ cpu->activateStage(FullCPU::FetchIdx);
+
+ _status = Active;
+ }
+}
+
+template <class Impl>
+inline void
+DefaultFetch<Impl>::switchToInactive()
+{
+ if (_status == Active) {
+ DPRINTF(Activity, "Deactivating stage.\n");
+
+ cpu->deactivateStage(FullCPU::FetchIdx);
+
+ _status = Inactive;
+ }
}
template <class Impl>
bool
-SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC)
+DefaultFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC)
{
// Do branch prediction check here.
// A bit of a misnomer...next_PC is actually the current PC until
@@ -211,7 +464,9 @@ SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC)
return false;
}
- predict_taken = branchPred.predict(inst, next_PC);
+ predict_taken = branchPred.predict(inst, next_PC, inst->threadNumber);
+
+ ++fetchedBranches;
if (predict_taken) {
++predictedBranches;
@@ -221,251 +476,425 @@ SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC)
}
template <class Impl>
-Fault
-SimpleFetch<Impl>::fetchCacheLine(Addr fetch_PC)
+bool
+DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid)
{
- // Check if the instruction exists within the cache.
- // If it does, then proceed on to read the instruction and the rest
- // of the instructions in the cache line until either the end of the
- // cache line or a predicted taken branch is encountered.
+ Fault fault = NoFault;
#if FULL_SYSTEM
// Flag to say whether or not address is physical addr.
- unsigned flags = cpu->inPalMode() ? PHYSICAL : 0;
+ unsigned flags = cpu->inPalMode(fetch_PC) ? PHYSICAL : 0;
#else
unsigned flags = 0;
#endif // FULL_SYSTEM
- Fault fault = NoFault;
+ if (cacheBlocked || (interruptPending && flags == 0) || switchedOut) {
+ // Hold off fetch from getting new instructions when:
+ // Cache is blocked, or
+ // while an interrupt is pending and we're not in PAL mode, or
+ // fetch is switched out.
+ return false;
+ }
// Align the fetch PC so it's at the start of a cache block.
fetch_PC = icacheBlockAlignPC(fetch_PC);
- // Setup the memReq to do a read of the first isntruction's address.
+ // Setup the memReq to do a read of the first instruction's address.
// Set the appropriate read size and flags as well.
- memReq->cmd = Read;
- memReq->reset(fetch_PC, cacheBlkSize, flags);
+ // Build request here.
+ RequestPtr mem_req = new Request(tid, fetch_PC, cacheBlkSize, flags,
+ fetch_PC, cpu->readCpuId(), tid);
- // Translate the instruction request.
- // Should this function be
- // in the CPU class ? Probably...ITB/DTB should exist within the
- // CPU.
+ memReq[tid] = mem_req;
- fault = cpu->translateInstReq(memReq);
+ // Translate the instruction request.
+ fault = cpu->translateInstReq(mem_req, cpu->thread[tid]);
// In the case of faults, the fetch stage may need to stall and wait
- // on what caused the fetch (ITB or Icache miss).
+ // for the ITB miss to be handled.
// If translation was successful, attempt to read the first
// instruction.
if (fault == NoFault) {
+#if 0
+ if (cpu->system->memctrl->badaddr(memReq[tid]->paddr) ||
+ memReq[tid]->flags & UNCACHEABLE) {
+ DPRINTF(Fetch, "Fetch: Bad address %#x (hopefully on a "
+ "misspeculating path)!",
+ memReq[tid]->paddr);
+ ret_fault = TheISA::genMachineCheckFault();
+ return false;
+ }
+#endif
+
+ // Build packet here.
+ PacketPtr data_pkt = new Packet(mem_req,
+ Packet::ReadReq, Packet::Broadcast);
+ data_pkt->dataStatic(cacheData[tid]);
+
DPRINTF(Fetch, "Fetch: Doing instruction read.\n");
- fault = cpu->mem->read(memReq, cacheData);
- // This read may change when the mem interface changes.
fetchedCacheLines++;
- }
- // Now do the timing access to see whether or not the instruction
- // exists within the cache.
- if (icacheInterface && fault == NoFault) {
- DPRINTF(Fetch, "Fetch: Doing timing memory access.\n");
- memReq->completionEvent = NULL;
-
- memReq->time = curTick;
+ // Now do the timing access to see whether or not the instruction
+ // exists within the cache.
+ if (!icachePort->sendTiming(data_pkt)) {
+ assert(retryPkt == NULL);
+ assert(retryTid == -1);
+ DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid);
+ fetchStatus[tid] = IcacheWaitRetry;
+ retryPkt = data_pkt;
+ retryTid = tid;
+ cacheBlocked = true;
+ return false;
+ }
- MemAccessResult result = icacheInterface->access(memReq);
+ DPRINTF(Fetch, "Doing cache access.\n");
- // If the cache missed (in this model functional and timing
- // memories are different), then schedule an event to wake
- // up this stage once the cache miss completes.
- if (result != MA_HIT && icacheInterface->doEvents()) {
- memReq->completionEvent = new CacheCompletionEvent(this);
+ lastIcacheStall[tid] = curTick;
- // How does current model work as far as individual
- // stages scheduling/unscheduling?
- // Perhaps have only the main CPU scheduled/unscheduled,
- // and have it choose what stages to run appropriately.
+ DPRINTF(Activity, "[tid:%i]: Activity: Waiting on I-cache "
+ "response.\n", tid);
- DPRINTF(Fetch, "Fetch: Stalling due to icache miss.\n");
- _status = IcacheMissStall;
- }
+ fetchStatus[tid] = IcacheWaitResponse;
+ } else {
+ delete mem_req;
+ memReq[tid] = NULL;
}
- return fault;
+ ret_fault = fault;
+ return true;
}
template <class Impl>
inline void
-SimpleFetch<Impl>::doSquash(const Addr &new_PC)
+DefaultFetch<Impl>::doSquash(const Addr &new_PC, unsigned tid)
{
- DPRINTF(Fetch, "Fetch: Squashing, setting PC to: %#x.\n", new_PC);
+ DPRINTF(Fetch, "[tid:%i]: Squashing, setting PC to: %#x.\n",
+ tid, new_PC);
- cpu->setNextPC(new_PC + instSize);
- cpu->setPC(new_PC);
+ PC[tid] = new_PC;
+ nextPC[tid] = new_PC + instSize;
// Clear the icache miss if it's outstanding.
- if (_status == IcacheMissStall && icacheInterface) {
- DPRINTF(Fetch, "Fetch: Squashing outstanding Icache miss.\n");
- // @todo: Use an actual thread number here.
- icacheInterface->squash(0);
+ if (fetchStatus[tid] == IcacheWaitResponse) {
+ DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n",
+ tid);
+ memReq[tid] = NULL;
}
- _status = Squashing;
+ // Get rid of the retrying packet if it was from this thread.
+ if (retryTid == tid) {
+ assert(cacheBlocked);
+ cacheBlocked = false;
+ retryTid = -1;
+ retryPkt = NULL;
+ delete retryPkt->req;
+ delete retryPkt;
+ }
+
+ fetchStatus[tid] = Squashing;
++fetchSquashCycles;
}
template<class Impl>
void
-SimpleFetch<Impl>::squashFromDecode(const Addr &new_PC,
- const InstSeqNum &seq_num)
+DefaultFetch<Impl>::squashFromDecode(const Addr &new_PC,
+ const InstSeqNum &seq_num,
+ unsigned tid)
{
- DPRINTF(Fetch, "Fetch: Squashing from decode.\n");
+ DPRINTF(Fetch, "[tid:%i]: Squashing from decode.\n",tid);
- doSquash(new_PC);
+ doSquash(new_PC, tid);
// Tell the CPU to remove any instructions that are in flight between
// fetch and decode.
- cpu->removeInstsUntil(seq_num);
+ cpu->removeInstsUntil(seq_num, tid);
+}
+
+template<class Impl>
+bool
+DefaultFetch<Impl>::checkStall(unsigned tid) const
+{
+ bool ret_val = false;
+
+ if (cpu->contextSwitch) {
+ DPRINTF(Fetch,"[tid:%i]: Stalling for a context switch.\n",tid);
+ ret_val = true;
+ } else if (stalls[tid].decode) {
+ DPRINTF(Fetch,"[tid:%i]: Stall from Decode stage detected.\n",tid);
+ ret_val = true;
+ } else if (stalls[tid].rename) {
+ DPRINTF(Fetch,"[tid:%i]: Stall from Rename stage detected.\n",tid);
+ ret_val = true;
+ } else if (stalls[tid].iew) {
+ DPRINTF(Fetch,"[tid:%i]: Stall from IEW stage detected.\n",tid);
+ ret_val = true;
+ } else if (stalls[tid].commit) {
+ DPRINTF(Fetch,"[tid:%i]: Stall from Commit stage detected.\n",tid);
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+template<class Impl>
+typename DefaultFetch<Impl>::FetchStatus
+DefaultFetch<Impl>::updateFetchStatus()
+{
+ //Check Running
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+
+ unsigned tid = *threads++;
+
+ if (fetchStatus[tid] == Running ||
+ fetchStatus[tid] == Squashing ||
+ fetchStatus[tid] == IcacheAccessComplete) {
+
+ if (_status == Inactive) {
+ DPRINTF(Activity, "[tid:%i]: Activating stage.\n",tid);
+
+ if (fetchStatus[tid] == IcacheAccessComplete) {
+ DPRINTF(Activity, "[tid:%i]: Activating fetch due to cache"
+ "completion\n",tid);
+ }
+
+ cpu->activateStage(FullCPU::FetchIdx);
+ }
+
+ return Active;
+ }
+ }
+
+ // Stage is switching from active to inactive, notify CPU of it.
+ if (_status == Active) {
+ DPRINTF(Activity, "Deactivating stage.\n");
+
+ cpu->deactivateStage(FullCPU::FetchIdx);
+ }
+
+ return Inactive;
}
template <class Impl>
void
-SimpleFetch<Impl>::squash(const Addr &new_PC)
+DefaultFetch<Impl>::squash(const Addr &new_PC, unsigned tid)
{
- DPRINTF(Fetch, "Fetch: Squash from commit.\n");
+ DPRINTF(Fetch, "[tid:%u]: Squash from commit.\n",tid);
- doSquash(new_PC);
+ doSquash(new_PC, tid);
// Tell the CPU to remove any instructions that are not in the ROB.
- cpu->removeInstsNotInROB();
+ cpu->removeInstsNotInROB(tid);
}
-template<class Impl>
+template <class Impl>
void
-SimpleFetch<Impl>::tick()
+DefaultFetch<Impl>::tick()
{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+ bool status_change = false;
+
+ wroteToTimeBuffer = false;
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ // Check the signals for each thread to determine the proper status
+ // for each thread.
+ bool updated_status = checkSignalsAndUpdate(tid);
+ status_change = status_change || updated_status;
+ }
+
+ DPRINTF(Fetch, "Running stage.\n");
+
+ // Reset the number of the instruction we're fetching.
+ numInst = 0;
+
+ if (fromCommit->commitInfo[0].interruptPending) {
+ interruptPending = true;
+ }
+ if (fromCommit->commitInfo[0].clearInterrupt) {
+ interruptPending = false;
+ }
+
+ for (threadFetched = 0; threadFetched < numFetchingThreads;
+ threadFetched++) {
+ // Fetch each of the actively fetching threads.
+ fetch(status_change);
+ }
+
+ // Record number of instructions fetched this cycle for distribution.
+ fetchNisnDist.sample(numInst);
+
+ if (status_change) {
+ // Change the fetch stage status if there was a status change.
+ _status = updateFetchStatus();
+ }
+
+ // If there was activity this cycle, inform the CPU of it.
+ if (wroteToTimeBuffer || cpu->contextSwitch) {
+ DPRINTF(Activity, "Activity this cycle.\n");
+
+ cpu->activityThisCycle();
+ }
+}
+
+template <class Impl>
+bool
+DefaultFetch<Impl>::checkSignalsAndUpdate(unsigned tid)
+{
+ // Update the per thread stall statuses.
+ if (fromDecode->decodeBlock[tid]) {
+ stalls[tid].decode = true;
+ }
+
+ if (fromDecode->decodeUnblock[tid]) {
+ assert(stalls[tid].decode);
+ assert(!fromDecode->decodeBlock[tid]);
+ stalls[tid].decode = false;
+ }
+
+ if (fromRename->renameBlock[tid]) {
+ stalls[tid].rename = true;
+ }
+
+ if (fromRename->renameUnblock[tid]) {
+ assert(stalls[tid].rename);
+ assert(!fromRename->renameBlock[tid]);
+ stalls[tid].rename = false;
+ }
+
+ if (fromIEW->iewBlock[tid]) {
+ stalls[tid].iew = true;
+ }
+
+ if (fromIEW->iewUnblock[tid]) {
+ assert(stalls[tid].iew);
+ assert(!fromIEW->iewBlock[tid]);
+ stalls[tid].iew = false;
+ }
+
+ if (fromCommit->commitBlock[tid]) {
+ stalls[tid].commit = true;
+ }
+
+ if (fromCommit->commitUnblock[tid]) {
+ assert(stalls[tid].commit);
+ assert(!fromCommit->commitBlock[tid]);
+ stalls[tid].commit = false;
+ }
+
// Check squash signals from commit.
- if (fromCommit->commitInfo.squash) {
- DPRINTF(Fetch, "Fetch: Squashing instructions due to squash "
- "from commit.\n");
+ if (fromCommit->commitInfo[tid].squash) {
+
+ DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash "
+ "from commit.\n",tid);
// In any case, squash.
- squash(fromCommit->commitInfo.nextPC);
+ squash(fromCommit->commitInfo[tid].nextPC,tid);
// Also check if there's a mispredict that happened.
- if (fromCommit->commitInfo.branchMispredict) {
- branchPred.squash(fromCommit->commitInfo.doneSeqNum,
- fromCommit->commitInfo.nextPC,
- fromCommit->commitInfo.branchTaken);
+ if (fromCommit->commitInfo[tid].branchMispredict) {
+ branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum,
+ fromCommit->commitInfo[tid].nextPC,
+ fromCommit->commitInfo[tid].branchTaken,
+ tid);
} else {
- branchPred.squash(fromCommit->commitInfo.doneSeqNum);
+ branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum,
+ tid);
}
- return;
- } else if (fromCommit->commitInfo.doneSeqNum) {
+ return true;
+ } else if (fromCommit->commitInfo[tid].doneSeqNum) {
// Update the branch predictor if it wasn't a squashed instruction
- // that was braodcasted.
- branchPred.update(fromCommit->commitInfo.doneSeqNum);
+ // that was broadcasted.
+ branchPred.update(fromCommit->commitInfo[tid].doneSeqNum, tid);
}
// Check ROB squash signals from commit.
- if (fromCommit->commitInfo.robSquashing) {
- DPRINTF(Fetch, "Fetch: ROB is still squashing.\n");
+ if (fromCommit->commitInfo[tid].robSquashing) {
+ DPRINTF(Fetch, "[tid:%u]: ROB is still squashing Thread %u.\n", tid);
// Continue to squash.
- _status = Squashing;
+ fetchStatus[tid] = Squashing;
- ++fetchSquashCycles;
- return;
+ return true;
}
// Check squash signals from decode.
- if (fromDecode->decodeInfo.squash) {
- DPRINTF(Fetch, "Fetch: Squashing instructions due to squash "
- "from decode.\n");
+ if (fromDecode->decodeInfo[tid].squash) {
+ DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash "
+ "from decode.\n",tid);
// Update the branch predictor.
- if (fromDecode->decodeInfo.branchMispredict) {
- branchPred.squash(fromDecode->decodeInfo.doneSeqNum,
- fromDecode->decodeInfo.nextPC,
- fromDecode->decodeInfo.branchTaken);
+ if (fromDecode->decodeInfo[tid].branchMispredict) {
+ branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum,
+ fromDecode->decodeInfo[tid].nextPC,
+ fromDecode->decodeInfo[tid].branchTaken,
+ tid);
} else {
- branchPred.squash(fromDecode->decodeInfo.doneSeqNum);
+ branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum,
+ tid);
}
- if (_status != Squashing) {
- // Squash unless we're already squashing?
- squashFromDecode(fromDecode->decodeInfo.nextPC,
- fromDecode->decodeInfo.doneSeqNum);
- return;
+ if (fetchStatus[tid] != Squashing) {
+ // Squash unless we're already squashing
+ squashFromDecode(fromDecode->decodeInfo[tid].nextPC,
+ fromDecode->decodeInfo[tid].doneSeqNum,
+ tid);
+
+ return true;
}
}
- // Check if any of the stall signals are high.
- if (fromDecode->decodeInfo.stall ||
- fromRename->renameInfo.stall ||
- fromIEW->iewInfo.stall ||
- fromCommit->commitInfo.stall)
- {
- // Block stage, regardless of current status.
-
- DPRINTF(Fetch, "Fetch: Stalling stage.\n");
- DPRINTF(Fetch, "Fetch: Statuses: Decode: %i Rename: %i IEW: %i "
- "Commit: %i\n",
- fromDecode->decodeInfo.stall,
- fromRename->renameInfo.stall,
- fromIEW->iewInfo.stall,
- fromCommit->commitInfo.stall);
-
- _status = Blocked;
+ if (checkStall(tid) && fetchStatus[tid] != IcacheWaitResponse) {
+ DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid);
- ++fetchBlockedCycles;
- return;
- } else if (_status == Blocked) {
- // Unblock stage if status is currently blocked and none of the
- // stall signals are being held high.
- _status = Running;
+ fetchStatus[tid] = Blocked;
- ++fetchBlockedCycles;
- return;
+ return true;
}
- // If fetch has reached this point, then there are no squash signals
- // still being held high. Check if fetch is in the squashing state;
- // if so, fetch can switch to running.
- // Similarly, there are no blocked signals still being held high.
- // Check if fetch is in the blocked state; if so, fetch can switch to
- // running.
- if (_status == Squashing) {
- DPRINTF(Fetch, "Fetch: Done squashing, switching to running.\n");
-
- // Switch status to running
- _status = Running;
-
- ++fetchCycles;
-
- fetch();
- } else if (_status != IcacheMissStall) {
- DPRINTF(Fetch, "Fetch: Running stage.\n");
+ if (fetchStatus[tid] == Blocked ||
+ fetchStatus[tid] == Squashing) {
+ // Switch status to running if fetch isn't being told to block or
+ // squash this cycle.
+ DPRINTF(Fetch, "[tid:%i]: Done squashing, switching to running.\n",
+ tid);
- ++fetchCycles;
+ fetchStatus[tid] = Running;
- fetch();
+ return true;
}
+
+ // If we've reached this point, we have not gotten any signals that
+ // cause fetch to change its status. Fetch remains the same as before.
+ return false;
}
template<class Impl>
void
-SimpleFetch<Impl>::fetch()
+DefaultFetch<Impl>::fetch(bool &status_change)
{
//////////////////////////////////////////
// Start actual fetch
//////////////////////////////////////////
+ int tid = getFetchingThread(fetchPolicy);
+
+ if (tid == -1) {
+ DPRINTF(Fetch,"There are no more threads available to fetch from.\n");
+
+ // Breaks looping condition in tick()
+ threadFetched = numFetchingThreads;
+ return;
+ }
// The current PC.
- Addr fetch_PC = cpu->readPC();
+ Addr &fetch_PC = PC[tid];
// Fault code for memory access.
Fault fault = NoFault;
@@ -473,60 +902,71 @@ SimpleFetch<Impl>::fetch()
// If returning from the delay of a cache miss, then update the status
// to running, otherwise do the cache access. Possibly move this up
// to tick() function.
- if (_status == IcacheMissComplete) {
- DPRINTF(Fetch, "Fetch: Icache miss is complete.\n");
-
- // Reset the completion event to NULL.
- memReq->completionEvent = NULL;
-
- _status = Running;
+ if (fetchStatus[tid] == IcacheAccessComplete) {
+ DPRINTF(Fetch, "[tid:%i]: Icache miss is complete.\n",
+ tid);
+
+ fetchStatus[tid] = Running;
+ status_change = true;
+ } else if (fetchStatus[tid] == Running) {
+ DPRINTF(Fetch, "[tid:%i]: Attempting to translate and read "
+ "instruction, starting at PC %08p.\n",
+ tid, fetch_PC);
+
+ bool fetch_success = fetchCacheLine(fetch_PC, fault, tid);
+ if (!fetch_success) {
+ ++fetchMiscStallCycles;
+ return;
+ }
} else {
- DPRINTF(Fetch, "Fetch: Attempting to translate and read "
- "instruction, starting at PC %08p.\n",
- fetch_PC);
+ if (fetchStatus[tid] == Idle) {
+ ++fetchIdleCycles;
+ } else if (fetchStatus[tid] == Blocked) {
+ ++fetchBlockedCycles;
+ } else if (fetchStatus[tid] == Squashing) {
+ ++fetchSquashCycles;
+ } else if (fetchStatus[tid] == IcacheWaitResponse) {
+ ++icacheStallCycles;
+ }
- fault = fetchCacheLine(fetch_PC);
+ // Status is Idle, Squashing, Blocked, or IcacheWaitResponse, so
+ // fetch should do nothing.
+ return;
}
- // If we had a stall due to an icache miss, then return. It'd
- // be nicer if this were handled through the kind of fault that
- // is returned by the function.
- if (_status == IcacheMissStall) {
+ ++fetchCycles;
+
+ // If we had a stall due to an icache miss, then return.
+ if (fetchStatus[tid] == IcacheWaitResponse) {
+ ++icacheStallCycles;
+ status_change = true;
return;
}
- // As far as timing goes, the CPU will need to send an event through
- // the MemReq in order to be woken up once the memory access completes.
- // Probably have a status on a per thread basis so each thread can
- // block independently and be woken up independently.
-
Addr next_PC = fetch_PC;
InstSeqNum inst_seq;
MachInst inst;
- unsigned offset = fetch_PC & cacheBlkMask;
- unsigned fetched;
+ ExtMachInst ext_inst;
+ // @todo: Fix this hack.
+ unsigned offset = (fetch_PC & cacheBlkMask) & ~3;
if (fault == NoFault) {
// If the read of the first instruction was successful, then grab the
// instructions from the rest of the cache line and put them into the
// queue heading to decode.
- DPRINTF(Fetch, "Fetch: Adding instructions to queue to decode.\n");
-
- //////////////////////////
- // Fetch first instruction
- //////////////////////////
+ DPRINTF(Fetch, "[tid:%i]: Adding instructions to queue to "
+ "decode.\n",tid);
// Need to keep track of whether or not a predicted branch
// ended this fetch block.
bool predicted_branch = false;
- for (fetched = 0;
+ for (;
offset < cacheBlkSize &&
- fetched < fetchWidth &&
+ numInst < fetchWidth &&
!predicted_branch;
- ++fetched)
- {
+ ++numInst) {
// Get a sequence number.
inst_seq = cpu->getAndIncrementInstSeq();
@@ -536,31 +976,40 @@ SimpleFetch<Impl>::fetch()
// Get the instruction from the array of the cache line.
inst = gtoh(*reinterpret_cast<MachInst *>
- (&cacheData[offset]));
+ (&cacheData[tid][offset]));
+
+ ext_inst = TheISA::makeExtMI(inst, fetch_PC);
// Create a new DynInst from the instruction fetched.
- DynInstPtr instruction = new DynInst(inst, fetch_PC, next_PC,
+ DynInstPtr instruction = new DynInst(ext_inst, fetch_PC,
+ next_PC,
inst_seq, cpu);
+ instruction->setThread(tid);
+
+ instruction->setASID(tid);
+
+ instruction->setState(cpu->thread[tid]);
- DPRINTF(Fetch, "Fetch: Instruction %i created, with PC %#x\n",
- inst_seq, instruction->readPC());
+ DPRINTF(Fetch, "[tid:%i]: Instruction PC %#x created "
+ "[sn:%lli]\n",
+ tid, instruction->readPC(), inst_seq);
- DPRINTF(Fetch, "Fetch: Instruction opcode is: %03p\n",
- OPCODE(inst));
+ DPRINTF(Fetch, "[tid:%i]: Instruction is: %s\n",
+ tid, instruction->staticInst->disassemble(fetch_PC));
instruction->traceData =
- Trace::getInstRecord(curTick, cpu->xcBase(), cpu,
+ Trace::getInstRecord(curTick, cpu->tcBase(tid), cpu,
instruction->staticInst,
- instruction->readPC(), 0);
+ instruction->readPC(),tid);
predicted_branch = lookupAndUpdateNextPC(instruction, next_PC);
// Add instruction to the CPU's list of instructions.
- cpu->addInst(instruction);
+ instruction->setInstListIt(cpu->addInst(instruction));
// Write the instruction to the first slot in the queue
// that heads to decode.
- toDecode->insts[fetched] = instruction;
+ toDecode->insts[numInst] = instruction;
toDecode->size++;
@@ -570,48 +1019,243 @@ SimpleFetch<Impl>::fetch()
// Move to the next instruction, unless we have a branch.
fetch_PC = next_PC;
+ if (instruction->isQuiesce()) {
+ warn("%lli: Quiesce instruction encountered, halting fetch!",
+ curTick);
+ fetchStatus[tid] = QuiescePending;
+ ++numInst;
+ status_change = true;
+ break;
+ }
+
offset+= instSize;
}
+ }
- fetch_nisn_dist.sample(fetched);
+ if (numInst > 0) {
+ wroteToTimeBuffer = true;
}
// Now that fetching is completed, update the PC to signify what the next
- // cycle will be. Might want to move this to the beginning of this
- // function so that the PC updates at the beginning of everything.
- // Or might want to leave setting the PC to the main CPU, with fetch
- // only changing the nextPC (will require correct determination of
- // next PC).
+ // cycle will be.
if (fault == NoFault) {
- DPRINTF(Fetch, "Fetch: Setting PC to %08p.\n", next_PC);
- cpu->setPC(next_PC);
- cpu->setNextPC(next_PC + instSize);
+ DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC);
+
+ PC[tid] = next_PC;
+ nextPC[tid] = next_PC + instSize;
} else {
- // If the issue was an icache miss, then we can just return and
- // wait until it is handled.
- if (_status == IcacheMissStall) {
- return;
+ // We shouldn't be in an icache miss and also have a fault (an ITB
+ // miss)
+ if (fetchStatus[tid] == IcacheWaitResponse) {
+ panic("Fetch should have exited prior to this!");
}
- // Handle the fault.
- // This stage will not be able to continue until all the ROB
- // slots are empty, at which point the fault can be handled.
- // The only other way it can wake up is if a squash comes along
- // and changes the PC. Not sure how to handle that case...perhaps
- // have it handled by the upper level CPU class which peeks into the
- // time buffer and sees if a squash comes along, in which case it
- // changes the status.
+ // Send the fault to commit. This thread will not do anything
+ // until commit handles the fault. The only other way it can
+ // wake up is if a squash comes along and changes the PC.
+#if FULL_SYSTEM
+ assert(numInst != fetchWidth);
+ // Get a sequence number.
+ inst_seq = cpu->getAndIncrementInstSeq();
+ // We will use a nop in order to carry the fault.
+ ext_inst = TheISA::NoopMachInst;
- DPRINTF(Fetch, "Fetch: Blocked, need to handle the trap.\n");
+ // Create a new DynInst from the dummy nop.
+ DynInstPtr instruction = new DynInst(ext_inst, fetch_PC,
+ next_PC,
+ inst_seq, cpu);
+ instruction->setPredTarg(next_PC + instSize);
+ instruction->setThread(tid);
- _status = Blocked;
-#if FULL_SYSTEM
-// cpu->trap(fault);
- // Send a signal to the ROB indicating that there's a trap from the
- // fetch stage that needs to be handled. Need to indicate that
- // there's a fault, and the fault type.
+ instruction->setASID(tid);
+
+ instruction->setState(cpu->thread[tid]);
+
+ instruction->traceData = NULL;
+
+ instruction->setInstListIt(cpu->addInst(instruction));
+
+ instruction->fault = fault;
+
+ toDecode->insts[numInst] = instruction;
+ toDecode->size++;
+
+ DPRINTF(Fetch, "[tid:%i]: Blocked, need to handle the trap.\n",tid);
+
+ fetchStatus[tid] = TrapPending;
+ status_change = true;
+
+ warn("%lli fault (%d) detected @ PC %08p", curTick, fault, PC[tid]);
#else // !FULL_SYSTEM
- fatal("fault (%d) detected @ PC %08p", fault, cpu->readPC());
+ warn("%lli fault (%d) detected @ PC %08p", curTick, fault, PC[tid]);
#endif // FULL_SYSTEM
}
}
+
+template<class Impl>
+void
+DefaultFetch<Impl>::recvRetry()
+{
+ assert(cacheBlocked);
+ if (retryPkt != NULL) {
+ assert(retryTid != -1);
+ assert(fetchStatus[retryTid] == IcacheWaitRetry);
+
+ if (icachePort->sendTiming(retryPkt)) {
+ fetchStatus[retryTid] = IcacheWaitResponse;
+ retryPkt = NULL;
+ retryTid = -1;
+ cacheBlocked = false;
+ }
+ } else {
+ assert(retryTid == -1);
+ // Access has been squashed since it was sent out. Just clear
+ // the cache being blocked.
+ cacheBlocked = false;
+ }
+}
+
+///////////////////////////////////////
+// //
+// SMT FETCH POLICY MAINTAINED HERE //
+// //
+///////////////////////////////////////
+template<class Impl>
+int
+DefaultFetch<Impl>::getFetchingThread(FetchPriority &fetch_priority)
+{
+ if (numThreads > 1) {
+ switch (fetch_priority) {
+
+ case SingleThread:
+ return 0;
+
+ case RoundRobin:
+ return roundRobin();
+
+ case IQ:
+ return iqCount();
+
+ case LSQ:
+ return lsqCount();
+
+ case Branch:
+ return branchCount();
+
+ default:
+ return -1;
+ }
+ } else {
+ int tid = *((*activeThreads).begin());
+
+ if (fetchStatus[tid] == Running ||
+ fetchStatus[tid] == IcacheAccessComplete ||
+ fetchStatus[tid] == Idle) {
+ return tid;
+ } else {
+ return -1;
+ }
+ }
+
+}
+
+
+template<class Impl>
+int
+DefaultFetch<Impl>::roundRobin()
+{
+ list<unsigned>::iterator pri_iter = priorityList.begin();
+ list<unsigned>::iterator end = priorityList.end();
+
+ int high_pri;
+
+ while (pri_iter != end) {
+ high_pri = *pri_iter;
+
+ assert(high_pri <= numThreads);
+
+ if (fetchStatus[high_pri] == Running ||
+ fetchStatus[high_pri] == IcacheAccessComplete ||
+ fetchStatus[high_pri] == Idle) {
+
+ priorityList.erase(pri_iter);
+ priorityList.push_back(high_pri);
+
+ return high_pri;
+ }
+
+ pri_iter++;
+ }
+
+ return -1;
+}
+
+template<class Impl>
+int
+DefaultFetch<Impl>::iqCount()
+{
+ priority_queue<unsigned> PQ;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ PQ.push(fromIEW->iewInfo[tid].iqCount);
+ }
+
+ while (!PQ.empty()) {
+
+ unsigned high_pri = PQ.top();
+
+ if (fetchStatus[high_pri] == Running ||
+ fetchStatus[high_pri] == IcacheAccessComplete ||
+ fetchStatus[high_pri] == Idle)
+ return high_pri;
+ else
+ PQ.pop();
+
+ }
+
+ return -1;
+}
+
+template<class Impl>
+int
+DefaultFetch<Impl>::lsqCount()
+{
+ priority_queue<unsigned> PQ;
+
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ PQ.push(fromIEW->iewInfo[tid].ldstqCount);
+ }
+
+ while (!PQ.empty()) {
+
+ unsigned high_pri = PQ.top();
+
+ if (fetchStatus[high_pri] == Running ||
+ fetchStatus[high_pri] == IcacheAccessComplete ||
+ fetchStatus[high_pri] == Idle)
+ return high_pri;
+ else
+ PQ.pop();
+
+ }
+
+ return -1;
+}
+
+template<class Impl>
+int
+DefaultFetch<Impl>::branchCount()
+{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ return *threads;
+}
diff --git a/src/cpu/o3/free_list.cc b/src/cpu/o3/free_list.cc
index 6f0b4be1e..ae651398b 100644
--- a/src/cpu/o3/free_list.cc
+++ b/src/cpu/o3/free_list.cc
@@ -24,13 +24,16 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "base/trace.hh"
#include "cpu/o3/free_list.hh"
-SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs,
+SimpleFreeList::SimpleFreeList(unsigned activeThreads,
+ unsigned _numLogicalIntRegs,
unsigned _numPhysicalIntRegs,
unsigned _numLogicalFloatRegs,
unsigned _numPhysicalFloatRegs)
@@ -40,43 +43,30 @@ SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs,
numPhysicalFloatRegs(_numPhysicalFloatRegs),
numPhysicalRegs(numPhysicalIntRegs + numPhysicalFloatRegs)
{
- DPRINTF(FreeList, "FreeList: Creating new free list object.\n");
-
- // DEBUG stuff.
- freeIntRegsScoreboard.resize(numPhysicalIntRegs);
-
- freeFloatRegsScoreboard.resize(numPhysicalRegs);
-
- for (PhysRegIndex i = 0; i < numLogicalIntRegs; ++i) {
- freeIntRegsScoreboard[i] = 0;
- }
+ DPRINTF(FreeList, "Creating new free list object.\n");
// Put all of the extra physical registers onto the free list. This
// means excluding all of the base logical registers.
- for (PhysRegIndex i = numLogicalIntRegs;
+ for (PhysRegIndex i = numLogicalIntRegs * activeThreads;
i < numPhysicalIntRegs; ++i)
{
freeIntRegs.push(i);
-
- freeIntRegsScoreboard[i] = 1;
- }
-
- for (PhysRegIndex i = 0; i < numPhysicalIntRegs + numLogicalFloatRegs;
- ++i)
- {
- freeFloatRegsScoreboard[i] = 0;
}
// Put all of the extra physical registers onto the free list. This
// means excluding all of the base logical registers. Because the
// float registers' indices start where the physical registers end,
// some math must be done to determine where the free registers start.
- for (PhysRegIndex i = numPhysicalIntRegs + numLogicalFloatRegs;
- i < numPhysicalRegs; ++i)
+ PhysRegIndex i = numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads);
+
+ for ( ; i < numPhysicalRegs; ++i)
{
freeFloatRegs.push(i);
-
- freeFloatRegsScoreboard[i] = 1;
}
}
+std::string
+SimpleFreeList::name() const
+{
+ return "cpu.freelist";
+}
diff --git a/src/cpu/o3/free_list.hh b/src/cpu/o3/free_list.hh
index 0b85dba1e..c669b0b34 100644
--- a/src/cpu/o3/free_list.hh
+++ b/src/cpu/o3/free_list.hh
@@ -24,15 +24,18 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_FREE_LIST_HH__
-#define __CPU_O3_CPU_FREE_LIST_HH__
+#ifndef __CPU_O3_FREE_LIST_HH__
+#define __CPU_O3_FREE_LIST_HH__
#include <iostream>
#include <queue>
#include "arch/isa_traits.hh"
+#include "base/misc.hh"
#include "base/trace.hh"
#include "base/traceflags.hh"
#include "cpu/o3/comm.hh"
@@ -45,10 +48,9 @@
* other classes, it assumes that the indices for the floating point
* registers starts after the integer registers end. Hence the variable
* numPhysicalIntRegs is logically equivalent to the baseFP dependency.
- * Note that
- * while this most likely should be called FreeList, the name "FreeList"
- * is used in a typedef within the CPU Policy, and therefore no class
- * can be named simply "FreeList".
+ * Note that while this most likely should be called FreeList, the name
+ * "FreeList" is used in a typedef within the CPU Policy, and therefore no
+ * class can be named simply "FreeList".
* @todo: Give a better name to the base FP dependency.
*/
class SimpleFreeList
@@ -75,36 +77,51 @@ class SimpleFreeList
/** Total number of physical registers. */
int numPhysicalRegs;
- /** DEBUG stuff below. */
- std::vector<int> freeIntRegsScoreboard;
-
- std::vector<bool> freeFloatRegsScoreboard;
-
public:
- SimpleFreeList(unsigned _numLogicalIntRegs,
+ /** Constructs a free list.
+ * @param activeThreads Number of active threads.
+ * @param _numLogicalIntRegs Number of logical integer registers.
+ * @param _numPhysicalIntRegs Number of physical integer registers.
+ * @param _numLogicalFloatRegs Number of logical fp registers.
+ * @param _numPhysicalFloatRegs Number of physical fp registers.
+ */
+ SimpleFreeList(unsigned activeThreads,
+ unsigned _numLogicalIntRegs,
unsigned _numPhysicalIntRegs,
unsigned _numLogicalFloatRegs,
unsigned _numPhysicalFloatRegs);
+ /** Gives the name of the freelist. */
+ std::string name() const;
+
+ /** Gets a free integer register. */
inline PhysRegIndex getIntReg();
+ /** Gets a free fp register. */
inline PhysRegIndex getFloatReg();
+ /** Adds a register back to the free list. */
inline void addReg(PhysRegIndex freed_reg);
+ /** Adds an integer register back to the free list. */
inline void addIntReg(PhysRegIndex freed_reg);
+ /** Adds a fp register back to the free list. */
inline void addFloatReg(PhysRegIndex freed_reg);
+ /** Checks if there are any free integer registers. */
bool hasFreeIntRegs()
{ return !freeIntRegs.empty(); }
+ /** Checks if there are any free fp registers. */
bool hasFreeFloatRegs()
{ return !freeFloatRegs.empty(); }
+ /** Returns the number of free integer registers. */
int numFreeIntRegs()
{ return freeIntRegs.size(); }
+ /** Returns the number of free fp registers. */
int numFreeFloatRegs()
{ return freeFloatRegs.size(); }
};
@@ -112,7 +129,8 @@ class SimpleFreeList
inline PhysRegIndex
SimpleFreeList::getIntReg()
{
- DPRINTF(Rename, "FreeList: Trying to get free integer register.\n");
+ DPRINTF(FreeList, "Trying to get free integer register.\n");
+
if (freeIntRegs.empty()) {
panic("No free integer registers!");
}
@@ -121,17 +139,14 @@ SimpleFreeList::getIntReg()
freeIntRegs.pop();
- // DEBUG
- assert(freeIntRegsScoreboard[free_reg]);
- freeIntRegsScoreboard[free_reg] = 0;
-
return(free_reg);
}
inline PhysRegIndex
SimpleFreeList::getFloatReg()
{
- DPRINTF(Rename, "FreeList: Trying to get free float register.\n");
+ DPRINTF(FreeList, "Trying to get free float register.\n");
+
if (freeFloatRegs.empty()) {
panic("No free integer registers!");
}
@@ -140,42 +155,28 @@ SimpleFreeList::getFloatReg()
freeFloatRegs.pop();
- // DEBUG
- assert(freeFloatRegsScoreboard[free_reg]);
- freeFloatRegsScoreboard[free_reg] = 0;
-
return(free_reg);
}
inline void
SimpleFreeList::addReg(PhysRegIndex freed_reg)
{
- DPRINTF(Rename, "Freelist: Freeing register %i.\n", freed_reg);
+ DPRINTF(FreeList,"Freeing register %i.\n", freed_reg);
//Might want to add in a check for whether or not this register is
//already in there. A bit vector or something similar would be useful.
if (freed_reg < numPhysicalIntRegs) {
- freeIntRegs.push(freed_reg);
-
- // DEBUG
- assert(freeIntRegsScoreboard[freed_reg] == false);
- freeIntRegsScoreboard[freed_reg] = 1;
+ if (freed_reg != TheISA::ZeroReg)
+ freeIntRegs.push(freed_reg);
} else if (freed_reg < numPhysicalRegs) {
- freeFloatRegs.push(freed_reg);
-
- // DEBUG
- assert(freeFloatRegsScoreboard[freed_reg] == false);
- freeFloatRegsScoreboard[freed_reg] = 1;
+ if (freed_reg != (TheISA::ZeroReg + numPhysicalIntRegs))
+ freeFloatRegs.push(freed_reg);
}
}
inline void
SimpleFreeList::addIntReg(PhysRegIndex freed_reg)
{
- DPRINTF(Rename, "Freelist: Freeing int register %i.\n", freed_reg);
-
- // DEBUG
- assert(!freeIntRegsScoreboard[freed_reg]);
- freeIntRegsScoreboard[freed_reg] = 1;
+ DPRINTF(FreeList,"Freeing int register %i.\n", freed_reg);
freeIntRegs.push(freed_reg);
}
@@ -183,13 +184,9 @@ SimpleFreeList::addIntReg(PhysRegIndex freed_reg)
inline void
SimpleFreeList::addFloatReg(PhysRegIndex freed_reg)
{
- DPRINTF(Rename, "Freelist: Freeing float register %i.\n", freed_reg);
-
- // DEBUG
- assert(!freeFloatRegsScoreboard[freed_reg]);
- freeFloatRegsScoreboard[freed_reg] = 1;
+ DPRINTF(FreeList,"Freeing float register %i.\n", freed_reg);
freeFloatRegs.push(freed_reg);
}
-#endif // __CPU_O3_CPU_FREE_LIST_HH__
+#endif // __CPU_O3_FREE_LIST_HH__
diff --git a/src/cpu/o3/fu_pool.cc b/src/cpu/o3/fu_pool.cc
new file mode 100644
index 000000000..545deea9b
--- /dev/null
+++ b/src/cpu/o3/fu_pool.cc
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#include <sstream>
+
+#include "cpu/o3/fu_pool.hh"
+#include "encumbered/cpu/full/fu_pool.hh"
+#include "sim/builder.hh"
+
+using namespace std;
+
+////////////////////////////////////////////////////////////////////////////
+//
+// A pool of function units
+//
+
+inline void
+FUPool::FUIdxQueue::addFU(int fu_idx)
+{
+ funcUnitsIdx.push_back(fu_idx);
+ ++size;
+}
+
+inline int
+FUPool::FUIdxQueue::getFU()
+{
+ int retval = funcUnitsIdx[idx++];
+
+ if (idx == size)
+ idx = 0;
+
+ return retval;
+}
+
+FUPool::~FUPool()
+{
+ fuListIterator i = funcUnits.begin();
+ fuListIterator end = funcUnits.end();
+ for (; i != end; ++i)
+ delete *i;
+}
+
+
+// Constructor
+FUPool::FUPool(string name, vector<FUDesc *> paramList)
+ : SimObject(name)
+{
+ numFU = 0;
+
+ funcUnits.clear();
+
+ for (int i = 0; i < Num_OpClasses; ++i) {
+ maxOpLatencies[i] = 0;
+ maxIssueLatencies[i] = 0;
+ }
+
+ //
+ // Iterate through the list of FUDescData structures
+ //
+ for (FUDDiterator i = paramList.begin(); i != paramList.end(); ++i) {
+
+ //
+ // Don't bother with this if we're not going to create any FU's
+ //
+ if ((*i)->number) {
+ //
+ // Create the FuncUnit object from this structure
+ // - add the capabilities listed in the FU's operation
+ // description
+ //
+ // We create the first unit, then duplicate it as needed
+ //
+ FuncUnit *fu = new FuncUnit;
+
+ OPDDiterator j = (*i)->opDescList.begin();
+ OPDDiterator end = (*i)->opDescList.end();
+ for (; j != end; ++j) {
+ // indicate that this pool has this capability
+ capabilityList.set((*j)->opClass);
+
+ // Add each of the FU's that will have this capability to the
+ // appropriate queue.
+ for (int k = 0; k < (*i)->number; ++k)
+ fuPerCapList[(*j)->opClass].addFU(numFU + k);
+
+ // indicate that this FU has the capability
+ fu->addCapability((*j)->opClass, (*j)->opLat, (*j)->issueLat);
+
+ if ((*j)->opLat > maxOpLatencies[(*j)->opClass])
+ maxOpLatencies[(*j)->opClass] = (*j)->opLat;
+
+ if ((*j)->issueLat > maxIssueLatencies[(*j)->opClass])
+ maxIssueLatencies[(*j)->opClass] = (*j)->issueLat;
+ }
+
+ numFU++;
+
+ // Add the appropriate number of copies of this FU to the list
+ ostringstream s;
+
+ s << (*i)->name() << "(0)";
+ fu->name = s.str();
+ funcUnits.push_back(fu);
+
+ for (int c = 1; c < (*i)->number; ++c) {
+ ostringstream s;
+ numFU++;
+ FuncUnit *fu2 = new FuncUnit(*fu);
+
+ s << (*i)->name() << "(" << c << ")";
+ fu2->name = s.str();
+ funcUnits.push_back(fu2);
+ }
+ }
+ }
+
+ unitBusy.resize(numFU);
+
+ for (int i = 0; i < numFU; i++) {
+ unitBusy[i] = false;
+ }
+}
+
+void
+FUPool::annotateMemoryUnits(unsigned hit_latency)
+{
+ maxOpLatencies[MemReadOp] = hit_latency;
+
+ fuListIterator i = funcUnits.begin();
+ fuListIterator iend = funcUnits.end();
+ for (; i != iend; ++i) {
+ if ((*i)->provides(MemReadOp))
+ (*i)->opLatency(MemReadOp) = hit_latency;
+
+ if ((*i)->provides(MemWriteOp))
+ (*i)->opLatency(MemWriteOp) = hit_latency;
+ }
+}
+
+int
+FUPool::getUnit(OpClass capability)
+{
+ // If this pool doesn't have the specified capability,
+ // return this information to the caller
+ if (!capabilityList[capability])
+ return -2;
+
+ int fu_idx = fuPerCapList[capability].getFU();
+ int start_idx = fu_idx;
+
+ // Iterate through the circular queue if needed, stopping if we've reached
+ // the first element again.
+ while (unitBusy[fu_idx]) {
+ fu_idx = fuPerCapList[capability].getFU();
+ if (fu_idx == start_idx) {
+ // No FU available
+ return -1;
+ }
+ }
+
+ assert(fu_idx < numFU);
+
+ unitBusy[fu_idx] = true;
+
+ return fu_idx;
+}
+
+void
+FUPool::freeUnitNextCycle(int fu_idx)
+{
+ assert(unitBusy[fu_idx]);
+ unitsToBeFreed.push_back(fu_idx);
+}
+
+void
+FUPool::processFreeUnits()
+{
+ while (!unitsToBeFreed.empty()) {
+ int fu_idx = unitsToBeFreed.back();
+ unitsToBeFreed.pop_back();
+
+ assert(unitBusy[fu_idx]);
+
+ unitBusy[fu_idx] = false;
+ }
+}
+
+void
+FUPool::dump()
+{
+ cout << "Function Unit Pool (" << name() << ")\n";
+ cout << "======================================\n";
+ cout << "Free List:\n";
+
+ for (int i = 0; i < numFU; ++i) {
+ if (unitBusy[i]) {
+ continue;
+ }
+
+ cout << " [" << i << "] : ";
+
+ cout << funcUnits[i]->name << " ";
+
+ cout << "\n";
+ }
+
+ cout << "======================================\n";
+ cout << "Busy List:\n";
+ for (int i = 0; i < numFU; ++i) {
+ if (!unitBusy[i]) {
+ continue;
+ }
+
+ cout << " [" << i << "] : ";
+
+ cout << funcUnits[i]->name << " ";
+
+ cout << "\n";
+ }
+}
+
+void
+FUPool::switchOut()
+{
+}
+
+void
+FUPool::takeOverFrom()
+{
+ for (int i = 0; i < numFU; i++) {
+ unitBusy[i] = false;
+ }
+ unitsToBeFreed.clear();
+}
+
+//
+
+////////////////////////////////////////////////////////////////////////////
+//
+// The SimObjects we use to get the FU information into the simulator
+//
+////////////////////////////////////////////////////////////////////////////
+
+//
+// FUPool - Contails a list of FUDesc objects to make available
+//
+
+//
+// The FuPool object
+//
+
+BEGIN_DECLARE_SIM_OBJECT_PARAMS(FUPool)
+
+ SimObjectVectorParam<FUDesc *> FUList;
+
+END_DECLARE_SIM_OBJECT_PARAMS(FUPool)
+
+
+BEGIN_INIT_SIM_OBJECT_PARAMS(FUPool)
+
+ INIT_PARAM(FUList, "list of FU's for this pool")
+
+END_INIT_SIM_OBJECT_PARAMS(FUPool)
+
+
+CREATE_SIM_OBJECT(FUPool)
+{
+ return new FUPool(getInstanceName(), FUList);
+}
+
+REGISTER_SIM_OBJECT("FUPool", FUPool)
+
diff --git a/src/cpu/o3/fu_pool.hh b/src/cpu/o3/fu_pool.hh
new file mode 100644
index 000000000..52d83f056
--- /dev/null
+++ b/src/cpu/o3/fu_pool.hh
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#ifndef __CPU_O3_FU_POOL_HH__
+#define __CPU_O3_FU_POOL_HH__
+
+#include <bitset>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/sched_list.hh"
+#include "cpu/op_class.hh"
+#include "sim/sim_object.hh"
+
+class FUDesc;
+class FuncUnit;
+
+/**
+ * Pool of FU's, specific to the new CPU model. The old FU pool had lists of
+ * free units and busy units, and whenever a FU was needed it would iterate
+ * through the free units to find a FU that provided the capability. This pool
+ * has lists of units specific to each of the capabilities, and whenever a FU
+ * is needed, it iterates through that list to find a free unit. The previous
+ * FU pool would have to be ticked each cycle to update which units became
+ * free. This FU pool lets the IEW stage handle freeing units, which frees
+ * them as their scheduled execution events complete. This limits units in this
+ * model to either have identical issue and op latencies, or 1 cycle issue
+ * latencies.
+ */
+class FUPool : public SimObject
+{
+ private:
+ /** Maximum op execution latencies, per op class. */
+ unsigned maxOpLatencies[Num_OpClasses];
+ /** Maximum issue latencies, per op class. */
+ unsigned maxIssueLatencies[Num_OpClasses];
+
+ /** Bitvector listing capabilities of this FU pool. */
+ std::bitset<Num_OpClasses> capabilityList;
+
+ /** Bitvector listing which FUs are busy. */
+ std::vector<bool> unitBusy;
+
+ /** List of units to be freed at the end of this cycle. */
+ std::vector<int> unitsToBeFreed;
+
+ /**
+ * Class that implements a circular queue to hold FU indices. The hope is
+ * that FUs that have been just used will be moved to the end of the queue
+ * by iterating through it, thus leaving free units at the head of the
+ * queue.
+ */
+ class FUIdxQueue {
+ public:
+ /** Constructs a circular queue of FU indices. */
+ FUIdxQueue()
+ : idx(0), size(0)
+ { }
+
+ /** Adds a FU to the queue. */
+ inline void addFU(int fu_idx);
+
+ /** Returns the index of the FU at the head of the queue, and changes
+ * the index to the next element.
+ */
+ inline int getFU();
+
+ private:
+ /** Circular queue index. */
+ int idx;
+
+ /** Size of the queue. */
+ int size;
+
+ /** Queue of FU indices. */
+ std::vector<int> funcUnitsIdx;
+ };
+
+ /** Per op class queues of FUs that provide that capability. */
+ FUIdxQueue fuPerCapList[Num_OpClasses];
+
+ /** Number of FUs. */
+ int numFU;
+
+ /** Functional units. */
+ std::vector<FuncUnit *> funcUnits;
+
+ typedef std::vector<FuncUnit *>::iterator fuListIterator;
+
+ public:
+
+ /** Constructs a FU pool. */
+ FUPool(std::string name, std::vector<FUDesc *> l);
+ ~FUPool();
+
+ /** Annotates units that provide memory operations. Included only because
+ * old FU pool provided this function.
+ */
+ void annotateMemoryUnits(unsigned hit_latency);
+
+ /**
+ * Gets a FU providing the requested capability. Will mark the unit as busy,
+ * but leaves the freeing of the unit up to the IEW stage.
+ * @param capability The capability requested.
+ * @return Returns -2 if the FU pool does not have the capability, -1 if
+ * there is no free FU, and the FU's index otherwise.
+ */
+ int getUnit(OpClass capability);
+
+ /** Frees a FU at the end of this cycle. */
+ void freeUnitNextCycle(int fu_idx);
+
+ /** Frees all FUs on the list. */
+ void processFreeUnits();
+
+ /** Returns the total number of FUs. */
+ int size() { return numFU; }
+
+ /** Debugging function used to dump FU information. */
+ void dump();
+
+ /** Returns the operation execution latency of the given capability. */
+ unsigned getOpLatency(OpClass capability) {
+ return maxOpLatencies[capability];
+ }
+
+ /** Returns the issue latency of the given capability. */
+ unsigned getIssueLatency(OpClass capability) {
+ return maxIssueLatencies[capability];
+ }
+
+ /** Switches out functional unit pool. */
+ void switchOut();
+
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
+};
+
+#endif // __CPU_O3_FU_POOL_HH__
diff --git a/src/cpu/o3/iew.cc b/src/cpu/o3/iew.cc
index 45b5610e7..8145f4cc7 100644
--- a/src/cpu/o3/iew.cc
+++ b/src/cpu/o3/iew.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
@@ -31,4 +33,4 @@
#include "cpu/o3/iew_impl.hh"
#include "cpu/o3/inst_queue.hh"
-template class SimpleIEW<AlphaSimpleImpl>;
+template class DefaultIEW<AlphaSimpleImpl>;
diff --git a/src/cpu/o3/iew.hh b/src/cpu/o3/iew.hh
index 1e370d4e6..2e61af5fc 100644
--- a/src/cpu/o3/iew.hh
+++ b/src/cpu/o3/iew.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,24 +24,45 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-//Todo: Update with statuses.
-//Need to handle delaying writes to the writeback bus if it's full at the
-//given time.
-
-#ifndef __CPU_O3_CPU_SIMPLE_IEW_HH__
-#define __CPU_O3_CPU_SIMPLE_IEW_HH__
+#ifndef __CPU_O3_IEW_HH__
+#define __CPU_O3_IEW_HH__
#include <queue>
-#include "config/full_system.hh"
#include "base/statistics.hh"
#include "base/timebuf.hh"
+#include "config/full_system.hh"
#include "cpu/o3/comm.hh"
-
+#include "cpu/o3/scoreboard.hh"
+#include "cpu/o3/lsq.hh"
+
+class FUPool;
+
+/**
+ * DefaultIEW handles both single threaded and SMT IEW
+ * (issue/execute/writeback). It handles the dispatching of
+ * instructions to the LSQ/IQ as part of the issue stage, and has the
+ * IQ try to issue instructions each cycle. The execute latency is
+ * actually tied into the issue latency to allow the IQ to be able to
+ * do back-to-back scheduling without having to speculatively schedule
+ * instructions. This happens by having the IQ have access to the
+ * functional units, and the IQ gets the execution latencies from the
+ * FUs when it issues instructions. Instructions reach the execute
+ * stage on the last cycle of their execution, which is when the IQ
+ * knows to wake up any dependent instructions, allowing back to back
+ * scheduling. The execute portion of IEW separates memory
+ * instructions from non-memory instructions, either telling the LSQ
+ * to execute the instruction, or executing the instruction directly.
+ * The writeback portion of IEW completes the instructions by waking
+ * up any dependents, and marking the register ready on the
+ * scoreboard.
+ */
template<class Impl>
-class SimpleIEW
+class DefaultIEW
{
private:
//Typedefs from Impl
@@ -52,7 +73,7 @@ class SimpleIEW
typedef typename CPUPol::IQ IQ;
typedef typename CPUPol::RenameMap RenameMap;
- typedef typename CPUPol::LDSTQ LDSTQ;
+ typedef typename CPUPol::LSQ LSQ;
typedef typename CPUPol::TimeStruct TimeStruct;
typedef typename CPUPol::IEWStruct IEWStruct;
@@ -60,77 +81,205 @@ class SimpleIEW
typedef typename CPUPol::IssueStruct IssueStruct;
friend class Impl::FullCPU;
+ friend class CPUPol::IQ;
+
public:
+ /** Overall IEW stage status. Used to determine if the CPU can
+ * deschedule itself due to a lack of activity.
+ */
enum Status {
+ Active,
+ Inactive
+ };
+
+ /** Status for Issue, Execute, and Writeback stages. */
+ enum StageStatus {
Running,
Blocked,
Idle,
+ StartSquash,
Squashing,
Unblocking
};
private:
+ /** Overall stage status. */
Status _status;
- Status _issueStatus;
- Status _exeStatus;
- Status _wbStatus;
+ /** Dispatch status. */
+ StageStatus dispatchStatus[Impl::MaxThreads];
+ /** Execute status. */
+ StageStatus exeStatus;
+ /** Writeback status. */
+ StageStatus wbStatus;
public:
- class WritebackEvent : public Event {
- private:
- DynInstPtr inst;
- SimpleIEW<Impl> *iewStage;
+ /** Constructs a DefaultIEW with the given parameters. */
+ DefaultIEW(Params *params);
- public:
- WritebackEvent(DynInstPtr &_inst, SimpleIEW<Impl> *_iew);
-
- virtual void process();
- virtual const char *description();
- };
-
- public:
- SimpleIEW(Params &params);
+ /** Returns the name of the DefaultIEW stage. */
+ std::string name() const;
+ /** Registers statistics. */
void regStats();
+ /** Initializes stage; sends back the number of free IQ and LSQ entries. */
+ void initStage();
+
+ /** Sets CPU pointer for IEW, IQ, and LSQ. */
void setCPU(FullCPU *cpu_ptr);
+ /** Sets main time buffer used for backwards communication. */
void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);
+ /** Sets time buffer for getting instructions coming from rename. */
void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr);
+ /** Sets time buffer to pass on instructions to commit. */
void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr);
- void setRenameMap(RenameMap *rm_ptr);
+ /** Sets pointer to list of active threads. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
- void squash();
+ /** Sets pointer to the scoreboard. */
+ void setScoreboard(Scoreboard *sb_ptr);
- void squashDueToBranch(DynInstPtr &inst);
+ /** Starts switch out of IEW stage. */
+ void switchOut();
- void squashDueToMem(DynInstPtr &inst);
+ /** Completes switch out of IEW stage. */
+ void doSwitchOut();
- void block();
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
- inline void unblock();
+ /** Returns if IEW is switched out. */
+ bool isSwitchedOut() { return switchedOut; }
+ /** Squashes instructions in IEW for a specific thread. */
+ void squash(unsigned tid);
+
+ /** Wakes all dependents of a completed instruction. */
void wakeDependents(DynInstPtr &inst);
+ /** Tells memory dependence unit that a memory instruction needs to be
+ * rescheduled. It will re-execute once replayMemInst() is called.
+ */
+ void rescheduleMemInst(DynInstPtr &inst);
+
+ /** Re-executes all rescheduled memory instructions. */
+ void replayMemInst(DynInstPtr &inst);
+
+ /** Sends an instruction to commit through the time buffer. */
void instToCommit(DynInstPtr &inst);
+ /** Inserts unused instructions of a thread into the skid buffer. */
+ void skidInsert(unsigned tid);
+
+ /** Returns the max of the number of entries in all of the skid buffers. */
+ int skidCount();
+
+ /** Returns if all of the skid buffers are empty. */
+ bool skidsEmpty();
+
+ /** Updates overall IEW status based on all of the stages' statuses. */
+ void updateStatus();
+
+ /** Resets entries of the IQ and the LSQ. */
+ void resetEntries();
+
+ /** Tells the CPU to wakeup if it has descheduled itself due to no
+ * activity. Used mainly by the LdWritebackEvent.
+ */
+ void wakeCPU();
+
+ /** Reports to the CPU that there is activity this cycle. */
+ void activityThisCycle();
+
+ /** Tells CPU that the IEW stage is active and running. */
+ inline void activateStage();
+
+ /** Tells CPU that the IEW stage is inactive and idle. */
+ inline void deactivateStage();
+
+ /** Returns if the LSQ has any stores to writeback. */
+ bool hasStoresToWB() { return ldstQueue.hasStoresToWB(); }
+
private:
- void dispatchInsts();
+ /** Sends commit proper information for a squash due to a branch
+ * mispredict.
+ */
+ void squashDueToBranch(DynInstPtr &inst, unsigned thread_id);
+
+ /** Sends commit proper information for a squash due to a memory order
+ * violation.
+ */
+ void squashDueToMemOrder(DynInstPtr &inst, unsigned thread_id);
+
+ /** Sends commit proper information for a squash due to memory becoming
+ * blocked (younger issued instructions must be retried).
+ */
+ void squashDueToMemBlocked(DynInstPtr &inst, unsigned thread_id);
+
+ /** Sets Dispatch to blocked, and signals back to other stages to block. */
+ void block(unsigned thread_id);
+
+ /** Unblocks Dispatch if the skid buffer is empty, and signals back to
+ * other stages to unblock.
+ */
+ void unblock(unsigned thread_id);
+
+ /** Determines proper actions to take given Dispatch's status. */
+ void dispatch(unsigned tid);
+ /** Dispatches instructions to IQ and LSQ. */
+ void dispatchInsts(unsigned tid);
+
+ /** Executes instructions. In the case of memory operations, it informs the
+ * LSQ to execute the instructions. Also handles any redirects that occur
+ * due to the executed instructions.
+ */
void executeInsts();
+ /** Writebacks instructions. In our model, the instruction's execute()
+ * function atomically reads registers, executes, and writes registers.
+ * Thus this writeback only wakes up dependent instructions, and informs
+ * the scoreboard of registers becoming ready.
+ */
+ void writebackInsts();
+
+ /** Returns the number of valid, non-squashed instructions coming from
+ * rename to dispatch.
+ */
+ unsigned validInstsFromRename();
+
+ /** Reads the stall signals. */
+ void readStallSignals(unsigned tid);
+
+ /** Checks if any of the stall conditions are currently true. */
+ bool checkStall(unsigned tid);
+
+ /** Processes inputs and changes state accordingly. */
+ void checkSignalsAndUpdate(unsigned tid);
+
+ /** Sorts instructions coming from rename into lists separated by thread. */
+ void sortInsts();
+
public:
+ /** Ticks IEW stage, causing Dispatch, the IQ, the LSQ, Execute, and
+ * Writeback to run for one cycle.
+ */
void tick();
- void iew();
+ private:
+ /** Updates execution stats based on the instruction. */
+ void updateExeInstStats(DynInstPtr &inst);
- //Interfaces to objects inside and outside of IEW.
- /** Time buffer interface. */
+ /** Pointer to main time buffer used for backwards communication. */
TimeBuffer<TimeStruct> *timeBuffer;
+ /** Wire to write information heading to previous stages. */
+ typename TimeBuffer<TimeStruct>::wire toFetch;
+
/** Wire to get commit's output from backwards time buffer. */
typename TimeBuffer<TimeStruct>::wire fromCommit;
@@ -158,32 +307,67 @@ class SimpleIEW
/** Wire to write infromation heading to commit. */
typename TimeBuffer<IEWStruct>::wire toCommit;
- //Will need internal queue to hold onto instructions coming from
- //the rename stage in case of a stall.
+ /** Queue of all instructions coming from rename this cycle. */
+ std::queue<DynInstPtr> insts[Impl::MaxThreads];
+
/** Skid buffer between rename and IEW. */
- std::queue<RenameStruct> skidBuffer;
+ std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads];
- protected:
+ /** Scoreboard pointer. */
+ Scoreboard* scoreboard;
+
+ public:
/** Instruction queue. */
IQ instQueue;
- LDSTQ ldstQueue;
+ /** Load / store queue. */
+ LSQ ldstQueue;
-#if !FULL_SYSTEM
- public:
- void lsqWriteback();
-#endif
+ /** Pointer to the functional unit pool. */
+ FUPool *fuPool;
private:
- /** Pointer to rename map. Might not want this stage to directly
- * access this though...
+ /** CPU pointer. */
+ FullCPU *cpu;
+
+ /** Records if IEW has written to the time buffer this cycle, so that the
+ * CPU can deschedule itself if there is no activity.
*/
- RenameMap *renameMap;
+ bool wroteToTimeBuffer;
- /** CPU interface. */
- FullCPU *cpu;
+ /** Source of possible stalls. */
+ struct Stalls {
+ bool commit;
+ };
+
+ /** Stages that are telling IEW to stall. */
+ Stalls stalls[Impl::MaxThreads];
+
+ /** Debug function to print instructions that are issued this cycle. */
+ void printAvailableInsts();
+
+ public:
+ /** Records if the LSQ needs to be updated on the next cycle, so that
+ * IEW knows if there will be activity on the next cycle.
+ */
+ bool updateLSQNextCycle;
private:
+ /** Records if there is a fetch redirect on this cycle for each thread. */
+ bool fetchRedirect[Impl::MaxThreads];
+
+ /** Used to track if all instructions have been dispatched this cycle.
+ * If they have not, then blocking must have occurred, and the instructions
+ * would already be added to the skid buffer.
+ * @todo: Fix this hack.
+ */
+ bool dispatchedAllInsts;
+
+ /** Records if the queues have been changed (inserted or issued insts),
+ * so that IEW knows to broadcast the updated amount of free entries.
+ */
+ bool updatedQueues;
+
/** Commit to IEW delay, in ticks. */
unsigned commitToIEWDelay;
@@ -211,29 +395,108 @@ class SimpleIEW
*/
unsigned executeWidth;
- /** Number of cycles stage has been squashing. Used so that the stage
- * knows when it can start unblocking, which is when the previous stage
- * has received the stall signal and clears up its outputs.
+ /** Index into queue of instructions being written back. */
+ unsigned wbNumInst;
+
+ /** Cycle number within the queue of instructions being written back.
+ * Used in case there are too many instructions writing back at the current
+ * cycle and writesbacks need to be scheduled for the future. See comments
+ * in instToCommit().
*/
- unsigned cyclesSquashing;
+ unsigned wbCycle;
+
+ /** Number of active threads. */
+ unsigned numThreads;
+
+ /** Pointer to list of active threads. */
+ std::list<unsigned> *activeThreads;
+ /** 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. */
Stats::Scalar<> iewSquashCycles;
+ /** Stat for total number of blocking cycles. */
Stats::Scalar<> iewBlockCycles;
+ /** Stat for total number of unblocking cycles. */
Stats::Scalar<> iewUnblockCycles;
-// Stats::Scalar<> iewWBInsts;
+ /** Stat for total number of instructions dispatched. */
Stats::Scalar<> iewDispatchedInsts;
+ /** Stat for total number of squashed instructions dispatch skips. */
Stats::Scalar<> iewDispSquashedInsts;
+ /** Stat for total number of dispatched load instructions. */
Stats::Scalar<> iewDispLoadInsts;
+ /** Stat for total number of dispatched store instructions. */
Stats::Scalar<> iewDispStoreInsts;
+ /** Stat for total number of dispatched non speculative instructions. */
Stats::Scalar<> iewDispNonSpecInsts;
+ /** Stat for number of times the IQ becomes full. */
Stats::Scalar<> iewIQFullEvents;
+ /** Stat for number of times the LSQ becomes full. */
+ Stats::Scalar<> iewLSQFullEvents;
+ /** Stat for total number of executed instructions. */
Stats::Scalar<> iewExecutedInsts;
- Stats::Scalar<> iewExecLoadInsts;
- Stats::Scalar<> iewExecStoreInsts;
+ /** Stat for total number of executed load instructions. */
+ Stats::Vector<> iewExecLoadInsts;
+ /** Stat for total number of executed store instructions. */
+// Stats::Scalar<> iewExecStoreInsts;
+ /** Stat for total number of squashed instructions skipped at execute. */
Stats::Scalar<> iewExecSquashedInsts;
+ /** Stat for total number of memory ordering violation events. */
Stats::Scalar<> memOrderViolationEvents;
+ /** Stat for total number of incorrect predicted taken branches. */
Stats::Scalar<> predictedTakenIncorrect;
+ /** Stat for total number of incorrect predicted not taken branches. */
+ Stats::Scalar<> predictedNotTakenIncorrect;
+ /** Stat for total number of mispredicted branches detected at execute. */
+ Stats::Formula branchMispredicts;
+
+ /** Number of executed software prefetches. */
+ Stats::Vector<> exeSwp;
+ /** Number of executed nops. */
+ Stats::Vector<> exeNop;
+ /** Number of executed meomory references. */
+ Stats::Vector<> exeRefs;
+ /** Number of executed branches. */
+ Stats::Vector<> exeBranches;
+
+// Stats::Vector<> issued_ops;
+/*
+ Stats::Vector<> stat_fu_busy;
+ Stats::Vector2d<> stat_fuBusy;
+ Stats::Vector<> dist_unissued;
+ Stats::Vector2d<> stat_issued_inst_type;
+*/
+ /** Number of instructions issued per cycle. */
+ Stats::Formula issueRate;
+ /** Number of executed store instructions. */
+ Stats::Formula iewExecStoreInsts;
+// Stats::Formula issue_op_rate;
+// Stats::Formula fu_busy_rate;
+ /** Number of instructions sent to commit. */
+ Stats::Vector<> iewInstsToCommit;
+ /** Number of instructions that writeback. */
+ Stats::Vector<> writebackCount;
+ /** Number of instructions that wake consumers. */
+ Stats::Vector<> producerInst;
+ /** Number of instructions that wake up from producers. */
+ Stats::Vector<> consumerInst;
+ /** Number of instructions that were delayed in writing back due
+ * to resource contention.
+ */
+ Stats::Vector<> wbPenalized;
+
+ /** Number of instructions per cycle written back. */
+ Stats::Formula wbRate;
+ /** Average number of woken instructions per writeback. */
+ Stats::Formula wbFanout;
+ /** Number of instructions per cycle delayed in writing back . */
+ Stats::Formula wbPenalizedRate;
};
-#endif // __CPU_O3_CPU_IEW_HH__
+#endif // __CPU_O3_IEW_HH__
diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh
index 85217dd10..3929f2e19 100644
--- a/src/cpu/o3/iew_impl.hh
+++ b/src/cpu/o3/iew_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,64 +24,41 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
// @todo: Fix the instantaneous communication among all the stages within
// iew. There's a clear delay between issue and execute, yet backwards
// communication happens simultaneously.
-// Update the statuses for each stage.
#include <queue>
#include "base/timebuf.hh"
+#include "cpu/o3/fu_pool.hh"
#include "cpu/o3/iew.hh"
-template<class Impl>
-SimpleIEW<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst,
- SimpleIEW<Impl> *_iew)
- : Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew)
-{
- this->setFlags(Event::AutoDelete);
-}
-
-template<class Impl>
-void
-SimpleIEW<Impl>::WritebackEvent::process()
-{
- DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n");
-
- // Need to insert instruction into queue to commit
- iewStage->instToCommit(inst);
- // Need to execute second half of the instruction, do actual writing to
- // registers and such
- inst->execute();
-}
-
-template<class Impl>
-const char *
-SimpleIEW<Impl>::WritebackEvent::description()
-{
- return "LSQ writeback event";
-}
+using namespace std;
template<class Impl>
-SimpleIEW<Impl>::SimpleIEW(Params &params)
- : // Just make this time buffer really big for now
+DefaultIEW<Impl>::DefaultIEW(Params *params)
+ : // @todo: Make this into a parameter.
issueToExecQueue(5, 5),
instQueue(params),
ldstQueue(params),
- commitToIEWDelay(params.commitToIEWDelay),
- renameToIEWDelay(params.renameToIEWDelay),
- issueToExecuteDelay(params.issueToExecuteDelay),
- issueReadWidth(params.issueWidth),
- issueWidth(params.issueWidth),
- executeWidth(params.executeWidth)
-{
- DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth);
- _status = Idle;
- _issueStatus = Idle;
- _exeStatus = Idle;
- _wbStatus = Idle;
+ fuPool(params->fuPool),
+ commitToIEWDelay(params->commitToIEWDelay),
+ renameToIEWDelay(params->renameToIEWDelay),
+ issueToExecuteDelay(params->issueToExecuteDelay),
+ issueReadWidth(params->issueWidth),
+ issueWidth(params->issueWidth),
+ executeWidth(params->executeWidth),
+ numThreads(params->numberOfThreads),
+ switchedOut(false)
+{
+ _status = Active;
+ exeStatus = Running;
+ wbStatus = Idle;
// Setup wire to read instructions coming from issue.
fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay);
@@ -89,13 +66,33 @@ SimpleIEW<Impl>::SimpleIEW(Params &params)
// Instruction queue needs the queue between issue and execute.
instQueue.setIssueToExecuteQueue(&issueToExecQueue);
+ instQueue.setIEW(this);
ldstQueue.setIEW(this);
+
+ for (int i=0; i < numThreads; i++) {
+ dispatchStatus[i] = Running;
+ stalls[i].commit = false;
+ fetchRedirect[i] = false;
+ }
+
+ updateLSQNextCycle = false;
+
+ skidBufferMax = (3 * (renameToIEWDelay * params->renameWidth)) + issueWidth;
+}
+
+template <class Impl>
+std::string
+DefaultIEW<Impl>::name() const
+{
+ return cpu->name() + ".iew";
}
template <class Impl>
void
-SimpleIEW<Impl>::regStats()
+DefaultIEW<Impl>::regStats()
{
+ using namespace Stats;
+
instQueue.regStats();
iewIdleCycles
@@ -114,8 +111,6 @@ SimpleIEW<Impl>::regStats()
.name(name() + ".iewUnblockCycles")
.desc("Number of cycles IEW is unblocking");
-// iewWBInsts;
-
iewDispatchedInsts
.name(name() + ".iewDispatchedInsts")
.desc("Number of instructions dispatched to IQ");
@@ -140,17 +135,19 @@ SimpleIEW<Impl>::regStats()
.name(name() + ".iewIQFullEvents")
.desc("Number of times the IQ has become full, causing a stall");
+ iewLSQFullEvents
+ .name(name() + ".iewLSQFullEvents")
+ .desc("Number of times the LSQ has become full, causing a stall");
+
iewExecutedInsts
.name(name() + ".iewExecutedInsts")
.desc("Number of executed instructions");
iewExecLoadInsts
+ .init(cpu->number_of_threads)
.name(name() + ".iewExecLoadInsts")
- .desc("Number of load instructions executed");
-
- iewExecStoreInsts
- .name(name() + ".iewExecStoreInsts")
- .desc("Number of store instructions executed");
+ .desc("Number of load instructions executed")
+ .flags(total);
iewExecSquashedInsts
.name(name() + ".iewExecSquashedInsts")
@@ -163,24 +160,161 @@ SimpleIEW<Impl>::regStats()
predictedTakenIncorrect
.name(name() + ".predictedTakenIncorrect")
.desc("Number of branches that were predicted taken incorrectly");
+
+ predictedNotTakenIncorrect
+ .name(name() + ".predictedNotTakenIncorrect")
+ .desc("Number of branches that were predicted not taken incorrectly");
+
+ branchMispredicts
+ .name(name() + ".branchMispredicts")
+ .desc("Number of branch mispredicts detected at execute");
+
+ branchMispredicts = predictedTakenIncorrect + predictedNotTakenIncorrect;
+
+ exeSwp
+ .init(cpu->number_of_threads)
+ .name(name() + ".EXEC:swp")
+ .desc("number of swp insts executed")
+ .flags(total)
+ ;
+
+ exeNop
+ .init(cpu->number_of_threads)
+ .name(name() + ".EXEC:nop")
+ .desc("number of nop insts executed")
+ .flags(total)
+ ;
+
+ exeRefs
+ .init(cpu->number_of_threads)
+ .name(name() + ".EXEC:refs")
+ .desc("number of memory reference insts executed")
+ .flags(total)
+ ;
+
+ exeBranches
+ .init(cpu->number_of_threads)
+ .name(name() + ".EXEC:branches")
+ .desc("Number of branches executed")
+ .flags(total)
+ ;
+
+ issueRate
+ .name(name() + ".EXEC:rate")
+ .desc("Inst execution rate")
+ .flags(total)
+ ;
+ issueRate = iewExecutedInsts / cpu->numCycles;
+
+ iewExecStoreInsts
+ .name(name() + ".EXEC:stores")
+ .desc("Number of stores executed")
+ .flags(total)
+ ;
+ iewExecStoreInsts = exeRefs - iewExecLoadInsts;
+/*
+ for (int i=0; i<Num_OpClasses; ++i) {
+ stringstream subname;
+ subname << opClassStrings[i] << "_delay";
+ issue_delay_dist.subname(i, subname.str());
+ }
+*/
+ //
+ // Other stats
+ //
+
+ iewInstsToCommit
+ .init(cpu->number_of_threads)
+ .name(name() + ".WB:sent")
+ .desc("cumulative count of insts sent to commit")
+ .flags(total)
+ ;
+
+ writebackCount
+ .init(cpu->number_of_threads)
+ .name(name() + ".WB:count")
+ .desc("cumulative count of insts written-back")
+ .flags(total)
+ ;
+
+ producerInst
+ .init(cpu->number_of_threads)
+ .name(name() + ".WB:producers")
+ .desc("num instructions producing a value")
+ .flags(total)
+ ;
+
+ consumerInst
+ .init(cpu->number_of_threads)
+ .name(name() + ".WB:consumers")
+ .desc("num instructions consuming a value")
+ .flags(total)
+ ;
+
+ wbPenalized
+ .init(cpu->number_of_threads)
+ .name(name() + ".WB:penalized")
+ .desc("number of instrctions required to write to 'other' IQ")
+ .flags(total)
+ ;
+
+ wbPenalizedRate
+ .name(name() + ".WB:penalized_rate")
+ .desc ("fraction of instructions written-back that wrote to 'other' IQ")
+ .flags(total)
+ ;
+
+ wbPenalizedRate = wbPenalized / writebackCount;
+
+ wbFanout
+ .name(name() + ".WB:fanout")
+ .desc("average fanout of values written-back")
+ .flags(total)
+ ;
+
+ wbFanout = producerInst / consumerInst;
+
+ wbRate
+ .name(name() + ".WB:rate")
+ .desc("insts written-back per cycle")
+ .flags(total)
+ ;
+ wbRate = writebackCount / cpu->numCycles;
+}
+
+template<class Impl>
+void
+DefaultIEW<Impl>::initStage()
+{
+ for (int tid=0; tid < numThreads; tid++) {
+ toRename->iewInfo[tid].usedIQ = true;
+ toRename->iewInfo[tid].freeIQEntries =
+ instQueue.numFreeEntries(tid);
+
+ toRename->iewInfo[tid].usedLSQ = true;
+ toRename->iewInfo[tid].freeLSQEntries =
+ ldstQueue.numFreeEntries(tid);
+ }
}
template<class Impl>
void
-SimpleIEW<Impl>::setCPU(FullCPU *cpu_ptr)
+DefaultIEW<Impl>::setCPU(FullCPU *cpu_ptr)
{
- DPRINTF(IEW, "IEW: Setting CPU pointer.\n");
+ DPRINTF(IEW, "Setting CPU pointer.\n");
cpu = cpu_ptr;
instQueue.setCPU(cpu_ptr);
ldstQueue.setCPU(cpu_ptr);
+
+ cpu->activateStage(FullCPU::IEWIdx);
}
template<class Impl>
void
-SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
+DefaultIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
- DPRINTF(IEW, "IEW: Setting time buffer pointer.\n");
+ DPRINTF(IEW, "Setting time buffer pointer.\n");
timeBuffer = tb_ptr;
// Setup wire to read information from time buffer, from commit.
@@ -189,15 +323,17 @@ SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
// Setup wire to write information back to previous stages.
toRename = timeBuffer->getWire(0);
+ toFetch = timeBuffer->getWire(0);
+
// Instruction queue also needs main time buffer.
instQueue.setTimeBuffer(tb_ptr);
}
template<class Impl>
void
-SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
+DefaultIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
{
- DPRINTF(IEW, "IEW: Setting rename queue pointer.\n");
+ DPRINTF(IEW, "Setting rename queue pointer.\n");
renameQueue = rq_ptr;
// Setup wire to read information from rename queue.
@@ -206,9 +342,9 @@ SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
template<class Impl>
void
-SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
+DefaultIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
{
- DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n");
+ DPRINTF(IEW, "Setting IEW queue pointer.\n");
iewQueue = iq_ptr;
// Setup wire to write instructions to commit.
@@ -217,355 +353,938 @@ SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
template<class Impl>
void
-SimpleIEW<Impl>::setRenameMap(RenameMap *rm_ptr)
+DefaultIEW<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ DPRINTF(IEW, "Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
+
+ ldstQueue.setActiveThreads(at_ptr);
+ instQueue.setActiveThreads(at_ptr);
+}
+
+template<class Impl>
+void
+DefaultIEW<Impl>::setScoreboard(Scoreboard *sb_ptr)
+{
+ DPRINTF(IEW, "Setting scoreboard pointer.\n");
+ scoreboard = sb_ptr;
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::switchOut()
{
- DPRINTF(IEW, "IEW: Setting rename map pointer.\n");
- renameMap = rm_ptr;
+ // IEW is ready to switch out at any time.
+ cpu->signalSwitched();
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::doSwitchOut()
+{
+ // Clear any state.
+ switchedOut = true;
+
+ instQueue.switchOut();
+ ldstQueue.switchOut();
+ fuPool->switchOut();
+
+ for (int i = 0; i < numThreads; i++) {
+ while (!insts[i].empty())
+ insts[i].pop();
+ while (!skidBuffer[i].empty())
+ skidBuffer[i].pop();
+ }
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::takeOverFrom()
+{
+ // Reset all state.
+ _status = Active;
+ exeStatus = Running;
+ wbStatus = Idle;
+ switchedOut = false;
+
+ instQueue.takeOverFrom();
+ ldstQueue.takeOverFrom();
+ fuPool->takeOverFrom();
+
+ initStage();
+ cpu->activityThisCycle();
+
+ for (int i=0; i < numThreads; i++) {
+ dispatchStatus[i] = Running;
+ stalls[i].commit = false;
+ fetchRedirect[i] = false;
+ }
+
+ updateLSQNextCycle = false;
+
+ // @todo: Fix hardcoded number
+ for (int i = 0; i < 6; ++i) {
+ issueToExecQueue.advance();
+ }
}
template<class Impl>
void
-SimpleIEW<Impl>::squash()
+DefaultIEW<Impl>::squash(unsigned tid)
{
- DPRINTF(IEW, "IEW: Squashing all instructions.\n");
- _status = Squashing;
+ DPRINTF(IEW, "[tid:%i]: Squashing all instructions.\n",
+ tid);
// Tell the IQ to start squashing.
- instQueue.squash();
+ instQueue.squash(tid);
// Tell the LDSTQ to start squashing.
- ldstQueue.squash(fromCommit->commitInfo.doneSeqNum);
+ ldstQueue.squash(fromCommit->commitInfo[tid].doneSeqNum, tid);
+
+ updatedQueues = true;
+
+ // Clear the skid buffer in case it has any data in it.
+ while (!skidBuffer[tid].empty()) {
+
+ if (skidBuffer[tid].front()->isLoad() ||
+ skidBuffer[tid].front()->isStore() ) {
+ toRename->iewInfo[tid].dispatchedToLSQ++;
+ }
+
+ toRename->iewInfo[tid].dispatched++;
+
+ skidBuffer[tid].pop();
+ }
+
+ while (!insts[tid].empty()) {
+ if (insts[tid].front()->isLoad() ||
+ insts[tid].front()->isStore() ) {
+ toRename->iewInfo[tid].dispatchedToLSQ++;
+ }
+
+ toRename->iewInfo[tid].dispatched++;
+
+ insts[tid].pop();
+ }
}
template<class Impl>
void
-SimpleIEW<Impl>::squashDueToBranch(DynInstPtr &inst)
-{
- DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
- inst->PC);
- // Perhaps leave the squashing up to the ROB stage to tell it when to
- // squash?
- _status = Squashing;
-
- // Tell rename to squash through the time buffer.
- toCommit->squash = true;
- // Also send PC update information back to prior stages.
- toCommit->squashedSeqNum = inst->seqNum;
- toCommit->mispredPC = inst->readPC();
- toCommit->nextPC = inst->readNextPC();
- toCommit->branchMispredict = true;
- // Prediction was incorrect, so send back inverse.
- toCommit->branchTaken = inst->readNextPC() !=
+DefaultIEW<Impl>::squashDueToBranch(DynInstPtr &inst, unsigned tid)
+{
+ DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, PC: %#x "
+ "[sn:%i].\n", tid, inst->readPC(), inst->seqNum);
+
+ toCommit->squash[tid] = true;
+ toCommit->squashedSeqNum[tid] = inst->seqNum;
+ toCommit->mispredPC[tid] = inst->readPC();
+ toCommit->nextPC[tid] = inst->readNextPC();
+ toCommit->branchMispredict[tid] = true;
+ toCommit->branchTaken[tid] = inst->readNextPC() !=
(inst->readPC() + sizeof(TheISA::MachInst));
+
+ toCommit->includeSquashInst[tid] = false;
+
+ wroteToTimeBuffer = true;
}
template<class Impl>
void
-SimpleIEW<Impl>::squashDueToMem(DynInstPtr &inst)
+DefaultIEW<Impl>::squashDueToMemOrder(DynInstPtr &inst, unsigned tid)
{
- DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
- inst->PC);
- // Perhaps leave the squashing up to the ROB stage to tell it when to
- // squash?
- _status = Squashing;
+ DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, "
+ "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum);
+
+ toCommit->squash[tid] = true;
+ toCommit->squashedSeqNum[tid] = inst->seqNum;
+ toCommit->nextPC[tid] = inst->readNextPC();
+
+ toCommit->includeSquashInst[tid] = false;
- // Tell rename to squash through the time buffer.
- toCommit->squash = true;
- // Also send PC update information back to prior stages.
- toCommit->squashedSeqNum = inst->seqNum;
- toCommit->nextPC = inst->readNextPC();
+ wroteToTimeBuffer = true;
}
template<class Impl>
void
-SimpleIEW<Impl>::block()
+DefaultIEW<Impl>::squashDueToMemBlocked(DynInstPtr &inst, unsigned tid)
{
- DPRINTF(IEW, "IEW: Blocking.\n");
- // Set the status to Blocked.
- _status = Blocked;
+ DPRINTF(IEW, "[tid:%i]: Memory blocked, squashing load and younger insts, "
+ "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum);
+
+ toCommit->squash[tid] = true;
+ toCommit->squashedSeqNum[tid] = inst->seqNum;
+ toCommit->nextPC[tid] = inst->readPC();
+
+ // Must include the broadcasted SN in the squash.
+ toCommit->includeSquashInst[tid] = true;
+
+ ldstQueue.setLoadBlockedHandled(tid);
+
+ wroteToTimeBuffer = true;
+}
+
+template<class Impl>
+void
+DefaultIEW<Impl>::block(unsigned tid)
+{
+ DPRINTF(IEW, "[tid:%u]: Blocking.\n", tid);
+
+ if (dispatchStatus[tid] != Blocked &&
+ dispatchStatus[tid] != Unblocking) {
+ toRename->iewBlock[tid] = true;
+ wroteToTimeBuffer = true;
+ }
// Add the current inputs to the skid buffer so they can be
// reprocessed when this stage unblocks.
- skidBuffer.push(*fromRename);
+ skidInsert(tid);
- // Note that this stage only signals previous stages to stall when
- // it is the cause of the stall originates at this stage. Otherwise
- // the previous stages are expected to check all possible stall signals.
+ dispatchStatus[tid] = Blocked;
}
template<class Impl>
-inline void
-SimpleIEW<Impl>::unblock()
+void
+DefaultIEW<Impl>::unblock(unsigned tid)
{
- // Check if there's information in the skid buffer. If there is, then
- // set status to unblocking, otherwise set it directly to running.
- DPRINTF(IEW, "IEW: Reading instructions out of the skid "
- "buffer.\n");
- // Remove the now processed instructions from the skid buffer.
- skidBuffer.pop();
-
- // If there's still information in the skid buffer, then
- // continue to tell previous stages to stall. They will be
- // able to restart once the skid buffer is empty.
- if (!skidBuffer.empty()) {
- toRename->iewInfo.stall = true;
- } else {
- DPRINTF(IEW, "IEW: Stage is done unblocking.\n");
- _status = Running;
+ DPRINTF(IEW, "[tid:%i]: Reading instructions out of the skid "
+ "buffer %u.\n",tid, tid);
+
+ // If the skid bufffer is empty, signal back to previous stages to unblock.
+ // Also switch status to running.
+ if (skidBuffer[tid].empty()) {
+ toRename->iewUnblock[tid] = true;
+ wroteToTimeBuffer = true;
+ DPRINTF(IEW, "[tid:%i]: Done unblocking.\n",tid);
+ dispatchStatus[tid] = Running;
}
}
template<class Impl>
void
-SimpleIEW<Impl>::wakeDependents(DynInstPtr &inst)
+DefaultIEW<Impl>::wakeDependents(DynInstPtr &inst)
{
instQueue.wakeDependents(inst);
}
+template<class Impl>
+void
+DefaultIEW<Impl>::rescheduleMemInst(DynInstPtr &inst)
+{
+ instQueue.rescheduleMemInst(inst);
+}
+
+template<class Impl>
+void
+DefaultIEW<Impl>::replayMemInst(DynInstPtr &inst)
+{
+ instQueue.replayMemInst(inst);
+}
template<class Impl>
void
-SimpleIEW<Impl>::instToCommit(DynInstPtr &inst)
+DefaultIEW<Impl>::instToCommit(DynInstPtr &inst)
{
+ // First check the time slot that this instruction will write
+ // to. If there are free write ports at the time, then go ahead
+ // and write the instruction to that time. If there are not,
+ // keep looking back to see where's the first time there's a
+ // free slot.
+ while ((*iewQueue)[wbCycle].insts[wbNumInst]) {
+ ++wbNumInst;
+ if (wbNumInst == issueWidth) {
+ ++wbCycle;
+ wbNumInst = 0;
+ }
+ assert(wbCycle < 5);
+ }
+
+ // Add finished instruction to queue to commit.
+ (*iewQueue)[wbCycle].insts[wbNumInst] = inst;
+ (*iewQueue)[wbCycle].size++;
}
template <class Impl>
+unsigned
+DefaultIEW<Impl>::validInstsFromRename()
+{
+ unsigned inst_count = 0;
+
+ for (int i=0; i<fromRename->size; i++) {
+ if (!fromRename->insts[i]->squashed)
+ inst_count++;
+ }
+
+ return inst_count;
+}
+
+template<class Impl>
void
-SimpleIEW<Impl>::dispatchInsts()
-{
- ////////////////////////////////////////
- // DISPATCH/ISSUE stage
- ////////////////////////////////////////
-
- //Put into its own function?
- //Add instructions to IQ if there are any instructions there
-
- // Check if there are any instructions coming from rename, and we're.
- // not squashing.
- if (fromRename->size > 0) {
- int insts_to_add = fromRename->size;
-
- // Loop through the instructions, putting them in the instruction
- // queue.
- for (int inst_num = 0; inst_num < insts_to_add; ++inst_num)
- {
- DynInstPtr inst = fromRename->insts[inst_num];
-
- // Make sure there's a valid instruction there.
- assert(inst);
-
- DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n",
- inst->readPC());
-
- // Be sure to mark these instructions as ready so that the
- // commit stage can go ahead and execute them, and mark
- // them as issued so the IQ doesn't reprocess them.
- if (inst->isSquashed()) {
- ++iewDispSquashedInsts;
- continue;
- } else if (instQueue.isFull()) {
- DPRINTF(IEW, "IEW: Issue: IQ has become full.\n");
- // Call function to start blocking.
- block();
- // Tell previous stage to stall.
- toRename->iewInfo.stall = true;
-
- ++iewIQFullEvents;
- break;
- } else if (inst->isLoad()) {
- DPRINTF(IEW, "IEW: Issue: Memory instruction "
- "encountered, adding to LDSTQ.\n");
-
- // Reserve a spot in the load store queue for this
- // memory access.
- ldstQueue.insertLoad(inst);
-
- ++iewDispLoadInsts;
- } else if (inst->isStore()) {
- ldstQueue.insertStore(inst);
+DefaultIEW<Impl>::skidInsert(unsigned tid)
+{
+ DynInstPtr inst = NULL;
- ++iewDispStoreInsts;
- } else if (inst->isNonSpeculative()) {
- DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction "
- "encountered, skipping.\n");
+ while (!insts[tid].empty()) {
+ inst = insts[tid].front();
- // Same hack as with stores.
- inst->setCanCommit();
+ insts[tid].pop();
+
+ DPRINTF(Decode,"[tid:%i]: Inserting [sn:%lli] PC:%#x into "
+ "dispatch skidBuffer %i\n",tid, inst->seqNum,
+ inst->readPC(),tid);
+
+ skidBuffer[tid].push(inst);
+ }
+
+ assert(skidBuffer[tid].size() <= skidBufferMax &&
+ "Skidbuffer Exceeded Max Size");
+}
+
+template<class Impl>
+int
+DefaultIEW<Impl>::skidCount()
+{
+ int max=0;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned thread_count = skidBuffer[*threads++].size();
+ if (max < thread_count)
+ max = thread_count;
+ }
+
+ return max;
+}
+
+template<class Impl>
+bool
+DefaultIEW<Impl>::skidsEmpty()
+{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ if (!skidBuffer[*threads++].empty())
+ return false;
+ }
+
+ return true;
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::updateStatus()
+{
+ bool any_unblocking = false;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (dispatchStatus[tid] == Unblocking) {
+ any_unblocking = true;
+ break;
+ }
+ }
+
+ // If there are no ready instructions waiting to be scheduled by the IQ,
+ // and there's no stores waiting to write back, and dispatch is not
+ // unblocking, then there is no internal activity for the IEW stage.
+ if (_status == Active && !instQueue.hasReadyInsts() &&
+ !ldstQueue.willWB() && !any_unblocking) {
+ DPRINTF(IEW, "IEW switching to idle\n");
+
+ deactivateStage();
+
+ _status = Inactive;
+ } else if (_status == Inactive && (instQueue.hasReadyInsts() ||
+ ldstQueue.willWB() ||
+ any_unblocking)) {
+ // Otherwise there is internal activity. Set to active.
+ DPRINTF(IEW, "IEW switching to active\n");
+
+ activateStage();
+
+ _status = Active;
+ }
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::resetEntries()
+{
+ instQueue.resetEntries();
+ ldstQueue.resetEntries();
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::readStallSignals(unsigned tid)
+{
+ if (fromCommit->commitBlock[tid]) {
+ stalls[tid].commit = true;
+ }
+
+ if (fromCommit->commitUnblock[tid]) {
+ assert(stalls[tid].commit);
+ stalls[tid].commit = false;
+ }
+}
+
+template <class Impl>
+bool
+DefaultIEW<Impl>::checkStall(unsigned tid)
+{
+ bool ret_val(false);
+
+ if (stalls[tid].commit) {
+ DPRINTF(IEW,"[tid:%i]: Stall from Commit stage detected.\n",tid);
+ ret_val = true;
+ } else if (instQueue.isFull(tid)) {
+ DPRINTF(IEW,"[tid:%i]: Stall: IQ is full.\n",tid);
+ ret_val = true;
+ } else if (ldstQueue.isFull(tid)) {
+ DPRINTF(IEW,"[tid:%i]: Stall: LSQ is full\n",tid);
+
+ if (ldstQueue.numLoads(tid) > 0 ) {
+
+ DPRINTF(IEW,"[tid:%i]: LSQ oldest load: [sn:%i] \n",
+ tid,ldstQueue.getLoadHeadSeqNum(tid));
+ }
+
+ if (ldstQueue.numStores(tid) > 0) {
+
+ DPRINTF(IEW,"[tid:%i]: LSQ oldest store: [sn:%i] \n",
+ tid,ldstQueue.getStoreHeadSeqNum(tid));
+ }
+
+ ret_val = true;
+ } else if (ldstQueue.isStalled(tid)) {
+ DPRINTF(IEW,"[tid:%i]: Stall: LSQ stall detected.\n",tid);
+ ret_val = true;
+ }
+
+ return ret_val;
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::checkSignalsAndUpdate(unsigned tid)
+{
+ // Check if there's a squash signal, squash if there is
+ // Check stall signals, block if there is.
+ // If status was Blocked
+ // if so then go to unblocking
+ // If status was Squashing
+ // check if squashing is not high. Switch to running this cycle.
+
+ readStallSignals(tid);
+
+ if (fromCommit->commitInfo[tid].squash) {
+ squash(tid);
+
+ if (dispatchStatus[tid] == Blocked ||
+ dispatchStatus[tid] == Unblocking) {
+ toRename->iewUnblock[tid] = true;
+ wroteToTimeBuffer = true;
+ }
+
+ dispatchStatus[tid] = Squashing;
+
+ fetchRedirect[tid] = false;
+ return;
+ }
+
+ if (fromCommit->commitInfo[tid].robSquashing) {
+ DPRINTF(IEW, "[tid:%i]: ROB is still squashing.\n");
+
+ dispatchStatus[tid] = Squashing;
+
+ return;
+ }
+
+ if (checkStall(tid)) {
+ block(tid);
+ dispatchStatus[tid] = Blocked;
+ return;
+ }
+
+ if (dispatchStatus[tid] == Blocked) {
+ // Status from previous cycle was blocked, but there are no more stall
+ // conditions. Switch over to unblocking.
+ DPRINTF(IEW, "[tid:%i]: Done blocking, switching to unblocking.\n",
+ tid);
+
+ dispatchStatus[tid] = Unblocking;
+
+ unblock(tid);
- // Specificall insert it as nonspeculative.
+ return;
+ }
+
+ if (dispatchStatus[tid] == Squashing) {
+ // Switch status to running if rename isn't being told to block or
+ // squash this cycle.
+ DPRINTF(IEW, "[tid:%i]: Done squashing, switching to running.\n",
+ tid);
+
+ dispatchStatus[tid] = Running;
+
+ return;
+ }
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::sortInsts()
+{
+ int insts_from_rename = fromRename->size;
+#ifdef DEBUG
+ for (int i = 0; i < numThreads; i++)
+ assert(insts[i].empty());
+#endif
+ for (int i = 0; i < insts_from_rename; ++i) {
+ insts[fromRename->insts[i]->threadNumber].push(fromRename->insts[i]);
+ }
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::wakeCPU()
+{
+ cpu->wakeCPU();
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::activityThisCycle()
+{
+ DPRINTF(Activity, "Activity this cycle.\n");
+ cpu->activityThisCycle();
+}
+
+template <class Impl>
+inline void
+DefaultIEW<Impl>::activateStage()
+{
+ DPRINTF(Activity, "Activating stage.\n");
+ cpu->activateStage(FullCPU::IEWIdx);
+}
+
+template <class Impl>
+inline void
+DefaultIEW<Impl>::deactivateStage()
+{
+ DPRINTF(Activity, "Deactivating stage.\n");
+ cpu->deactivateStage(FullCPU::IEWIdx);
+}
+
+template<class Impl>
+void
+DefaultIEW<Impl>::dispatch(unsigned tid)
+{
+ // If status is Running or idle,
+ // call dispatchInsts()
+ // If status is Unblocking,
+ // buffer any instructions coming from rename
+ // continue trying to empty skid buffer
+ // check if stall conditions have passed
+
+ if (dispatchStatus[tid] == Blocked) {
+ ++iewBlockCycles;
+
+ } else if (dispatchStatus[tid] == Squashing) {
+ ++iewSquashCycles;
+ }
+
+ // Dispatch should try to dispatch as many instructions as its bandwidth
+ // will allow, as long as it is not currently blocked.
+ if (dispatchStatus[tid] == Running ||
+ dispatchStatus[tid] == Idle) {
+ DPRINTF(IEW, "[tid:%i] Not blocked, so attempting to run "
+ "dispatch.\n", tid);
+
+ dispatchInsts(tid);
+ } else if (dispatchStatus[tid] == Unblocking) {
+ // Make sure that the skid buffer has something in it if the
+ // status is unblocking.
+ assert(!skidsEmpty());
+
+ // If the status was unblocking, then instructions from the skid
+ // buffer were used. Remove those instructions and handle
+ // the rest of unblocking.
+ dispatchInsts(tid);
+
+ ++iewUnblockCycles;
+
+ if (validInstsFromRename() && dispatchedAllInsts) {
+ // Add the current inputs to the skid buffer so they can be
+ // reprocessed when this stage unblocks.
+ skidInsert(tid);
+ }
+
+ unblock(tid);
+ }
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::dispatchInsts(unsigned tid)
+{
+ dispatchedAllInsts = true;
+
+ // Obtain instructions from skid buffer if unblocking, or queue from rename
+ // otherwise.
+ std::queue<DynInstPtr> &insts_to_dispatch =
+ dispatchStatus[tid] == Unblocking ?
+ skidBuffer[tid] : insts[tid];
+
+ int insts_to_add = insts_to_dispatch.size();
+
+ DynInstPtr inst;
+ bool add_to_iq = false;
+ int dis_num_inst = 0;
+
+ // Loop through the instructions, putting them in the instruction
+ // queue.
+ for ( ; dis_num_inst < insts_to_add &&
+ dis_num_inst < issueReadWidth;
+ ++dis_num_inst)
+ {
+ inst = insts_to_dispatch.front();
+
+ if (dispatchStatus[tid] == Unblocking) {
+ DPRINTF(IEW, "[tid:%i]: Issue: Examining instruction from skid "
+ "buffer\n", tid);
+ }
+
+ // Make sure there's a valid instruction there.
+ assert(inst);
+
+ DPRINTF(IEW, "[tid:%i]: Issue: Adding PC %#x [sn:%lli] [tid:%i] to "
+ "IQ.\n",
+ tid, inst->readPC(), inst->seqNum, inst->threadNumber);
+
+ // Be sure to mark these instructions as ready so that the
+ // commit stage can go ahead and execute them, and mark
+ // them as issued so the IQ doesn't reprocess them.
+
+ // Check for squashed instructions.
+ if (inst->isSquashed()) {
+ DPRINTF(IEW, "[tid:%i]: Issue: Squashed instruction encountered, "
+ "not adding to IQ.\n", tid);
+
+ ++iewDispSquashedInsts;
+
+ insts_to_dispatch.pop();
+
+ //Tell Rename That An Instruction has been processed
+ if (inst->isLoad() || inst->isStore()) {
+ toRename->iewInfo[tid].dispatchedToLSQ++;
+ }
+ toRename->iewInfo[tid].dispatched++;
+
+ continue;
+ }
+
+ // Check for full conditions.
+ if (instQueue.isFull(tid)) {
+ DPRINTF(IEW, "[tid:%i]: Issue: IQ has become full.\n", tid);
+
+ // Call function to start blocking.
+ block(tid);
+
+ // Set unblock to false. Special case where we are using
+ // skidbuffer (unblocking) instructions but then we still
+ // get full in the IQ.
+ toRename->iewUnblock[tid] = false;
+
+ dispatchedAllInsts = false;
+
+ ++iewIQFullEvents;
+ break;
+ } else if (ldstQueue.isFull(tid)) {
+ DPRINTF(IEW, "[tid:%i]: Issue: LSQ has become full.\n",tid);
+
+ // Call function to start blocking.
+ block(tid);
+
+ // Set unblock to false. Special case where we are using
+ // skidbuffer (unblocking) instructions but then we still
+ // get full in the IQ.
+ toRename->iewUnblock[tid] = false;
+
+ dispatchedAllInsts = false;
+
+ ++iewLSQFullEvents;
+ break;
+ }
+
+ // Otherwise issue the instruction just fine.
+ if (inst->isLoad()) {
+ DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction "
+ "encountered, adding to LSQ.\n", tid);
+
+ // Reserve a spot in the load store queue for this
+ // memory access.
+ ldstQueue.insertLoad(inst);
+
+ ++iewDispLoadInsts;
+
+ add_to_iq = true;
+
+ toRename->iewInfo[tid].dispatchedToLSQ++;
+ } else if (inst->isStore()) {
+ DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction "
+ "encountered, adding to LSQ.\n", tid);
+
+ ldstQueue.insertStore(inst);
+
+ ++iewDispStoreInsts;
+
+ if (inst->isStoreConditional()) {
+ // Store conditionals need to be set as "canCommit()"
+ // so that commit can process them when they reach the
+ // head of commit.
+ // @todo: This is somewhat specific to Alpha.
+ inst->setCanCommit();
instQueue.insertNonSpec(inst);
+ add_to_iq = false;
++iewDispNonSpecInsts;
+ } else {
+ add_to_iq = true;
+ }
- continue;
- } else if (inst->isNop()) {
- DPRINTF(IEW, "IEW: Issue: Nop instruction encountered "
- ", skipping.\n");
+ toRename->iewInfo[tid].dispatchedToLSQ++;
+#if FULL_SYSTEM
+ } else if (inst->isMemBarrier() || inst->isWriteBarrier()) {
+ // Same as non-speculative stores.
+ inst->setCanCommit();
+ instQueue.insertBarrier(inst);
+ add_to_iq = false;
+#endif
+ } else if (inst->isNonSpeculative()) {
+ DPRINTF(IEW, "[tid:%i]: Issue: Nonspeculative instruction "
+ "encountered, skipping.\n", tid);
- inst->setIssued();
- inst->setExecuted();
- inst->setCanCommit();
+ // Same as non-speculative stores.
+ inst->setCanCommit();
- instQueue.advanceTail(inst);
+ // Specifically insert it as nonspeculative.
+ instQueue.insertNonSpec(inst);
- continue;
- } else if (inst->isExecuted()) {
- assert(0 && "Instruction shouldn't be executed.\n");
- DPRINTF(IEW, "IEW: Issue: Executed branch encountered, "
- "skipping.\n");
+ ++iewDispNonSpecInsts;
- inst->setIssued();
- inst->setCanCommit();
+ add_to_iq = false;
+ } else if (inst->isNop()) {
+ DPRINTF(IEW, "[tid:%i]: Issue: Nop instruction encountered, "
+ "skipping.\n", tid);
- instQueue.advanceTail(inst);
+ inst->setIssued();
+ inst->setExecuted();
+ inst->setCanCommit();
- continue;
- }
+ instQueue.recordProducer(inst);
+
+ exeNop[tid]++;
+
+ add_to_iq = false;
+ } else if (inst->isExecuted()) {
+ assert(0 && "Instruction shouldn't be executed.\n");
+ DPRINTF(IEW, "Issue: Executed branch encountered, "
+ "skipping.\n");
+
+ inst->setIssued();
+ inst->setCanCommit();
+
+ instQueue.recordProducer(inst);
- // If the instruction queue is not full, then add the
- // instruction.
- instQueue.insert(fromRename->insts[inst_num]);
+ add_to_iq = false;
+ } else {
+ add_to_iq = true;
+ }
- ++iewDispatchedInsts;
+ // If the instruction queue is not full, then add the
+ // instruction.
+ if (add_to_iq) {
+ instQueue.insert(inst);
}
+
+ insts_to_dispatch.pop();
+
+ toRename->iewInfo[tid].dispatched++;
+
+ ++iewDispatchedInsts;
+ }
+
+ if (!insts_to_dispatch.empty()) {
+ DPRINTF(IEW,"[tid:%i]: Issue: Bandwidth Full. Blocking.\n");
+ block(tid);
+ toRename->iewUnblock[tid] = false;
+ }
+
+ if (dispatchStatus[tid] == Idle && dis_num_inst) {
+ dispatchStatus[tid] = Running;
+
+ updatedQueues = true;
+ }
+
+ dis_num_inst = 0;
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::printAvailableInsts()
+{
+ int inst = 0;
+
+ cout << "Available Instructions: ";
+
+ while (fromIssue->insts[inst]) {
+
+ if (inst%3==0) cout << "\n\t";
+
+ cout << "PC: " << fromIssue->insts[inst]->readPC()
+ << " TN: " << fromIssue->insts[inst]->threadNumber
+ << " SN: " << fromIssue->insts[inst]->seqNum << " | ";
+
+ inst++;
+
}
+
+ cout << "\n";
}
template <class Impl>
void
-SimpleIEW<Impl>::executeInsts()
+DefaultIEW<Impl>::executeInsts()
{
- ////////////////////////////////////////
- //EXECUTE/WRITEBACK stage
- ////////////////////////////////////////
+ wbNumInst = 0;
+ wbCycle = 0;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
- //Put into its own function?
- //Similarly should probably have separate execution for int vs FP.
- // Above comment is handled by the issue queue only issuing a valid
- // mix of int/fp instructions.
- //Actually okay to just have one execution, buuuuuut will need
- //somewhere that defines the execution latency of all instructions.
- // @todo: Move to the FU pool used in the current full cpu.
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+ fetchRedirect[tid] = false;
+ }
- int fu_usage = 0;
- bool fetch_redirect = false;
- int inst_slot = 0;
- int time_slot = 0;
+ // Uncomment this if you want to see all available instructions.
+// printAvailableInsts();
// Execute/writeback any instructions that are available.
- for (int inst_num = 0;
- fu_usage < executeWidth && /* Haven't exceeded available FU's. */
- inst_num < issueWidth &&
- fromIssue->insts[inst_num];
- ++inst_num) {
+ int insts_to_execute = fromIssue->size;
+ int inst_num = 0;
+ for (; inst_num < insts_to_execute;
+ ++inst_num) {
- DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n");
+ DPRINTF(IEW, "Execute: Executing instructions from IQ.\n");
- // Get instruction from issue's queue.
- DynInstPtr inst = fromIssue->insts[inst_num];
+ DynInstPtr inst = instQueue.getInstToExecute();
- DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC());
+ DPRINTF(IEW, "Execute: Processing PC %#x, [tid:%i] [sn:%i].\n",
+ inst->readPC(), inst->threadNumber,inst->seqNum);
// Check if the instruction is squashed; if so then skip it
- // and don't count it towards the FU usage.
if (inst->isSquashed()) {
- DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n");
+ DPRINTF(IEW, "Execute: Instruction was squashed.\n");
// Consider this instruction executed so that commit can go
// ahead and retire the instruction.
inst->setExecuted();
- toCommit->insts[inst_num] = inst;
+ // Not sure if I should set this here or just let commit try to
+ // commit any squashed instructions. I like the latter a bit more.
+ inst->setCanCommit();
++iewExecSquashedInsts;
continue;
}
- inst->setExecuted();
-
- // If an instruction is executed, then count it towards FU usage.
- ++fu_usage;
+ Fault fault = NoFault;
// Execute instruction.
// Note that if the instruction faults, it will be handled
// at the commit stage.
- if (inst->isMemRef()) {
- DPRINTF(IEW, "IEW: Execute: Calculating address for memory "
+ if (inst->isMemRef() &&
+ (!inst->isDataPrefetch() && !inst->isInstPrefetch())) {
+ DPRINTF(IEW, "Execute: Calculating address for memory "
"reference.\n");
// Tell the LDSTQ to execute this instruction (if it is a load).
if (inst->isLoad()) {
- ldstQueue.executeLoad(inst);
-
- ++iewExecLoadInsts;
+ // Loads will mark themselves as executed, and their writeback
+ // event adds the instruction to the queue to commit
+ fault = ldstQueue.executeLoad(inst);
} else if (inst->isStore()) {
ldstQueue.executeStore(inst);
- ++iewExecStoreInsts;
+ // If the store had a fault then it may not have a mem req
+ if (inst->req && !(inst->req->getFlags() & LOCKED)) {
+ inst->setExecuted();
+
+ instToCommit(inst);
+ }
+
+ // Store conditionals will mark themselves as
+ // executed, and their writeback event will add the
+ // instruction to the queue to commit.
} else {
- panic("IEW: Unexpected memory type!\n");
+ panic("Unexpected memory type!\n");
}
} else {
inst->execute();
- ++iewExecutedInsts;
+ inst->setExecuted();
+
+ instToCommit(inst);
}
- // First check the time slot that this instruction will write
- // to. If there are free write ports at the time, then go ahead
- // and write the instruction to that time. If there are not,
- // keep looking back to see where's the first time there's a
- // free slot. What happens if you run out of free spaces?
- // For now naively assume that all instructions take one cycle.
- // Otherwise would have to look into the time buffer based on the
- // latency of the instruction.
- (*iewQueue)[time_slot].insts[inst_slot];
- while ((*iewQueue)[time_slot].insts[inst_slot]) {
- if (inst_slot < issueWidth) {
- ++inst_slot;
- } else {
- ++time_slot;
- inst_slot = 0;
- }
+ updateExeInstStats(inst);
- assert(time_slot < 5);
- }
+ // Check if branch prediction was correct, if not then we need
+ // to tell commit to squash in flight instructions. Only
+ // handle this if there hasn't already been something that
+ // redirects fetch in this group of instructions.
- // May actually have to work this out, especially with loads and stores
+ // This probably needs to prioritize the redirects if a different
+ // scheduler is used. Currently the scheduler schedules the oldest
+ // instruction first, so the branch resolution order will be correct.
+ unsigned tid = inst->threadNumber;
- // Add finished instruction to queue to commit.
- (*iewQueue)[time_slot].insts[inst_slot] = inst;
- (*iewQueue)[time_slot].size++;
+ if (!fetchRedirect[tid]) {
- // Check if branch was correct. This check happens after the
- // instruction is added to the queue because even if the branch
- // is mispredicted, the branch instruction itself is still valid.
- // Only handle this if there hasn't already been something that
- // redirects fetch in this group of instructions.
- if (!fetch_redirect) {
if (inst->mispredicted()) {
- fetch_redirect = true;
+ fetchRedirect[tid] = true;
- DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n");
- DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n",
+ DPRINTF(IEW, "Execute: Branch mispredict detected.\n");
+ DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n",
inst->nextPC);
// If incorrect, then signal the ROB that it must be squashed.
- squashDueToBranch(inst);
+ squashDueToBranch(inst, tid);
if (inst->predTaken()) {
predictedTakenIncorrect++;
+ } else {
+ predictedNotTakenIncorrect++;
}
- } else if (ldstQueue.violation()) {
- fetch_redirect = true;
+ } else if (ldstQueue.violation(tid)) {
+ fetchRedirect[tid] = true;
- // Get the DynInst that caused the violation.
- DynInstPtr violator = ldstQueue.getMemDepViolator();
+ // If there was an ordering violation, then get the
+ // DynInst that caused the violation. Note that this
+ // clears the violation signal.
+ DynInstPtr violator;
+ violator = ldstQueue.getMemDepViolator(tid);
- DPRINTF(IEW, "IEW: LDSTQ detected a violation. Violator PC: "
+ DPRINTF(IEW, "LDSTQ detected a violation. Violator PC: "
"%#x, inst PC: %#x. Addr is: %#x.\n",
violator->readPC(), inst->readPC(), inst->physEffAddr);
@@ -573,164 +1292,236 @@ SimpleIEW<Impl>::executeInsts()
instQueue.violation(inst, violator);
// Squash.
- squashDueToMem(inst);
+ squashDueToMemOrder(inst,tid);
++memOrderViolationEvents;
+ } else if (ldstQueue.loadBlocked(tid) &&
+ !ldstQueue.isLoadBlockedHandled(tid)) {
+ fetchRedirect[tid] = true;
+
+ DPRINTF(IEW, "Load operation couldn't execute because the "
+ "memory system is blocked. PC: %#x [sn:%lli]\n",
+ inst->readPC(), inst->seqNum);
+
+ squashDueToMemBlocked(inst, tid);
+ }
+ }
+ }
+
+ // Update and record activity if we processed any instructions.
+ if (inst_num) {
+ if (exeStatus == Idle) {
+ exeStatus = Running;
+ }
+
+ updatedQueues = true;
+
+ cpu->activityThisCycle();
+ }
+
+ // Need to reset this in case a writeback event needs to write into the
+ // iew queue. That way the writeback event will write into the correct
+ // spot in the queue.
+ wbNumInst = 0;
+}
+
+template <class Impl>
+void
+DefaultIEW<Impl>::writebackInsts()
+{
+ // Loop through the head of the time buffer and wake any
+ // dependents. These instructions are about to write back. Also
+ // mark scoreboard that this instruction is finally complete.
+ // Either have IEW have direct access to scoreboard, or have this
+ // as part of backwards communication.
+ for (int inst_num = 0; inst_num < issueWidth &&
+ toCommit->insts[inst_num]; inst_num++) {
+ DynInstPtr inst = toCommit->insts[inst_num];
+ int tid = inst->threadNumber;
+
+ DPRINTF(IEW, "Sending instructions to commit, [sn:%lli] PC %#x.\n",
+ inst->seqNum, inst->readPC());
+
+ iewInstsToCommit[tid]++;
+
+ // Some instructions will be sent to commit without having
+ // executed because they need commit to handle them.
+ // E.g. Uncached loads have not actually executed when they
+ // are first sent to commit. Instead commit must tell the LSQ
+ // when it's ready to execute the uncached load.
+ if (!inst->isSquashed() && inst->isExecuted()) {
+ int dependents = instQueue.wakeDependents(inst);
+
+ for (int i = 0; i < inst->numDestRegs(); i++) {
+ //mark as Ready
+ DPRINTF(IEW,"Setting Destination Register %i\n",
+ inst->renamedDestRegIdx(i));
+ scoreboard->setReg(inst->renamedDestRegIdx(i));
+ }
+
+ if (dependents) {
+ producerInst[tid]++;
+ consumerInst[tid]+= dependents;
}
+ writebackCount[tid]++;
}
}
}
template<class Impl>
void
-SimpleIEW<Impl>::tick()
+DefaultIEW<Impl>::tick()
{
- // Considering putting all the state-determining stuff in this section.
+ wbNumInst = 0;
+ wbCycle = 0;
- // Try to fill up issue queue with as many instructions as bandwidth
- // allows.
- // Decode should try to execute as many instructions as its bandwidth
- // will allow, as long as it is not currently blocked.
+ wroteToTimeBuffer = false;
+ updatedQueues = false;
- // Check if the stage is in a running status.
- if (_status != Blocked && _status != Squashing) {
- DPRINTF(IEW, "IEW: Status is not blocked, attempting to run "
- "stage.\n");
- iew();
+ sortInsts();
- // If it's currently unblocking, check to see if it should switch
- // to running.
- if (_status == Unblocking) {
- unblock();
+ // Free function units marked as being freed this cycle.
+ fuPool->processFreeUnits();
- ++iewUnblockCycles;
- }
- } else if (_status == Squashing) {
+ list<unsigned>::iterator threads = (*activeThreads).begin();
- DPRINTF(IEW, "IEW: Still squashing.\n");
+ // Check stall and squash signals, dispatch any instructions.
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
- // Check if stage should remain squashing. Stop squashing if the
- // squash signal clears.
- if (!fromCommit->commitInfo.squash &&
- !fromCommit->commitInfo.robSquashing) {
- DPRINTF(IEW, "IEW: Done squashing, changing status to "
- "running.\n");
+ DPRINTF(IEW,"Issue: Processing [tid:%i]\n",tid);
- _status = Running;
- instQueue.stopSquash();
- } else {
- instQueue.doSquash();
- }
+ checkSignalsAndUpdate(tid);
+ dispatch(tid);
+ }
- ++iewSquashCycles;
- } else if (_status == Blocked) {
- // Continue to tell previous stage to stall.
- toRename->iewInfo.stall = true;
-
- // Check if possible stall conditions have cleared.
- if (!fromCommit->commitInfo.stall &&
- !instQueue.isFull()) {
- DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n");
- _status = Unblocking;
- }
+ if (exeStatus != Squashing) {
+ executeInsts();
- // If there's still instructions coming from rename, continue to
- // put them on the skid buffer.
- if (fromRename->size == 0) {
- block();
- }
+ writebackInsts();
- if (fromCommit->commitInfo.squash ||
- fromCommit->commitInfo.robSquashing) {
- squash();
- }
+ // Have the instruction queue try to schedule any ready instructions.
+ // (In actuality, this scheduling is for instructions that will
+ // be executed next cycle.)
+ instQueue.scheduleReadyInsts();
- ++iewBlockCycles;
+ // Also should advance its own time buffers if the stage ran.
+ // Not the best place for it, but this works (hopefully).
+ issueToExecQueue.advance();
}
- // @todo: Maybe put these at the beginning, so if it's idle it can
- // return early.
- // Write back number of free IQ entries here.
- toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries();
+ bool broadcast_free_entries = false;
+
+ if (updatedQueues || exeStatus == Running || updateLSQNextCycle) {
+ exeStatus = Idle;
+ updateLSQNextCycle = false;
+
+ broadcast_free_entries = true;
+ }
+ // Writeback any stores using any leftover bandwidth.
ldstQueue.writebackStores();
// Check the committed load/store signals to see if there's a load
// or store to commit. Also check if it's being told to execute a
// nonspeculative instruction.
// This is pretty inefficient...
- if (!fromCommit->commitInfo.squash &&
- !fromCommit->commitInfo.robSquashing) {
- ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum);
- ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum);
- }
- if (fromCommit->commitInfo.nonSpecSeqNum != 0) {
- instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum);
- }
+ threads = (*activeThreads).begin();
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = (*threads++);
- DPRINTF(IEW, "IEW: IQ has %i free entries.\n",
- instQueue.numFreeEntries());
-}
+ DPRINTF(IEW,"Processing [tid:%i]\n",tid);
-template<class Impl>
-void
-SimpleIEW<Impl>::iew()
-{
- // Might want to put all state checks in the tick() function.
- // Check if being told to stall from commit.
- if (fromCommit->commitInfo.stall) {
- block();
- return;
- } else if (fromCommit->commitInfo.squash ||
- fromCommit->commitInfo.robSquashing) {
- // Also check if commit is telling this stage to squash.
- squash();
- return;
- }
+ // Update structures based on instructions committed.
+ if (fromCommit->commitInfo[tid].doneSeqNum != 0 &&
+ !fromCommit->commitInfo[tid].squash &&
+ !fromCommit->commitInfo[tid].robSquashing) {
- dispatchInsts();
+ ldstQueue.commitStores(fromCommit->commitInfo[tid].doneSeqNum,tid);
- // Have the instruction queue try to schedule any ready instructions.
- instQueue.scheduleReadyInsts();
+ ldstQueue.commitLoads(fromCommit->commitInfo[tid].doneSeqNum,tid);
- executeInsts();
+ updateLSQNextCycle = true;
+ instQueue.commit(fromCommit->commitInfo[tid].doneSeqNum,tid);
+ }
- // Loop through the head of the time buffer and wake any dependents.
- // These instructions are about to write back. In the simple model
- // this loop can really happen within the previous loop, but when
- // instructions have actual latencies, this loop must be separate.
- // Also mark scoreboard that this instruction is finally complete.
- // Either have IEW have direct access to rename map, or have this as
- // part of backwards communication.
- for (int inst_num = 0; inst_num < issueWidth &&
- toCommit->insts[inst_num]; inst_num++)
- {
- DynInstPtr inst = toCommit->insts[inst_num];
+ if (fromCommit->commitInfo[tid].nonSpecSeqNum != 0) {
- DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n",
- inst->readPC());
+ //DPRINTF(IEW,"NonspecInst from thread %i",tid);
+ if (fromCommit->commitInfo[tid].uncached) {
+ instQueue.replayMemInst(fromCommit->commitInfo[tid].uncachedLoad);
+ } else {
+ instQueue.scheduleNonSpec(
+ fromCommit->commitInfo[tid].nonSpecSeqNum);
+ }
+ }
- if(!inst->isSquashed()) {
- instQueue.wakeDependents(inst);
+ if (broadcast_free_entries) {
+ toFetch->iewInfo[tid].iqCount =
+ instQueue.getCount(tid);
+ toFetch->iewInfo[tid].ldstqCount =
+ ldstQueue.getCount(tid);
- for (int i = 0; i < inst->numDestRegs(); i++)
- {
- renameMap->markAsReady(inst->renamedDestRegIdx(i));
- }
+ toRename->iewInfo[tid].usedIQ = true;
+ toRename->iewInfo[tid].freeIQEntries =
+ instQueue.numFreeEntries();
+ toRename->iewInfo[tid].usedLSQ = true;
+ toRename->iewInfo[tid].freeLSQEntries =
+ ldstQueue.numFreeEntries(tid);
+
+ wroteToTimeBuffer = true;
}
+
+ DPRINTF(IEW, "[tid:%i], Dispatch dispatched %i instructions.\n",
+ tid, toRename->iewInfo[tid].dispatched);
}
- // Also should advance its own time buffers if the stage ran.
- // Not the best place for it, but this works (hopefully).
- issueToExecQueue.advance();
+ DPRINTF(IEW, "IQ has %i free entries (Can schedule: %i). "
+ "LSQ has %i free entries.\n",
+ instQueue.numFreeEntries(), instQueue.hasReadyInsts(),
+ ldstQueue.numFreeEntries());
+
+ updateStatus();
+
+ if (wroteToTimeBuffer) {
+ DPRINTF(Activity, "Activity this cycle.\n");
+ cpu->activityThisCycle();
+ }
}
-#if !FULL_SYSTEM
-template<class Impl>
+template <class Impl>
void
-SimpleIEW<Impl>::lsqWriteback()
+DefaultIEW<Impl>::updateExeInstStats(DynInstPtr &inst)
{
- ldstQueue.writebackAllInsts();
-}
+ int thread_number = inst->threadNumber;
+
+ //
+ // Pick off the software prefetches
+ //
+#ifdef TARGET_ALPHA
+ if (inst->isDataPrefetch())
+ exeSwp[thread_number]++;
+ else
+ iewExecutedInsts++;
+#else
+ iewExecutedInsts++;
#endif
+
+ //
+ // Control operations
+ //
+ if (inst->isControl())
+ exeBranches[thread_number]++;
+
+ //
+ // Memory operations
+ //
+ if (inst->isMemRef()) {
+ exeRefs[thread_number]++;
+
+ if (inst->isLoad()) {
+ iewExecLoadInsts[thread_number]++;
+ }
+ }
+}
diff --git a/src/cpu/o3/inst_queue.cc b/src/cpu/o3/inst_queue.cc
index 2ff2282b4..f2c6b8213 100644
--- a/src/cpu/o3/inst_queue.cc
+++ b/src/cpu/o3/inst_queue.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
@@ -32,7 +34,3 @@
// Force instantiation of InstructionQueue.
template class InstructionQueue<AlphaSimpleImpl>;
-
-template<>
-unsigned
-InstructionQueue<AlphaSimpleImpl>::DependencyEntry::mem_alloc_counter = 0;
diff --git a/src/cpu/o3/inst_queue.hh b/src/cpu/o3/inst_queue.hh
index 43fe96c49..60a713020 100644
--- a/src/cpu/o3/inst_queue.hh
+++ b/src/cpu/o3/inst_queue.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_INST_QUEUE_HH__
-#define __CPU_O3_CPU_INST_QUEUE_HH__
+#ifndef __CPU_O3_INST_QUEUE_HH__
+#define __CPU_O3_INST_QUEUE_HH__
#include <list>
#include <map>
@@ -37,8 +39,13 @@
#include "base/statistics.hh"
#include "base/timebuf.hh"
#include "cpu/inst_seq.hh"
+#include "cpu/o3/dep_graph.hh"
+#include "cpu/op_class.hh"
#include "sim/host.hh"
+class FUPool;
+class MemInterface;
+
/**
* A standard instruction queue class. It holds ready instructions, in
* order, in seperate priority queues to facilitate the scheduling of
@@ -47,7 +54,14 @@
* floating point registers have their indices start after the integer
* registers (ie with 96 int and 96 fp registers, regs 0-95 are integer
* and 96-191 are fp). This remains true even for both logical and
- * physical register indices.
+ * physical register indices. The IQ depends on the memory dependence unit to
+ * track when memory operations are ready in terms of ordering; register
+ * dependencies are tracked normally. Right now the IQ also handles the
+ * execution timing; this is mainly to allow back-to-back scheduling without
+ * requiring IEW to be able to peek into the IQ. At the end of the execution
+ * latency, the instruction is put into the queue to execute, where it will
+ * have the execute() function called on it.
+ * @todo: Make IQ able to handle multiple FU pools.
*/
template <class Impl>
class InstructionQueue
@@ -58,87 +72,198 @@ class InstructionQueue
typedef typename Impl::DynInstPtr DynInstPtr;
typedef typename Impl::Params Params;
+ typedef typename Impl::CPUPol::IEW IEW;
typedef typename Impl::CPUPol::MemDepUnit MemDepUnit;
typedef typename Impl::CPUPol::IssueStruct IssueStruct;
typedef typename Impl::CPUPol::TimeStruct TimeStruct;
- // Typedef of iterator through the list of instructions. Might be
- // better to untie this from the FullCPU or pass its information to
- // the stages.
+ // Typedef of iterator through the list of instructions.
typedef typename std::list<DynInstPtr>::iterator ListIt;
- /**
- * Struct for comparing entries to be added to the priority queue. This
- * gives reverse ordering to the instructions in terms of sequence
- * numbers: the instructions with smaller sequence numbers (and hence
- * are older) will be at the top of the priority queue.
- */
- struct pqCompare
- {
- bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const
- {
- return lhs->seqNum > rhs->seqNum;
- }
- };
+ friend class Impl::FullCPU;
- /**
- * Struct for comparing entries to be added to the set. This gives
- * standard ordering in terms of sequence numbers.
- */
- struct setCompare
- {
- bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const
- {
- return lhs->seqNum < rhs->seqNum;
- }
+ /** FU completion event class. */
+ class FUCompletion : public Event {
+ private:
+ /** Executing instruction. */
+ DynInstPtr inst;
+
+ /** Index of the FU used for executing. */
+ int fuIdx;
+
+ /** Pointer back to the instruction queue. */
+ InstructionQueue<Impl> *iqPtr;
+
+ /** Should the FU be added to the list to be freed upon
+ * completing this event.
+ */
+ bool freeFU;
+
+ public:
+ /** Construct a FU completion event. */
+ FUCompletion(DynInstPtr &_inst, int fu_idx,
+ InstructionQueue<Impl> *iq_ptr);
+
+ virtual void process();
+ virtual const char *description();
+ void setFreeFU() { freeFU = true; }
};
- typedef std::priority_queue<DynInstPtr, vector<DynInstPtr>, pqCompare>
- ReadyInstQueue;
+ /** Constructs an IQ. */
+ InstructionQueue(Params *params);
- InstructionQueue(Params &params);
+ /** Destructs the IQ. */
+ ~InstructionQueue();
+ /** Returns the name of the IQ. */
+ std::string name() const;
+
+ /** Registers statistics. */
void regStats();
- void setCPU(FullCPU *cpu);
+ /** Resets all instruction queue state. */
+ void resetState();
+
+ /** Sets CPU pointer. */
+ void setCPU(FullCPU *_cpu) { cpu = _cpu; }
+
+ /** Sets active threads list. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
+
+ /** Sets the IEW pointer. */
+ void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; }
+ /** Sets the timer buffer between issue and execute. */
void setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2eQueue);
+ /** Sets the global time buffer. */
void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);
+ /** Switches out the instruction queue. */
+ void switchOut();
+
+ /** 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(int num_threads);
+
+ /** Resets max entries for all threads. */
+ void resetEntries();
+
+ /** Returns total number of free entries. */
unsigned numFreeEntries();
+ /** Returns number of free entries for a thread. */
+ unsigned numFreeEntries(unsigned tid);
+
+ /** Returns whether or not the IQ is full. */
bool isFull();
+ /** Returns whether or not the IQ is full for a specific thread. */
+ bool isFull(unsigned tid);
+
+ /** Returns if there are any ready instructions in the IQ. */
+ bool hasReadyInsts();
+
+ /** Inserts a new instruction into the IQ. */
void insert(DynInstPtr &new_inst);
+ /** Inserts a new, non-speculative instruction into the IQ. */
void insertNonSpec(DynInstPtr &new_inst);
- void advanceTail(DynInstPtr &inst);
+ /** Inserts a memory or write barrier into the IQ to make sure
+ * loads and stores are ordered properly.
+ */
+ void insertBarrier(DynInstPtr &barr_inst);
+
+ /** Returns the oldest scheduled instruction, and removes it from
+ * the list of instructions waiting to execute.
+ */
+ DynInstPtr getInstToExecute();
+
+ /**
+ * Records the instruction as the producer of a register without
+ * adding it to the rest of the IQ.
+ */
+ void recordProducer(DynInstPtr &inst)
+ { addToProducers(inst); }
+ /** Process FU completion event. */
+ void processFUCompletion(DynInstPtr &inst, int fu_idx);
+
+ /**
+ * Schedules ready instructions, adding the ready ones (oldest first) to
+ * the queue to execute.
+ */
void scheduleReadyInsts();
+ /** Schedules a single specific non-speculative instruction. */
void scheduleNonSpec(const InstSeqNum &inst);
- void wakeDependents(DynInstPtr &completed_inst);
+ /**
+ * Commits all instructions up to and including the given sequence number,
+ * for a specific thread.
+ */
+ void commit(const InstSeqNum &inst, unsigned tid = 0);
+
+ /** Wakes all dependents of a completed instruction. */
+ int wakeDependents(DynInstPtr &completed_inst);
+
+ /** Adds a ready memory instruction to the ready list. */
+ void addReadyMemInst(DynInstPtr &ready_inst);
+
+ /**
+ * Reschedules a memory instruction. It will be ready to issue once
+ * replayMemInst() is called.
+ */
+ void rescheduleMemInst(DynInstPtr &resched_inst);
+ /** Replays a memory instruction. It must be rescheduled first. */
+ void replayMemInst(DynInstPtr &replay_inst);
+
+ /** Completes a memory operation. */
+ void completeMemInst(DynInstPtr &completed_inst);
+
+ /** Indicates an ordering violation between a store and a load. */
void violation(DynInstPtr &store, DynInstPtr &faulting_load);
- // Change this to take in the sequence number
- void squash();
+ /**
+ * Squashes instructions for a thread. Squashing information is obtained
+ * from the time buffer.
+ */
+ void squash(unsigned tid);
- void doSquash();
+ /** Returns the number of used entries for a thread. */
+ unsigned getCount(unsigned tid) { return count[tid]; };
- void stopSquash();
+ /** Debug function to print all instructions. */
+ void printInsts();
private:
+ /** Does the actual squashing. */
+ void doSquash(unsigned tid);
+
+ /////////////////////////
+ // Various pointers
+ /////////////////////////
+
/** Pointer to the CPU. */
FullCPU *cpu;
+ /** Cache interface. */
+ MemInterface *dcacheInterface;
+
+ /** Pointer to IEW stage. */
+ IEW *iewStage;
+
/** The memory dependence unit, which tracks/predicts memory dependences
* between instructions.
*/
- MemDepUnit memDepUnit;
+ MemDepUnit memDepUnit[Impl::MaxThreads];
/** The queue to the execute stage. Issued instructions will be written
* into it.
@@ -151,36 +276,40 @@ class InstructionQueue
/** Wire to read information from timebuffer. */
typename TimeBuffer<TimeStruct>::wire fromCommit;
- enum InstList {
- Int,
- Float,
- Branch,
- Memory,
- Misc,
- Squashed,
- None
- };
+ /** Function unit pool. */
+ FUPool *fuPool;
- /** List of ready int instructions. Used to keep track of the order in
- * which instructions should issue.
- */
- ReadyInstQueue readyIntInsts;
+ //////////////////////////////////////
+ // Instruction lists, ready queues, and ordering
+ //////////////////////////////////////
+
+ /** List of all the instructions in the IQ (some of which may be issued). */
+ std::list<DynInstPtr> instList[Impl::MaxThreads];
- /** List of ready floating point instructions. */
- ReadyInstQueue readyFloatInsts;
+ /** List of instructions that are ready to be executed. */
+ std::list<DynInstPtr> instsToExecute;
- /** List of ready branch instructions. */
- ReadyInstQueue readyBranchInsts;
+ /**
+ * Struct for comparing entries to be added to the priority queue.
+ * This gives reverse ordering to the instructions in terms of
+ * sequence numbers: the instructions with smaller sequence
+ * numbers (and hence are older) will be at the top of the
+ * priority queue.
+ */
+ struct pqCompare {
+ bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const
+ {
+ return lhs->seqNum > rhs->seqNum;
+ }
+ };
- /** List of ready miscellaneous instructions. */
- ReadyInstQueue readyMiscInsts;
+ typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare>
+ ReadyInstQueue;
- /** List of squashed instructions (which are still valid and in IQ).
- * Implemented using a priority queue; the entries must contain both
- * the IQ index and sequence number of each instruction so that
- * ordering based on sequence numbers can be used.
+ /** List of ready instructions, per op class. They are separated by op
+ * class to allow for easy mapping to FUs.
*/
- ReadyInstQueue squashedInsts;
+ ReadyInstQueue readyInsts[Num_OpClasses];
/** List of non-speculative instructions that will be scheduled
* once the IQ gets a signal from commit. While it's redundant to
@@ -191,34 +320,80 @@ class InstructionQueue
*/
std::map<InstSeqNum, DynInstPtr> nonSpecInsts;
- typedef typename std::map<InstSeqNum, DynInstPtr>::iterator non_spec_it_t;
+ typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt;
- /** Number of free IQ entries left. */
- unsigned freeEntries;
+ /** Entry for the list age ordering by op class. */
+ struct ListOrderEntry {
+ OpClass queueType;
+ InstSeqNum oldestInst;
+ };
- /** The number of entries in the instruction queue. */
- unsigned numEntries;
+ /** List that contains the age order of the oldest instruction of each
+ * ready queue. Used to select the oldest instruction available
+ * among op classes.
+ * @todo: Might be better to just move these entries around instead
+ * of creating new ones every time the position changes due to an
+ * instruction issuing. Not sure std::list supports this.
+ */
+ std::list<ListOrderEntry> listOrder;
- /** The number of integer instructions that can be issued in one
- * cycle.
+ typedef typename std::list<ListOrderEntry>::iterator ListOrderIt;
+
+ /** Tracks if each ready queue is on the age order list. */
+ bool queueOnList[Num_OpClasses];
+
+ /** Iterators of each ready queue. Points to their spot in the age order
+ * list.
*/
- unsigned intWidth;
+ ListOrderIt readyIt[Num_OpClasses];
+
+ /** Add an op class to the age order list. */
+ void addToOrderList(OpClass op_class);
- /** The number of floating point instructions that can be issued
- * in one cycle.
+ /**
+ * Called when the oldest instruction has been removed from a ready queue;
+ * this places that ready queue into the proper spot in the age order list.
*/
- unsigned floatWidth;
+ void moveToYoungerInst(ListOrderIt age_order_it);
+
+ DependencyGraph<DynInstPtr> dependGraph;
+
+ //////////////////////////////////////
+ // Various parameters
+ //////////////////////////////////////
+
+ /** IQ Resource Sharing Policy */
+ enum IQPolicy {
+ Dynamic,
+ Partitioned,
+ Threshold
+ };
+
+ /** IQ sharing policy for SMT. */
+ IQPolicy iqPolicy;
+
+ /** Number of Total Threads*/
+ unsigned numThreads;
- /** The number of branches that can be issued in one cycle. */
- unsigned branchWidth;
+ /** Pointer to list of active threads. */
+ std::list<unsigned> *activeThreads;
- /** The number of memory instructions that can be issued in one cycle. */
- unsigned memoryWidth;
+ /** Per Thread IQ count */
+ unsigned count[Impl::MaxThreads];
+
+ /** Max IQ Entries Per Thread */
+ unsigned maxEntries[Impl::MaxThreads];
+
+ /** Number of free IQ entries left. */
+ unsigned freeEntries;
+
+ /** The number of entries in the instruction queue. */
+ unsigned numEntries;
/** The total number of instructions that can be issued in one cycle. */
unsigned totalWidth;
- //The number of physical registers in the CPU.
+ /** The number of physical registers in the CPU. */
unsigned numPhysRegs;
/** The number of physical integer registers in the CPU. */
@@ -232,55 +407,11 @@ class InstructionQueue
*/
unsigned commitToIEWDelay;
- //////////////////////////////////
- // Variables needed for squashing
- //////////////////////////////////
+ /** Is the IQ switched out. */
+ bool switchedOut;
/** The sequence number of the squashed instruction. */
- InstSeqNum squashedSeqNum;
-
- /** Iterator that points to the youngest instruction in the IQ. */
- ListIt tail;
-
- /** Iterator that points to the last instruction that has been squashed.
- * This will not be valid unless the IQ is in the process of squashing.
- */
- ListIt squashIt;
-
- ///////////////////////////////////
- // Dependency graph stuff
- ///////////////////////////////////
-
- class DependencyEntry
- {
- public:
- DynInstPtr inst;
- //Might want to include data about what arch. register the
- //dependence is waiting on.
- DependencyEntry *next;
-
- //This function, and perhaps this whole class, stand out a little
- //bit as they don't fit a classification well. I want access
- //to the underlying structure of the linked list, yet at
- //the same time it feels like this should be something abstracted
- //away. So for now it will sit here, within the IQ, until
- //a better implementation is decided upon.
- // This function probably shouldn't be within the entry...
- void insert(DynInstPtr &new_inst);
-
- void remove(DynInstPtr &inst_to_remove);
-
- // Debug variable, remove when done testing.
- static unsigned mem_alloc_counter;
- };
-
- /** Array of linked lists. Each linked list is a list of all the
- * instructions that depend upon a given register. The actual
- * register's index is used to index into the graph; ie all
- * instructions in flight that are dependent upon r34 will be
- * in the linked list of dependGraph[34].
- */
- DependencyEntry *dependGraph;
+ InstSeqNum squashedSeqNum[Impl::MaxThreads];
/** A cache of the recently woken registers. It is 1 if the register
* has been woken up recently, and 0 if the register has been added
@@ -288,49 +419,85 @@ class InstructionQueue
* is basically a secondary scoreboard, and should pretty much mirror
* the scoreboard that exists in the rename map.
*/
- vector<bool> regScoreboard;
+ std::vector<bool> regScoreboard;
+ /** Adds an instruction to the dependency graph, as a consumer. */
bool addToDependents(DynInstPtr &new_inst);
- void insertDependency(DynInstPtr &new_inst);
- void createDependency(DynInstPtr &new_inst);
+ /** Adds an instruction to the dependency graph, as a producer. */
+ void addToProducers(DynInstPtr &new_inst);
+
+ /** Moves an instruction to the ready queue if it is ready. */
void addIfReady(DynInstPtr &inst);
- private:
/** Debugging function to count how many entries are in the IQ. It does
* a linear walk through the instructions, so do not call this function
* during normal execution.
*/
int countInsts();
- /** Debugging function to dump out the dependency graph.
- */
- void dumpDependGraph();
-
/** Debugging function to dump all the list sizes, as well as print
* out the list of nonspeculative instructions. Should not be used
* in any other capacity, but it has no harmful sideaffects.
*/
void dumpLists();
+ /** Debugging function to dump out all instructions that are in the
+ * IQ.
+ */
+ void dumpInsts();
+
+ /** Stat for number of instructions added. */
Stats::Scalar<> iqInstsAdded;
+ /** Stat for number of non-speculative instructions added. */
Stats::Scalar<> iqNonSpecInstsAdded;
-// Stats::Scalar<> iqIntInstsAdded;
+
+ Stats::Scalar<> iqInstsIssued;
+ /** Stat for number of integer instructions issued. */
Stats::Scalar<> iqIntInstsIssued;
-// Stats::Scalar<> iqFloatInstsAdded;
+ /** Stat for number of floating point instructions issued. */
Stats::Scalar<> iqFloatInstsIssued;
-// Stats::Scalar<> iqBranchInstsAdded;
+ /** Stat for number of branch instructions issued. */
Stats::Scalar<> iqBranchInstsIssued;
-// Stats::Scalar<> iqMemInstsAdded;
+ /** Stat for number of memory instructions issued. */
Stats::Scalar<> iqMemInstsIssued;
-// Stats::Scalar<> iqMiscInstsAdded;
+ /** Stat for number of miscellaneous instructions issued. */
Stats::Scalar<> iqMiscInstsIssued;
+ /** Stat for number of squashed instructions that were ready to issue. */
Stats::Scalar<> iqSquashedInstsIssued;
- Stats::Scalar<> iqLoopSquashStalls;
+ /** Stat for number of squashed instructions examined when squashing. */
Stats::Scalar<> iqSquashedInstsExamined;
+ /** Stat for number of squashed instruction operands examined when
+ * squashing.
+ */
Stats::Scalar<> iqSquashedOperandsExamined;
+ /** Stat for number of non-speculative instructions removed due to a squash.
+ */
Stats::Scalar<> iqSquashedNonSpecRemoved;
+ /** Distribution of number of instructions in the queue. */
+ Stats::VectorDistribution<> queueResDist;
+ /** Distribution of the number of instructions issued. */
+ Stats::Distribution<> numIssuedDist;
+ /** Distribution of the cycles it takes to issue an instruction. */
+ Stats::VectorDistribution<> issueDelayDist;
+
+ /** Number of times an instruction could not be issued because a
+ * FU was busy.
+ */
+ Stats::Vector<> statFuBusy;
+// Stats::Vector<> dist_unissued;
+ /** Stat for total number issued for each instruction type. */
+ Stats::Vector2d<> statIssuedInstType;
+
+ /** Number of instructions issued per cycle. */
+ Stats::Formula issueRate;
+// Stats::Formula issue_stores;
+// Stats::Formula issue_op_rate;
+ /** Number of times the FU was busy. */
+ Stats::Vector<> fuBusy;
+ /** Number of times the FU was busy per instruction issued. */
+ Stats::Formula fuBusyRate;
};
-#endif //__CPU_O3_CPU_INST_QUEUE_HH__
+#endif //__CPU_O3_INST_QUEUE_HH__
diff --git a/src/cpu/o3/inst_queue_impl.hh b/src/cpu/o3/inst_queue_impl.hh
index 048dc7c00..06a052c6f 100644
--- a/src/cpu/o3/inst_queue_impl.hh
+++ b/src/cpu/o3/inst_queue_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,75 +24,152 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Todo:
-// Current ordering allows for 0 cycle added-to-scheduled. Could maybe fake
-// it; either do in reverse order, or have added instructions put into a
-// different ready queue that, in scheduleRreadyInsts(), gets put onto the
-// normal ready queue. This would however give only a one cycle delay,
-// but probably is more flexible to actually add in a delay parameter than
-// just running it backwards.
-
#include <limits>
#include <vector>
#include "sim/root.hh"
+#include "cpu/o3/fu_pool.hh"
#include "cpu/o3/inst_queue.hh"
-// Either compile error or max int due to sign extension.
-// Hack to avoid compile warnings.
-const InstSeqNum MaxInstSeqNum = std::numeric_limits<InstSeqNum>::max();
+using namespace std;
template <class Impl>
-InstructionQueue<Impl>::InstructionQueue(Params &params)
- : memDepUnit(params),
- numEntries(params.numIQEntries),
- intWidth(params.executeIntWidth),
- floatWidth(params.executeFloatWidth),
- branchWidth(params.executeBranchWidth),
- memoryWidth(params.executeMemoryWidth),
- totalWidth(params.issueWidth),
- numPhysIntRegs(params.numPhysIntRegs),
- numPhysFloatRegs(params.numPhysFloatRegs),
- commitToIEWDelay(params.commitToIEWDelay)
+InstructionQueue<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst,
+ int fu_idx,
+ InstructionQueue<Impl> *iq_ptr)
+ : Event(&mainEventQueue, Stat_Event_Pri),
+ inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr), freeFU(false)
{
- // Initialize the number of free IQ entries.
- freeEntries = numEntries;
+ this->setFlags(Event::AutoDelete);
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::FUCompletion::process()
+{
+ iqPtr->processFUCompletion(inst, freeFU ? fuIdx : -1);
+ inst = NULL;
+}
+
+
+template <class Impl>
+const char *
+InstructionQueue<Impl>::FUCompletion::description()
+{
+ return "Functional unit completion event";
+}
+
+template <class Impl>
+InstructionQueue<Impl>::InstructionQueue(Params *params)
+ : fuPool(params->fuPool),
+ numEntries(params->numIQEntries),
+ totalWidth(params->issueWidth),
+ numPhysIntRegs(params->numPhysIntRegs),
+ numPhysFloatRegs(params->numPhysFloatRegs),
+ commitToIEWDelay(params->commitToIEWDelay)
+{
+ assert(fuPool);
+
+ switchedOut = false;
+
+ numThreads = params->numberOfThreads;
// Set the number of physical registers as the number of int + float
numPhysRegs = numPhysIntRegs + numPhysFloatRegs;
- DPRINTF(IQ, "IQ: There are %i physical registers.\n", numPhysRegs);
+ DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs);
//Create an entry for each physical register within the
//dependency graph.
- dependGraph = new DependencyEntry[numPhysRegs];
+ dependGraph.resize(numPhysRegs);
// Resize the register scoreboard.
regScoreboard.resize(numPhysRegs);
- // Initialize all the head pointers to point to NULL, and all the
- // entries as unready.
- // Note that in actuality, the registers corresponding to the logical
- // registers start off as ready. However this doesn't matter for the
- // IQ as the instruction should have been correctly told if those
- // registers are ready in rename. Thus it can all be initialized as
- // unready.
- for (int i = 0; i < numPhysRegs; ++i)
- {
- dependGraph[i].next = NULL;
- dependGraph[i].inst = NULL;
- regScoreboard[i] = false;
+ //Initialize Mem Dependence Units
+ for (int i = 0; i < numThreads; i++) {
+ memDepUnit[i].init(params,i);
+ memDepUnit[i].setIQ(this);
}
+ resetState();
+
+ string policy = params->smtIQPolicy;
+
+ //Convert string to lowercase
+ std::transform(policy.begin(), policy.end(), policy.begin(),
+ (int(*)(int)) tolower);
+
+ //Figure out resource sharing policy
+ if (policy == "dynamic") {
+ iqPolicy = Dynamic;
+
+ //Set Max Entries to Total ROB Capacity
+ for (int i = 0; i < numThreads; i++) {
+ maxEntries[i] = numEntries;
+ }
+
+ } else if (policy == "partitioned") {
+ iqPolicy = Partitioned;
+
+ //@todo:make work if part_amt doesnt divide evenly.
+ int part_amt = numEntries / numThreads;
+
+ //Divide ROB up evenly
+ for (int i = 0; i < numThreads; i++) {
+ maxEntries[i] = part_amt;
+ }
+
+ DPRINTF(Fetch, "IQ sharing policy set to Partitioned:"
+ "%i entries per thread.\n",part_amt);
+
+ } else if (policy == "threshold") {
+ iqPolicy = Threshold;
+
+ double threshold = (double)params->smtIQThreshold / 100;
+
+ int thresholdIQ = (int)((double)threshold * numEntries);
+
+ //Divide up by threshold amount
+ for (int i = 0; i < numThreads; i++) {
+ maxEntries[i] = thresholdIQ;
+ }
+
+ DPRINTF(Fetch, "IQ sharing policy set to Threshold:"
+ "%i entries per thread.\n",thresholdIQ);
+ } else {
+ assert(0 && "Invalid IQ Sharing Policy.Options Are:{Dynamic,"
+ "Partitioned, Threshold}");
+ }
+}
+
+template <class Impl>
+InstructionQueue<Impl>::~InstructionQueue()
+{
+ dependGraph.reset();
+#ifdef DEBUG
+ cprintf("Nodes traversed: %i, removed: %i\n",
+ dependGraph.nodesTraversed, dependGraph.nodesRemoved);
+#endif
+}
+
+template <class Impl>
+std::string
+InstructionQueue<Impl>::name() const
+{
+ return cpu->name() + ".iq";
}
template <class Impl>
void
InstructionQueue<Impl>::regStats()
{
+ using namespace Stats;
iqInstsAdded
.name(name() + ".iqInstsAdded")
.desc("Number of instructions added to the IQ (excludes non-spec)")
@@ -103,36 +180,31 @@ InstructionQueue<Impl>::regStats()
.desc("Number of non-speculative instructions added to the IQ")
.prereq(iqNonSpecInstsAdded);
-// iqIntInstsAdded;
+ iqInstsIssued
+ .name(name() + ".iqInstsIssued")
+ .desc("Number of instructions issued")
+ .prereq(iqInstsIssued);
iqIntInstsIssued
.name(name() + ".iqIntInstsIssued")
.desc("Number of integer instructions issued")
.prereq(iqIntInstsIssued);
-// iqFloatInstsAdded;
-
iqFloatInstsIssued
.name(name() + ".iqFloatInstsIssued")
.desc("Number of float instructions issued")
.prereq(iqFloatInstsIssued);
-// iqBranchInstsAdded;
-
iqBranchInstsIssued
.name(name() + ".iqBranchInstsIssued")
.desc("Number of branch instructions issued")
.prereq(iqBranchInstsIssued);
-// iqMemInstsAdded;
-
iqMemInstsIssued
.name(name() + ".iqMemInstsIssued")
.desc("Number of memory instructions issued")
.prereq(iqMemInstsIssued);
-// iqMiscInstsAdded;
-
iqMiscInstsIssued
.name(name() + ".iqMiscInstsIssued")
.desc("Number of miscellaneous instructions issued")
@@ -143,12 +215,6 @@ InstructionQueue<Impl>::regStats()
.desc("Number of squashed instructions issued")
.prereq(iqSquashedInstsIssued);
- iqLoopSquashStalls
- .name(name() + ".iqLoopSquashStalls")
- .desc("Number of times issue loop had to restart due to squashed "
- "inst; mainly for profiling")
- .prereq(iqLoopSquashStalls);
-
iqSquashedInstsExamined
.name(name() + ".iqSquashedInstsExamined")
.desc("Number of squashed instructions iterated over during squash;"
@@ -166,25 +232,158 @@ InstructionQueue<Impl>::regStats()
.desc("Number of squashed non-spec instructions that were removed")
.prereq(iqSquashedNonSpecRemoved);
- // Tell mem dependence unit to reg stats as well.
- memDepUnit.regStats();
+ queueResDist
+ .init(Num_OpClasses, 0, 99, 2)
+ .name(name() + ".IQ:residence:")
+ .desc("cycles from dispatch to issue")
+ .flags(total | pdf | cdf )
+ ;
+ for (int i = 0; i < Num_OpClasses; ++i) {
+ queueResDist.subname(i, opClassStrings[i]);
+ }
+ numIssuedDist
+ .init(0,totalWidth,1)
+ .name(name() + ".ISSUE:issued_per_cycle")
+ .desc("Number of insts issued each cycle")
+ .flags(pdf)
+ ;
+/*
+ dist_unissued
+ .init(Num_OpClasses+2)
+ .name(name() + ".ISSUE:unissued_cause")
+ .desc("Reason ready instruction not issued")
+ .flags(pdf | dist)
+ ;
+ for (int i=0; i < (Num_OpClasses + 2); ++i) {
+ dist_unissued.subname(i, unissued_names[i]);
+ }
+*/
+ statIssuedInstType
+ .init(numThreads,Num_OpClasses)
+ .name(name() + ".ISSUE:FU_type")
+ .desc("Type of FU issued")
+ .flags(total | pdf | dist)
+ ;
+ statIssuedInstType.ysubnames(opClassStrings);
+
+ //
+ // How long did instructions for a particular FU type wait prior to issue
+ //
+
+ issueDelayDist
+ .init(Num_OpClasses,0,99,2)
+ .name(name() + ".ISSUE:")
+ .desc("cycles from operands ready to issue")
+ .flags(pdf | cdf)
+ ;
+
+ for (int i=0; i<Num_OpClasses; ++i) {
+ stringstream subname;
+ subname << opClassStrings[i] << "_delay";
+ issueDelayDist.subname(i, subname.str());
+ }
+
+ issueRate
+ .name(name() + ".ISSUE:rate")
+ .desc("Inst issue rate")
+ .flags(total)
+ ;
+ issueRate = iqInstsIssued / cpu->numCycles;
+/*
+ issue_stores
+ .name(name() + ".ISSUE:stores")
+ .desc("Number of stores issued")
+ .flags(total)
+ ;
+ issue_stores = exe_refs - exe_loads;
+*/
+/*
+ issue_op_rate
+ .name(name() + ".ISSUE:op_rate")
+ .desc("Operation issue rate")
+ .flags(total)
+ ;
+ issue_op_rate = issued_ops / numCycles;
+*/
+ statFuBusy
+ .init(Num_OpClasses)
+ .name(name() + ".ISSUE:fu_full")
+ .desc("attempts to use FU when none available")
+ .flags(pdf | dist)
+ ;
+ for (int i=0; i < Num_OpClasses; ++i) {
+ statFuBusy.subname(i, opClassStrings[i]);
+ }
+
+ fuBusy
+ .init(numThreads)
+ .name(name() + ".ISSUE:fu_busy_cnt")
+ .desc("FU busy when requested")
+ .flags(total)
+ ;
+
+ fuBusyRate
+ .name(name() + ".ISSUE:fu_busy_rate")
+ .desc("FU busy rate (busy events/executed inst)")
+ .flags(total)
+ ;
+ fuBusyRate = fuBusy / iqInstsIssued;
+
+ for ( int i=0; i < numThreads; i++) {
+ // Tell mem dependence unit to reg stats as well.
+ memDepUnit[i].regStats();
+ }
}
template <class Impl>
void
-InstructionQueue<Impl>::setCPU(FullCPU *cpu_ptr)
+InstructionQueue<Impl>::resetState()
{
- cpu = cpu_ptr;
+ //Initialize thread IQ counts
+ for (int i = 0; i <numThreads; i++) {
+ count[i] = 0;
+ instList[i].clear();
+ }
+
+ // Initialize the number of free IQ entries.
+ freeEntries = numEntries;
- tail = cpu->instList.begin();
+ // Note that in actuality, the registers corresponding to the logical
+ // registers start off as ready. However this doesn't matter for the
+ // IQ as the instruction should have been correctly told if those
+ // registers are ready in rename. Thus it can all be initialized as
+ // unready.
+ for (int i = 0; i < numPhysRegs; ++i) {
+ regScoreboard[i] = false;
+ }
+
+ for (int i = 0; i < numThreads; ++i) {
+ squashedSeqNum[i] = 0;
+ }
+
+ for (int i = 0; i < Num_OpClasses; ++i) {
+ while (!readyInsts[i].empty())
+ readyInsts[i].pop();
+ queueOnList[i] = false;
+ readyIt[i] = listOrder.end();
+ }
+ nonSpecInsts.clear();
+ listOrder.clear();
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ DPRINTF(IQ, "Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
}
template <class Impl>
void
-InstructionQueue<Impl>::setIssueToExecuteQueue(
- TimeBuffer<IssueStruct> *i2e_ptr)
+InstructionQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2e_ptr)
{
- DPRINTF(IQ, "IQ: Set the issue to execute queue.\n");
+ DPRINTF(IQ, "Set the issue to execute queue.\n");
issueToExecuteQueue = i2e_ptr;
}
@@ -192,19 +391,77 @@ template <class Impl>
void
InstructionQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
- DPRINTF(IQ, "IQ: Set the time buffer.\n");
+ DPRINTF(IQ, "Set the time buffer.\n");
timeBuffer = tb_ptr;
fromCommit = timeBuffer->getWire(-commitToIEWDelay);
}
template <class Impl>
+void
+InstructionQueue<Impl>::switchOut()
+{
+ resetState();
+ dependGraph.reset();
+ switchedOut = true;
+ for (int i = 0; i < numThreads; ++i) {
+ memDepUnit[i].switchOut();
+ }
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::takeOverFrom()
+{
+ switchedOut = false;
+}
+
+template <class Impl>
+int
+InstructionQueue<Impl>::entryAmount(int num_threads)
+{
+ if (iqPolicy == Partitioned) {
+ return numEntries / num_threads;
+ } else {
+ return 0;
+ }
+}
+
+
+template <class Impl>
+void
+InstructionQueue<Impl>::resetEntries()
+{
+ if (iqPolicy != Dynamic || numThreads > 1) {
+ int active_threads = (*activeThreads).size();
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+ list<unsigned>::iterator list_end = (*activeThreads).end();
+
+ while (threads != list_end) {
+ if (iqPolicy == Partitioned) {
+ maxEntries[*threads++] = numEntries / active_threads;
+ } else if(iqPolicy == Threshold && active_threads == 1) {
+ maxEntries[*threads++] = numEntries;
+ }
+ }
+ }
+}
+
+template <class Impl>
unsigned
InstructionQueue<Impl>::numFreeEntries()
{
return freeEntries;
}
+template <class Impl>
+unsigned
+InstructionQueue<Impl>::numFreeEntries(unsigned tid)
+{
+ return maxEntries[tid] - count[tid];
+}
+
// Might want to do something more complex if it knows how many instructions
// will be issued this cycle.
template <class Impl>
@@ -219,447 +476,412 @@ InstructionQueue<Impl>::isFull()
}
template <class Impl>
+bool
+InstructionQueue<Impl>::isFull(unsigned tid)
+{
+ if (numFreeEntries(tid) == 0) {
+ return(true);
+ } else {
+ return(false);
+ }
+}
+
+template <class Impl>
+bool
+InstructionQueue<Impl>::hasReadyInsts()
+{
+ if (!listOrder.empty()) {
+ return true;
+ }
+
+ for (int i = 0; i < Num_OpClasses; ++i) {
+ if (!readyInsts[i].empty()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <class Impl>
void
InstructionQueue<Impl>::insert(DynInstPtr &new_inst)
{
// Make sure the instruction is valid
assert(new_inst);
- DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n",
- new_inst->readPC());
+ DPRINTF(IQ, "Adding instruction [sn:%lli] PC %#x to the IQ.\n",
+ new_inst->seqNum, new_inst->readPC());
- // Check if there are any free entries. Panic if there are none.
- // Might want to have this return a fault in the future instead of
- // panicing.
assert(freeEntries != 0);
- // If the IQ currently has nothing in it, then there's a possibility
- // that the tail iterator is invalid (might have been pointing at an
- // instruction that was retired). Reset the tail iterator.
- if (freeEntries == numEntries) {
- tail = cpu->instList.begin();
- }
-
- // Move the tail iterator. Instructions may not have been issued
- // to the IQ, so we may have to increment the iterator more than once.
- while ((*tail) != new_inst) {
- tail++;
-
- // Make sure the tail iterator points at something legal.
- assert(tail != cpu->instList.end());
- }
-
+ instList[new_inst->threadNumber].push_back(new_inst);
- // Decrease the number of free entries.
--freeEntries;
+ new_inst->setInIQ();
+
// Look through its source registers (physical regs), and mark any
// dependencies.
addToDependents(new_inst);
// Have this instruction set itself as the producer of its destination
// register(s).
- createDependency(new_inst);
+ addToProducers(new_inst);
- // If it's a memory instruction, add it to the memory dependency
- // unit.
if (new_inst->isMemRef()) {
- memDepUnit.insert(new_inst);
- // Uh..forgot to look it up and put it on the proper dependency list
- // if the instruction should not go yet.
+ memDepUnit[new_inst->threadNumber].insert(new_inst);
} else {
- // If the instruction is ready then add it to the ready list.
addIfReady(new_inst);
}
++iqInstsAdded;
+ count[new_inst->threadNumber]++;
+
assert(freeEntries == (numEntries - countInsts()));
}
template <class Impl>
void
-InstructionQueue<Impl>::insertNonSpec(DynInstPtr &inst)
+InstructionQueue<Impl>::insertNonSpec(DynInstPtr &new_inst)
{
- nonSpecInsts[inst->seqNum] = inst;
-
// @todo: Clean up this code; can do it by setting inst as unable
// to issue, then calling normal insert on the inst.
- // Make sure the instruction is valid
- assert(inst);
-
- DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n",
- inst->readPC());
+ assert(new_inst);
- // Check if there are any free entries. Panic if there are none.
- // Might want to have this return a fault in the future instead of
- // panicing.
- assert(freeEntries != 0);
+ nonSpecInsts[new_inst->seqNum] = new_inst;
- // If the IQ currently has nothing in it, then there's a possibility
- // that the tail iterator is invalid (might have been pointing at an
- // instruction that was retired). Reset the tail iterator.
- if (freeEntries == numEntries) {
- tail = cpu->instList.begin();
- }
+ DPRINTF(IQ, "Adding non-speculative instruction [sn:%lli] PC %#x "
+ "to the IQ.\n",
+ new_inst->seqNum, new_inst->readPC());
- // Move the tail iterator. Instructions may not have been issued
- // to the IQ, so we may have to increment the iterator more than once.
- while ((*tail) != inst) {
- tail++;
+ assert(freeEntries != 0);
- // Make sure the tail iterator points at something legal.
- assert(tail != cpu->instList.end());
- }
+ instList[new_inst->threadNumber].push_back(new_inst);
- // Decrease the number of free entries.
--freeEntries;
+ new_inst->setInIQ();
+
// Have this instruction set itself as the producer of its destination
// register(s).
- createDependency(inst);
+ addToProducers(new_inst);
// If it's a memory instruction, add it to the memory dependency
// unit.
- if (inst->isMemRef()) {
- memDepUnit.insertNonSpec(inst);
+ if (new_inst->isMemRef()) {
+ memDepUnit[new_inst->threadNumber].insertNonSpec(new_inst);
}
++iqNonSpecInstsAdded;
+
+ count[new_inst->threadNumber]++;
+
+ assert(freeEntries == (numEntries - countInsts()));
}
-// Slightly hack function to advance the tail iterator in the case that
-// the IEW stage issues an instruction that is not added to the IQ. This
-// is needed in case a long chain of such instructions occurs.
-// I don't think this is used anymore.
template <class Impl>
void
-InstructionQueue<Impl>::advanceTail(DynInstPtr &inst)
+InstructionQueue<Impl>::insertBarrier(DynInstPtr &barr_inst)
{
- // Make sure the instruction is valid
- assert(inst);
-
- DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n",
- inst->readPC());
-
- // Check if there are any free entries. Panic if there are none.
- // Might want to have this return a fault in the future instead of
- // panicing.
- assert(freeEntries != 0);
-
- // If the IQ currently has nothing in it, then there's a possibility
- // that the tail iterator is invalid (might have been pointing at an
- // instruction that was retired). Reset the tail iterator.
- if (freeEntries == numEntries) {
- tail = cpu->instList.begin();
- }
-
- // Move the tail iterator. Instructions may not have been issued
- // to the IQ, so we may have to increment the iterator more than once.
- while ((*tail) != inst) {
- tail++;
+ memDepUnit[barr_inst->threadNumber].insertBarrier(barr_inst);
- // Make sure the tail iterator points at something legal.
- assert(tail != cpu->instList.end());
- }
-
- assert(freeEntries <= numEntries);
+ insertNonSpec(barr_inst);
+}
- // Have this instruction set itself as the producer of its destination
- // register(s).
- createDependency(inst);
+template <class Impl>
+typename Impl::DynInstPtr
+InstructionQueue<Impl>::getInstToExecute()
+{
+ assert(!instsToExecute.empty());
+ DynInstPtr inst = instsToExecute.front();
+ instsToExecute.pop_front();
+ return inst;
}
-// Need to make sure the number of float and integer instructions
-// issued does not exceed the total issue bandwidth.
-// @todo: Figure out a better way to remove the squashed items from the
-// lists. Checking the top item of each list to see if it's squashed
-// wastes time and forces jumps.
template <class Impl>
void
-InstructionQueue<Impl>::scheduleReadyInsts()
+InstructionQueue<Impl>::addToOrderList(OpClass op_class)
{
- DPRINTF(IQ, "IQ: Attempting to schedule ready instructions from "
- "the IQ.\n");
-
- int int_issued = 0;
- int float_issued = 0;
- int branch_issued = 0;
- int memory_issued = 0;
- int squashed_issued = 0;
- int total_issued = 0;
-
- IssueStruct *i2e_info = issueToExecuteQueue->access(0);
-
- bool insts_available = !readyBranchInsts.empty() ||
- !readyIntInsts.empty() ||
- !readyFloatInsts.empty() ||
- !memDepUnit.empty() ||
- !readyMiscInsts.empty() ||
- !squashedInsts.empty();
-
- // Note: Requires a globally defined constant.
- InstSeqNum oldest_inst = MaxInstSeqNum;
- InstList list_with_oldest = None;
-
- // Temporary values.
- DynInstPtr int_head_inst;
- DynInstPtr float_head_inst;
- DynInstPtr branch_head_inst;
- DynInstPtr mem_head_inst;
- DynInstPtr misc_head_inst;
- DynInstPtr squashed_head_inst;
-
- // Somewhat nasty code to look at all of the lists where issuable
- // instructions are located, and choose the oldest instruction among
- // those lists. Consider a rewrite in the future.
- while (insts_available && total_issued < totalWidth)
- {
- // Set this to false. Each if-block is required to set it to true
- // if there were instructions available this check. This will cause
- // this loop to run once more than necessary, but avoids extra calls.
- insts_available = false;
+ assert(!readyInsts[op_class].empty());
- oldest_inst = MaxInstSeqNum;
+ ListOrderEntry queue_entry;
- list_with_oldest = None;
+ queue_entry.queueType = op_class;
- if (!readyIntInsts.empty() &&
- int_issued < intWidth) {
+ queue_entry.oldestInst = readyInsts[op_class].top()->seqNum;
- insts_available = true;
+ ListOrderIt list_it = listOrder.begin();
+ ListOrderIt list_end_it = listOrder.end();
- int_head_inst = readyIntInsts.top();
-
- if (int_head_inst->isSquashed()) {
- readyIntInsts.pop();
-
- ++iqLoopSquashStalls;
-
- continue;
- }
-
- oldest_inst = int_head_inst->seqNum;
-
- list_with_oldest = Int;
+ while (list_it != list_end_it) {
+ if ((*list_it).oldestInst > queue_entry.oldestInst) {
+ break;
}
- if (!readyFloatInsts.empty() &&
- float_issued < floatWidth) {
-
- insts_available = true;
-
- float_head_inst = readyFloatInsts.top();
-
- if (float_head_inst->isSquashed()) {
- readyFloatInsts.pop();
-
- ++iqLoopSquashStalls;
-
- continue;
- } else if (float_head_inst->seqNum < oldest_inst) {
- oldest_inst = float_head_inst->seqNum;
+ list_it++;
+ }
- list_with_oldest = Float;
- }
- }
+ readyIt[op_class] = listOrder.insert(list_it, queue_entry);
+ queueOnList[op_class] = true;
+}
- if (!readyBranchInsts.empty() &&
- branch_issued < branchWidth) {
+template <class Impl>
+void
+InstructionQueue<Impl>::moveToYoungerInst(ListOrderIt list_order_it)
+{
+ // Get iterator of next item on the list
+ // Delete the original iterator
+ // Determine if the next item is either the end of the list or younger
+ // than the new instruction. If so, then add in a new iterator right here.
+ // If not, then move along.
+ ListOrderEntry queue_entry;
+ OpClass op_class = (*list_order_it).queueType;
+ ListOrderIt next_it = list_order_it;
+
+ ++next_it;
+
+ queue_entry.queueType = op_class;
+ queue_entry.oldestInst = readyInsts[op_class].top()->seqNum;
+
+ while (next_it != listOrder.end() &&
+ (*next_it).oldestInst < queue_entry.oldestInst) {
+ ++next_it;
+ }
- insts_available = true;
+ readyIt[op_class] = listOrder.insert(next_it, queue_entry);
+}
- branch_head_inst = readyBranchInsts.top();
+template <class Impl>
+void
+InstructionQueue<Impl>::processFUCompletion(DynInstPtr &inst, int fu_idx)
+{
+ // 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()) {
+ return;
+ }
- if (branch_head_inst->isSquashed()) {
- readyBranchInsts.pop();
+ iewStage->wakeCPU();
- ++iqLoopSquashStalls;
+ if (fu_idx > -1)
+ fuPool->freeUnitNextCycle(fu_idx);
- continue;
- } else if (branch_head_inst->seqNum < oldest_inst) {
- oldest_inst = branch_head_inst->seqNum;
+ // @todo: Ensure that these FU Completions happen at the beginning
+ // of a cycle, otherwise they could add too many instructions to
+ // the queue.
+ issueToExecuteQueue->access(0)->size++;
+ instsToExecute.push_back(inst);
+}
- list_with_oldest = Branch;
- }
+// @todo: Figure out a better way to remove the squashed items from the
+// lists. Checking the top item of each list to see if it's squashed
+// wastes time and forces jumps.
+template <class Impl>
+void
+InstructionQueue<Impl>::scheduleReadyInsts()
+{
+ DPRINTF(IQ, "Attempting to schedule ready instructions from "
+ "the IQ.\n");
- }
+ IssueStruct *i2e_info = issueToExecuteQueue->access(0);
- if (!memDepUnit.empty() &&
- memory_issued < memoryWidth) {
+ // Have iterator to head of the list
+ // While I haven't exceeded bandwidth or reached the end of the list,
+ // Try to get a FU that can do what this op needs.
+ // If successful, change the oldestInst to the new top of the list, put
+ // the queue in the proper place in the list.
+ // Increment the iterator.
+ // This will avoid trying to schedule a certain op class if there are no
+ // FUs that handle it.
+ ListOrderIt order_it = listOrder.begin();
+ ListOrderIt order_end_it = listOrder.end();
+ int total_issued = 0;
- insts_available = true;
+ while (total_issued < totalWidth &&
+ order_it != order_end_it) {
+ OpClass op_class = (*order_it).queueType;
- mem_head_inst = memDepUnit.top();
+ assert(!readyInsts[op_class].empty());
- if (mem_head_inst->isSquashed()) {
- memDepUnit.pop();
+ DynInstPtr issuing_inst = readyInsts[op_class].top();
- ++iqLoopSquashStalls;
+ assert(issuing_inst->seqNum == (*order_it).oldestInst);
- continue;
- } else if (mem_head_inst->seqNum < oldest_inst) {
- oldest_inst = mem_head_inst->seqNum;
+ if (issuing_inst->isSquashed()) {
+ readyInsts[op_class].pop();
- list_with_oldest = Memory;
+ if (!readyInsts[op_class].empty()) {
+ moveToYoungerInst(order_it);
+ } else {
+ readyIt[op_class] = listOrder.end();
+ queueOnList[op_class] = false;
}
- }
-
- if (!readyMiscInsts.empty()) {
-
- insts_available = true;
-
- misc_head_inst = readyMiscInsts.top();
- if (misc_head_inst->isSquashed()) {
- readyMiscInsts.pop();
+ listOrder.erase(order_it++);
- ++iqLoopSquashStalls;
+ ++iqSquashedInstsIssued;
- continue;
- } else if (misc_head_inst->seqNum < oldest_inst) {
- oldest_inst = misc_head_inst->seqNum;
-
- list_with_oldest = Misc;
- }
+ continue;
}
- if (!squashedInsts.empty()) {
-
- insts_available = true;
+ int idx = -2;
+ int op_latency = 1;
+ int tid = issuing_inst->threadNumber;
- squashed_head_inst = squashedInsts.top();
+ if (op_class != No_OpClass) {
+ idx = fuPool->getUnit(op_class);
- if (squashed_head_inst->seqNum < oldest_inst) {
- list_with_oldest = Squashed;
+ if (idx > -1) {
+ op_latency = fuPool->getOpLatency(op_class);
}
-
}
- DynInstPtr issuing_inst = NULL;
-
- switch (list_with_oldest) {
- case None:
- DPRINTF(IQ, "IQ: Not able to schedule any instructions. Issuing "
- "inst is %#x.\n", issuing_inst);
- break;
-
- case Int:
- issuing_inst = int_head_inst;
- readyIntInsts.pop();
- ++int_issued;
- DPRINTF(IQ, "IQ: Issuing integer instruction PC %#x.\n",
- issuing_inst->readPC());
- break;
-
- case Float:
- issuing_inst = float_head_inst;
- readyFloatInsts.pop();
- ++float_issued;
- DPRINTF(IQ, "IQ: Issuing float instruction PC %#x.\n",
- issuing_inst->readPC());
- break;
-
- case Branch:
- issuing_inst = branch_head_inst;
- readyBranchInsts.pop();
- ++branch_issued;
- DPRINTF(IQ, "IQ: Issuing branch instruction PC %#x.\n",
- issuing_inst->readPC());
- break;
-
- case Memory:
- issuing_inst = mem_head_inst;
+ // If we have an instruction that doesn't require a FU, or a
+ // valid FU, then schedule for execution.
+ if (idx == -2 || idx != -1) {
+ if (op_latency == 1) {
+ i2e_info->size++;
+ instsToExecute.push_back(issuing_inst);
+
+ // Add the FU onto the list of FU's to be freed next
+ // cycle if we used one.
+ if (idx >= 0)
+ fuPool->freeUnitNextCycle(idx);
+ } else {
+ int issue_latency = fuPool->getIssueLatency(op_class);
+ // Generate completion event for the FU
+ FUCompletion *execution = new FUCompletion(issuing_inst,
+ idx, this);
+
+ execution->schedule(curTick + cpu->cycles(issue_latency - 1));
+
+ // @todo: Enforce that issue_latency == 1 or op_latency
+ if (issue_latency > 1) {
+ // If FU isn't pipelined, then it must be freed
+ // upon the execution completing.
+ execution->setFreeFU();
+ } else {
+ // Add the FU onto the list of FU's to be freed next cycle.
+ fuPool->freeUnitNextCycle(idx);
+ }
+ }
- memDepUnit.pop();
- ++memory_issued;
- DPRINTF(IQ, "IQ: Issuing memory instruction PC %#x.\n",
- issuing_inst->readPC());
- break;
+ DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x "
+ "[sn:%lli]\n",
+ tid, issuing_inst->readPC(),
+ issuing_inst->seqNum);
- case Misc:
- issuing_inst = misc_head_inst;
- readyMiscInsts.pop();
+ readyInsts[op_class].pop();
- ++iqMiscInstsIssued;
+ if (!readyInsts[op_class].empty()) {
+ moveToYoungerInst(order_it);
+ } else {
+ readyIt[op_class] = listOrder.end();
+ queueOnList[op_class] = false;
+ }
- DPRINTF(IQ, "IQ: Issuing a miscellaneous instruction PC %#x.\n",
- issuing_inst->readPC());
- break;
+ issuing_inst->setIssued();
+ ++total_issued;
- case Squashed:
- assert(0 && "Squashed insts should not issue any more!");
- squashedInsts.pop();
- // Set the squashed instruction as able to commit so that commit
- // can just drop it from the ROB. This is a bit faked.
- ++squashed_issued;
- ++freeEntries;
+ if (!issuing_inst->isMemRef()) {
+ // Memory instructions can not be freed from the IQ until they
+ // complete.
+ ++freeEntries;
+ count[tid]--;
+ issuing_inst->removeInIQ();
+ } else {
+ memDepUnit[tid].issue(issuing_inst);
+ }
- DPRINTF(IQ, "IQ: Issuing squashed instruction PC %#x.\n",
- squashed_head_inst->readPC());
- break;
+ listOrder.erase(order_it++);
+ statIssuedInstType[tid][op_class]++;
+ } else {
+ statFuBusy[op_class]++;
+ fuBusy[tid]++;
+ ++order_it;
}
+ }
- if (list_with_oldest != None && list_with_oldest != Squashed) {
- i2e_info->insts[total_issued] = issuing_inst;
- i2e_info->size++;
-
- issuing_inst->setIssued();
-
- ++freeEntries;
- ++total_issued;
- }
+ numIssuedDist.sample(total_issued);
+ iqInstsIssued+= total_issued;
- assert(freeEntries == (numEntries - countInsts()));
+ // If we issued any instructions, tell the CPU we had activity.
+ if (total_issued) {
+ cpu->activityThisCycle();
+ } else {
+ DPRINTF(IQ, "Not able to schedule any instructions.\n");
}
-
- iqIntInstsIssued += int_issued;
- iqFloatInstsIssued += float_issued;
- iqBranchInstsIssued += branch_issued;
- iqMemInstsIssued += memory_issued;
- iqSquashedInstsIssued += squashed_issued;
}
template <class Impl>
void
InstructionQueue<Impl>::scheduleNonSpec(const InstSeqNum &inst)
{
- DPRINTF(IQ, "IQ: Marking nonspeculative instruction with sequence "
- "number %i as ready to execute.\n", inst);
+ DPRINTF(IQ, "Marking nonspeculative instruction [sn:%lli] as ready "
+ "to execute.\n", inst);
- non_spec_it_t inst_it = nonSpecInsts.find(inst);
+ NonSpecMapIt inst_it = nonSpecInsts.find(inst);
assert(inst_it != nonSpecInsts.end());
- // Mark this instruction as ready to issue.
+ unsigned tid = (*inst_it).second->threadNumber;
+
(*inst_it).second->setCanIssue();
- // Now schedule the instruction.
if (!(*inst_it).second->isMemRef()) {
addIfReady((*inst_it).second);
} else {
- memDepUnit.nonSpecInstReady((*inst_it).second);
+ memDepUnit[tid].nonSpecInstReady((*inst_it).second);
}
+ (*inst_it).second = NULL;
+
nonSpecInsts.erase(inst_it);
}
template <class Impl>
void
+InstructionQueue<Impl>::commit(const InstSeqNum &inst, unsigned tid)
+{
+ DPRINTF(IQ, "[tid:%i]: Committing instructions older than [sn:%i]\n",
+ tid,inst);
+
+ ListIt iq_it = instList[tid].begin();
+
+ while (iq_it != instList[tid].end() &&
+ (*iq_it)->seqNum <= inst) {
+ ++iq_it;
+ instList[tid].pop_front();
+ }
+
+ assert(freeEntries == (numEntries - countInsts()));
+}
+
+template <class Impl>
+int
InstructionQueue<Impl>::wakeDependents(DynInstPtr &completed_inst)
{
- DPRINTF(IQ, "IQ: Waking dependents of completed instruction.\n");
- //Look at the physical destination register of the DynInst
- //and look it up on the dependency graph. Then mark as ready
- //any instructions within the instruction queue.
- DependencyEntry *curr;
+ int dependents = 0;
- // Tell the memory dependence unit to wake any dependents on this
- // instruction if it is a memory instruction.
+ DPRINTF(IQ, "Waking dependents of completed instruction.\n");
+
+ assert(!completed_inst->isSquashed());
+ // Tell the memory dependence unit to wake any dependents on this
+ // instruction if it is a memory instruction. Also complete the memory
+ // instruction at this point since we know it executed without issues.
+ // @todo: Might want to rename "completeMemInst" to something that
+ // indicates that it won't need to be replayed, and call this
+ // earlier. Might not be a big deal.
if (completed_inst->isMemRef()) {
- memDepUnit.wakeDependents(completed_inst);
+ memDepUnit[completed_inst->threadNumber].wakeDependents(completed_inst);
+ completeMemInst(completed_inst);
+ } else if (completed_inst->isMemBarrier() ||
+ completed_inst->isWriteBarrier()) {
+ memDepUnit[completed_inst->threadNumber].completeBarrier(completed_inst);
}
for (int dest_reg_idx = 0;
@@ -676,44 +898,94 @@ InstructionQueue<Impl>::wakeDependents(DynInstPtr &completed_inst)
continue;
}
- DPRINTF(IQ, "IQ: Waking any dependents on register %i.\n",
+ DPRINTF(IQ, "Waking any dependents on register %i.\n",
(int) dest_reg);
- //Maybe abstract this part into a function.
- //Go through the dependency chain, marking the registers as ready
- //within the waiting instructions.
- while (dependGraph[dest_reg].next) {
+ //Go through the dependency chain, marking the registers as
+ //ready within the waiting instructions.
+ DynInstPtr dep_inst = dependGraph.pop(dest_reg);
- curr = dependGraph[dest_reg].next;
-
- DPRINTF(IQ, "IQ: Waking up a dependent instruction, PC%#x.\n",
- curr->inst->readPC());
+ while (dep_inst) {
+ DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n",
+ dep_inst->readPC());
// Might want to give more information to the instruction
- // so that it knows which of its source registers is ready.
- // However that would mean that the dependency graph entries
- // would need to hold the src_reg_idx.
- curr->inst->markSrcRegReady();
-
- addIfReady(curr->inst);
-
- dependGraph[dest_reg].next = curr->next;
+ // so that it knows which of its source registers is
+ // ready. However that would mean that the dependency
+ // graph entries would need to hold the src_reg_idx.
+ dep_inst->markSrcRegReady();
- DependencyEntry::mem_alloc_counter--;
+ addIfReady(dep_inst);
- curr->inst = NULL;
+ dep_inst = dependGraph.pop(dest_reg);
- delete curr;
+ ++dependents;
}
- // Reset the head node now that all of its dependents have been woken
- // up.
- dependGraph[dest_reg].next = NULL;
- dependGraph[dest_reg].inst = NULL;
+ // Reset the head node now that all of its dependents have
+ // been woken up.
+ assert(dependGraph.empty(dest_reg));
+ dependGraph.clearInst(dest_reg);
// Mark the scoreboard as having that register ready.
regScoreboard[dest_reg] = true;
}
+ return dependents;
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst)
+{
+ OpClass op_class = ready_inst->opClass();
+
+ readyInsts[op_class].push(ready_inst);
+
+ // Will need to reorder the list if either a queue is not on the list,
+ // or it has an older instruction than last time.
+ if (!queueOnList[op_class]) {
+ addToOrderList(op_class);
+ } else if (readyInsts[op_class].top()->seqNum <
+ (*readyIt[op_class]).oldestInst) {
+ listOrder.erase(readyIt[op_class]);
+ addToOrderList(op_class);
+ }
+
+ DPRINTF(IQ, "Instruction is ready to issue, putting it onto "
+ "the ready list, PC %#x opclass:%i [sn:%lli].\n",
+ ready_inst->readPC(), op_class, ready_inst->seqNum);
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst)
+{
+ memDepUnit[resched_inst->threadNumber].reschedule(resched_inst);
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::replayMemInst(DynInstPtr &replay_inst)
+{
+ memDepUnit[replay_inst->threadNumber].replay(replay_inst);
+}
+
+template <class Impl>
+void
+InstructionQueue<Impl>::completeMemInst(DynInstPtr &completed_inst)
+{
+ int tid = completed_inst->threadNumber;
+
+ DPRINTF(IQ, "Completing mem instruction PC:%#x [sn:%lli]\n",
+ completed_inst->readPC(), completed_inst->seqNum);
+
+ ++freeEntries;
+
+ completed_inst->memOpDone = true;
+
+ memDepUnit[tid].completed(completed_inst);
+
+ count[tid]--;
}
template <class Impl>
@@ -721,58 +993,64 @@ void
InstructionQueue<Impl>::violation(DynInstPtr &store,
DynInstPtr &faulting_load)
{
- memDepUnit.violation(store, faulting_load);
+ memDepUnit[store->threadNumber].violation(store, faulting_load);
}
template <class Impl>
void
-InstructionQueue<Impl>::squash()
+InstructionQueue<Impl>::squash(unsigned tid)
{
- DPRINTF(IQ, "IQ: Starting to squash instructions in the IQ.\n");
+ DPRINTF(IQ, "[tid:%i]: Starting to squash instructions in "
+ "the IQ.\n", tid);
// Read instruction sequence number of last instruction out of the
// time buffer.
- squashedSeqNum = fromCommit->commitInfo.doneSeqNum;
-
- // Setup the squash iterator to point to the tail.
- squashIt = tail;
+ squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum;
// Call doSquash if there are insts in the IQ
- if (freeEntries != numEntries) {
- doSquash();
+ if (count[tid] > 0) {
+ doSquash(tid);
}
// Also tell the memory dependence unit to squash.
- memDepUnit.squash(squashedSeqNum);
+ memDepUnit[tid].squash(squashedSeqNum[tid], tid);
}
template <class Impl>
void
-InstructionQueue<Impl>::doSquash()
+InstructionQueue<Impl>::doSquash(unsigned tid)
{
- // Make sure the squash iterator isn't pointing to nothing.
- assert(squashIt != cpu->instList.end());
- // Make sure the squashed sequence number is valid.
- assert(squashedSeqNum != 0);
+ // Start at the tail.
+ ListIt squash_it = instList[tid].end();
+ --squash_it;
- DPRINTF(IQ, "IQ: Squashing instructions in the IQ.\n");
+ DPRINTF(IQ, "[tid:%i]: Squashing until sequence number %i!\n",
+ tid, squashedSeqNum[tid]);
// Squash any instructions younger than the squashed sequence number
// given.
- while ((*squashIt)->seqNum > squashedSeqNum) {
- DynInstPtr squashed_inst = (*squashIt);
+ while (squash_it != instList[tid].end() &&
+ (*squash_it)->seqNum > squashedSeqNum[tid]) {
+
+ DynInstPtr squashed_inst = (*squash_it);
// Only handle the instruction if it actually is in the IQ and
// hasn't already been squashed in the IQ.
- if (!squashed_inst->isIssued() &&
- !squashed_inst->isSquashedInIQ()) {
+ if (squashed_inst->threadNumber != tid ||
+ squashed_inst->isSquashedInIQ()) {
+ --squash_it;
+ continue;
+ }
+
+ if (!squashed_inst->isIssued() ||
+ (squashed_inst->isMemRef() &&
+ !squashed_inst->memOpDone)) {
// Remove the instruction from the dependency list.
- // Hack for now: These below don't add themselves to the
- // dependency list, so don't try to remove them.
- if (!squashed_inst->isNonSpeculative()/* &&
- !squashed_inst->isStore()*/
- ) {
+ if (!squashed_inst->isNonSpeculative() &&
+ !squashed_inst->isStoreConditional() &&
+ !squashed_inst->isMemBarrier() &&
+ !squashed_inst->isWriteBarrier()) {
for (int src_reg_idx = 0;
src_reg_idx < squashed_inst->numSrcRegs();
@@ -781,25 +1059,31 @@ InstructionQueue<Impl>::doSquash()
PhysRegIndex src_reg =
squashed_inst->renamedSrcRegIdx(src_reg_idx);
- // Only remove it from the dependency graph if it was
- // placed there in the first place.
- // HACK: This assumes that instructions woken up from the
- // dependency chain aren't informed that a specific src
- // register has become ready. This may not always be true
- // in the future.
+ // Only remove it from the dependency graph if it
+ // was placed there in the first place.
+
+ // Instead of doing a linked list traversal, we
+ // can just remove these squashed instructions
+ // either at issue time, or when the register is
+ // overwritten. The only downside to this is it
+ // leaves more room for error.
+
if (!squashed_inst->isReadySrcRegIdx(src_reg_idx) &&
src_reg < numPhysRegs) {
- dependGraph[src_reg].remove(squashed_inst);
+ dependGraph.remove(src_reg, squashed_inst);
}
+
++iqSquashedOperandsExamined;
}
-
- // Might want to remove producers as well.
} else {
- nonSpecInsts[squashed_inst->seqNum] = NULL;
+ NonSpecMapIt ns_inst_it =
+ nonSpecInsts.find(squashed_inst->seqNum);
+ assert(ns_inst_it != nonSpecInsts.end());
- nonSpecInsts.erase(squashed_inst->seqNum);
+ (*ns_inst_it).second = NULL;
+
+ nonSpecInsts.erase(ns_inst_it);
++iqSquashedNonSpecRemoved;
}
@@ -809,91 +1093,25 @@ InstructionQueue<Impl>::doSquash()
// Mark it as squashed within the IQ.
squashed_inst->setSquashedInIQ();
-// squashedInsts.push(squashed_inst);
+ // @todo: Remove this hack where several statuses are set so the
+ // inst will flow through the rest of the pipeline.
squashed_inst->setIssued();
squashed_inst->setCanCommit();
+ squashed_inst->removeInIQ();
+
+ //Update Thread IQ Count
+ count[squashed_inst->threadNumber]--;
++freeEntries;
- DPRINTF(IQ, "IQ: Instruction PC %#x squashed.\n",
- squashed_inst->readPC());
+ DPRINTF(IQ, "[tid:%i]: Instruction [sn:%lli] PC %#x "
+ "squashed.\n",
+ tid, squashed_inst->seqNum, squashed_inst->readPC());
}
- --squashIt;
+ instList[tid].erase(squash_it--);
++iqSquashedInstsExamined;
}
-
- assert(freeEntries <= numEntries);
-
- if (freeEntries == numEntries) {
- tail = cpu->instList.end();
- }
-
-}
-
-template <class Impl>
-void
-InstructionQueue<Impl>::stopSquash()
-{
- // Clear up the squash variables to ensure that squashing doesn't
- // get called improperly.
- squashedSeqNum = 0;
-
- squashIt = cpu->instList.end();
-}
-
-template <class Impl>
-void
-InstructionQueue<Impl>::DependencyEntry::insert(DynInstPtr &new_inst)
-{
- //Add this new, dependent instruction at the head of the dependency
- //chain.
-
- // First create the entry that will be added to the head of the
- // dependency chain.
- DependencyEntry *new_entry = new DependencyEntry;
- new_entry->next = this->next;
- new_entry->inst = new_inst;
-
- // Then actually add it to the chain.
- this->next = new_entry;
-
- ++mem_alloc_counter;
-}
-
-template <class Impl>
-void
-InstructionQueue<Impl>::DependencyEntry::remove(DynInstPtr &inst_to_remove)
-{
- DependencyEntry *prev = this;
- DependencyEntry *curr = this->next;
-
- // Make sure curr isn't NULL. Because this instruction is being
- // removed from a dependency list, it must have been placed there at
- // an earlier time. The dependency chain should not be empty,
- // unless the instruction dependent upon it is already ready.
- if (curr == NULL) {
- return;
- }
-
- // Find the instruction to remove within the dependency linked list.
- while(curr->inst != inst_to_remove)
- {
- prev = curr;
- curr = curr->next;
-
- assert(curr != NULL);
- }
-
- // Now remove this instruction from the list.
- prev->next = curr->next;
-
- --mem_alloc_counter;
-
- // Could push this off to the destructor of DependencyEntry
- curr->inst = NULL;
-
- delete curr;
}
template <class Impl>
@@ -920,21 +1138,21 @@ InstructionQueue<Impl>::addToDependents(DynInstPtr &new_inst)
if (src_reg >= numPhysRegs) {
continue;
} else if (regScoreboard[src_reg] == false) {
- DPRINTF(IQ, "IQ: Instruction PC %#x has src reg %i that "
+ DPRINTF(IQ, "Instruction PC %#x has src reg %i that "
"is being added to the dependency chain.\n",
new_inst->readPC(), src_reg);
- dependGraph[src_reg].insert(new_inst);
+ dependGraph.insert(src_reg, new_inst);
// Change the return value to indicate that something
// was added to the dependency graph.
return_val = true;
} else {
- DPRINTF(IQ, "IQ: Instruction PC %#x has src reg %i that "
+ DPRINTF(IQ, "Instruction PC %#x has src reg %i that "
"became ready before it reached the IQ.\n",
new_inst->readPC(), src_reg);
// Mark a register ready within the instruction.
- new_inst->markSrcRegReady();
+ new_inst->markSrcRegReady(src_reg_idx);
}
}
}
@@ -944,12 +1162,12 @@ InstructionQueue<Impl>::addToDependents(DynInstPtr &new_inst)
template <class Impl>
void
-InstructionQueue<Impl>::createDependency(DynInstPtr &new_inst)
+InstructionQueue<Impl>::addToProducers(DynInstPtr &new_inst)
{
- //Actually nothing really needs to be marked when an
- //instruction becomes the producer of a register's value,
- //but for convenience a ptr to the producing instruction will
- //be placed in the head node of the dependency links.
+ // Nothing really needs to be marked when an instruction becomes
+ // the producer of a register's value, but for convenience a ptr
+ // to the producing instruction will be placed in the head node of
+ // the dependency links.
int8_t total_dest_regs = new_inst->numDestRegs();
for (int dest_reg_idx = 0;
@@ -966,13 +1184,13 @@ InstructionQueue<Impl>::createDependency(DynInstPtr &new_inst)
continue;
}
- dependGraph[dest_reg].inst = new_inst;
-
- if (dependGraph[dest_reg].next) {
- dumpDependGraph();
- panic("IQ: Dependency graph not empty!");
+ if (!dependGraph.empty(dest_reg)) {
+ dependGraph.dump();
+ panic("Dependency graph %i not empty!", dest_reg);
}
+ dependGraph.setInst(dest_reg, new_inst);
+
// Mark the scoreboard to say it's not yet ready.
regScoreboard[dest_reg] = false;
}
@@ -982,155 +1200,204 @@ template <class Impl>
void
InstructionQueue<Impl>::addIfReady(DynInstPtr &inst)
{
- //If the instruction now has all of its source registers
+ // If the instruction now has all of its source registers
// available, then add it to the list of ready instructions.
if (inst->readyToIssue()) {
//Add the instruction to the proper ready list.
- if (inst->isControl()) {
+ if (inst->isMemRef()) {
- DPRINTF(IQ, "IQ: Branch instruction is ready to issue, "
- "putting it onto the ready list, PC %#x.\n",
- inst->readPC());
- readyBranchInsts.push(inst);
-
- } else if (inst->isMemRef()) {
-
- DPRINTF(IQ, "IQ: Checking if memory instruction can issue.\n");
+ DPRINTF(IQ, "Checking if memory instruction can issue.\n");
// Message to the mem dependence unit that this instruction has
// its registers ready.
+ memDepUnit[inst->threadNumber].regsReady(inst);
- memDepUnit.regsReady(inst);
-
-#if 0
- if (memDepUnit.readyToIssue(inst)) {
- DPRINTF(IQ, "IQ: Memory instruction is ready to issue, "
- "putting it onto the ready list, PC %#x.\n",
- inst->readPC());
- readyMemInsts.push(inst);
- } else {
- // Make dependent on the store.
- // Will need some way to get the store instruction it should
- // be dependent upon; then when the store issues it can
- // put the instruction on the ready list.
- // Yet another tree?
- assert(0 && "Instruction has no way to actually issue");
- }
-#endif
-
- } else if (inst->isInteger()) {
-
- DPRINTF(IQ, "IQ: Integer instruction is ready to issue, "
- "putting it onto the ready list, PC %#x.\n",
- inst->readPC());
- readyIntInsts.push(inst);
+ return;
+ }
- } else if (inst->isFloating()) {
+ OpClass op_class = inst->opClass();
- DPRINTF(IQ, "IQ: Floating instruction is ready to issue, "
- "putting it onto the ready list, PC %#x.\n",
- inst->readPC());
- readyFloatInsts.push(inst);
+ DPRINTF(IQ, "Instruction is ready to issue, putting it onto "
+ "the ready list, PC %#x opclass:%i [sn:%lli].\n",
+ inst->readPC(), op_class, inst->seqNum);
- } else {
- DPRINTF(IQ, "IQ: Miscellaneous instruction is ready to issue, "
- "putting it onto the ready list, PC %#x..\n",
- inst->readPC());
+ readyInsts[op_class].push(inst);
- readyMiscInsts.push(inst);
+ // Will need to reorder the list if either a queue is not on the list,
+ // or it has an older instruction than last time.
+ if (!queueOnList[op_class]) {
+ addToOrderList(op_class);
+ } else if (readyInsts[op_class].top()->seqNum <
+ (*readyIt[op_class]).oldestInst) {
+ listOrder.erase(readyIt[op_class]);
+ addToOrderList(op_class);
}
}
}
-/*
- * Caution, this function must not be called prior to tail being updated at
- * least once, otherwise it will fail the assertion. This is because
- * instList.begin() actually changes upon the insertion of an element into the
- * list when the list is empty.
- */
template <class Impl>
int
InstructionQueue<Impl>::countInsts()
{
- ListIt count_it = cpu->instList.begin();
+#if 0
+ //ksewell:This works but definitely could use a cleaner write
+ //with a more intuitive way of counting. Right now it's
+ //just brute force ....
+ // Change the #if if you want to use this method.
int total_insts = 0;
- if (tail == cpu->instList.end())
- return 0;
+ for (int i = 0; i < numThreads; ++i) {
+ ListIt count_it = instList[i].begin();
+
+ while (count_it != instList[i].end()) {
+ if (!(*count_it)->isSquashed() && !(*count_it)->isSquashedInIQ()) {
+ if (!(*count_it)->isIssued()) {
+ ++total_insts;
+ } else if ((*count_it)->isMemRef() &&
+ !(*count_it)->memOpDone) {
+ // Loads that have not been marked as executed still count
+ // towards the total instructions.
+ ++total_insts;
+ }
+ }
- while (count_it != tail) {
- if (!(*count_it)->isIssued()) {
- ++total_insts;
+ ++count_it;
}
-
- ++count_it;
-
- assert(count_it != cpu->instList.end());
- }
-
- // Need to count the tail iterator as well.
- if (count_it != cpu->instList.end() &&
- (*count_it) &&
- !(*count_it)->isIssued()) {
- ++total_insts;
}
return total_insts;
+#else
+ return numEntries - freeEntries;
+#endif
}
template <class Impl>
void
-InstructionQueue<Impl>::dumpDependGraph()
+InstructionQueue<Impl>::dumpLists()
{
- DependencyEntry *curr;
+ for (int i = 0; i < Num_OpClasses; ++i) {
+ cprintf("Ready list %i size: %i\n", i, readyInsts[i].size());
- for (int i = 0; i < numPhysRegs; ++i)
- {
- curr = &dependGraph[i];
+ cprintf("\n");
+ }
- if (curr->inst) {
- cprintf("dependGraph[%i]: producer: %#x consumer: ", i,
- curr->inst->readPC());
- } else {
- cprintf("dependGraph[%i]: No producer. consumer: ", i);
- }
+ cprintf("Non speculative list size: %i\n", nonSpecInsts.size());
- while (curr->next != NULL) {
- curr = curr->next;
+ NonSpecMapIt non_spec_it = nonSpecInsts.begin();
+ NonSpecMapIt non_spec_end_it = nonSpecInsts.end();
- cprintf("%#x ", curr->inst->readPC());
- }
+ cprintf("Non speculative list: ");
- cprintf("\n");
+ while (non_spec_it != non_spec_end_it) {
+ cprintf("%#x [sn:%lli]", (*non_spec_it).second->readPC(),
+ (*non_spec_it).second->seqNum);
+ ++non_spec_it;
+ }
+
+ cprintf("\n");
+
+ ListOrderIt list_order_it = listOrder.begin();
+ ListOrderIt list_order_end_it = listOrder.end();
+ int i = 1;
+
+ cprintf("List order: ");
+
+ while (list_order_it != list_order_end_it) {
+ cprintf("%i OpClass:%i [sn:%lli] ", i, (*list_order_it).queueType,
+ (*list_order_it).oldestInst);
+
+ ++list_order_it;
+ ++i;
}
+
+ cprintf("\n");
}
+
template <class Impl>
void
-InstructionQueue<Impl>::dumpLists()
+InstructionQueue<Impl>::dumpInsts()
{
- cprintf("Ready integer list size: %i\n", readyIntInsts.size());
+ for (int i = 0; i < numThreads; ++i) {
+ int num = 0;
+ int valid_num = 0;
+ ListIt inst_list_it = instList[i].begin();
+
+ while (inst_list_it != instList[i].end())
+ {
+ cprintf("Instruction:%i\n",
+ num);
+ if (!(*inst_list_it)->isSquashed()) {
+ if (!(*inst_list_it)->isIssued()) {
+ ++valid_num;
+ cprintf("Count:%i\n", valid_num);
+ } else if ((*inst_list_it)->isMemRef() &&
+ !(*inst_list_it)->memOpDone) {
+ // Loads that have not been marked as executed
+ // still count towards the total instructions.
+ ++valid_num;
+ cprintf("Count:%i\n", valid_num);
+ }
+ }
+
+ cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
+ "Issued:%i\nSquashed:%i\n",
+ (*inst_list_it)->readPC(),
+ (*inst_list_it)->seqNum,
+ (*inst_list_it)->threadNumber,
+ (*inst_list_it)->isIssued(),
+ (*inst_list_it)->isSquashed());
- cprintf("Ready float list size: %i\n", readyFloatInsts.size());
+ if ((*inst_list_it)->isMemRef()) {
+ cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone);
+ }
- cprintf("Ready branch list size: %i\n", readyBranchInsts.size());
+ cprintf("\n");
- cprintf("Ready misc list size: %i\n", readyMiscInsts.size());
+ inst_list_it++;
+ ++num;
+ }
+ }
- cprintf("Squashed list size: %i\n", squashedInsts.size());
+ cprintf("Insts to Execute list:\n");
- cprintf("Non speculative list size: %i\n", nonSpecInsts.size());
+ int num = 0;
+ int valid_num = 0;
+ ListIt inst_list_it = instsToExecute.begin();
- non_spec_it_t non_spec_it = nonSpecInsts.begin();
+ while (inst_list_it != instsToExecute.end())
+ {
+ cprintf("Instruction:%i\n",
+ num);
+ if (!(*inst_list_it)->isSquashed()) {
+ if (!(*inst_list_it)->isIssued()) {
+ ++valid_num;
+ cprintf("Count:%i\n", valid_num);
+ } else if ((*inst_list_it)->isMemRef() &&
+ !(*inst_list_it)->memOpDone) {
+ // Loads that have not been marked as executed
+ // still count towards the total instructions.
+ ++valid_num;
+ cprintf("Count:%i\n", valid_num);
+ }
+ }
- cprintf("Non speculative list: ");
+ cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
+ "Issued:%i\nSquashed:%i\n",
+ (*inst_list_it)->readPC(),
+ (*inst_list_it)->seqNum,
+ (*inst_list_it)->threadNumber,
+ (*inst_list_it)->isIssued(),
+ (*inst_list_it)->isSquashed());
- while (non_spec_it != nonSpecInsts.end()) {
- cprintf("%#x ", (*non_spec_it).second->readPC());
- ++non_spec_it;
- }
+ if ((*inst_list_it)->isMemRef()) {
+ cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone);
+ }
- cprintf("\n");
+ cprintf("\n");
+ inst_list_it++;
+ ++num;
+ }
}
diff --git a/src/cpu/o3/lsq.cc b/src/cpu/o3/lsq.cc
new file mode 100644
index 000000000..de0325920
--- /dev/null
+++ b/src/cpu/o3/lsq.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Korey Sewell
+ */
+
+#include "cpu/o3/alpha_dyn_inst.hh"
+#include "cpu/o3/alpha_cpu.hh"
+#include "cpu/o3/alpha_impl.hh"
+#include "cpu/o3/lsq_impl.hh"
+
+// Force the instantiation of LDSTQ for all the implementations we care about.
+template class LSQ<AlphaSimpleImpl>;
+
diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh
new file mode 100644
index 000000000..bc4154c85
--- /dev/null
+++ b/src/cpu/o3/lsq.hh
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Korey Sewell
+ */
+
+#ifndef __CPU_O3_LSQ_HH__
+#define __CPU_O3_LSQ_HH__
+
+#include <map>
+#include <queue>
+
+#include "config/full_system.hh"
+#include "cpu/inst_seq.hh"
+#include "cpu/o3/lsq_unit.hh"
+#include "mem/port.hh"
+#include "sim/sim_object.hh"
+
+template <class Impl>
+class LSQ {
+ public:
+ typedef typename Impl::Params Params;
+ typedef typename Impl::FullCPU FullCPU;
+ typedef typename Impl::DynInstPtr DynInstPtr;
+ typedef typename Impl::CPUPol::IEW IEW;
+ typedef typename Impl::CPUPol::LSQUnit LSQUnit;
+
+ /** SMT policy. */
+ enum LSQPolicy {
+ Dynamic,
+ Partitioned,
+ Threshold
+ };
+
+ /** Constructs an LSQ with the given parameters. */
+ LSQ(Params *params);
+
+ /** Returns the name of the LSQ. */
+ std::string name() const;
+
+ /** Sets the pointer to the list of active threads. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
+ /** Sets the CPU pointer. */
+ void setCPU(FullCPU *cpu_ptr);
+ /** Sets the IEW stage pointer. */
+ void setIEW(IEW *iew_ptr);
+ /** Switches out the LSQ. */
+ void switchOut();
+ /** Takes over execution from another CPU's thread. */
+ void takeOverFrom();
+
+ /** Number of entries needed for the given amount of threads.*/
+ int entryAmount(int num_threads);
+ void removeEntries(unsigned tid);
+ /** Reset the max entries for each thread. */
+ void resetEntries();
+ /** Resize the max entries for a thread. */
+ void resizeEntries(unsigned size, unsigned tid);
+
+ /** Ticks the LSQ. */
+ void tick();
+ /** Ticks a specific LSQ Unit. */
+ void tick(unsigned tid)
+ { thread[tid].tick(); }
+
+ /** Inserts a load into the LSQ. */
+ void insertLoad(DynInstPtr &load_inst);
+ /** Inserts a store into the LSQ. */
+ void insertStore(DynInstPtr &store_inst);
+
+ /** Executes a load. */
+ Fault executeLoad(DynInstPtr &inst);
+
+ /** Executes a store. */
+ Fault executeStore(DynInstPtr &inst);
+
+ /**
+ * Commits loads up until the given sequence number for a specific thread.
+ */
+ void commitLoads(InstSeqNum &youngest_inst, unsigned tid)
+ { thread[tid].commitLoads(youngest_inst); }
+
+ /**
+ * Commits stores up until the given sequence number for a specific thread.
+ */
+ void commitStores(InstSeqNum &youngest_inst, unsigned tid)
+ { thread[tid].commitStores(youngest_inst); }
+
+ /**
+ * Attempts to write back stores until all cache ports are used or the
+ * interface becomes blocked.
+ */
+ void writebackStores();
+ /** Same as above, but only for one thread. */
+ void writebackStores(unsigned tid);
+
+ /**
+ * Squash instructions from a thread until the specified sequence number.
+ */
+ void squash(const InstSeqNum &squashed_num, unsigned tid)
+ { thread[tid].squash(squashed_num); }
+
+ /** Returns whether or not there was a memory ordering violation. */
+ bool violation();
+ /**
+ * Returns whether or not there was a memory ordering violation for a
+ * specific thread.
+ */
+ bool violation(unsigned tid)
+ { return thread[tid].violation(); }
+
+ /** Returns if a load is blocked due to the memory system for a specific
+ * thread.
+ */
+ bool loadBlocked(unsigned tid)
+ { return thread[tid].loadBlocked(); }
+
+ bool isLoadBlockedHandled(unsigned tid)
+ { return thread[tid].isLoadBlockedHandled(); }
+
+ void setLoadBlockedHandled(unsigned tid)
+ { thread[tid].setLoadBlockedHandled(); }
+
+ /** Gets the instruction that caused the memory ordering violation. */
+ DynInstPtr getMemDepViolator(unsigned tid)
+ { return thread[tid].getMemDepViolator(); }
+
+ /** Returns the head index of the load queue for a specific thread. */
+ int getLoadHead(unsigned tid)
+ { return thread[tid].getLoadHead(); }
+
+ /** Returns the sequence number of the head of the load queue. */
+ InstSeqNum getLoadHeadSeqNum(unsigned tid)
+ {
+ return thread[tid].getLoadHeadSeqNum();
+ }
+
+ /** Returns the head index of the store queue. */
+ int getStoreHead(unsigned tid)
+ { return thread[tid].getStoreHead(); }
+
+ /** Returns the sequence number of the head of the store queue. */
+ InstSeqNum getStoreHeadSeqNum(unsigned tid)
+ {
+ return thread[tid].getStoreHeadSeqNum();
+ }
+
+ /** Returns the number of instructions in all of the queues. */
+ int getCount();
+ /** Returns the number of instructions in the queues of one thread. */
+ int getCount(unsigned tid)
+ { return thread[tid].getCount(); }
+
+ /** Returns the total number of loads in the load queue. */
+ int numLoads();
+ /** Returns the total number of loads for a single thread. */
+ int numLoads(unsigned tid)
+ { return thread[tid].numLoads(); }
+
+ /** Returns the total number of stores in the store queue. */
+ int numStores();
+ /** Returns the total number of stores for a single thread. */
+ int numStores(unsigned tid)
+ { return thread[tid].numStores(); }
+
+ /** Returns the total number of loads that are ready. */
+ int numLoadsReady();
+ /** Returns the number of loads that are ready for a single thread. */
+ int numLoadsReady(unsigned tid)
+ { return thread[tid].numLoadsReady(); }
+
+ /** Returns the number of free entries. */
+ unsigned numFreeEntries();
+ /** Returns the number of free entries for a specific thread. */
+ unsigned numFreeEntries(unsigned tid);
+
+ /** Returns if the LSQ is full (either LQ or SQ is full). */
+ bool isFull();
+ /**
+ * Returns if the LSQ is full for a specific thread (either LQ or SQ is
+ * full).
+ */
+ bool isFull(unsigned tid);
+
+ /** Returns if any of the LQs are full. */
+ bool lqFull();
+ /** Returns if the LQ of a given thread is full. */
+ bool lqFull(unsigned tid);
+
+ /** Returns if any of the SQs are full. */
+ bool sqFull();
+ /** Returns if the SQ of a given thread is full. */
+ bool sqFull(unsigned tid);
+
+ /**
+ * Returns if the LSQ is stalled due to a memory operation that must be
+ * replayed.
+ */
+ bool isStalled();
+ /**
+ * Returns if the LSQ of a specific thread is stalled due to a memory
+ * operation that must be replayed.
+ */
+ bool isStalled(unsigned tid);
+
+ /** Returns whether or not there are any stores to write back to memory. */
+ bool hasStoresToWB();
+
+ /** Returns whether or not a specific thread has any stores to write back
+ * to memory.
+ */
+ bool hasStoresToWB(unsigned tid)
+ { return thread[tid].hasStoresToWB(); }
+
+ /** Returns the number of stores a specific thread has to write back. */
+ int numStoresToWB(unsigned tid)
+ { return thread[tid].numStoresToWB(); }
+
+ /** Returns if the LSQ will write back to memory this cycle. */
+ bool willWB();
+ /** Returns if the LSQ of a specific thread will write back to memory this
+ * cycle.
+ */
+ bool willWB(unsigned tid)
+ { return thread[tid].willWB(); }
+
+ /** Debugging function to print out all instructions. */
+ void dumpInsts();
+ /** Debugging function to print out instructions from a specific thread. */
+ void dumpInsts(unsigned tid)
+ { thread[tid].dumpInsts(); }
+
+ /** Executes a read operation, using the load specified at the load index. */
+ template <class T>
+ Fault read(RequestPtr req, T &data, int load_idx);
+
+ /** Executes a store operation, using the store specified at the store
+ * index.
+ */
+ template <class T>
+ Fault write(RequestPtr req, T &data, int store_idx);
+
+ private:
+ /** The LSQ policy for SMT mode. */
+ LSQPolicy lsqPolicy;
+
+ /** The LSQ units for individual threads. */
+ LSQUnit thread[Impl::MaxThreads];
+
+ /** The CPU pointer. */
+ FullCPU *cpu;
+
+ /** The IEW stage pointer. */
+ IEW *iewStage;
+
+ /** List of Active Threads in System. */
+ std::list<unsigned> *activeThreads;
+
+ /** Total Size of LQ Entries. */
+ unsigned LQEntries;
+ /** Total Size of SQ Entries. */
+ unsigned SQEntries;
+
+ /** Max LQ Size - Used to Enforce Sharing Policies. */
+ unsigned maxLQEntries;
+
+ /** Max SQ Size - Used to Enforce Sharing Policies. */
+ unsigned maxSQEntries;
+
+ /** Number of Threads. */
+ unsigned numThreads;
+};
+
+template <class Impl>
+template <class T>
+Fault
+LSQ<Impl>::read(RequestPtr req, T &data, int load_idx)
+{
+ unsigned tid = req->getThreadNum();
+
+ return thread[tid].read(req, data, load_idx);
+}
+
+template <class Impl>
+template <class T>
+Fault
+LSQ<Impl>::write(RequestPtr req, T &data, int store_idx)
+{
+ unsigned tid = req->getThreadNum();
+
+ return thread[tid].write(req, data, store_idx);
+}
+
+#endif // __CPU_O3_LSQ_HH__
diff --git a/src/cpu/o3/lsq_impl.hh b/src/cpu/o3/lsq_impl.hh
new file mode 100644
index 000000000..27aa0dc3c
--- /dev/null
+++ b/src/cpu/o3/lsq_impl.hh
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Korey Sewell
+ */
+
+#include <algorithm>
+#include <string>
+
+#include "cpu/o3/lsq.hh"
+
+using namespace std;
+
+template <class Impl>
+LSQ<Impl>::LSQ(Params *params)
+ : LQEntries(params->LQEntries), SQEntries(params->SQEntries),
+ numThreads(params->numberOfThreads)
+{
+ DPRINTF(LSQ, "Creating LSQ object.\n");
+
+ //**********************************************/
+ //************ Handle SMT Parameters ***********/
+ //**********************************************/
+ string policy = params->smtLSQPolicy;
+
+ //Convert string to lowercase
+ std::transform(policy.begin(), policy.end(), policy.begin(),
+ (int(*)(int)) tolower);
+
+ //Figure out fetch policy
+ if (policy == "dynamic") {
+ lsqPolicy = Dynamic;
+
+ maxLQEntries = LQEntries;
+ maxSQEntries = SQEntries;
+
+ DPRINTF(LSQ, "LSQ sharing policy set to Dynamic\n");
+
+ } else if (policy == "partitioned") {
+ lsqPolicy = Partitioned;
+
+ //@todo:make work if part_amt doesnt divide evenly.
+ maxLQEntries = LQEntries / numThreads;
+ maxSQEntries = SQEntries / numThreads;
+
+ DPRINTF(Fetch, "LSQ sharing policy set to Partitioned: "
+ "%i entries per LQ | %i entries per SQ",
+ maxLQEntries,maxSQEntries);
+
+ } else if (policy == "threshold") {
+ lsqPolicy = Threshold;
+
+ assert(params->smtLSQThreshold > LQEntries);
+ assert(params->smtLSQThreshold > SQEntries);
+
+ //Divide up by threshold amount
+ //@todo: Should threads check the max and the total
+ //amount of the LSQ
+ maxLQEntries = params->smtLSQThreshold;
+ maxSQEntries = params->smtLSQThreshold;
+
+ DPRINTF(LSQ, "LSQ sharing policy set to Threshold: "
+ "%i entries per LQ | %i entries per SQ",
+ maxLQEntries,maxSQEntries);
+
+ } else {
+ assert(0 && "Invalid LSQ Sharing Policy.Options Are:{Dynamic,"
+ "Partitioned, Threshold}");
+ }
+
+ //Initialize LSQs
+ for (int tid=0; tid < numThreads; tid++) {
+ thread[tid].init(params, maxLQEntries, maxSQEntries, tid);
+ }
+}
+
+
+template<class Impl>
+std::string
+LSQ<Impl>::name() const
+{
+ return iewStage->name() + ".lsq";
+}
+
+template<class Impl>
+void
+LSQ<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ activeThreads = at_ptr;
+ assert(activeThreads != 0);
+}
+
+template<class Impl>
+void
+LSQ<Impl>::setCPU(FullCPU *cpu_ptr)
+{
+ cpu = cpu_ptr;
+
+ for (int tid=0; tid < numThreads; tid++) {
+ thread[tid].setCPU(cpu_ptr);
+ }
+}
+
+template<class Impl>
+void
+LSQ<Impl>::setIEW(IEW *iew_ptr)
+{
+ iewStage = iew_ptr;
+
+ for (int tid=0; tid < numThreads; tid++) {
+ thread[tid].setIEW(iew_ptr);
+ }
+}
+
+template <class Impl>
+void
+LSQ<Impl>::switchOut()
+{
+ for (int tid = 0; tid < numThreads; tid++) {
+ thread[tid].switchOut();
+ }
+}
+
+template <class Impl>
+void
+LSQ<Impl>::takeOverFrom()
+{
+ for (int tid = 0; tid < numThreads; tid++) {
+ thread[tid].takeOverFrom();
+ }
+}
+
+template <class Impl>
+int
+LSQ<Impl>::entryAmount(int num_threads)
+{
+ if (lsqPolicy == Partitioned) {
+ return LQEntries / num_threads;
+ } else {
+ return 0;
+ }
+}
+
+template <class Impl>
+void
+LSQ<Impl>::resetEntries()
+{
+ if (lsqPolicy != Dynamic || numThreads > 1) {
+ int active_threads = (*activeThreads).size();
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+ list<unsigned>::iterator list_end = (*activeThreads).end();
+
+ int maxEntries;
+
+ if (lsqPolicy == Partitioned) {
+ maxEntries = LQEntries / active_threads;
+ } else if (lsqPolicy == Threshold && active_threads == 1) {
+ maxEntries = LQEntries;
+ } else {
+ maxEntries = LQEntries;
+ }
+
+ while (threads != list_end) {
+ resizeEntries(maxEntries,*threads++);
+ }
+ }
+}
+
+template<class Impl>
+void
+LSQ<Impl>::removeEntries(unsigned tid)
+{
+ thread[tid].clearLQ();
+ thread[tid].clearSQ();
+}
+
+template<class Impl>
+void
+LSQ<Impl>::resizeEntries(unsigned size,unsigned tid)
+{
+ thread[tid].resizeLQ(size);
+ thread[tid].resizeSQ(size);
+}
+
+template<class Impl>
+void
+LSQ<Impl>::tick()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+
+ thread[tid].tick();
+ }
+}
+
+template<class Impl>
+void
+LSQ<Impl>::insertLoad(DynInstPtr &load_inst)
+{
+ unsigned tid = load_inst->threadNumber;
+
+ thread[tid].insertLoad(load_inst);
+}
+
+template<class Impl>
+void
+LSQ<Impl>::insertStore(DynInstPtr &store_inst)
+{
+ unsigned tid = store_inst->threadNumber;
+
+ thread[tid].insertStore(store_inst);
+}
+
+template<class Impl>
+Fault
+LSQ<Impl>::executeLoad(DynInstPtr &inst)
+{
+ unsigned tid = inst->threadNumber;
+
+ return thread[tid].executeLoad(inst);
+}
+
+template<class Impl>
+Fault
+LSQ<Impl>::executeStore(DynInstPtr &inst)
+{
+ unsigned tid = inst->threadNumber;
+
+ return thread[tid].executeStore(inst);
+}
+
+template<class Impl>
+void
+LSQ<Impl>::writebackStores()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+
+ if (numStoresToWB(tid) > 0) {
+ DPRINTF(Writeback,"[tid:%i] Writing back stores. %i stores "
+ "available for Writeback.\n", tid, numStoresToWB(tid));
+ }
+
+ thread[tid].writebackStores();
+ }
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::violation()
+{
+ /* Answers: Does Anybody Have a Violation?*/
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (thread[tid].violation())
+ return true;
+ }
+
+ return false;
+}
+
+template<class Impl>
+int
+LSQ<Impl>::getCount()
+{
+ unsigned total = 0;
+
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ total += getCount(tid);
+ }
+
+ return total;
+}
+
+template<class Impl>
+int
+LSQ<Impl>::numLoads()
+{
+ unsigned total = 0;
+
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ total += numLoads(tid);
+ }
+
+ return total;
+}
+
+template<class Impl>
+int
+LSQ<Impl>::numStores()
+{
+ unsigned total = 0;
+
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ total += thread[tid].numStores();
+ }
+
+ return total;
+}
+
+template<class Impl>
+int
+LSQ<Impl>::numLoadsReady()
+{
+ unsigned total = 0;
+
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ total += thread[tid].numLoadsReady();
+ }
+
+ return total;
+}
+
+template<class Impl>
+unsigned
+LSQ<Impl>::numFreeEntries()
+{
+ unsigned total = 0;
+
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ total += thread[tid].numFreeEntries();
+ }
+
+ return total;
+}
+
+template<class Impl>
+unsigned
+LSQ<Impl>::numFreeEntries(unsigned tid)
+{
+ //if( lsqPolicy == Dynamic )
+ //return numFreeEntries();
+ //else
+ return thread[tid].numFreeEntries();
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::isFull()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (! (thread[tid].lqFull() || thread[tid].sqFull()) )
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::isFull(unsigned tid)
+{
+ //@todo: Change to Calculate All Entries for
+ //Dynamic Policy
+ if( lsqPolicy == Dynamic )
+ return isFull();
+ else
+ return thread[tid].lqFull() || thread[tid].sqFull();
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::lqFull()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (!thread[tid].lqFull())
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::lqFull(unsigned tid)
+{
+ //@todo: Change to Calculate All Entries for
+ //Dynamic Policy
+ if( lsqPolicy == Dynamic )
+ return lqFull();
+ else
+ return thread[tid].lqFull();
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::sqFull()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (!sqFull(tid))
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::sqFull(unsigned tid)
+{
+ //@todo: Change to Calculate All Entries for
+ //Dynamic Policy
+ if( lsqPolicy == Dynamic )
+ return sqFull();
+ else
+ return thread[tid].sqFull();
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::isStalled()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (!thread[tid].isStalled())
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::isStalled(unsigned tid)
+{
+ if( lsqPolicy == Dynamic )
+ return isStalled();
+ else
+ return thread[tid].isStalled();
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::hasStoresToWB()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (!hasStoresToWB(tid))
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+bool
+LSQ<Impl>::willWB()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ if (!willWB(tid))
+ return false;
+ }
+
+ return true;
+}
+
+template<class Impl>
+void
+LSQ<Impl>::dumpInsts()
+{
+ list<unsigned>::iterator active_threads = (*activeThreads).begin();
+
+ while (active_threads != (*activeThreads).end()) {
+ unsigned tid = *active_threads++;
+ thread[tid].dumpInsts();
+ }
+}
diff --git a/src/cpu/o3/lsq_unit.cc b/src/cpu/o3/lsq_unit.cc
new file mode 100644
index 000000000..e935ffa5c
--- /dev/null
+++ b/src/cpu/o3/lsq_unit.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ * Korey Sewell
+ */
+
+#include "cpu/o3/alpha_dyn_inst.hh"
+#include "cpu/o3/alpha_cpu.hh"
+#include "cpu/o3/alpha_impl.hh"
+#include "cpu/o3/lsq_unit_impl.hh"
+
+// Force the instantiation of LDSTQ for all the implementations we care about.
+template class LSQUnit<AlphaSimpleImpl>;
+
diff --git a/src/cpu/o3/lsq_unit.hh b/src/cpu/o3/lsq_unit.hh
new file mode 100644
index 000000000..ce0cdd36f
--- /dev/null
+++ b/src/cpu/o3/lsq_unit.hh
@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ * Korey Sewell
+ */
+
+#ifndef __CPU_O3_LSQ_UNIT_HH__
+#define __CPU_O3_LSQ_UNIT_HH__
+
+#include <algorithm>
+#include <map>
+#include <queue>
+
+#include "arch/faults.hh"
+#include "config/full_system.hh"
+#include "base/hashmap.hh"
+#include "cpu/inst_seq.hh"
+#include "mem/packet.hh"
+#include "mem/port.hh"
+
+/**
+ * Class that implements the actual LQ and SQ for each specific
+ * thread. Both are circular queues; load entries are freed upon
+ * committing, while store entries are freed once they writeback. The
+ * LSQUnit tracks if there are memory ordering violations, and also
+ * detects partial load to store forwarding cases (a store only has
+ * part of a load's data) that requires the load to wait until the
+ * store writes back. In the former case it holds onto the instruction
+ * until the dependence unit looks at it, and in the latter it stalls
+ * the LSQ until the store writes back. At that point the load is
+ * replayed.
+ */
+template <class Impl>
+class LSQUnit {
+ protected:
+ typedef TheISA::IntReg IntReg;
+ public:
+ typedef typename Impl::Params Params;
+ typedef typename Impl::FullCPU FullCPU;
+ typedef typename Impl::DynInstPtr DynInstPtr;
+ typedef typename Impl::CPUPol::IEW IEW;
+ typedef typename Impl::CPUPol::IssueStruct IssueStruct;
+
+ public:
+ /** Constructs an LSQ unit. init() must be called prior to use. */
+ LSQUnit();
+
+ /** Initializes the LSQ unit with the specified number of entries. */
+ void init(Params *params, unsigned maxLQEntries,
+ unsigned maxSQEntries, unsigned id);
+
+ /** Returns the name of the LSQ unit. */
+ std::string name() const;
+
+ /** Sets the CPU pointer. */
+ void setCPU(FullCPU *cpu_ptr);
+
+ /** Sets the IEW stage pointer. */
+ void setIEW(IEW *iew_ptr)
+ { iewStage = iew_ptr; }
+
+ /** Switches out LSQ unit. */
+ void switchOut();
+
+ /** 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
+ * be shared by all LSQ units.
+ */
+ void tick() { usedPorts = 0; }
+
+ /** Inserts an instruction. */
+ void insert(DynInstPtr &inst);
+ /** Inserts a load instruction. */
+ void insertLoad(DynInstPtr &load_inst);
+ /** Inserts a store instruction. */
+ void insertStore(DynInstPtr &store_inst);
+
+ /** Executes a load instruction. */
+ Fault executeLoad(DynInstPtr &inst);
+
+ Fault executeLoad(int lq_idx) { panic("Not implemented"); return NoFault; }
+ /** Executes a store instruction. */
+ Fault executeStore(DynInstPtr &inst);
+
+ /** Commits the head load. */
+ void commitLoad();
+ /** Commits loads older than a specific sequence number. */
+ void commitLoads(InstSeqNum &youngest_inst);
+
+ /** Commits stores older than a specific sequence number. */
+ void commitStores(InstSeqNum &youngest_inst);
+
+ /** Writes back stores. */
+ void writebackStores();
+
+ void completeDataAccess(PacketPtr pkt);
+
+ // @todo: Include stats in the LSQ unit.
+ //void regStats();
+
+ /** Clears all the entries in the LQ. */
+ void clearLQ();
+
+ /** Clears all the entries in the SQ. */
+ void clearSQ();
+
+ /** Resizes the LQ to a given size. */
+ void resizeLQ(unsigned size);
+
+ /** Resizes the SQ to a given size. */
+ void resizeSQ(unsigned size);
+
+ /** Squashes all instructions younger than a specific sequence number. */
+ void squash(const InstSeqNum &squashed_num);
+
+ /** Returns if there is a memory ordering violation. Value is reset upon
+ * call to getMemDepViolator().
+ */
+ bool violation() { return memDepViolator; }
+
+ /** Returns the memory ordering violator. */
+ DynInstPtr getMemDepViolator();
+
+ /** Returns if a load became blocked due to the memory system. */
+ bool loadBlocked()
+ { return isLoadBlocked; }
+
+ /** Clears the signal that a load became blocked. */
+ void clearLoadBlocked()
+ { isLoadBlocked = false; }
+
+ /** Returns if the blocked load was handled. */
+ bool isLoadBlockedHandled()
+ { return loadBlockedHandled; }
+
+ /** Records the blocked load as being handled. */
+ void setLoadBlockedHandled()
+ { loadBlockedHandled = true; }
+
+ /** Returns the number of free entries (min of free LQ and SQ entries). */
+ unsigned numFreeEntries();
+
+ /** Returns the number of loads ready to execute. */
+ int numLoadsReady();
+
+ /** Returns the number of loads in the LQ. */
+ int numLoads() { return loads; }
+
+ /** Returns the number of stores in the SQ. */
+ int numStores() { return stores; }
+
+ /** Returns if either the LQ or SQ is full. */
+ bool isFull() { return lqFull() || sqFull(); }
+
+ /** 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 the number of instructions in the LSQ. */
+ unsigned getCount() { return loads + stores; }
+
+ /** Returns if there are any stores to writeback. */
+ bool hasStoresToWB() { return storesToWB; }
+
+ /** Returns the number of stores to writeback. */
+ int numStoresToWB() { return storesToWB; }
+
+ /** Returns if the LSQ unit will writeback on this cycle. */
+ bool willWB() { return storeQueue[storeWBIdx].canWB &&
+ !storeQueue[storeWBIdx].completed &&
+ !isStoreBlocked; }
+
+ private:
+ /** Writes back the instruction, sending it to IEW. */
+ void writeback(DynInstPtr &inst, PacketPtr pkt);
+
+ /** Handles completing the send of a store to memory. */
+ void storePostSend(Packet *pkt);
+
+ /** Completes the store at the specified index. */
+ void completeStore(int store_idx);
+
+ /** Handles doing the retry. */
+ void recvRetry();
+
+ /** Increments the given store index (circular queue). */
+ inline void incrStIdx(int &store_idx);
+ /** Decrements the given store index (circular queue). */
+ inline void decrStIdx(int &store_idx);
+ /** Increments the given load index (circular queue). */
+ inline void incrLdIdx(int &load_idx);
+ /** Decrements the given load index (circular queue). */
+ inline void decrLdIdx(int &load_idx);
+
+ public:
+ /** Debugging function to dump instructions in the LSQ. */
+ void dumpInsts();
+
+ private:
+ /** Pointer to the CPU. */
+ FullCPU *cpu;
+
+ /** Pointer to the IEW stage. */
+ IEW *iewStage;
+
+ /** Pointer to memory object. */
+ MemObject *mem;
+
+ /** DcachePort class for this LSQ Unit. Handles doing the
+ * communication with the cache/memory.
+ * @todo: Needs to be moved to the LSQ level and have some sort
+ * of arbitration.
+ */
+ class DcachePort : public Port
+ {
+ protected:
+ /** Pointer to CPU. */
+ FullCPU *cpu;
+ /** Pointer to LSQ. */
+ LSQUnit *lsq;
+
+ public:
+ /** Default constructor. */
+ DcachePort(FullCPU *_cpu, LSQUnit *_lsq)
+ : Port(_lsq->name() + "-dport"), cpu(_cpu), lsq(_lsq)
+ { }
+
+ protected:
+ /** Atomic version of receive. Panics. */
+ virtual Tick recvAtomic(PacketPtr pkt);
+
+ /** Functional version of receive. Panics. */
+ virtual void recvFunctional(PacketPtr pkt);
+
+ /** Receives status change. Other than range changing, panics. */
+ virtual void recvStatusChange(Status status);
+
+ /** Returns the address ranges of this device. */
+ virtual void getDeviceAddressRanges(AddrRangeList &resp,
+ AddrRangeList &snoop)
+ { resp.clear(); snoop.clear(); }
+
+ /** Timing version of receive. Handles writing back and
+ * completing the load or store that has returned from
+ * memory. */
+ virtual bool recvTiming(PacketPtr pkt);
+
+ /** Handles doing a retry of the previous send. */
+ virtual void recvRetry();
+ };
+
+ /** Pointer to the D-cache. */
+ DcachePort *dcachePort;
+
+ /** Derived class to hold any sender state the LSQ needs. */
+ class LSQSenderState : public Packet::SenderState
+ {
+ public:
+ /** Default constructor. */
+ LSQSenderState()
+ : noWB(false)
+ { }
+
+ /** Instruction who initiated the access to memory. */
+ DynInstPtr inst;
+ /** Whether or not it is a load. */
+ bool isLoad;
+ /** The LQ/SQ index of the instruction. */
+ int idx;
+ /** Whether or not the instruction will need to writeback. */
+ bool noWB;
+ };
+
+ /** Writeback event, specifically for when stores forward data to loads. */
+ class WritebackEvent : public Event {
+ public:
+ /** Constructs a writeback event. */
+ WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, LSQUnit *lsq_ptr);
+
+ /** Processes the writeback event. */
+ void process();
+
+ /** Returns the description of this event. */
+ const char *description();
+
+ private:
+ /** Instruction whose results are being written back. */
+ DynInstPtr inst;
+
+ /** The packet that would have been sent to memory. */
+ PacketPtr pkt;
+
+ /** The pointer to the LSQ unit that issued the store. */
+ LSQUnit<Impl> *lsqPtr;
+ };
+
+ public:
+ struct SQEntry {
+ /** Constructs an empty store queue entry. */
+ SQEntry()
+ : inst(NULL), req(NULL), size(0), data(0),
+ canWB(0), committed(0), completed(0)
+ { }
+
+ /** Constructs a store queue entry for a given instruction. */
+ SQEntry(DynInstPtr &_inst)
+ : inst(_inst), req(NULL), size(0), data(0),
+ canWB(0), committed(0), completed(0)
+ { }
+
+ /** The store instruction. */
+ DynInstPtr inst;
+ /** The request for the store. */
+ RequestPtr req;
+ /** The size of the store. */
+ int size;
+ /** The store data. */
+ IntReg data;
+ /** Whether or not the store can writeback. */
+ bool canWB;
+ /** Whether or not the store is committed. */
+ bool committed;
+ /** Whether or not the store is completed. */
+ bool completed;
+ };
+
+ private:
+ /** The LSQUnit thread id. */
+ unsigned lsqID;
+
+ /** The store queue. */
+ std::vector<SQEntry> storeQueue;
+
+ /** The load queue. */
+ std::vector<DynInstPtr> loadQueue;
+
+ /** The number of LQ entries, plus a sentinel entry (circular queue).
+ * @todo: Consider having var that records the true number of LQ entries.
+ */
+ unsigned LQEntries;
+ /** The number of SQ entries, plus a sentinel entry (circular queue).
+ * @todo: Consider having var that records the true number of SQ entries.
+ */
+ unsigned SQEntries;
+
+ /** The number of load instructions in the LQ. */
+ int loads;
+ /** The number of store instructions in the SQ. */
+ int stores;
+ /** The number of store instructions in the SQ waiting to writeback. */
+ int storesToWB;
+
+ /** The index of the head instruction in the LQ. */
+ int loadHead;
+ /** The index of the tail instruction in the LQ. */
+ int loadTail;
+
+ /** The index of the head instruction in the SQ. */
+ int storeHead;
+ /** The index of the first instruction that may be ready to be
+ * written back, and has not yet been written back.
+ */
+ int storeWBIdx;
+ /** The index of the tail instruction in the SQ. */
+ int storeTail;
+
+ /// @todo Consider moving to a more advanced model with write vs read ports
+ /** The number of cache ports available each cycle. */
+ int cachePorts;
+
+ /** The number of used cache ports in this cycle. */
+ int usedPorts;
+
+ /** Is the LSQ switched out. */
+ bool switchedOut;
+
+ //list<InstSeqNum> mshrSeqNums;
+
+ /** Wire to read information from the issue stage time queue. */
+ typename TimeBuffer<IssueStruct>::wire fromIssue;
+
+ /** Whether or not the LSQ is stalled. */
+ bool stalled;
+ /** The store that causes the stall due to partial store to load
+ * forwarding.
+ */
+ InstSeqNum stallingStoreIsn;
+ /** The index of the above store. */
+ int stallingLoadIdx;
+
+ /** The packet that needs to be retried. */
+ PacketPtr retryPkt;
+
+ /** Whehter or not a store is blocked due to the memory system. */
+ bool isStoreBlocked;
+
+ /** Whether or not a load is blocked due to the memory system. */
+ bool isLoadBlocked;
+
+ /** Has the blocked load been handled. */
+ bool loadBlockedHandled;
+
+ /** The sequence number of the blocked load. */
+ InstSeqNum blockedLoadSeqNum;
+
+ /** The oldest load that caused a memory ordering violation. */
+ DynInstPtr memDepViolator;
+
+ // Will also need how many read/write ports the Dcache has. Or keep track
+ // of that in stage that is one level up, and only call executeLoad/Store
+ // the appropriate number of times.
+/*
+ // total number of loads forwaded from LSQ stores
+ Stats::Vector<> lsq_forw_loads;
+
+ // total number of loads ignored due to invalid addresses
+ Stats::Vector<> inv_addr_loads;
+
+ // total number of software prefetches ignored due to invalid addresses
+ Stats::Vector<> inv_addr_swpfs;
+
+ // total non-speculative bogus addresses seen (debug var)
+ Counter sim_invalid_addrs;
+ Stats::Vector<> fu_busy; //cumulative fu busy
+
+ // ready loads blocked due to memory disambiguation
+ Stats::Vector<> lsq_blocked_loads;
+
+ Stats::Scalar<> lsqInversion;
+*/
+ public:
+ /** Executes the load at the given index. */
+ template <class T>
+ Fault read(Request *req, T &data, int load_idx);
+
+ /** Executes the store at the given index. */
+ template <class T>
+ Fault write(Request *req, T &data, int store_idx);
+
+ /** Returns the index of the head load instruction. */
+ int getLoadHead() { return loadHead; }
+ /** Returns the sequence number of the head load instruction. */
+ InstSeqNum getLoadHeadSeqNum()
+ {
+ if (loadQueue[loadHead]) {
+ return loadQueue[loadHead]->seqNum;
+ } else {
+ return 0;
+ }
+
+ }
+
+ /** Returns the index of the head store instruction. */
+ int getStoreHead() { return storeHead; }
+ /** Returns the sequence number of the head store instruction. */
+ InstSeqNum getStoreHeadSeqNum()
+ {
+ if (storeQueue[storeHead].inst) {
+ return storeQueue[storeHead].inst->seqNum;
+ } else {
+ return 0;
+ }
+
+ }
+
+ /** Returns whether or not the LSQ unit is stalled. */
+ bool isStalled() { return stalled; }
+};
+
+template <class Impl>
+template <class T>
+Fault
+LSQUnit<Impl>::read(Request *req, T &data, int load_idx)
+{
+ DynInstPtr load_inst = loadQueue[load_idx];
+
+ assert(load_inst);
+
+ assert(!load_inst->isExecuted());
+
+ // Make sure this isn't an uncacheable access
+ // A bit of a hackish way to get uncached accesses to work only if they're
+ // at the head of the LSQ and are ready to commit (at the head of the ROB
+ // too).
+ if (req->getFlags() & UNCACHEABLE &&
+ (load_idx != loadHead || !load_inst->reachedCommit)) {
+ iewStage->rescheduleMemInst(load_inst);
+ return TheISA::genMachineCheckFault();
+ }
+
+ // Check the SQ for any previous stores that might lead to forwarding
+ int store_idx = load_inst->sqIdx;
+
+ int store_size = 0;
+
+ DPRINTF(LSQUnit, "Read called, load idx: %i, store idx: %i, "
+ "storeHead: %i addr: %#x\n",
+ load_idx, store_idx, storeHead, req->getPaddr());
+
+#if FULL_SYSTEM
+ if (req->getFlags() & LOCKED) {
+ cpu->lockAddr = req->getPaddr();
+ cpu->lockFlag = true;
+ }
+#endif
+
+ while (store_idx != -1) {
+ // End once we've reached the top of the LSQ
+ if (store_idx == storeWBIdx) {
+ break;
+ }
+
+ // Move the index to one younger
+ if (--store_idx < 0)
+ store_idx += SQEntries;
+
+ assert(storeQueue[store_idx].inst);
+
+ store_size = storeQueue[store_idx].size;
+
+ if (store_size == 0)
+ continue;
+
+ // Check if the store data is within the lower and upper bounds of
+ // addresses that the request needs.
+ bool store_has_lower_limit =
+ req->getVaddr() >= storeQueue[store_idx].inst->effAddr;
+ bool store_has_upper_limit =
+ (req->getVaddr() + req->getSize()) <=
+ (storeQueue[store_idx].inst->effAddr + store_size);
+ bool lower_load_has_store_part =
+ req->getVaddr() < (storeQueue[store_idx].inst->effAddr +
+ store_size);
+ bool upper_load_has_store_part =
+ (req->getVaddr() + req->getSize()) >
+ storeQueue[store_idx].inst->effAddr;
+
+ // If the store's data has all of the data needed, we can forward.
+ if (store_has_lower_limit && store_has_upper_limit) {
+ // Get shift amount for offset into the store's data.
+ int shift_amt = req->getVaddr() & (store_size - 1);
+ // @todo: Magic number, assumes byte addressing
+ shift_amt = shift_amt << 3;
+
+ // Cast this to type T?
+ data = storeQueue[store_idx].data >> shift_amt;
+
+ assert(!load_inst->memData);
+ load_inst->memData = new uint8_t[64];
+
+ memcpy(load_inst->memData, &data, req->getSize());
+
+ DPRINTF(LSQUnit, "Forwarding from store idx %i to load to "
+ "addr %#x, data %#x\n",
+ store_idx, req->getVaddr(), data);
+
+ PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast);
+ data_pkt->dataStatic(load_inst->memData);
+
+ WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, this);
+
+ // We'll say this has a 1 cycle load-store forwarding latency
+ // for now.
+ // @todo: Need to make this a parameter.
+ wb->schedule(curTick);
+
+ // Should keep track of stat for forwarded data
+ return NoFault;
+ } else if ((store_has_lower_limit && lower_load_has_store_part) ||
+ (store_has_upper_limit && upper_load_has_store_part) ||
+ (lower_load_has_store_part && upper_load_has_store_part)) {
+ // This is the partial store-load forwarding case where a store
+ // has only part of the load's data.
+
+ // If it's already been written back, then don't worry about
+ // stalling on it.
+ if (storeQueue[store_idx].completed) {
+ continue;
+ }
+
+ // Must stall load and force it to retry, so long as it's the oldest
+ // load that needs to do so.
+ if (!stalled ||
+ (stalled &&
+ load_inst->seqNum <
+ loadQueue[stallingLoadIdx]->seqNum)) {
+ stalled = true;
+ stallingStoreIsn = storeQueue[store_idx].inst->seqNum;
+ stallingLoadIdx = load_idx;
+ }
+
+ // Tell IQ/mem dep unit that this instruction will need to be
+ // rescheduled eventually
+ iewStage->rescheduleMemInst(load_inst);
+
+ // Do not generate a writeback event as this instruction is not
+ // complete.
+ DPRINTF(LSQUnit, "Load-store forwarding mis-match. "
+ "Store idx %i to load addr %#x\n",
+ store_idx, req->getVaddr());
+
+ return NoFault;
+ }
+ }
+
+ // If there's no forwarding case, then go access memory
+ DPRINTF(LSQUnit, "Doing functional access for inst [sn:%lli] PC %#x\n",
+ load_inst->seqNum, load_inst->readPC());
+
+ assert(!load_inst->memData);
+ load_inst->memData = new uint8_t[64];
+
+ ++usedPorts;
+
+ DPRINTF(LSQUnit, "Doing timing access for inst PC %#x\n",
+ load_inst->readPC());
+
+ PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast);
+ data_pkt->dataStatic(load_inst->memData);
+
+ LSQSenderState *state = new LSQSenderState;
+ state->isLoad = true;
+ state->idx = load_idx;
+ state->inst = load_inst;
+ data_pkt->senderState = state;
+
+ // if we have a cache, do cache access too
+ if (!dcachePort->sendTiming(data_pkt)) {
+ // There's an older load that's already going to squash.
+ if (isLoadBlocked && blockedLoadSeqNum < load_inst->seqNum)
+ return NoFault;
+
+ // Record that the load was blocked due to memory. This
+ // load will squash all instructions after it, be
+ // refetched, and re-executed.
+ isLoadBlocked = true;
+ loadBlockedHandled = false;
+ blockedLoadSeqNum = load_inst->seqNum;
+ // No fault occurred, even though the interface is blocked.
+ return NoFault;
+ }
+
+ if (data_pkt->result != Packet::Success) {
+ DPRINTF(LSQUnit, "LSQUnit: D-cache miss!\n");
+ DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n",
+ load_inst->seqNum);
+ } else {
+ DPRINTF(LSQUnit, "LSQUnit: D-cache hit!\n");
+ DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n",
+ load_inst->seqNum);
+ }
+
+ return NoFault;
+}
+
+template <class Impl>
+template <class T>
+Fault
+LSQUnit<Impl>::write(Request *req, T &data, int store_idx)
+{
+ assert(storeQueue[store_idx].inst);
+
+ DPRINTF(LSQUnit, "Doing write to store idx %i, addr %#x data %#x"
+ " | storeHead:%i [sn:%i]\n",
+ store_idx, req->getPaddr(), data, storeHead,
+ storeQueue[store_idx].inst->seqNum);
+
+ storeQueue[store_idx].req = req;
+ storeQueue[store_idx].size = sizeof(T);
+ storeQueue[store_idx].data = data;
+
+ // This function only writes the data to the store queue, so no fault
+ // can happen here.
+ return NoFault;
+}
+
+#endif // __CPU_O3_LSQ_UNIT_HH__
diff --git a/src/cpu/o3/lsq_unit_impl.hh b/src/cpu/o3/lsq_unit_impl.hh
new file mode 100644
index 000000000..6f32ec304
--- /dev/null
+++ b/src/cpu/o3/lsq_unit_impl.hh
@@ -0,0 +1,929 @@
+/*
+ * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ * Korey Sewell
+ */
+
+#include "cpu/checker/cpu.hh"
+#include "cpu/o3/lsq_unit.hh"
+#include "base/str.hh"
+#include "mem/request.hh"
+
+template<class Impl>
+LSQUnit<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt,
+ LSQUnit *lsq_ptr)
+ : Event(&mainEventQueue), inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr)
+{
+ this->setFlags(Event::AutoDelete);
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::WritebackEvent::process()
+{
+ if (!lsqPtr->isSwitchedOut()) {
+ lsqPtr->writeback(inst, pkt);
+ }
+ delete pkt;
+}
+
+template<class Impl>
+const char *
+LSQUnit<Impl>::WritebackEvent::description()
+{
+ return "Store writeback event";
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::completeDataAccess(PacketPtr pkt)
+{
+ LSQSenderState *state = dynamic_cast<LSQSenderState *>(pkt->senderState);
+ DynInstPtr inst = state->inst;
+ DPRINTF(IEW, "Writeback event [sn:%lli]\n", inst->seqNum);
+ DPRINTF(Activity, "Activity: Writeback event [sn:%lli]\n", inst->seqNum);
+
+ //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum);
+
+ if (isSwitchedOut() || inst->isSquashed()) {
+ delete state;
+ delete pkt;
+ return;
+ } else {
+ if (!state->noWB) {
+ writeback(inst, pkt);
+ }
+
+ if (inst->isStore()) {
+ completeStore(state->idx);
+ }
+ }
+
+ delete state;
+ delete pkt;
+}
+
+template <class Impl>
+Tick
+LSQUnit<Impl>::DcachePort::recvAtomic(PacketPtr pkt)
+{
+ panic("O3CPU model does not work with atomic mode!");
+ return curTick;
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::DcachePort::recvFunctional(PacketPtr pkt)
+{
+ panic("O3CPU doesn't expect recvFunctional callback!");
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::DcachePort::recvStatusChange(Status status)
+{
+ if (status == RangeChange)
+ return;
+
+ panic("O3CPU doesn't expect recvStatusChange callback!");
+}
+
+template <class Impl>
+bool
+LSQUnit<Impl>::DcachePort::recvTiming(PacketPtr pkt)
+{
+ lsq->completeDataAccess(pkt);
+ return true;
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::DcachePort::recvRetry()
+{
+ lsq->recvRetry();
+}
+
+template <class Impl>
+LSQUnit<Impl>::LSQUnit()
+ : loads(0), stores(0), storesToWB(0), stalled(false),
+ isStoreBlocked(false), isLoadBlocked(false),
+ loadBlockedHandled(false)
+{
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::init(Params *params, unsigned maxLQEntries,
+ unsigned maxSQEntries, unsigned id)
+{
+ DPRINTF(LSQUnit, "Creating LSQUnit%i object.\n",id);
+
+ switchedOut = false;
+
+ lsqID = id;
+
+ // Add 1 for the sentinel entry (they are circular queues).
+ LQEntries = maxLQEntries + 1;
+ SQEntries = maxSQEntries + 1;
+
+ loadQueue.resize(LQEntries);
+ storeQueue.resize(SQEntries);
+
+ loadHead = loadTail = 0;
+
+ storeHead = storeWBIdx = storeTail = 0;
+
+ usedPorts = 0;
+ cachePorts = params->cachePorts;
+
+ mem = params->mem;
+
+ memDepViolator = NULL;
+
+ blockedLoadSeqNum = 0;
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::setCPU(FullCPU *cpu_ptr)
+{
+ cpu = cpu_ptr;
+ dcachePort = new DcachePort(cpu, this);
+
+ Port *mem_dport = mem->getPort("");
+ dcachePort->setPeer(mem_dport);
+ mem_dport->setPeer(dcachePort);
+
+ if (cpu->checker) {
+ cpu->checker->setDcachePort(dcachePort);
+ }
+}
+
+template<class Impl>
+std::string
+LSQUnit<Impl>::name() const
+{
+ if (Impl::MaxThreads == 1) {
+ return iewStage->name() + ".lsq";
+ } else {
+ return iewStage->name() + ".lsq.thread." + to_string(lsqID);
+ }
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::clearLQ()
+{
+ loadQueue.clear();
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::clearSQ()
+{
+ storeQueue.clear();
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::switchOut()
+{
+ switchedOut = true;
+ for (int i = 0; i < loadQueue.size(); ++i)
+ loadQueue[i] = NULL;
+
+ assert(storesToWB == 0);
+}
+
+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;
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::resizeLQ(unsigned size)
+{
+ unsigned size_plus_sentinel = size + 1;
+ assert(size_plus_sentinel >= LQEntries);
+
+ if (size_plus_sentinel > LQEntries) {
+ while (size_plus_sentinel > loadQueue.size()) {
+ DynInstPtr dummy;
+ loadQueue.push_back(dummy);
+ LQEntries++;
+ }
+ } else {
+ LQEntries = size_plus_sentinel;
+ }
+
+}
+
+template<class Impl>
+void
+LSQUnit<Impl>::resizeSQ(unsigned size)
+{
+ unsigned size_plus_sentinel = size + 1;
+ if (size_plus_sentinel > SQEntries) {
+ while (size_plus_sentinel > storeQueue.size()) {
+ SQEntry dummy;
+ storeQueue.push_back(dummy);
+ SQEntries++;
+ }
+ } else {
+ SQEntries = size_plus_sentinel;
+ }
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::insert(DynInstPtr &inst)
+{
+ assert(inst->isMemRef());
+
+ assert(inst->isLoad() || inst->isStore());
+
+ if (inst->isLoad()) {
+ insertLoad(inst);
+ } else {
+ insertStore(inst);
+ }
+
+ inst->setInLSQ();
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::insertLoad(DynInstPtr &load_inst)
+{
+ assert((loadTail + 1) % LQEntries != loadHead);
+ assert(loads < LQEntries);
+
+ DPRINTF(LSQUnit, "Inserting load PC %#x, idx:%i [sn:%lli]\n",
+ load_inst->readPC(), loadTail, load_inst->seqNum);
+
+ load_inst->lqIdx = loadTail;
+
+ if (stores == 0) {
+ load_inst->sqIdx = -1;
+ } else {
+ load_inst->sqIdx = storeTail;
+ }
+
+ loadQueue[loadTail] = load_inst;
+
+ incrLdIdx(loadTail);
+
+ ++loads;
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::insertStore(DynInstPtr &store_inst)
+{
+ // Make sure it is not full before inserting an instruction.
+ assert((storeTail + 1) % SQEntries != storeHead);
+ assert(stores < SQEntries);
+
+ DPRINTF(LSQUnit, "Inserting store PC %#x, idx:%i [sn:%lli]\n",
+ store_inst->readPC(), storeTail, store_inst->seqNum);
+
+ store_inst->sqIdx = storeTail;
+ store_inst->lqIdx = loadTail;
+
+ storeQueue[storeTail] = SQEntry(store_inst);
+
+ incrStIdx(storeTail);
+
+ ++stores;
+}
+
+template <class Impl>
+typename Impl::DynInstPtr
+LSQUnit<Impl>::getMemDepViolator()
+{
+ DynInstPtr temp = memDepViolator;
+
+ memDepViolator = NULL;
+
+ return temp;
+}
+
+template <class Impl>
+unsigned
+LSQUnit<Impl>::numFreeEntries()
+{
+ unsigned free_lq_entries = LQEntries - loads;
+ unsigned free_sq_entries = SQEntries - stores;
+
+ // Both the LQ and SQ entries have an extra dummy entry to differentiate
+ // empty/full conditions. Subtract 1 from the free entries.
+ if (free_lq_entries < free_sq_entries) {
+ return free_lq_entries - 1;
+ } else {
+ return free_sq_entries - 1;
+ }
+}
+
+template <class Impl>
+int
+LSQUnit<Impl>::numLoadsReady()
+{
+ int load_idx = loadHead;
+ int retval = 0;
+
+ while (load_idx != loadTail) {
+ assert(loadQueue[load_idx]);
+
+ if (loadQueue[load_idx]->readyToIssue()) {
+ ++retval;
+ }
+ }
+
+ return retval;
+}
+
+template <class Impl>
+Fault
+LSQUnit<Impl>::executeLoad(DynInstPtr &inst)
+{
+ // Execute a specific load.
+ Fault load_fault = NoFault;
+
+ DPRINTF(LSQUnit, "Executing load PC %#x, [sn:%lli]\n",
+ inst->readPC(),inst->seqNum);
+
+ load_fault = inst->initiateAcc();
+
+ // If the instruction faulted, then we need to send it along to commit
+ // without the instruction completing.
+ if (load_fault != NoFault) {
+ // Send this instruction to commit, also make sure iew stage
+ // realizes there is activity.
+ iewStage->instToCommit(inst);
+ iewStage->activityThisCycle();
+ }
+
+ return load_fault;
+}
+
+template <class Impl>
+Fault
+LSQUnit<Impl>::executeStore(DynInstPtr &store_inst)
+{
+ using namespace TheISA;
+ // Make sure that a store exists.
+ assert(stores != 0);
+
+ int store_idx = store_inst->sqIdx;
+
+ DPRINTF(LSQUnit, "Executing store PC %#x [sn:%lli]\n",
+ store_inst->readPC(), store_inst->seqNum);
+
+ // Check the recently completed loads to see if any match this store's
+ // address. If so, then we have a memory ordering violation.
+ int load_idx = store_inst->lqIdx;
+
+ Fault store_fault = store_inst->initiateAcc();
+
+ if (storeQueue[store_idx].size == 0) {
+ DPRINTF(LSQUnit,"Fault on Store PC %#x, [sn:%lli],Size = 0\n",
+ store_inst->readPC(),store_inst->seqNum);
+
+ return store_fault;
+ }
+
+ assert(store_fault == NoFault);
+
+ if (store_inst->isStoreConditional()) {
+ // Store conditionals need to set themselves as able to
+ // writeback if we haven't had a fault by here.
+ storeQueue[store_idx].canWB = true;
+
+ ++storesToWB;
+ }
+
+ if (!memDepViolator) {
+ while (load_idx != loadTail) {
+ // Really only need to check loads that have actually executed
+ // It's safe to check all loads because effAddr is set to
+ // InvalAddr when the dyn inst is created.
+
+ // @todo: For now this is extra conservative, detecting a
+ // violation if the addresses match assuming all accesses
+ // are quad word accesses.
+
+ // @todo: Fix this, magic number being used here
+ if ((loadQueue[load_idx]->effAddr >> 8) ==
+ (store_inst->effAddr >> 8)) {
+ // A load incorrectly passed this store. Squash and refetch.
+ // For now return a fault to show that it was unsuccessful.
+ memDepViolator = loadQueue[load_idx];
+
+ return genMachineCheckFault();
+ }
+
+ incrLdIdx(load_idx);
+ }
+
+ // If we've reached this point, there was no violation.
+ memDepViolator = NULL;
+ }
+
+ return store_fault;
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::commitLoad()
+{
+ assert(loadQueue[loadHead]);
+
+ DPRINTF(LSQUnit, "Committing head load instruction, PC %#x\n",
+ loadQueue[loadHead]->readPC());
+
+ loadQueue[loadHead] = NULL;
+
+ incrLdIdx(loadHead);
+
+ --loads;
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::commitLoads(InstSeqNum &youngest_inst)
+{
+ assert(loads == 0 || loadQueue[loadHead]);
+
+ while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) {
+ commitLoad();
+ }
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::commitStores(InstSeqNum &youngest_inst)
+{
+ assert(stores == 0 || storeQueue[storeHead].inst);
+
+ int store_idx = storeHead;
+
+ while (store_idx != storeTail) {
+ assert(storeQueue[store_idx].inst);
+ // Mark any stores that are now committed and have not yet
+ // been marked as able to write back.
+ if (!storeQueue[store_idx].canWB) {
+ if (storeQueue[store_idx].inst->seqNum > youngest_inst) {
+ break;
+ }
+ DPRINTF(LSQUnit, "Marking store as able to write back, PC "
+ "%#x [sn:%lli]\n",
+ storeQueue[store_idx].inst->readPC(),
+ storeQueue[store_idx].inst->seqNum);
+
+ storeQueue[store_idx].canWB = true;
+
+ ++storesToWB;
+ }
+
+ incrStIdx(store_idx);
+ }
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::writebackStores()
+{
+ while (storesToWB > 0 &&
+ storeWBIdx != storeTail &&
+ storeQueue[storeWBIdx].inst &&
+ storeQueue[storeWBIdx].canWB &&
+ usedPorts < cachePorts) {
+
+ if (isStoreBlocked) {
+ DPRINTF(LSQUnit, "Unable to write back any more stores, cache"
+ " is blocked!\n");
+ break;
+ }
+
+ // Store didn't write any data so no need to write it back to
+ // memory.
+ if (storeQueue[storeWBIdx].size == 0) {
+ completeStore(storeWBIdx);
+
+ incrStIdx(storeWBIdx);
+
+ continue;
+ }
+
+ ++usedPorts;
+
+ if (storeQueue[storeWBIdx].inst->isDataPrefetch()) {
+ incrStIdx(storeWBIdx);
+
+ continue;
+ }
+
+ assert(storeQueue[storeWBIdx].req);
+ assert(!storeQueue[storeWBIdx].committed);
+
+ DynInstPtr inst = storeQueue[storeWBIdx].inst;
+
+ Request *req = storeQueue[storeWBIdx].req;
+ storeQueue[storeWBIdx].committed = true;
+
+ assert(!inst->memData);
+ inst->memData = new uint8_t[64];
+ memcpy(inst->memData, (uint8_t *)&storeQueue[storeWBIdx].data,
+ req->getSize());
+
+ PacketPtr data_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast);
+ data_pkt->dataStatic(inst->memData);
+
+ LSQSenderState *state = new LSQSenderState;
+ state->isLoad = false;
+ state->idx = storeWBIdx;
+ state->inst = inst;
+ data_pkt->senderState = state;
+
+ DPRINTF(LSQUnit, "D-Cache: Writing back store idx:%i PC:%#x "
+ "to Addr:%#x, data:%#x [sn:%lli]\n",
+ storeWBIdx, storeQueue[storeWBIdx].inst->readPC(),
+ req->getPaddr(), *(inst->memData),
+ storeQueue[storeWBIdx].inst->seqNum);
+
+ // @todo: Remove this SC hack once the memory system handles it.
+ if (req->getFlags() & LOCKED) {
+ if (req->getFlags() & UNCACHEABLE) {
+ req->setScResult(2);
+ } else {
+ if (cpu->lockFlag) {
+ req->setScResult(1);
+ } else {
+ req->setScResult(0);
+ // Hack: Instantly complete this store.
+ completeDataAccess(data_pkt);
+ incrStIdx(storeWBIdx);
+ continue;
+ }
+ }
+ } else {
+ // Non-store conditionals do not need a writeback.
+ state->noWB = true;
+ }
+
+ if (!dcachePort->sendTiming(data_pkt)) {
+ // Need to handle becoming blocked on a store.
+ isStoreBlocked = true;
+
+ assert(retryPkt == NULL);
+ retryPkt = data_pkt;
+ } else {
+ storePostSend(data_pkt);
+ }
+ }
+
+ // Not sure this should set it to 0.
+ usedPorts = 0;
+
+ assert(stores >= 0 && storesToWB >= 0);
+}
+
+/*template <class Impl>
+void
+LSQUnit<Impl>::removeMSHR(InstSeqNum seqNum)
+{
+ list<InstSeqNum>::iterator mshr_it = find(mshrSeqNums.begin(),
+ mshrSeqNums.end(),
+ seqNum);
+
+ if (mshr_it != mshrSeqNums.end()) {
+ mshrSeqNums.erase(mshr_it);
+ DPRINTF(LSQUnit, "Removing MSHR. count = %i\n",mshrSeqNums.size());
+ }
+}*/
+
+template <class Impl>
+void
+LSQUnit<Impl>::squash(const InstSeqNum &squashed_num)
+{
+ DPRINTF(LSQUnit, "Squashing until [sn:%lli]!"
+ "(Loads:%i Stores:%i)\n", squashed_num, loads, stores);
+
+ int load_idx = loadTail;
+ decrLdIdx(load_idx);
+
+ while (loads != 0 && loadQueue[load_idx]->seqNum > squashed_num) {
+ DPRINTF(LSQUnit,"Load Instruction PC %#x squashed, "
+ "[sn:%lli]\n",
+ loadQueue[load_idx]->readPC(),
+ loadQueue[load_idx]->seqNum);
+
+ if (isStalled() && load_idx == stallingLoadIdx) {
+ stalled = false;
+ stallingStoreIsn = 0;
+ stallingLoadIdx = 0;
+ }
+
+ // Clear the smart pointer to make sure it is decremented.
+ loadQueue[load_idx]->squashed = true;
+ loadQueue[load_idx] = NULL;
+ --loads;
+
+ // Inefficient!
+ loadTail = load_idx;
+
+ decrLdIdx(load_idx);
+ }
+
+ if (isLoadBlocked) {
+ if (squashed_num < blockedLoadSeqNum) {
+ isLoadBlocked = false;
+ loadBlockedHandled = false;
+ blockedLoadSeqNum = 0;
+ }
+ }
+
+ int store_idx = storeTail;
+ decrStIdx(store_idx);
+
+ while (stores != 0 &&
+ storeQueue[store_idx].inst->seqNum > squashed_num) {
+ // Instructions marked as can WB are already committed.
+ if (storeQueue[store_idx].canWB) {
+ break;
+ }
+
+ DPRINTF(LSQUnit,"Store Instruction PC %#x squashed, "
+ "idx:%i [sn:%lli]\n",
+ storeQueue[store_idx].inst->readPC(),
+ store_idx, storeQueue[store_idx].inst->seqNum);
+
+ // I don't think this can happen. It should have been cleared
+ // by the stalling load.
+ if (isStalled() &&
+ storeQueue[store_idx].inst->seqNum == stallingStoreIsn) {
+ panic("Is stalled should have been cleared by stalling load!\n");
+ stalled = false;
+ stallingStoreIsn = 0;
+ }
+
+ // Clear the smart pointer to make sure it is decremented.
+ storeQueue[store_idx].inst->squashed = true;
+ storeQueue[store_idx].inst = NULL;
+ storeQueue[store_idx].canWB = 0;
+
+ storeQueue[store_idx].req = NULL;
+ --stores;
+
+ // Inefficient!
+ storeTail = store_idx;
+
+ decrStIdx(store_idx);
+ }
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::storePostSend(Packet *pkt)
+{
+ if (isStalled() &&
+ storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) {
+ DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] "
+ "load idx:%i\n",
+ stallingStoreIsn, stallingLoadIdx);
+ stalled = false;
+ stallingStoreIsn = 0;
+ iewStage->replayMemInst(loadQueue[stallingLoadIdx]);
+ }
+
+ if (!storeQueue[storeWBIdx].inst->isStoreConditional()) {
+ // The store is basically completed at this time. This
+ // only works so long as the checker doesn't try to
+ // verify the value in memory for stores.
+ storeQueue[storeWBIdx].inst->setCompleted();
+ if (cpu->checker) {
+ cpu->checker->tick(storeQueue[storeWBIdx].inst);
+ }
+ }
+
+ if (pkt->result != Packet::Success) {
+ DPRINTF(LSQUnit,"D-Cache Write Miss on idx:%i!\n",
+ storeWBIdx);
+
+ DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n",
+ storeQueue[storeWBIdx].inst->seqNum);
+
+ //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum);
+
+ //DPRINTF(LSQUnit, "Added MSHR. count = %i\n",mshrSeqNums.size());
+
+ // @todo: Increment stat here.
+ } else {
+ DPRINTF(LSQUnit,"D-Cache: Write Hit on idx:%i !\n",
+ storeWBIdx);
+
+ DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n",
+ storeQueue[storeWBIdx].inst->seqNum);
+ }
+
+ incrStIdx(storeWBIdx);
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::writeback(DynInstPtr &inst, PacketPtr pkt)
+{
+ iewStage->wakeCPU();
+
+ // Squashed instructions do not need to complete their access.
+ if (inst->isSquashed()) {
+ assert(!inst->isStore());
+ return;
+ }
+
+ if (!inst->isExecuted()) {
+ inst->setExecuted();
+
+ // Complete access to copy data to proper place.
+ inst->completeAcc(pkt);
+ }
+
+ // Need to insert instruction into queue to commit
+ iewStage->instToCommit(inst);
+
+ iewStage->activityThisCycle();
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::completeStore(int store_idx)
+{
+ assert(storeQueue[store_idx].inst);
+ storeQueue[store_idx].completed = true;
+ --storesToWB;
+ // A bit conservative because a store completion may not free up entries,
+ // but hopefully avoids two store completions in one cycle from making
+ // the CPU tick twice.
+ cpu->activityThisCycle();
+
+ if (store_idx == storeHead) {
+ do {
+ incrStIdx(storeHead);
+
+ --stores;
+ } while (storeQueue[storeHead].completed &&
+ storeHead != storeTail);
+
+ iewStage->updateLSQNextCycle = true;
+ }
+
+ DPRINTF(LSQUnit, "Completing store [sn:%lli], idx:%i, store head "
+ "idx:%i\n",
+ storeQueue[store_idx].inst->seqNum, store_idx, storeHead);
+
+ if (isStalled() &&
+ storeQueue[store_idx].inst->seqNum == stallingStoreIsn) {
+ DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] "
+ "load idx:%i\n",
+ stallingStoreIsn, stallingLoadIdx);
+ stalled = false;
+ stallingStoreIsn = 0;
+ iewStage->replayMemInst(loadQueue[stallingLoadIdx]);
+ }
+
+ storeQueue[store_idx].inst->setCompleted();
+
+ // Tell the checker we've completed this instruction. Some stores
+ // may get reported twice to the checker, but the checker can
+ // handle that case.
+ if (cpu->checker) {
+ cpu->checker->tick(storeQueue[store_idx].inst);
+ }
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::recvRetry()
+{
+ if (isStoreBlocked) {
+ assert(retryPkt != NULL);
+
+ if (dcachePort->sendTiming(retryPkt)) {
+ storePostSend(retryPkt);
+ retryPkt = NULL;
+ isStoreBlocked = false;
+ } else {
+ // Still blocked!
+ }
+ } else if (isLoadBlocked) {
+ DPRINTF(LSQUnit, "Loads squash themselves and all younger insts, "
+ "no need to resend packet.\n");
+ } else {
+ DPRINTF(LSQUnit, "Retry received but LSQ is no longer blocked.\n");
+ }
+}
+
+template <class Impl>
+inline void
+LSQUnit<Impl>::incrStIdx(int &store_idx)
+{
+ if (++store_idx >= SQEntries)
+ store_idx = 0;
+}
+
+template <class Impl>
+inline void
+LSQUnit<Impl>::decrStIdx(int &store_idx)
+{
+ if (--store_idx < 0)
+ store_idx += SQEntries;
+}
+
+template <class Impl>
+inline void
+LSQUnit<Impl>::incrLdIdx(int &load_idx)
+{
+ if (++load_idx >= LQEntries)
+ load_idx = 0;
+}
+
+template <class Impl>
+inline void
+LSQUnit<Impl>::decrLdIdx(int &load_idx)
+{
+ if (--load_idx < 0)
+ load_idx += LQEntries;
+}
+
+template <class Impl>
+void
+LSQUnit<Impl>::dumpInsts()
+{
+ cprintf("Load store queue: Dumping instructions.\n");
+ cprintf("Load queue size: %i\n", loads);
+ cprintf("Load queue: ");
+
+ int load_idx = loadHead;
+
+ while (load_idx != loadTail && loadQueue[load_idx]) {
+ cprintf("%#x ", loadQueue[load_idx]->readPC());
+
+ incrLdIdx(load_idx);
+ }
+
+ cprintf("Store queue size: %i\n", stores);
+ cprintf("Store queue: ");
+
+ int store_idx = storeHead;
+
+ while (store_idx != storeTail && storeQueue[store_idx].inst) {
+ cprintf("%#x ", storeQueue[store_idx].inst->readPC());
+
+ incrStIdx(store_idx);
+ }
+
+ cprintf("\n");
+}
diff --git a/src/cpu/o3/mem_dep_unit.cc b/src/cpu/o3/mem_dep_unit.cc
index 9c1e7f9d8..a95103266 100644
--- a/src/cpu/o3/mem_dep_unit.cc
+++ b/src/cpu/o3/mem_dep_unit.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
@@ -34,3 +36,15 @@
// Force instantation of memory dependency unit using store sets and
// AlphaSimpleImpl.
template class MemDepUnit<StoreSet, AlphaSimpleImpl>;
+
+#ifdef DEBUG
+template <>
+int
+MemDepUnit<StoreSet, AlphaSimpleImpl>::MemDepEntry::memdep_count = 0;
+template <>
+int
+MemDepUnit<StoreSet, AlphaSimpleImpl>::MemDepEntry::memdep_insert = 0;
+template <>
+int
+MemDepUnit<StoreSet, AlphaSimpleImpl>::MemDepEntry::memdep_erase = 0;
+#endif
diff --git a/src/cpu/o3/mem_dep_unit.hh b/src/cpu/o3/mem_dep_unit.hh
index ca63577a1..e399f0133 100644
--- a/src/cpu/o3/mem_dep_unit.hh
+++ b/src/cpu/o3/mem_dep_unit.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,17 +24,33 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_MEM_DEP_UNIT_HH__
-#define __CPU_O3_CPU_MEM_DEP_UNIT_HH__
+#ifndef __CPU_O3_MEM_DEP_UNIT_HH__
+#define __CPU_O3_MEM_DEP_UNIT_HH__
-#include <map>
+#include <list>
#include <set>
+#include "base/hashmap.hh"
+#include "base/refcnt.hh"
#include "base/statistics.hh"
#include "cpu/inst_seq.hh"
+struct SNHash {
+ size_t operator() (const InstSeqNum &seq_num) const {
+ unsigned a = (unsigned)seq_num;
+ unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF;
+
+ return hash;
+ }
+};
+
+template <class Impl>
+class InstructionQueue;
+
/**
* Memory dependency unit class. This holds the memory dependence predictor.
* As memory operations are issued to the IQ, they are also issued to this
@@ -52,101 +68,166 @@ class MemDepUnit {
typedef typename Impl::Params Params;
typedef typename Impl::DynInstPtr DynInstPtr;
- public:
- MemDepUnit(Params &params);
+ /** Empty constructor. Must call init() prior to using in this case. */
+ MemDepUnit() {}
+
+ /** Constructs a MemDepUnit with given parameters. */
+ MemDepUnit(Params *params);
+
+ /** Frees up any memory allocated. */
+ ~MemDepUnit();
+
+ /** Returns the name of the memory dependence unit. */
+ std::string name() const;
+
+ /** Initializes the unit with parameters and a thread id. */
+ void init(Params *params, int tid);
+ /** Registers statistics. */
void regStats();
+ /** Switches out the memory dependence predictor. */
+ void switchOut();
+
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
+
+ /** Sets the pointer to the IQ. */
+ void setIQ(InstructionQueue<Impl> *iq_ptr);
+
+ /** Inserts a memory instruction. */
void insert(DynInstPtr &inst);
+ /** Inserts a non-speculative memory instruction. */
void insertNonSpec(DynInstPtr &inst);
- // Will want to make this operation relatively fast. Right now it
- // is somewhat slow.
- DynInstPtr &top();
-
- void pop();
+ /** Inserts a barrier instruction. */
+ void insertBarrier(DynInstPtr &barr_inst);
+ /** Indicate that an instruction has its registers ready. */
void regsReady(DynInstPtr &inst);
+ /** Indicate that a non-speculative instruction is ready. */
void nonSpecInstReady(DynInstPtr &inst);
- void issue(DynInstPtr &inst);
+ /** Reschedules an instruction to be re-executed. */
+ void reschedule(DynInstPtr &inst);
+
+ /** Replays all instructions that have been rescheduled by moving them to
+ * the ready list.
+ */
+ void replay(DynInstPtr &inst);
+
+ /** Completes a memory instruction. */
+ void completed(DynInstPtr &inst);
+
+ /** Completes a barrier instruction. */
+ void completeBarrier(DynInstPtr &inst);
+ /** Wakes any dependents of a memory instruction. */
void wakeDependents(DynInstPtr &inst);
- void squash(const InstSeqNum &squashed_num);
+ /** Squashes all instructions up until a given sequence number for a
+ * specific thread.
+ */
+ void squash(const InstSeqNum &squashed_num, unsigned tid);
+ /** Indicates an ordering violation between a store and a younger load. */
void violation(DynInstPtr &store_inst, DynInstPtr &violating_load);
- inline bool empty()
- { return readyInsts.empty(); }
+ /** Issues the given instruction */
+ void issue(DynInstPtr &inst);
+
+ /** Debugging function to dump the lists of instructions. */
+ void dumpLists();
private:
- typedef typename std::set<InstSeqNum>::iterator sn_it_t;
- typedef typename std::map<InstSeqNum, DynInstPtr>::iterator dyn_it_t;
-
- // Forward declarations so that the following two typedefs work.
- class Dependency;
- class ltDependency;
-
- typedef typename std::set<Dependency, ltDependency>::iterator dep_it_t;
- typedef typename std::map<InstSeqNum, vector<dep_it_t> >::iterator
- sd_it_t;
-
- struct Dependency {
- Dependency(const InstSeqNum &_seqNum)
- : seqNum(_seqNum), regsReady(0), memDepReady(0)
- { }
-
- Dependency(const InstSeqNum &_seqNum, bool _regsReady,
- bool _memDepReady)
- : seqNum(_seqNum), regsReady(_regsReady),
- memDepReady(_memDepReady)
- { }
-
- InstSeqNum seqNum;
- mutable bool regsReady;
- mutable bool memDepReady;
- mutable sd_it_t storeDep;
- };
+ typedef typename std::list<DynInstPtr>::iterator ListIt;
+
+ class MemDepEntry;
- struct ltDependency {
- bool operator() (const Dependency &lhs, const Dependency &rhs)
+ typedef RefCountingPtr<MemDepEntry> MemDepEntryPtr;
+
+ /** Memory dependence entries that track memory operations, marking
+ * when the instruction is ready to execute and what instructions depend
+ * upon it.
+ */
+ class MemDepEntry : public RefCounted {
+ public:
+ /** Constructs a memory dependence entry. */
+ MemDepEntry(DynInstPtr &new_inst)
+ : inst(new_inst), regsReady(false), memDepReady(false),
+ completed(false), squashed(false)
{
- return lhs.seqNum < rhs.seqNum;
+#ifdef DEBUG
+ ++memdep_count;
+
+ DPRINTF(MemDepUnit, "Memory dependency entry created. "
+ "memdep_count=%i\n", memdep_count);
+#endif
}
+
+ /** Frees any pointers. */
+ ~MemDepEntry()
+ {
+ for (int i = 0; i < dependInsts.size(); ++i) {
+ dependInsts[i] = NULL;
+ }
+#ifdef DEBUG
+ --memdep_count;
+
+ DPRINTF(MemDepUnit, "Memory dependency entry deleted. "
+ "memdep_count=%i\n", memdep_count);
+#endif
+ }
+
+ /** Returns the name of the memory dependence entry. */
+ std::string name() const { return "memdepentry"; }
+
+ /** The instruction being tracked. */
+ DynInstPtr inst;
+
+ /** The iterator to the instruction's location inside the list. */
+ ListIt listIt;
+
+ /** A vector of any dependent instructions. */
+ std::vector<MemDepEntryPtr> dependInsts;
+
+ /** If the registers are ready or not. */
+ bool regsReady;
+ /** If all memory dependencies have been satisfied. */
+ bool memDepReady;
+ /** If the instruction is completed. */
+ bool completed;
+ /** If the instruction is squashed. */
+ bool squashed;
+
+ /** For debugging. */
+#ifdef DEBUG
+ static int memdep_count;
+ static int memdep_insert;
+ static int memdep_erase;
+#endif
};
- inline void moveToReady(dep_it_t &woken_inst);
+ /** Finds the memory dependence entry in the hash map. */
+ inline MemDepEntryPtr &findInHash(const DynInstPtr &inst);
- /** List of instructions that have passed through rename, yet are still
- * waiting on either a memory dependence to resolve or source registers to
- * become available before they can issue.
- */
- std::set<Dependency, ltDependency> waitingInsts;
+ /** Moves an entry to the ready list. */
+ inline void moveToReady(MemDepEntryPtr &ready_inst_entry);
- /** List of instructions that have all their predicted memory dependences
- * resolved and their source registers ready.
- */
- std::set<InstSeqNum> readyInsts;
+ typedef m5::hash_map<InstSeqNum, MemDepEntryPtr, SNHash> MemDepHash;
- // Change this to hold a vector of iterators, which will point to the
- // entry of the waiting instructions.
- /** List of stores' sequence numbers, each of which has a vector of
- * iterators. The iterators point to the appropriate node within
- * waitingInsts that has the depenendent instruction.
- */
- std::map<InstSeqNum, vector<dep_it_t> > storeDependents;
+ typedef typename MemDepHash::iterator MemDepHashIt;
- // For now will implement this as a map...hash table might not be too
- // bad, or could move to something that mimics the current dependency
- // graph.
- std::map<InstSeqNum, DynInstPtr> memInsts;
+ /** A hash map of all memory dependence entries. */
+ MemDepHash memDepHash;
- // Iterator pointer to the top instruction which has is ready.
- // Is set by the top() call.
- dyn_it_t topInst;
+ /** A list of all instructions in the memory dependence unit. */
+ std::list<DynInstPtr> instList[Impl::MaxThreads];
+
+ /** A list of all instructions that are going to be replayed. */
+ std::list<DynInstPtr> instsToReplay;
/** The memory dependence predictor. It is accessed upon new
* instructions being added to the IQ, and responds by telling
@@ -155,10 +236,29 @@ class MemDepUnit {
*/
MemDepPred depPred;
+ /** Is there an outstanding load barrier that loads must wait on. */
+ bool loadBarrier;
+ /** The sequence number of the load barrier. */
+ InstSeqNum loadBarrierSN;
+ /** Is there an outstanding store barrier that loads must wait on. */
+ bool storeBarrier;
+ /** The sequence number of the store barrier. */
+ InstSeqNum storeBarrierSN;
+
+ /** Pointer to the IQ. */
+ InstructionQueue<Impl> *iqPtr;
+
+ /** The thread id of this memory dependence unit. */
+ int id;
+
+ /** Stat for number of inserted loads. */
Stats::Scalar<> insertedLoads;
+ /** Stat for number of inserted stores. */
Stats::Scalar<> insertedStores;
+ /** Stat for number of conflicting loads that had to wait for a store. */
Stats::Scalar<> conflictingLoads;
+ /** Stat for number of conflicting stores that had to wait for a store. */
Stats::Scalar<> conflictingStores;
};
-#endif // __CPU_O3_CPU_MEM_DEP_UNIT_HH__
+#endif // __CPU_O3_MEM_DEP_UNIT_HH__
diff --git a/src/cpu/o3/mem_dep_unit_impl.hh b/src/cpu/o3/mem_dep_unit_impl.hh
index 296db4c4e..16f67a4e0 100644
--- a/src/cpu/o3/mem_dep_unit_impl.hh
+++ b/src/cpu/o3/mem_dep_unit_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,17 +24,64 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include <map>
+#include "cpu/o3/inst_queue.hh"
#include "cpu/o3/mem_dep_unit.hh"
template <class MemDepPred, class Impl>
-MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params &params)
- : depPred(params.SSITSize, params.LFSTSize)
+MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params *params)
+ : depPred(params->SSITSize, params->LFSTSize), loadBarrier(false),
+ loadBarrierSN(0), storeBarrier(false), storeBarrierSN(0), iqPtr(NULL)
+{
+ DPRINTF(MemDepUnit, "Creating MemDepUnit object.\n");
+}
+
+template <class MemDepPred, class Impl>
+MemDepUnit<MemDepPred, Impl>::~MemDepUnit()
+{
+ for (int tid=0; tid < Impl::MaxThreads; tid++) {
+
+ ListIt inst_list_it = instList[tid].begin();
+
+ MemDepHashIt hash_it;
+
+ while (!instList[tid].empty()) {
+ hash_it = memDepHash.find((*inst_list_it)->seqNum);
+
+ assert(hash_it != memDepHash.end());
+
+ memDepHash.erase(hash_it);
+
+ instList[tid].erase(inst_list_it++);
+ }
+ }
+
+#ifdef DEBUG
+ assert(MemDepEntry::memdep_count == 0);
+#endif
+}
+
+template <class MemDepPred, class Impl>
+std::string
+MemDepUnit<MemDepPred, Impl>::name() const
{
- DPRINTF(MemDepUnit, "MemDepUnit: Creating MemDepUnit object.\n");
+ return "memdepunit";
+}
+
+template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::init(Params *params, int tid)
+{
+ DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid);
+
+ id = tid;
+
+ depPred.init(params->SSITSize, params->LFSTSize);
}
template <class MemDepPred, class Impl>
@@ -60,56 +107,99 @@ MemDepUnit<MemDepPred, Impl>::regStats()
template <class MemDepPred, class Impl>
void
+MemDepUnit<MemDepPred, Impl>::switchOut()
+{
+ // Clear any state.
+ for (int i = 0; i < Impl::MaxThreads; ++i) {
+ instList[i].clear();
+ }
+ instsToReplay.clear();
+ memDepHash.clear();
+}
+
+template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::takeOverFrom()
+{
+ // Be sure to reset all state.
+ loadBarrier = storeBarrier = false;
+ loadBarrierSN = storeBarrierSN = 0;
+ depPred.clear();
+}
+
+template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::setIQ(InstructionQueue<Impl> *iq_ptr)
+{
+ iqPtr = iq_ptr;
+}
+
+template <class MemDepPred, class Impl>
+void
MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst)
{
- InstSeqNum inst_seq_num = inst->seqNum;
+ unsigned tid = inst->threadNumber;
+
+ MemDepEntryPtr inst_entry = new MemDepEntry(inst);
+
+ // Add the MemDepEntry to the hash.
+ memDepHash.insert(
+ std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry));
+#ifdef DEBUG
+ MemDepEntry::memdep_insert++;
+#endif
- Dependency unresolved_dependencies(inst_seq_num);
+ instList[tid].push_back(inst);
- InstSeqNum producing_store = depPred.checkInst(inst->readPC());
+ inst_entry->listIt = --(instList[tid].end());
- if (producing_store == 0 ||
- storeDependents.find(producing_store) == storeDependents.end()) {
+ // Check any barriers and the dependence predictor for any
+ // producing memrefs/stores.
+ InstSeqNum producing_store;
+ if (inst->isLoad() && loadBarrier) {
+ producing_store = loadBarrierSN;
+ } else if (inst->isStore() && storeBarrier) {
+ producing_store = storeBarrierSN;
+ } else {
+ producing_store = depPred.checkInst(inst->readPC());
+ }
- DPRINTF(MemDepUnit, "MemDepUnit: No dependency for inst PC "
- "%#x.\n", inst->readPC());
+ MemDepEntryPtr store_entry = NULL;
- unresolved_dependencies.storeDep = storeDependents.end();
+ // If there is a producing store, try to find the entry.
+ if (producing_store != 0) {
+ MemDepHashIt hash_it = memDepHash.find(producing_store);
+
+ if (hash_it != memDepHash.end()) {
+ store_entry = (*hash_it).second;
+ }
+ }
+
+ // If no store entry, then instruction can issue as soon as the registers
+ // are ready.
+ if (!store_entry) {
+ DPRINTF(MemDepUnit, "No dependency for inst PC "
+ "%#x [sn:%lli].\n", inst->readPC(), inst->seqNum);
+
+ inst_entry->memDepReady = true;
if (inst->readyToIssue()) {
- readyInsts.insert(inst_seq_num);
- } else {
- unresolved_dependencies.memDepReady = true;
+ inst_entry->regsReady = true;
- waitingInsts.insert(unresolved_dependencies);
+ moveToReady(inst_entry);
}
} else {
- DPRINTF(MemDepUnit, "MemDepUnit: Adding to dependency list; "
- "inst PC %#x is dependent on seq num %i.\n",
+ // Otherwise make the instruction dependent on the store/barrier.
+ DPRINTF(MemDepUnit, "Adding to dependency list; "
+ "inst PC %#x is dependent on [sn:%lli].\n",
inst->readPC(), producing_store);
if (inst->readyToIssue()) {
- unresolved_dependencies.regsReady = true;
+ inst_entry->regsReady = true;
}
- // Find the store that this instruction is dependent on.
- sd_it_t store_loc = storeDependents.find(producing_store);
-
- assert(store_loc != storeDependents.end());
-
- // Record the location of the store that this instruction is
- // dependent on.
- unresolved_dependencies.storeDep = store_loc;
-
- // If it's not already ready, then add it to the renamed
- // list and the dependencies.
- dep_it_t inst_loc =
- (waitingInsts.insert(unresolved_dependencies)).first;
-
// Add this instruction to the list of dependents.
- (*store_loc).second.push_back(inst_loc);
-
- assert(!(*store_loc).second.empty());
+ store_entry->dependInsts.push_back(inst_entry);
if (inst->isLoad()) {
++conflictingLoads;
@@ -119,277 +209,288 @@ MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst)
}
if (inst->isStore()) {
- DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n",
- inst->readPC());
-
- depPred.insertStore(inst->readPC(), inst_seq_num);
-
- // Make sure this store isn't already in this list.
- assert(storeDependents.find(inst_seq_num) == storeDependents.end());
-
- // Put a dependency entry in at the store's sequence number.
- // Uh, not sure how this works...I want to create an entry but
- // I don't have anything to put into the value yet.
- storeDependents[inst_seq_num];
+ DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n",
+ inst->readPC(), inst->seqNum);
- assert(storeDependents.size() != 0);
+ depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber);
++insertedStores;
-
} else if (inst->isLoad()) {
++insertedLoads;
} else {
- panic("MemDepUnit: Unknown type! (most likely a barrier).");
+ panic("Unknown type! (most likely a barrier).");
}
-
- memInsts[inst_seq_num] = inst;
}
template <class MemDepPred, class Impl>
void
MemDepUnit<MemDepPred, Impl>::insertNonSpec(DynInstPtr &inst)
{
- InstSeqNum inst_seq_num = inst->seqNum;
+ unsigned tid = inst->threadNumber;
+
+ MemDepEntryPtr inst_entry = new MemDepEntry(inst);
- Dependency non_spec_inst(inst_seq_num);
+ // Insert the MemDepEntry into the hash.
+ memDepHash.insert(
+ std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry));
+#ifdef DEBUG
+ MemDepEntry::memdep_insert++;
+#endif
- non_spec_inst.storeDep = storeDependents.end();
+ // Add the instruction to the list.
+ instList[tid].push_back(inst);
- waitingInsts.insert(non_spec_inst);
+ inst_entry->listIt = --(instList[tid].end());
// Might want to turn this part into an inline function or something.
// It's shared between both insert functions.
if (inst->isStore()) {
- DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n",
- inst->readPC());
-
- depPred.insertStore(inst->readPC(), inst_seq_num);
-
- // Make sure this store isn't already in this list.
- assert(storeDependents.find(inst_seq_num) == storeDependents.end());
-
- // Put a dependency entry in at the store's sequence number.
- // Uh, not sure how this works...I want to create an entry but
- // I don't have anything to put into the value yet.
- storeDependents[inst_seq_num];
+ DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n",
+ inst->readPC(), inst->seqNum);
- assert(storeDependents.size() != 0);
+ depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber);
++insertedStores;
-
} else if (inst->isLoad()) {
++insertedLoads;
} else {
- panic("MemDepUnit: Unknown type! (most likely a barrier).");
+ panic("Unknown type! (most likely a barrier).");
}
-
- memInsts[inst_seq_num] = inst;
}
template <class MemDepPred, class Impl>
-typename Impl::DynInstPtr &
-MemDepUnit<MemDepPred, Impl>::top()
+void
+MemDepUnit<MemDepPred, Impl>::insertBarrier(DynInstPtr &barr_inst)
{
- topInst = memInsts.find( (*readyInsts.begin()) );
+ InstSeqNum barr_sn = barr_inst->seqNum;
+ // Memory barriers block loads and stores, write barriers only stores.
+ if (barr_inst->isMemBarrier()) {
+ loadBarrier = true;
+ loadBarrierSN = barr_sn;
+ storeBarrier = true;
+ storeBarrierSN = barr_sn;
+ DPRINTF(MemDepUnit, "Inserted a memory barrier\n");
+ } else if (barr_inst->isWriteBarrier()) {
+ storeBarrier = true;
+ storeBarrierSN = barr_sn;
+ DPRINTF(MemDepUnit, "Inserted a write barrier\n");
+ }
+
+ unsigned tid = barr_inst->threadNumber;
+
+ MemDepEntryPtr inst_entry = new MemDepEntry(barr_inst);
+
+ // Add the MemDepEntry to the hash.
+ memDepHash.insert(
+ std::pair<InstSeqNum, MemDepEntryPtr>(barr_sn, inst_entry));
+#ifdef DEBUG
+ MemDepEntry::memdep_insert++;
+#endif
- DPRINTF(MemDepUnit, "MemDepUnit: Top instruction is PC %#x.\n",
- (*topInst).second->readPC());
+ // Add the instruction to the instruction list.
+ instList[tid].push_back(barr_inst);
- return (*topInst).second;
+ inst_entry->listIt = --(instList[tid].end());
}
template <class MemDepPred, class Impl>
void
-MemDepUnit<MemDepPred, Impl>::pop()
+MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst)
{
- DPRINTF(MemDepUnit, "MemDepUnit: Removing instruction PC %#x.\n",
- (*topInst).second->readPC());
+ DPRINTF(MemDepUnit, "Marking registers as ready for "
+ "instruction PC %#x [sn:%lli].\n",
+ inst->readPC(), inst->seqNum);
- wakeDependents((*topInst).second);
+ MemDepEntryPtr inst_entry = findInHash(inst);
- issue((*topInst).second);
+ inst_entry->regsReady = true;
- memInsts.erase(topInst);
+ if (inst_entry->memDepReady) {
+ DPRINTF(MemDepUnit, "Instruction has its memory "
+ "dependencies resolved, adding it to the ready list.\n");
- topInst = memInsts.end();
+ moveToReady(inst_entry);
+ } else {
+ DPRINTF(MemDepUnit, "Instruction still waiting on "
+ "memory dependency.\n");
+ }
}
template <class MemDepPred, class Impl>
void
-MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst)
+MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst)
{
- DPRINTF(MemDepUnit, "MemDepUnit: Marking registers as ready for "
- "instruction PC %#x.\n",
- inst->readPC());
+ DPRINTF(MemDepUnit, "Marking non speculative "
+ "instruction PC %#x as ready [sn:%lli].\n",
+ inst->readPC(), inst->seqNum);
- InstSeqNum inst_seq_num = inst->seqNum;
+ MemDepEntryPtr inst_entry = findInHash(inst);
+
+ moveToReady(inst_entry);
+}
- Dependency inst_to_find(inst_seq_num);
+template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::reschedule(DynInstPtr &inst)
+{
+ instsToReplay.push_back(inst);
+}
- dep_it_t waiting_inst = waitingInsts.find(inst_to_find);
+template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::replay(DynInstPtr &inst)
+{
+ DynInstPtr temp_inst;
+ bool found_inst = false;
- assert(waiting_inst != waitingInsts.end());
+ // For now this replay function replays all waiting memory ops.
+ while (!instsToReplay.empty()) {
+ temp_inst = instsToReplay.front();
- if ((*waiting_inst).memDepReady) {
- DPRINTF(MemDepUnit, "MemDepUnit: Instruction has its memory "
- "dependencies resolved, adding it to the ready list.\n");
+ MemDepEntryPtr inst_entry = findInHash(temp_inst);
- moveToReady(waiting_inst);
- } else {
- DPRINTF(MemDepUnit, "MemDepUnit: Instruction still waiting on "
- "memory dependency.\n");
+ DPRINTF(MemDepUnit, "Replaying mem instruction PC %#x "
+ "[sn:%lli].\n",
+ temp_inst->readPC(), temp_inst->seqNum);
- (*waiting_inst).regsReady = true;
+ moveToReady(inst_entry);
+
+ if (temp_inst == inst) {
+ found_inst = true;
+ }
+
+ instsToReplay.pop_front();
}
+
+ assert(found_inst);
}
template <class MemDepPred, class Impl>
void
-MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst)
+MemDepUnit<MemDepPred, Impl>::completed(DynInstPtr &inst)
{
- DPRINTF(MemDepUnit, "MemDepUnit: Marking non speculative "
- "instruction PC %#x as ready.\n",
- inst->readPC());
+ DPRINTF(MemDepUnit, "Completed mem instruction PC %#x "
+ "[sn:%lli].\n",
+ inst->readPC(), inst->seqNum);
- InstSeqNum inst_seq_num = inst->seqNum;
+ unsigned tid = inst->threadNumber;
- Dependency inst_to_find(inst_seq_num);
+ // Remove the instruction from the hash and the list.
+ MemDepHashIt hash_it = memDepHash.find(inst->seqNum);
- dep_it_t waiting_inst = waitingInsts.find(inst_to_find);
+ assert(hash_it != memDepHash.end());
- assert(waiting_inst != waitingInsts.end());
+ instList[tid].erase((*hash_it).second->listIt);
- moveToReady(waiting_inst);
+ (*hash_it).second = NULL;
+
+ memDepHash.erase(hash_it);
+#ifdef DEBUG
+ MemDepEntry::memdep_erase++;
+#endif
}
template <class MemDepPred, class Impl>
void
-MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst)
+MemDepUnit<MemDepPred, Impl>::completeBarrier(DynInstPtr &inst)
{
- assert(readyInsts.find(inst->seqNum) != readyInsts.end());
-
- DPRINTF(MemDepUnit, "MemDepUnit: Issuing instruction PC %#x.\n",
- inst->readPC());
-
- // Remove the instruction from the ready list.
- readyInsts.erase(inst->seqNum);
-
- depPred.issued(inst->readPC(), inst->seqNum, inst->isStore());
+ wakeDependents(inst);
+ completed(inst);
+
+ InstSeqNum barr_sn = inst->seqNum;
+
+ if (inst->isMemBarrier()) {
+ assert(loadBarrier && storeBarrier);
+ if (loadBarrierSN == barr_sn)
+ loadBarrier = false;
+ if (storeBarrierSN == barr_sn)
+ storeBarrier = false;
+ } else if (inst->isWriteBarrier()) {
+ assert(storeBarrier);
+ if (storeBarrierSN == barr_sn)
+ storeBarrier = false;
+ }
}
template <class MemDepPred, class Impl>
void
MemDepUnit<MemDepPred, Impl>::wakeDependents(DynInstPtr &inst)
{
- // Only stores have dependents.
- if (!inst->isStore()) {
+ // Only stores and barriers have dependents.
+ if (!inst->isStore() && !inst->isMemBarrier() && !inst->isWriteBarrier()) {
return;
}
- // Wake any dependencies.
- sd_it_t sd_it = storeDependents.find(inst->seqNum);
+ MemDepEntryPtr inst_entry = findInHash(inst);
- // If there's no entry, then return. Really there should only be
- // no entry if the instruction is a load.
- if (sd_it == storeDependents.end()) {
- DPRINTF(MemDepUnit, "MemDepUnit: Instruction PC %#x, sequence "
- "number %i has no dependents.\n",
- inst->readPC(), inst->seqNum);
+ for (int i = 0; i < inst_entry->dependInsts.size(); ++i ) {
+ MemDepEntryPtr woken_inst = inst_entry->dependInsts[i];
- return;
- }
-
- for (int i = 0; i < (*sd_it).second.size(); ++i ) {
- dep_it_t woken_inst = (*sd_it).second[i];
-
- DPRINTF(MemDepUnit, "MemDepUnit: Waking up a dependent inst, "
- "sequence number %i.\n",
- (*woken_inst).seqNum);
-#if 0
- // Should we have reached instructions that are actually squashed,
- // there will be no more useful instructions in this dependency
- // list. Break out early.
- if (waitingInsts.find(woken_inst) == waitingInsts.end()) {
- DPRINTF(MemDepUnit, "MemDepUnit: Dependents on inst PC %#x "
- "are squashed, starting at SN %i. Breaking early.\n",
- inst->readPC(), woken_inst);
- break;
+ if (!woken_inst->inst) {
+ // Potentially removed mem dep entries could be on this list
+ continue;
}
-#endif
- if ((*woken_inst).regsReady) {
+ DPRINTF(MemDepUnit, "Waking up a dependent inst, "
+ "[sn:%lli].\n",
+ woken_inst->inst->seqNum);
+
+ if (woken_inst->regsReady && !woken_inst->squashed) {
moveToReady(woken_inst);
} else {
- (*woken_inst).memDepReady = true;
+ woken_inst->memDepReady = true;
}
}
- storeDependents.erase(sd_it);
+ inst_entry->dependInsts.clear();
}
template <class MemDepPred, class Impl>
void
-MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num)
+MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num,
+ unsigned tid)
{
-
- if (!waitingInsts.empty()) {
- dep_it_t waiting_it = waitingInsts.end();
-
- --waiting_it;
-
- // Remove entries from the renamed list as long as we haven't reached
- // the end and the entries continue to be younger than the squashed.
- while (!waitingInsts.empty() &&
- (*waiting_it).seqNum > squashed_num)
- {
- if (!(*waiting_it).memDepReady &&
- (*waiting_it).storeDep != storeDependents.end()) {
- sd_it_t sd_it = (*waiting_it).storeDep;
-
- // Make sure the iterator that the store has pointing
- // back is actually to this instruction.
- assert((*sd_it).second.back() == waiting_it);
-
- // Now remove this from the store's list of dependent
- // instructions.
- (*sd_it).second.pop_back();
+ if (!instsToReplay.empty()) {
+ ListIt replay_it = instsToReplay.begin();
+ while (replay_it != instsToReplay.end()) {
+ if ((*replay_it)->threadNumber == tid &&
+ (*replay_it)->seqNum > squashed_num) {
+ instsToReplay.erase(replay_it++);
+ } else {
+ ++replay_it;
}
-
- waitingInsts.erase(waiting_it--);
}
}
- if (!readyInsts.empty()) {
- sn_it_t ready_it = readyInsts.end();
+ ListIt squash_it = instList[tid].end();
+ --squash_it;
- --ready_it;
+ MemDepHashIt hash_it;
- // Same for the ready list.
- while (!readyInsts.empty() &&
- (*ready_it) > squashed_num)
- {
- readyInsts.erase(ready_it--);
- }
- }
+ while (!instList[tid].empty() &&
+ (*squash_it)->seqNum > squashed_num) {
- if (!storeDependents.empty()) {
- sd_it_t dep_it = storeDependents.end();
+ DPRINTF(MemDepUnit, "Squashing inst [sn:%lli]\n",
+ (*squash_it)->seqNum);
- --dep_it;
+ hash_it = memDepHash.find((*squash_it)->seqNum);
- // Same for the dependencies list.
- while (!storeDependents.empty() &&
- (*dep_it).first > squashed_num)
- {
- // This store's list of dependent instructions should be empty.
- assert((*dep_it).second.empty());
+ assert(hash_it != memDepHash.end());
- storeDependents.erase(dep_it--);
- }
+ (*hash_it).second->squashed = true;
+
+ (*hash_it).second = NULL;
+
+ memDepHash.erase(hash_it);
+#ifdef DEBUG
+ MemDepEntry::memdep_erase++;
+#endif
+
+ instList[tid].erase(squash_it--);
}
// Tell the dependency predictor to squash as well.
- depPred.squash(squashed_num);
+ depPred.squash(squashed_num, tid);
}
template <class MemDepPred, class Impl>
@@ -397,7 +498,7 @@ void
MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst,
DynInstPtr &violating_load)
{
- DPRINTF(MemDepUnit, "MemDepUnit: Passing violating PCs to store sets,"
+ DPRINTF(MemDepUnit, "Passing violating PCs to store sets,"
" load: %#x, store: %#x\n", violating_load->readPC(),
store_inst->readPC());
// Tell the memory dependence unit of the violation.
@@ -405,15 +506,66 @@ MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst,
}
template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst)
+{
+ DPRINTF(MemDepUnit, "Issuing instruction PC %#x [sn:%lli].\n",
+ inst->readPC(), inst->seqNum);
+
+ depPred.issued(inst->readPC(), inst->seqNum, inst->isStore());
+}
+
+template <class MemDepPred, class Impl>
+inline typename MemDepUnit<MemDepPred,Impl>::MemDepEntryPtr &
+MemDepUnit<MemDepPred, Impl>::findInHash(const DynInstPtr &inst)
+{
+ MemDepHashIt hash_it = memDepHash.find(inst->seqNum);
+
+ assert(hash_it != memDepHash.end());
+
+ return (*hash_it).second;
+}
+
+template <class MemDepPred, class Impl>
inline void
-MemDepUnit<MemDepPred, Impl>::moveToReady(dep_it_t &woken_inst)
+MemDepUnit<MemDepPred, Impl>::moveToReady(MemDepEntryPtr &woken_inst_entry)
{
- DPRINTF(MemDepUnit, "MemDepUnit: Adding instruction sequence number %i "
- "to the ready list.\n", (*woken_inst).seqNum);
+ DPRINTF(MemDepUnit, "Adding instruction [sn:%lli] "
+ "to the ready list.\n", woken_inst_entry->inst->seqNum);
- // Add it to the ready list.
- readyInsts.insert((*woken_inst).seqNum);
+ assert(!woken_inst_entry->squashed);
+
+ iqPtr->addReadyMemInst(woken_inst_entry->inst);
+}
- // Remove it from the waiting instructions.
- waitingInsts.erase(woken_inst);
+
+template <class MemDepPred, class Impl>
+void
+MemDepUnit<MemDepPred, Impl>::dumpLists()
+{
+ for (unsigned tid=0; tid < Impl::MaxThreads; tid++) {
+ cprintf("Instruction list %i size: %i\n",
+ tid, instList[tid].size());
+
+ ListIt inst_list_it = instList[tid].begin();
+ int num = 0;
+
+ while (inst_list_it != instList[tid].end()) {
+ cprintf("Instruction:%i\nPC:%#x\n[sn:%i]\n[tid:%i]\nIssued:%i\n"
+ "Squashed:%i\n\n",
+ num, (*inst_list_it)->readPC(),
+ (*inst_list_it)->seqNum,
+ (*inst_list_it)->threadNumber,
+ (*inst_list_it)->isIssued(),
+ (*inst_list_it)->isSquashed());
+ inst_list_it++;
+ ++num;
+ }
+ }
+
+ cprintf("Memory dependence hash size: %i\n", memDepHash.size());
+
+#ifdef DEBUG
+ cprintf("Memory dependence entries: %i\n", MemDepEntry::memdep_count);
+#endif
}
diff --git a/src/cpu/o3/ras.cc b/src/cpu/o3/ras.cc
index 0a7d6ca63..f9939259a 100644
--- a/src/cpu/o3/ras.cc
+++ b/src/cpu/o3/ras.cc
@@ -24,16 +24,30 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/ras.hh"
-ReturnAddrStack::ReturnAddrStack(unsigned _numEntries)
- : numEntries(_numEntries), usedEntries(0),
- tos(0)
+void
+ReturnAddrStack::init(unsigned _numEntries)
{
- addrStack = new Addr[numEntries];
+ numEntries = _numEntries;
+ usedEntries = 0;
+ tos = 0;
+
+ addrStack.resize(numEntries);
+
+ for (int i = 0; i < numEntries; ++i)
+ addrStack[i] = 0;
+}
+void
+ReturnAddrStack::reset()
+{
+ usedEntries = 0;
+ tos = 0;
for (int i = 0; i < numEntries; ++i)
addrStack[i] = 0;
}
@@ -53,9 +67,6 @@ ReturnAddrStack::push(const Addr &return_addr)
void
ReturnAddrStack::pop()
{
- // Not sure it's possible to really track usedEntries properly.
-// assert(usedEntries > 0);
-
if (usedEntries > 0) {
--usedEntries;
}
diff --git a/src/cpu/o3/ras.hh b/src/cpu/o3/ras.hh
index 46d98181e..5c8a93285 100644
--- a/src/cpu/o3/ras.hh
+++ b/src/cpu/o3/ras.hh
@@ -24,45 +24,74 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_RAS_HH__
-#define __CPU_O3_CPU_RAS_HH__
+#ifndef __CPU_O3_RAS_HH__
+#define __CPU_O3_RAS_HH__
// For Addr type.
#include "arch/isa_traits.hh"
+#include <vector>
+/** Return address stack class, implements a simple RAS. */
class ReturnAddrStack
{
public:
- ReturnAddrStack(unsigned numEntries);
+ /** Creates a return address stack, but init() must be called prior to
+ * use.
+ */
+ ReturnAddrStack() {}
+
+ /** Initializes RAS with a specified number of entries.
+ * @param numEntries Number of entries in the RAS.
+ */
+ void init(unsigned numEntries);
+
+ void reset();
+ /** Returns the top address on the RAS. */
Addr top()
{ return addrStack[tos]; }
+ /** Returns the index of the top of the RAS. */
unsigned topIdx()
{ return tos; }
+ /** Pushes an address onto the RAS. */
void push(const Addr &return_addr);
+ /** Pops the top address from the RAS. */
void pop();
+ /** Changes index to the top of the RAS, and replaces the top address with
+ * a new target.
+ * @param top_entry_idx The index of the RAS that will now be the top.
+ * @param restored_target The new target address of the new top of the RAS.
+ */
void restore(unsigned top_entry_idx, const Addr &restored_target);
private:
+ /** Increments the top of stack index. */
inline void incrTos()
{ if (++tos == numEntries) tos = 0; }
+ /** Decrements the top of stack index. */
inline void decrTos()
{ tos = (tos == 0 ? numEntries - 1 : tos - 1); }
- Addr *addrStack;
+ /** The RAS itself. */
+ std::vector<Addr> addrStack;
+ /** The number of entries in the RAS. */
unsigned numEntries;
+ /** The number of used entries in the RAS. */
unsigned usedEntries;
+ /** The top of stack index. */
unsigned tos;
};
-#endif // __CPU_O3_CPU_RAS_HH__
+#endif // __CPU_O3_RAS_HH__
diff --git a/src/cpu/o3/regfile.hh b/src/cpu/o3/regfile.hh
index a5cfa8f3c..ade5e4e56 100644
--- a/src/cpu/o3/regfile.hh
+++ b/src/cpu/o3/regfile.hh
@@ -24,15 +24,17 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ * Gabe Black
*/
-#ifndef __CPU_O3_CPU_REGFILE_HH__
-#define __CPU_O3_CPU_REGFILE_HH__
-
-// @todo: Destructor
+#ifndef __CPU_O3_REGFILE_HH__
+#define __CPU_O3_REGFILE_HH__
#include "arch/isa_traits.hh"
#include "arch/faults.hh"
+#include "arch/types.hh"
#include "base/trace.hh"
#include "config/full_system.hh"
#include "cpu/o3/comm.hh"
@@ -42,32 +44,40 @@
#endif
-// This really only depends on the ISA, and not the Impl. It might be nicer
-// to see if I can make it depend on nothing...
-// Things that are in the ifdef FULL_SYSTEM are pretty dependent on the ISA,
-// and should go in the AlphaFullCPU.
+#include <vector>
+/**
+ * Simple physical register file class.
+ * Right now this is specific to Alpha until we decide if/how to make things
+ * generic enough to support other ISAs.
+ */
template <class Impl>
class PhysRegFile
{
protected:
typedef TheISA::IntReg IntReg;
typedef TheISA::FloatReg FloatReg;
+ typedef TheISA::FloatRegBits FloatRegBits;
typedef TheISA::MiscRegFile MiscRegFile;
typedef TheISA::MiscReg MiscReg;
- //Note that most of the definitions of the IntReg, FloatReg, etc. exist
- //within the Impl/ISA class and not within this PhysRegFile class.
+ typedef union {
+ FloatReg d;
+ FloatRegBits q;
+ } PhysFloatReg;
- //Will need some way to allow stuff like swap_palshadow to access the
- //correct registers. Might require code changes to swap_palshadow and
- //other execution contexts.
+ // Note that most of the definitions of the IntReg, FloatReg, etc. exist
+ // within the Impl/ISA class and not within this PhysRegFile class.
- //Will make these registers public for now, but they probably should
- //be private eventually with some accessor functions.
+ // Will make these registers public for now, but they probably should
+ // be private eventually with some accessor functions.
public:
typedef typename Impl::FullCPU FullCPU;
+ /**
+ * Constructs a physical register file with the specified amount of
+ * integer and floating point registers.
+ */
PhysRegFile(unsigned _numPhysicalIntRegs,
unsigned _numPhysicalFloatRegs);
@@ -80,12 +90,13 @@ class PhysRegFile
// void serialize(std::ostream &os);
// void unserialize(Checkpoint *cp, const std::string &section);
+ /** Reads an integer register. */
uint64_t readIntReg(PhysRegIndex reg_idx)
{
assert(reg_idx < numPhysicalIntRegs);
DPRINTF(IEW, "RegFile: Access to int register %i, has data "
- "%i\n", int(reg_idx), intRegFile[reg_idx]);
+ "%#x\n", int(reg_idx), intRegFile[reg_idx]);
return intRegFile[reg_idx];
}
@@ -96,14 +107,15 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- FloatReg floatReg = floatRegFile.readReg(reg_idx, width);
+ FloatReg floatReg = floatRegFile[reg_idx].d;
DPRINTF(IEW, "RegFile: Access to %d byte float register %i, has "
- "data %8.8d\n", int(reg_idx), (double)floatReg);
+ "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q);
return floatReg;
}
+ /** Reads a floating point register (double precision). */
FloatReg readFloatReg(PhysRegIndex reg_idx)
{
// Remove the base Float reg dependency.
@@ -111,14 +123,15 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- FloatReg floatReg = floatRegFile.readReg(reg_idx);
+ FloatReg floatReg = floatRegFile[reg_idx].d;
DPRINTF(IEW, "RegFile: Access to float register %i, has "
- "data %8.8d\n", int(reg_idx), (double)floatReg);
+ "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q);
return floatReg;
}
+ /** Reads a floating point register as an integer. */
FloatRegBits readFloatRegBits(PhysRegIndex reg_idx, int width)
{
// Remove the base Float reg dependency.
@@ -126,10 +139,10 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- FloatRegBits floatRegBits = floatRegFile.readRegBits(reg_idx, width);
+ FloatRegBits floatRegBits = floatRegFile[reg_idx].q;
- DPRINTF(IEW, "RegFile: Access to %d byte float register %i as int, "
- "has data %lli\n", int(reg_idx), (uint64_t)floatRegBits);
+ DPRINTF(IEW, "RegFile: Access to float register %i as int, "
+ "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits);
return floatRegBits;
}
@@ -141,24 +154,27 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- FloatRegBits floatRegBits = floatRegFile.readRegBits(reg_idx);
+ FloatRegBits floatRegBits = floatRegFile[reg_idx].q;
DPRINTF(IEW, "RegFile: Access to float register %i as int, "
- "has data %lli\n", int(reg_idx), (uint64_t)floatRegBits);
+ "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits);
return floatRegBits;
}
+ /** Sets an integer register to the given value. */
void setIntReg(PhysRegIndex reg_idx, uint64_t val)
{
assert(reg_idx < numPhysicalIntRegs);
- DPRINTF(IEW, "RegFile: Setting int register %i to %lli\n",
+ DPRINTF(IEW, "RegFile: Setting int register %i to %#x\n",
int(reg_idx), val);
- intRegFile[reg_idx] = val;
+ if (reg_idx != TheISA::ZeroReg)
+ intRegFile[reg_idx] = val;
}
+ /** Sets a single precision floating point register to the given value. */
void setFloatReg(PhysRegIndex reg_idx, FloatReg val, int width)
{
// Remove the base Float reg dependency.
@@ -166,12 +182,14 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- DPRINTF(IEW, "RegFile: Setting float register %i to %8.8d\n",
- int(reg_idx), (double)val);
+ DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n",
+ int(reg_idx), (uint64_t)val);
- floatRegFile.setReg(reg_idx, val, width);
+ if (reg_idx != TheISA::ZeroReg)
+ floatRegFile[reg_idx].d = val;
}
+ /** Sets a double precision floating point register to the given value. */
void setFloatReg(PhysRegIndex reg_idx, FloatReg val)
{
// Remove the base Float reg dependency.
@@ -179,12 +197,14 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- DPRINTF(IEW, "RegFile: Setting float register %i to %8.8d\n",
- int(reg_idx), (double)val);
+ DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n",
+ int(reg_idx), (uint64_t)val);
- floatRegFile.setReg(reg_idx, val);
+ if (reg_idx != TheISA::ZeroReg)
+ floatRegFile[reg_idx].d = val;
}
+ /** Sets a floating point register to the given integer value. */
void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val, int width)
{
// Remove the base Float reg dependency.
@@ -192,10 +212,10 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n",
+ DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n",
int(reg_idx), (uint64_t)val);
- floatRegFile.setRegBits(reg_idx, val, width);
+ floatRegFile[reg_idx].q = val;
}
void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val)
@@ -205,81 +225,68 @@ class PhysRegFile
assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs);
- DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n",
+ DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n",
int(reg_idx), (uint64_t)val);
- floatRegFile.setRegBits(reg_idx, val);
- }
-
- uint64_t readPC()
- {
- return pc;
+ floatRegFile[reg_idx].q = val;
}
- void setPC(uint64_t val)
+ MiscReg readMiscReg(int misc_reg, unsigned thread_id)
{
- pc = val;
+ return miscRegs[thread_id].readReg(misc_reg);
}
- void setNextPC(uint64_t val)
+ MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault,
+ unsigned thread_id)
{
- npc = val;
+ return miscRegs[thread_id].readRegWithEffect(misc_reg, fault,
+ cpu->tcBase(thread_id));
}
- //Consider leaving this stuff and below in some implementation specific
- //file as opposed to the general register file. Or have a derived class.
- MiscReg readMiscReg(int misc_reg)
+ Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned thread_id)
{
- // Dummy function for now.
- // @todo: Fix this once proxy XC is used.
- return 0;
+ return miscRegs[thread_id].setReg(misc_reg, val);
}
- Fault setMiscReg(int misc_reg, const MiscReg &val)
+ Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val,
+ unsigned thread_id)
{
- // Dummy function for now.
- // @todo: Fix this once proxy XC is used.
- return NoFault;
+ return miscRegs[thread_id].setRegWithEffect(misc_reg, val,
+ cpu->tcBase(thread_id));
}
#if FULL_SYSTEM
int readIntrFlag() { return intrflag; }
+ /** Sets an interrupt flag. */
void setIntrFlag(int val) { intrflag = val; }
#endif
- // These should be private eventually, but will be public for now
- // so that I can hack around the initregs issue.
public:
/** (signed) integer register file. */
IntReg *intRegFile;
/** Floating point register file. */
- FloatReg *floatRegFile;
+ PhysFloatReg *floatRegFile;
/** Miscellaneous register file. */
- MiscRegFile miscRegs;
-
- /** Program counter. */
- Addr pc;
-
- /** Next-cycle program counter. */
- Addr npc;
+ MiscRegFile miscRegs[Impl::MaxThreads];
#if FULL_SYSTEM
private:
- // This is ISA specifc stuff; remove it eventually once ISAImpl is used
-// IntReg palregs[NumIntRegs]; // PAL shadow registers
int intrflag; // interrupt flag
- bool pal_shadow; // using pal_shadow registers
#endif
private:
+ /** CPU pointer. */
FullCPU *cpu;
public:
+ /** Sets the CPU pointer. */
void setCPU(FullCPU *cpu_ptr) { cpu = cpu_ptr; }
+ /** Number of physical integer registers. */
unsigned numPhysicalIntRegs;
+ /** Number of physical floating point registers. */
unsigned numPhysicalFloatRegs;
};
@@ -290,10 +297,14 @@ PhysRegFile<Impl>::PhysRegFile(unsigned _numPhysicalIntRegs,
numPhysicalFloatRegs(_numPhysicalFloatRegs)
{
intRegFile = new IntReg[numPhysicalIntRegs];
- floatRegFile = new FloatReg[numPhysicalFloatRegs];
+ floatRegFile = new PhysFloatReg[numPhysicalFloatRegs];
- memset(intRegFile, 0, sizeof(*intRegFile));
- memset(floatRegFile, 0, sizeof(*floatRegFile));
+ for (int i = 0; i < Impl::MaxThreads; ++i) {
+ miscRegs[i].clear();
+ }
+
+ memset(intRegFile, 0, sizeof(IntReg) * numPhysicalIntRegs);
+ memset(floatRegFile, 0, sizeof(PhysFloatReg) * numPhysicalFloatRegs);
}
-#endif // __CPU_O3_CPU_REGFILE_HH__
+#endif
diff --git a/src/cpu/o3/rename.cc b/src/cpu/o3/rename.cc
index 6e9ee23da..9ca8e82c6 100644
--- a/src/cpu/o3/rename.cc
+++ b/src/cpu/o3/rename.cc
@@ -24,10 +24,12 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "cpu/o3/alpha_dyn_inst.hh"
#include "cpu/o3/alpha_impl.hh"
#include "cpu/o3/rename_impl.hh"
-template class SimpleRename<AlphaSimpleImpl>;
+template class DefaultRename<AlphaSimpleImpl>;
diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh
index 07b442964..42fdf6bf5 100644
--- a/src/cpu/o3/rename.hh
+++ b/src/cpu/o3/rename.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,25 +24,32 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Todo:
-// Fix up trap and barrier handling.
-// May want to have different statuses to differentiate the different stall
-// conditions.
-
-#ifndef __CPU_O3_CPU_SIMPLE_RENAME_HH__
-#define __CPU_O3_CPU_SIMPLE_RENAME_HH__
+#ifndef __CPU_O3_RENAME_HH__
+#define __CPU_O3_RENAME_HH__
#include <list>
#include "base/statistics.hh"
#include "base/timebuf.hh"
-// Will need rename maps for both the int reg file and fp reg file.
-// Or change rename map class to handle both. (RegFile handles both.)
+/**
+ * DefaultRename handles both single threaded and SMT rename. Its
+ * width is specified by the parameters; each cycle it tries to rename
+ * that many instructions. It holds onto the rename history of all
+ * instructions with destination registers, storing the
+ * arch. register, the new physical register, and the old physical
+ * register, to allow for undoing of mappings if squashing happens, or
+ * freeing up registers upon commit. Rename handles blocking if the
+ * ROB, IQ, or LSQ is going to be full. Rename also handles barriers,
+ * and does so by stalling on the instruction until the ROB is empty
+ * and there are no instructions in flight to the ROB.
+ */
template<class Impl>
-class SimpleRename
+class DefaultRename
{
public:
// Typedefs from the Impl.
@@ -51,112 +58,242 @@ class SimpleRename
typedef typename Impl::FullCPU FullCPU;
typedef typename Impl::Params Params;
- typedef typename CPUPol::FetchStruct FetchStruct;
+ // Typedefs from the CPUPol
typedef typename CPUPol::DecodeStruct DecodeStruct;
typedef typename CPUPol::RenameStruct RenameStruct;
typedef typename CPUPol::TimeStruct TimeStruct;
-
- // Typedefs from the CPUPol
typedef typename CPUPol::FreeList FreeList;
typedef typename CPUPol::RenameMap RenameMap;
+ // These are used only for initialization.
+ typedef typename CPUPol::IEW IEW;
+ typedef typename CPUPol::Commit Commit;
// Typedefs from the ISA.
typedef TheISA::RegIndex RegIndex;
+ // A list is used to queue the instructions. Barrier insts must
+ // be added to the front of the list, which is the only reason for
+ // using a list instead of a queue. (Most other stages use a
+ // queue)
+ typedef std::list<DynInstPtr> InstQueue;
+
public:
- // Rename will block if ROB becomes full or issue queue becomes full,
- // or there are no free registers to rename to.
- // Only case where rename squashes is if IEW squashes.
- enum Status {
+ /** Overall rename status. Used to determine if the CPU can
+ * deschedule itself due to a lack of activity.
+ */
+ enum RenameStatus {
+ Active,
+ Inactive
+ };
+
+ /** Individual thread status. */
+ enum ThreadStatus {
Running,
Idle,
+ StartSquash,
Squashing,
Blocked,
Unblocking,
- BarrierStall
+ SerializeStall
};
private:
- Status _status;
+ /** Rename status. */
+ RenameStatus _status;
+
+ /** Per-thread status. */
+ ThreadStatus renameStatus[Impl::MaxThreads];
public:
- SimpleRename(Params &params);
+ /** DefaultRename constructor. */
+ DefaultRename(Params *params);
+
+ /** Returns the name of rename. */
+ std::string name() const;
+ /** Registers statistics. */
void regStats();
+ /** Sets CPU pointer. */
void setCPU(FullCPU *cpu_ptr);
+ /** Sets the main backwards communication time buffer pointer. */
void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr);
+ /** Sets pointer to time buffer used to communicate to the next stage. */
void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr);
+ /** Sets pointer to time buffer coming from decode. */
void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr);
- void setRenameMap(RenameMap *rm_ptr);
+ /** Sets pointer to IEW stage. Used only for initialization. */
+ void setIEWStage(IEW *iew_stage)
+ { iew_ptr = iew_stage; }
+
+ /** Sets pointer to commit stage. Used only for initialization. */
+ void setCommitStage(Commit *commit_stage)
+ { commit_ptr = commit_stage; }
+
+ private:
+ /** Pointer to IEW stage. Used only for initialization. */
+ IEW *iew_ptr;
+
+ /** Pointer to commit stage. Used only for initialization. */
+ Commit *commit_ptr;
+
+ public:
+ /** Initializes variables for the stage. */
+ void initStage();
+
+ /** Sets pointer to list of active threads. */
+ void setActiveThreads(std::list<unsigned> *at_ptr);
+
+ /** Sets pointer to rename maps (per-thread structures). */
+ void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]);
+ /** Sets pointer to the free list. */
void setFreeList(FreeList *fl_ptr);
- void dumpHistory();
+ /** Sets pointer to the scoreboard. */
+ void setScoreboard(Scoreboard *_scoreboard);
- void tick();
+ /** Switches out the rename stage. */
+ void switchOut();
+
+ /** Completes the switch out. */
+ void doSwitchOut();
+
+ /** Takes over from another CPU's thread. */
+ void takeOverFrom();
+
+ /** Squashes all instructions in a thread. */
+ void squash(unsigned tid);
- void rename();
+ /** Ticks rename, which processes all input signals and attempts to rename
+ * as many instructions as possible.
+ */
+ void tick();
- void squash();
+ /** Debugging function used to dump history buffer of renamings. */
+ void dumpHistory();
private:
- void block();
+ /** 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).
+ * @param tid Thread id to rename instructions from.
+ */
+ void rename(bool &status_change, unsigned tid);
+
+ /** Renames instructions for the given thread. Also handles serializing
+ * instructions.
+ */
+ void renameInsts(unsigned tid);
+
+ /** Inserts unused instructions from a given thread into the skid buffer,
+ * to be renamed once rename unblocks.
+ */
+ void skidInsert(unsigned tid);
+
+ /** Separates instructions from decode into individual lists of instructions
+ * sorted by thread.
+ */
+ void sortInsts();
+
+ /** Returns if all of the skid buffers are empty. */
+ bool skidsEmpty();
+
+ /** Updates overall rename status based on all of the threads' statuses. */
+ void updateStatus();
+
+ /** Switches rename to blocking, and signals back that rename has become
+ * blocked.
+ * @return Returns true if there is a status change.
+ */
+ bool block(unsigned tid);
+
+ /** Switches rename to unblocking if the skid buffer is empty, and signals
+ * back that rename has unblocked.
+ * @return Returns true if there is a status change.
+ */
+ bool unblock(unsigned tid);
+
+ /** Executes actual squash, removing squashed instructions. */
+ void doSquash(unsigned tid);
+
+ /** Removes a committed instruction's rename history. */
+ void removeFromHistory(InstSeqNum inst_seq_num, unsigned tid);
+
+ /** Renames the source registers of an instruction. */
+ inline void renameSrcRegs(DynInstPtr &inst, unsigned tid);
+
+ /** Renames the destination registers of an instruction. */
+ inline void renameDestRegs(DynInstPtr &inst, unsigned tid);
- inline void unblock();
+ /** Calculates the number of free ROB entries for a specific thread. */
+ inline int calcFreeROBEntries(unsigned tid);
- void doSquash();
+ /** Calculates the number of free IQ entries for a specific thread. */
+ inline int calcFreeIQEntries(unsigned tid);
- void removeFromHistory(InstSeqNum inst_seq_num);
+ /** Calculates the number of free LSQ entries for a specific thread. */
+ inline int calcFreeLSQEntries(unsigned tid);
- inline void renameSrcRegs(DynInstPtr &inst);
+ /** Returns the number of valid instructions coming from decode. */
+ unsigned validInsts();
- inline void renameDestRegs(DynInstPtr &inst);
+ /** Reads signals telling rename to block/unblock. */
+ void readStallSignals(unsigned tid);
- inline int calcFreeROBEntries();
+ /** Checks if any stages are telling rename to block. */
+ bool checkStall(unsigned tid);
- inline int calcFreeIQEntries();
+ /** Gets the number of free entries for a specific thread. */
+ void readFreeEntries(unsigned tid);
- /** Holds the previous information for each rename.
- * Note that often times the inst may have been deleted, so only access
- * the pointer for the address and do not dereference it.
+ /** Checks the signals and updates the status. */
+ bool checkSignalsAndUpdate(unsigned tid);
+
+ /** Either serializes on the next instruction available in the InstQueue,
+ * or records that it must serialize on the next instruction to enter
+ * rename.
+ * @param inst_list The list of younger, unprocessed instructions for the
+ * thread that has the serializeAfter instruction.
+ * @param tid The thread id.
+ */
+ void serializeAfter(InstQueue &inst_list, unsigned tid);
+
+ /** Holds the information for each destination register rename. It holds
+ * the instruction's sequence number, the arch register, the old physical
+ * register for that arch. register, and the new physical register.
*/
struct RenameHistory {
RenameHistory(InstSeqNum _instSeqNum, RegIndex _archReg,
PhysRegIndex _newPhysReg, PhysRegIndex _prevPhysReg)
: instSeqNum(_instSeqNum), archReg(_archReg),
- newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg),
- placeHolder(false)
- {
- }
-
- /** Constructor used specifically for cases where a place holder
- * rename history entry is being made.
- */
- RenameHistory(InstSeqNum _instSeqNum)
- : instSeqNum(_instSeqNum), archReg(0), newPhysReg(0),
- prevPhysReg(0), placeHolder(true)
+ newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg)
{
}
+ /** The sequence number of the instruction that renamed. */
InstSeqNum instSeqNum;
+ /** The architectural register index that was renamed. */
RegIndex archReg;
+ /** The new physical register that the arch. register is renamed to. */
PhysRegIndex newPhysReg;
+ /** The old physical register that the arch. register was renamed to. */
PhysRegIndex prevPhysReg;
- bool placeHolder;
};
- std::list<RenameHistory> historyBuffer;
+ /** A per-thread list of all destination register renames, used to either
+ * undo rename mappings or free old physical registers.
+ */
+ std::list<RenameHistory> historyBuffer[Impl::MaxThreads];
- /** CPU interface. */
+ /** Pointer to CPU. */
FullCPU *cpu;
- // Interfaces to objects outside of rename.
- /** Time buffer interface. */
+ /** Pointer to main time buffer used for backwards communication. */
TimeBuffer<TimeStruct> *timeBuffer;
/** Wire to get IEW's output from backwards time buffer. */
@@ -166,7 +303,6 @@ class SimpleRename
typename TimeBuffer<TimeStruct>::wire fromCommit;
/** Wire to write infromation heading to previous stages. */
- // Might not be the best name as not only decode will read it.
typename TimeBuffer<TimeStruct>::wire toDecode;
/** Rename instruction queue. */
@@ -181,15 +317,71 @@ class SimpleRename
/** Wire to get decode's output from decode queue. */
typename TimeBuffer<DecodeStruct>::wire fromDecode;
+ /** Queue of all instructions coming from decode this cycle. */
+ InstQueue insts[Impl::MaxThreads];
+
/** Skid buffer between rename and decode. */
- std::queue<DecodeStruct> skidBuffer;
+ InstQueue skidBuffer[Impl::MaxThreads];
/** Rename map interface. */
- SimpleRenameMap *renameMap;
+ RenameMap *renameMap[Impl::MaxThreads];
/** Free list interface. */
FreeList *freeList;
+ /** Pointer to the list of active threads. */
+ std::list<unsigned> *activeThreads;
+
+ /** Pointer to the scoreboard. */
+ Scoreboard *scoreboard;
+
+ /** Count of instructions in progress that have been sent off to the IQ
+ * and ROB, but are not yet included in their occupancy counts.
+ */
+ int instsInProgress[Impl::MaxThreads];
+
+ /** Variable that tracks if decode has written to the time buffer this
+ * cycle. Used to tell CPU if there is activity this cycle.
+ */
+ bool wroteToTimeBuffer;
+
+ /** Structures whose free entries impact the amount of instructions that
+ * can be renamed.
+ */
+ struct FreeEntries {
+ unsigned iqEntries;
+ unsigned lsqEntries;
+ unsigned robEntries;
+ };
+
+ /** Per-thread tracking of the number of free entries of back-end
+ * structures.
+ */
+ FreeEntries freeEntries[Impl::MaxThreads];
+
+ /** Records if the ROB is empty. In SMT mode the ROB may be dynamically
+ * partitioned between threads, so the ROB must tell rename when it is
+ * empty.
+ */
+ bool emptyROB[Impl::MaxThreads];
+
+ /** Source of possible stalls. */
+ struct Stalls {
+ bool iew;
+ bool commit;
+ };
+
+ /** Tracks which stages are telling decode to stall. */
+ Stalls stalls[Impl::MaxThreads];
+
+ /** The serialize instruction that rename has stalled on. */
+ DynInstPtr serializeInst[Impl::MaxThreads];
+
+ /** Records if rename needs to serialize on the next instruction for any
+ * thread.
+ */
+ bool serializeOnNextInst[Impl::MaxThreads];
+
/** Delay between iew and rename, in ticks. */
int iewToRenameDelay;
@@ -207,27 +399,74 @@ class SimpleRename
*/
unsigned commitWidth;
- /** The instruction that rename is currently on. It needs to have
- * persistent state so that when a stall occurs in the middle of a
- * group of instructions, it can restart at the proper instruction.
+ /** The index of the instruction in the time buffer to IEW that rename is
+ * currently using.
+ */
+ unsigned toIEWIndex;
+
+ /** Whether or not rename needs to block this cycle. */
+ bool blockThisCycle;
+
+ /** The number of threads active in rename. */
+ unsigned numThreads;
+
+ /** The maximum skid buffer size. */
+ unsigned skidBufferMax;
+
+ /** Enum to record the source of a structure full stall. Can come from
+ * either ROB, IQ, LSQ, and it is priortized in that order.
+ */
+ enum FullSource {
+ ROB,
+ IQ,
+ LSQ,
+ NONE
+ };
+
+ /** Function used to increment the stat that corresponds to the source of
+ * the stall.
*/
- unsigned numInst;
+ inline void incrFullStat(const FullSource &source);
+ /** Stat for total number of cycles spent squashing. */
Stats::Scalar<> renameSquashCycles;
+ /** Stat for total number of cycles spent idle. */
Stats::Scalar<> renameIdleCycles;
+ /** Stat for total number of cycles spent blocking. */
Stats::Scalar<> renameBlockCycles;
+ /** Stat for total number of cycles spent stalling for a serializing inst. */
+ Stats::Scalar<> renameSerializeStallCycles;
+ /** Stat for total number of cycles spent running normally. */
+ Stats::Scalar<> renameRunCycles;
+ /** Stat for total number of cycles spent unblocking. */
Stats::Scalar<> renameUnblockCycles;
+ /** Stat for total number of renamed instructions. */
Stats::Scalar<> renameRenamedInsts;
+ /** Stat for total number of squashed instructions that rename discards. */
Stats::Scalar<> renameSquashedInsts;
+ /** Stat for total number of times that the ROB starts a stall in rename. */
Stats::Scalar<> renameROBFullEvents;
+ /** Stat for total number of times that the IQ starts a stall in rename. */
Stats::Scalar<> renameIQFullEvents;
+ /** Stat for total number of times that the LSQ starts a stall in rename. */
+ Stats::Scalar<> renameLSQFullEvents;
+ /** Stat for total number of times that rename runs out of free registers
+ * to use to rename. */
Stats::Scalar<> renameFullRegistersEvents;
+ /** Stat for total number of renamed destination registers. */
Stats::Scalar<> renameRenamedOperands;
+ /** Stat for total number of source register rename lookups. */
Stats::Scalar<> renameRenameLookups;
- Stats::Scalar<> renameHBPlaceHolders;
+ /** Stat for total number of committed renaming mappings. */
Stats::Scalar<> renameCommittedMaps;
+ /** Stat for total number of mappings that were undone due to a squash. */
Stats::Scalar<> renameUndoneMaps;
- Stats::Scalar<> renameValidUndoneMaps;
+ /** Number of serialize instructions handled. */
+ Stats::Scalar<> renamedSerializing;
+ /** Number of instructions marked as temporarily serializing. */
+ Stats::Scalar<> renamedTempSerializing;
+ /** Number of instructions inserted into skid buffers. */
+ Stats::Scalar<> renameSkidInsts;
};
-#endif // __CPU_O3_CPU_SIMPLE_RENAME_HH__
+#endif // __CPU_O3_RENAME_HH__
diff --git a/src/cpu/o3/rename_impl.hh b/src/cpu/o3/rename_impl.hh
index 2068b36ab..df33b98ee 100644
--- a/src/cpu/o3/rename_impl.hh
+++ b/src/cpu/o3/rename_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include <list>
@@ -31,97 +33,146 @@
#include "config/full_system.hh"
#include "cpu/o3/rename.hh"
+using namespace std;
+
+template <class Impl>
+DefaultRename<Impl>::DefaultRename(Params *params)
+ : iewToRenameDelay(params->iewToRenameDelay),
+ decodeToRenameDelay(params->decodeToRenameDelay),
+ commitToRenameDelay(params->commitToRenameDelay),
+ renameWidth(params->renameWidth),
+ commitWidth(params->commitWidth),
+ numThreads(params->numberOfThreads)
+{
+ _status = Inactive;
+
+ for (int i=0; i< numThreads; i++) {
+ renameStatus[i] = Idle;
+
+ freeEntries[i].iqEntries = 0;
+ freeEntries[i].lsqEntries = 0;
+ freeEntries[i].robEntries = 0;
+
+ stalls[i].iew = false;
+ stalls[i].commit = false;
+ serializeInst[i] = NULL;
+
+ instsInProgress[i] = 0;
+
+ emptyROB[i] = true;
+
+ serializeOnNextInst[i] = false;
+ }
+
+ // @todo: Make into a parameter.
+ skidBufferMax = (2 * (iewToRenameDelay * params->decodeWidth)) + renameWidth;
+}
+
template <class Impl>
-SimpleRename<Impl>::SimpleRename(Params &params)
- : iewToRenameDelay(params.iewToRenameDelay),
- decodeToRenameDelay(params.decodeToRenameDelay),
- commitToRenameDelay(params.commitToRenameDelay),
- renameWidth(params.renameWidth),
- commitWidth(params.commitWidth),
- numInst(0)
+std::string
+DefaultRename<Impl>::name() const
{
- _status = Idle;
+ return cpu->name() + ".rename";
}
template <class Impl>
void
-SimpleRename<Impl>::regStats()
+DefaultRename<Impl>::regStats()
{
renameSquashCycles
- .name(name() + ".renameSquashCycles")
+ .name(name() + ".RENAME:SquashCycles")
.desc("Number of cycles rename is squashing")
.prereq(renameSquashCycles);
renameIdleCycles
- .name(name() + ".renameIdleCycles")
+ .name(name() + ".RENAME:IdleCycles")
.desc("Number of cycles rename is idle")
.prereq(renameIdleCycles);
renameBlockCycles
- .name(name() + ".renameBlockCycles")
+ .name(name() + ".RENAME:BlockCycles")
.desc("Number of cycles rename is blocking")
.prereq(renameBlockCycles);
+ renameSerializeStallCycles
+ .name(name() + ".RENAME:serializeStallCycles")
+ .desc("count of cycles rename stalled for serializing inst")
+ .flags(Stats::total);
+ renameRunCycles
+ .name(name() + ".RENAME:RunCycles")
+ .desc("Number of cycles rename is running")
+ .prereq(renameIdleCycles);
renameUnblockCycles
- .name(name() + ".renameUnblockCycles")
+ .name(name() + ".RENAME:UnblockCycles")
.desc("Number of cycles rename is unblocking")
.prereq(renameUnblockCycles);
renameRenamedInsts
- .name(name() + ".renameRenamedInsts")
+ .name(name() + ".RENAME:RenamedInsts")
.desc("Number of instructions processed by rename")
.prereq(renameRenamedInsts);
renameSquashedInsts
- .name(name() + ".renameSquashedInsts")
+ .name(name() + ".RENAME:SquashedInsts")
.desc("Number of squashed instructions processed by rename")
.prereq(renameSquashedInsts);
renameROBFullEvents
- .name(name() + ".renameROBFullEvents")
- .desc("Number of times rename has considered the ROB 'full'")
+ .name(name() + ".RENAME:ROBFullEvents")
+ .desc("Number of times rename has blocked due to ROB full")
.prereq(renameROBFullEvents);
renameIQFullEvents
- .name(name() + ".renameIQFullEvents")
- .desc("Number of times rename has considered the IQ 'full'")
+ .name(name() + ".RENAME:IQFullEvents")
+ .desc("Number of times rename has blocked due to IQ full")
.prereq(renameIQFullEvents);
+ renameLSQFullEvents
+ .name(name() + ".RENAME:LSQFullEvents")
+ .desc("Number of times rename has blocked due to LSQ full")
+ .prereq(renameLSQFullEvents);
renameFullRegistersEvents
- .name(name() + ".renameFullRegisterEvents")
+ .name(name() + ".RENAME:FullRegisterEvents")
.desc("Number of times there has been no free registers")
.prereq(renameFullRegistersEvents);
renameRenamedOperands
- .name(name() + ".renameRenamedOperands")
+ .name(name() + ".RENAME:RenamedOperands")
.desc("Number of destination operands rename has renamed")
.prereq(renameRenamedOperands);
renameRenameLookups
- .name(name() + ".renameRenameLookups")
+ .name(name() + ".RENAME:RenameLookups")
.desc("Number of register rename lookups that rename has made")
.prereq(renameRenameLookups);
- renameHBPlaceHolders
- .name(name() + ".renameHBPlaceHolders")
- .desc("Number of place holders added to the history buffer")
- .prereq(renameHBPlaceHolders);
renameCommittedMaps
- .name(name() + ".renameCommittedMaps")
+ .name(name() + ".RENAME:CommittedMaps")
.desc("Number of HB maps that are committed")
.prereq(renameCommittedMaps);
renameUndoneMaps
- .name(name() + ".renameUndoneMaps")
+ .name(name() + ".RENAME:UndoneMaps")
.desc("Number of HB maps that are undone due to squashing")
.prereq(renameUndoneMaps);
- renameValidUndoneMaps
- .name(name() + ".renameValidUndoneMaps")
- .desc("Number of HB maps that are undone, and are not place holders")
- .prereq(renameValidUndoneMaps);
+ renamedSerializing
+ .name(name() + ".RENAME:serializingInsts")
+ .desc("count of serializing insts renamed")
+ .flags(Stats::total)
+ ;
+ renamedTempSerializing
+ .name(name() + ".RENAME:tempSerializingInsts")
+ .desc("count of temporary serializing insts renamed")
+ .flags(Stats::total)
+ ;
+ renameSkidInsts
+ .name(name() + ".RENAME:skidInsts")
+ .desc("count of insts added to the skid buffer")
+ .flags(Stats::total)
+ ;
}
template <class Impl>
void
-SimpleRename<Impl>::setCPU(FullCPU *cpu_ptr)
+DefaultRename<Impl>::setCPU(FullCPU *cpu_ptr)
{
- DPRINTF(Rename, "Rename: Setting CPU pointer.\n");
+ DPRINTF(Rename, "Setting CPU pointer.\n");
cpu = cpu_ptr;
}
template <class Impl>
void
-SimpleRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
+DefaultRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
- DPRINTF(Rename, "Rename: Setting time buffer pointer.\n");
+ DPRINTF(Rename, "Setting time buffer pointer.\n");
timeBuffer = tb_ptr;
// Setup wire to read information from time buffer, from IEW stage.
@@ -136,9 +187,9 @@ SimpleRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
template <class Impl>
void
-SimpleRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
+DefaultRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
{
- DPRINTF(Rename, "Rename: Setting rename queue pointer.\n");
+ DPRINTF(Rename, "Setting rename queue pointer.\n");
renameQueue = rq_ptr;
// Setup wire to write information to future stages.
@@ -147,9 +198,9 @@ SimpleRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
template <class Impl>
void
-SimpleRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
+DefaultRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
{
- DPRINTF(Rename, "Rename: Setting decode queue pointer.\n");
+ DPRINTF(Rename, "Setting decode queue pointer.\n");
decodeQueue = dq_ptr;
// Setup wire to get information from decode.
@@ -158,214 +209,721 @@ SimpleRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
template <class Impl>
void
-SimpleRename<Impl>::setRenameMap(RenameMap *rm_ptr)
+DefaultRename<Impl>::initStage()
{
- DPRINTF(Rename, "Rename: Setting rename map pointer.\n");
- renameMap = rm_ptr;
+ // Grab the number of free entries directly from the stages.
+ for (int tid=0; tid < numThreads; tid++) {
+ 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;
+ }
}
+template<class Impl>
+void
+DefaultRename<Impl>::setActiveThreads(list<unsigned> *at_ptr)
+{
+ DPRINTF(Rename, "Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
+}
+
+
template <class Impl>
void
-SimpleRename<Impl>::setFreeList(FreeList *fl_ptr)
+DefaultRename<Impl>::setRenameMap(RenameMap rm_ptr[])
{
- DPRINTF(Rename, "Rename: Setting free list pointer.\n");
+ DPRINTF(Rename, "Setting rename map pointers.\n");
+
+ for (int i=0; i<numThreads; i++) {
+ renameMap[i] = &rm_ptr[i];
+ }
+}
+
+template <class Impl>
+void
+DefaultRename<Impl>::setFreeList(FreeList *fl_ptr)
+{
+ DPRINTF(Rename, "Setting free list pointer.\n");
freeList = fl_ptr;
}
+template<class Impl>
+void
+DefaultRename<Impl>::setScoreboard(Scoreboard *_scoreboard)
+{
+ DPRINTF(Rename, "Setting scoreboard pointer.\n");
+ scoreboard = _scoreboard;
+}
+
+template <class Impl>
+void
+DefaultRename<Impl>::switchOut()
+{
+ // Rename is ready to switch out at any time.
+ cpu->signalSwitched();
+}
+
template <class Impl>
void
-SimpleRename<Impl>::dumpHistory()
+DefaultRename<Impl>::doSwitchOut()
{
- typename list<RenameHistory>::iterator buf_it = historyBuffer.begin();
+ // Clear any state, fix up the rename map.
+ for (int i = 0; i < numThreads; i++) {
+ typename list<RenameHistory>::iterator hb_it = historyBuffer[i].begin();
- while (buf_it != historyBuffer.end())
- {
- cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys "
- "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg,
- (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg);
+ while (!historyBuffer[i].empty()) {
+ assert(hb_it != historyBuffer[i].end());
- buf_it++;
+ DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence "
+ "number %i.\n", i, (*hb_it).instSeqNum);
+
+ // Tell the rename map to set the architected register to the
+ // previous physical register that it was renamed to.
+ renameMap[i]->setEntry(hb_it->archReg, hb_it->prevPhysReg);
+
+ // Put the renamed physical register back on the free list.
+ freeList->addReg(hb_it->newPhysReg);
+
+ historyBuffer[i].erase(hb_it++);
+ }
+ insts[i].clear();
+ skidBuffer[i].clear();
}
}
template <class Impl>
void
-SimpleRename<Impl>::block()
+DefaultRename<Impl>::takeOverFrom()
{
- DPRINTF(Rename, "Rename: Blocking.\n");
- // Set status to Blocked.
- _status = Blocked;
+ _status = Inactive;
+ initStage();
- // Add the current inputs onto the skid buffer, so they can be
- // reprocessed when this stage unblocks.
- skidBuffer.push(*fromDecode);
+ // Reset all state prior to taking over from the other CPU.
+ for (int i=0; i< numThreads; i++) {
+ renameStatus[i] = Idle;
+
+ stalls[i].iew = false;
+ stalls[i].commit = false;
+ serializeInst[i] = NULL;
+
+ instsInProgress[i] = 0;
- // Note that this stage only signals previous stages to stall when
- // it is the cause of the stall originates at this stage. Otherwise
- // the previous stages are expected to check all possible stall signals.
+ emptyROB[i] = true;
+
+ serializeOnNextInst[i] = false;
+ }
}
template <class Impl>
-inline void
-SimpleRename<Impl>::unblock()
-{
- DPRINTF(Rename, "Rename: Read instructions out of skid buffer this "
- "cycle.\n");
- // Remove the now processed instructions from the skid buffer.
- skidBuffer.pop();
-
- // If there's still information in the skid buffer, then
- // continue to tell previous stages to stall. They will be
- // able to restart once the skid buffer is empty.
- if (!skidBuffer.empty()) {
- toDecode->renameInfo.stall = true;
- } else {
- DPRINTF(Rename, "Rename: Done unblocking.\n");
- _status = Running;
+void
+DefaultRename<Impl>::squash(unsigned tid)
+{
+ DPRINTF(Rename, "[tid:%u]: Squashing instructions.\n",tid);
+
+ // Clear the stall signal if rename was blocked or unblocking before.
+ // If it still needs to block, the blocking should happen the next
+ // cycle and there should be space to hold everything due to the squash.
+ if (renameStatus[tid] == Blocked ||
+ renameStatus[tid] == Unblocking ||
+ renameStatus[tid] == SerializeStall) {
+
+ toDecode->renameUnblock[tid] = 1;
+
+ serializeInst[tid] = NULL;
}
+
+ // Set the status to Squashing.
+ renameStatus[tid] = Squashing;
+
+ // Squash any instructions from decode.
+ unsigned squashCount = 0;
+
+ for (int i=0; i<fromDecode->size; i++) {
+ if (fromDecode->insts[i]->threadNumber == tid) {
+ fromDecode->insts[i]->squashed = true;
+ wroteToTimeBuffer = true;
+ squashCount++;
+ }
+ }
+
+ insts[tid].clear();
+
+ // Clear the skid buffer in case it has any data in it.
+ skidBuffer[tid].clear();
+
+ doSquash(tid);
}
template <class Impl>
void
-SimpleRename<Impl>::doSquash()
+DefaultRename<Impl>::tick()
{
- typename list<RenameHistory>::iterator hb_it = historyBuffer.begin();
+ wroteToTimeBuffer = false;
- InstSeqNum squashed_seq_num = fromCommit->commitInfo.doneSeqNum;
+ blockThisCycle = false;
-#if FULL_SYSTEM
- assert(!historyBuffer.empty());
-#else
- // After a syscall squashes everything, the history buffer may be empty
- // but the ROB may still be squashing instructions.
- if (historyBuffer.empty()) {
+ bool status_change = false;
+
+ toIEWIndex = 0;
+
+ sortInsts();
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ // Check stall and squash signals.
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ DPRINTF(Rename, "Processing [tid:%i]\n", tid);
+
+ status_change = checkSignalsAndUpdate(tid) || status_change;
+
+ rename(status_change, tid);
+ }
+
+ if (status_change) {
+ updateStatus();
+ }
+
+ if (wroteToTimeBuffer) {
+ DPRINTF(Activity, "Activity this cycle.\n");
+ cpu->activityThisCycle();
+ }
+
+ threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ // If we committed this cycle then doneSeqNum will be > 0
+ if (fromCommit->commitInfo[tid].doneSeqNum != 0 &&
+ !fromCommit->commitInfo[tid].squash &&
+ renameStatus[tid] != Squashing) {
+
+ removeFromHistory(fromCommit->commitInfo[tid].doneSeqNum,
+ tid);
+ }
+ }
+
+ // @todo: make into updateProgress function
+ for (int tid=0; tid < numThreads; tid++) {
+ instsInProgress[tid] -= fromIEW->iewInfo[tid].dispatched;
+
+ assert(instsInProgress[tid] >=0);
+ }
+
+}
+
+template<class Impl>
+void
+DefaultRename<Impl>::rename(bool &status_change, unsigned tid)
+{
+ // If status is Running or idle,
+ // call renameInsts()
+ // If status is Unblocking,
+ // buffer any instructions coming from decode
+ // continue trying to empty skid buffer
+ // check if stall conditions have passed
+
+ if (renameStatus[tid] == Blocked) {
+ ++renameBlockCycles;
+ } else if (renameStatus[tid] == Squashing) {
+ ++renameSquashCycles;
+ } else if (renameStatus[tid] == SerializeStall) {
+ ++renameSerializeStallCycles;
+ }
+
+ if (renameStatus[tid] == Running ||
+ renameStatus[tid] == Idle) {
+ DPRINTF(Rename, "[tid:%u]: Not blocked, so attempting to run "
+ "stage.\n", tid);
+
+ renameInsts(tid);
+ } else if (renameStatus[tid] == Unblocking) {
+ renameInsts(tid);
+
+ if (validInsts()) {
+ // Add the current inputs to the skid buffer so they can be
+ // reprocessed when this stage unblocks.
+ skidInsert(tid);
+ }
+
+ // If we switched over to blocking, then there's a potential for
+ // an overall status change.
+ status_change = unblock(tid) || status_change || blockThisCycle;
+ }
+}
+
+template <class Impl>
+void
+DefaultRename<Impl>::renameInsts(unsigned tid)
+{
+ // Instructions can be either in the skid buffer or the queue of
+ // instructions coming from decode, depending on the status.
+ int insts_available = renameStatus[tid] == Unblocking ?
+ skidBuffer[tid].size() : insts[tid].size();
+
+ // Check the decode queue to see if instructions are available.
+ // If there are no available instructions to rename, then do nothing.
+ if (insts_available == 0) {
+ DPRINTF(Rename, "[tid:%u]: Nothing to do, breaking out early.\n",
+ tid);
+ // Should I change status to idle?
+ ++renameIdleCycles;
return;
+ } else if (renameStatus[tid] == Unblocking) {
+ ++renameUnblockCycles;
+ } else if (renameStatus[tid] == Running) {
+ ++renameRunCycles;
}
-#endif // FULL_SYSTEM
- // Go through the most recent instructions, undoing the mappings
- // they did and freeing up the registers.
- while ((*hb_it).instSeqNum > squashed_seq_num)
- {
- assert(hb_it != historyBuffer.end());
+ DynInstPtr inst;
- DPRINTF(Rename, "Rename: Removing history entry with sequence "
- "number %i.\n", (*hb_it).instSeqNum);
+ // Will have to do a different calculation for the number of free
+ // entries.
+ int free_rob_entries = calcFreeROBEntries(tid);
+ int free_iq_entries = calcFreeIQEntries(tid);
+ int free_lsq_entries = calcFreeLSQEntries(tid);
+ int min_free_entries = free_rob_entries;
- // If it's not simply a place holder, then add the registers.
- if (!(*hb_it).placeHolder) {
- // Tell the rename map to set the architected register to the
- // previous physical register that it was renamed to.
- renameMap->setEntry(hb_it->archReg, hb_it->prevPhysReg);
+ FullSource source = ROB;
- // Put the renamed physical register back on the free list.
- freeList->addReg(hb_it->newPhysReg);
+ if (free_iq_entries < min_free_entries) {
+ min_free_entries = free_iq_entries;
+ source = IQ;
+ }
- ++renameValidUndoneMaps;
+ if (free_lsq_entries < min_free_entries) {
+ min_free_entries = free_lsq_entries;
+ source = LSQ;
+ }
+
+ // Check if there's any space left.
+ if (min_free_entries <= 0) {
+ DPRINTF(Rename, "[tid:%u]: Blocking due to no free ROB/IQ/LSQ "
+ "entries.\n"
+ "ROB has %i free entries.\n"
+ "IQ has %i free entries.\n"
+ "LSQ has %i free entries.\n",
+ tid,
+ free_rob_entries,
+ free_iq_entries,
+ free_lsq_entries);
+
+ blockThisCycle = true;
+
+ block(tid);
+
+ incrFullStat(source);
+
+ return;
+ } else if (min_free_entries < insts_available) {
+ DPRINTF(Rename, "[tid:%u]: Will have to block this cycle."
+ "%i insts available, but only %i insts can be "
+ "renamed due to ROB/IQ/LSQ limits.\n",
+ tid, insts_available, min_free_entries);
+
+ insts_available = min_free_entries;
+
+ blockThisCycle = true;
+
+ incrFullStat(source);
+ }
+
+ InstQueue &insts_to_rename = renameStatus[tid] == Unblocking ?
+ skidBuffer[tid] : insts[tid];
+
+ DPRINTF(Rename, "[tid:%u]: %i available instructions to "
+ "send iew.\n", tid, insts_available);
+
+ DPRINTF(Rename, "[tid:%u]: %i insts pipelining from Rename | %i insts "
+ "dispatched to IQ last cycle.\n",
+ tid, instsInProgress[tid], fromIEW->iewInfo[tid].dispatched);
+
+ // Handle serializing the next instruction if necessary.
+ if (serializeOnNextInst[tid]) {
+ if (emptyROB[tid] && instsInProgress[tid] == 0) {
+ // ROB already empty; no need to serialize.
+ serializeOnNextInst[tid] = false;
+ } else if (!insts_to_rename.empty()) {
+ insts_to_rename.front()->setSerializeBefore();
}
+ }
- historyBuffer.erase(hb_it++);
+ int renamed_insts = 0;
- ++renameUndoneMaps;
+ while (insts_available > 0 && toIEWIndex < renameWidth) {
+ DPRINTF(Rename, "[tid:%u]: Sending instructions to IEW.\n", tid);
+
+ assert(!insts_to_rename.empty());
+
+ inst = insts_to_rename.front();
+
+ insts_to_rename.pop_front();
+
+ if (renameStatus[tid] == Unblocking) {
+ DPRINTF(Rename,"[tid:%u]: Removing [sn:%lli] PC:%#x from rename "
+ "skidBuffer\n",
+ tid, inst->seqNum, inst->readPC());
+ }
+
+ if (inst->isSquashed()) {
+ DPRINTF(Rename, "[tid:%u]: instruction %i with PC %#x is "
+ "squashed, skipping.\n",
+ tid, inst->seqNum, inst->threadNumber,inst->readPC());
+
+ ++renameSquashedInsts;
+
+ // Decrement how many instructions are available.
+ --insts_available;
+
+ continue;
+ }
+
+ DPRINTF(Rename, "[tid:%u]: Processing instruction [sn:%lli] with "
+ "PC %#x.\n",
+ tid, inst->seqNum, inst->readPC());
+
+ // Handle serializeAfter/serializeBefore instructions.
+ // serializeAfter marks the next instruction as serializeBefore.
+ // serializeBefore makes the instruction wait in rename until the ROB
+ // is empty.
+
+ // In this model, IPR accesses are serialize before
+ // instructions, and store conditionals are serialize after
+ // instructions. This is mainly due to lack of support for
+ // out-of-order operations of either of those classes of
+ // instructions.
+ if ((inst->isIprAccess() || inst->isSerializeBefore()) &&
+ !inst->isSerializeHandled()) {
+ DPRINTF(Rename, "Serialize before instruction encountered.\n");
+
+ if (!inst->isTempSerializeBefore()) {
+ renamedSerializing++;
+ inst->setSerializeHandled();
+ } else {
+ renamedTempSerializing++;
+ }
+
+ // Change status over to SerializeStall so that other stages know
+ // what this is blocked on.
+ renameStatus[tid] = SerializeStall;
+
+ serializeInst[tid] = inst;
+
+ blockThisCycle = true;
+
+ break;
+ } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) &&
+ !inst->isSerializeHandled()) {
+ DPRINTF(Rename, "Serialize after instruction encountered.\n");
+
+ renamedSerializing++;
+
+ inst->setSerializeHandled();
+
+ serializeAfter(insts_to_rename, tid);
+ }
+
+ // Check here to make sure there are enough destination registers
+ // to rename to. Otherwise block.
+ if (renameMap[tid]->numFreeEntries() < inst->numDestRegs()) {
+ DPRINTF(Rename, "Blocking due to lack of free "
+ "physical registers to rename to.\n");
+ blockThisCycle = true;
+
+ ++renameFullRegistersEvents;
+
+ break;
+ }
+
+ renameSrcRegs(inst, inst->threadNumber);
+
+ renameDestRegs(inst, inst->threadNumber);
+
+ ++renamed_insts;
+
+ // Put instruction in rename queue.
+ toIEW->insts[toIEWIndex] = inst;
+ ++(toIEW->size);
+
+ // Increment which instruction we're on.
+ ++toIEWIndex;
+
+ // Decrement how many instructions are available.
+ --insts_available;
+ }
+
+ instsInProgress[tid] += renamed_insts;
+ renameRenamedInsts += renamed_insts;
+
+ // If we wrote to the time buffer, record this.
+ if (toIEWIndex) {
+ wroteToTimeBuffer = true;
+ }
+
+ // Check if there's any instructions left that haven't yet been renamed.
+ // If so then block.
+ if (insts_available) {
+ blockThisCycle = true;
+ }
+
+ if (blockThisCycle) {
+ block(tid);
+ toDecode->renameUnblock[tid] = false;
}
}
+template<class Impl>
+void
+DefaultRename<Impl>::skidInsert(unsigned tid)
+{
+ DynInstPtr inst = NULL;
+
+ while (!insts[tid].empty()) {
+ inst = insts[tid].front();
+
+ insts[tid].pop_front();
+
+ assert(tid == inst->threadNumber);
+
+ DPRINTF(Rename, "[tid:%u]: Inserting [sn:%lli] PC:%#x into Rename "
+ "skidBuffer\n", tid, inst->seqNum, inst->readPC());
+
+ ++renameSkidInsts;
+
+ skidBuffer[tid].push_back(inst);
+ }
+
+ if (skidBuffer[tid].size() > skidBufferMax)
+ panic("Skidbuffer Exceeded Max Size");
+}
+
template <class Impl>
void
-SimpleRename<Impl>::squash()
+DefaultRename<Impl>::sortInsts()
{
- DPRINTF(Rename, "Rename: Squashing instructions.\n");
- // Set the status to Squashing.
- _status = Squashing;
+ int insts_from_decode = fromDecode->size;
+#ifdef DEBUG
+ for (int i=0; i < numThreads; i++)
+ assert(insts[i].empty());
+#endif
+ for (int i = 0; i < insts_from_decode; ++i) {
+ DynInstPtr inst = fromDecode->insts[i];
+ insts[inst->threadNumber].push_back(inst);
+ }
+}
- numInst = 0;
+template<class Impl>
+bool
+DefaultRename<Impl>::skidsEmpty()
+{
+ list<unsigned>::iterator threads = (*activeThreads).begin();
- // Clear the skid buffer in case it has any data in it.
- while (!skidBuffer.empty())
- {
- skidBuffer.pop();
+ while (threads != (*activeThreads).end()) {
+ if (!skidBuffer[*threads++].empty())
+ return false;
}
- doSquash();
+ return true;
}
template<class Impl>
void
-SimpleRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num)
+DefaultRename<Impl>::updateStatus()
{
- DPRINTF(Rename, "Rename: Removing a committed instruction from the "
- "history buffer, until sequence number %lli.\n", inst_seq_num);
- typename list<RenameHistory>::iterator hb_it = historyBuffer.end();
+ bool any_unblocking = false;
- --hb_it;
+ list<unsigned>::iterator threads = (*activeThreads).begin();
- if (hb_it->instSeqNum > inst_seq_num) {
- DPRINTF(Rename, "Rename: Old sequence number encountered. Ensure "
- "that a syscall happened recently.\n");
- return;
+ threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (renameStatus[tid] == Unblocking) {
+ any_unblocking = true;
+ break;
+ }
}
- while ((*hb_it).instSeqNum != inst_seq_num)
- {
- // Make sure we haven't gone off the end of the list.
- assert(hb_it != historyBuffer.end());
-
- // In theory instructions at the end of the history buffer
- // should be older than the instruction being removed, which
- // means they will have a lower sequence number. Also the
- // instruction being removed from the history really should
- // be the last instruction in the list, as it is the instruction
- // that was just committed that is being removed.
- assert(hb_it->instSeqNum < inst_seq_num);
- DPRINTF(Rename, "Rename: Freeing up older rename of reg %i, sequence"
- " number %i.\n",
- (*hb_it).prevPhysReg, (*hb_it).instSeqNum);
-
- if (!(*hb_it).placeHolder) {
- freeList->addReg((*hb_it).prevPhysReg);
- ++renameCommittedMaps;
+ // Rename will have activity if it's unblocking.
+ if (any_unblocking) {
+ if (_status == Inactive) {
+ _status = Active;
+
+ DPRINTF(Activity, "Activating stage.\n");
+
+ cpu->activateStage(FullCPU::RenameIdx);
}
+ } else {
+ // If it's not unblocking, then rename will not have any internal
+ // activity. Switch it to inactive.
+ if (_status == Active) {
+ _status = Inactive;
+ DPRINTF(Activity, "Deactivating stage.\n");
- historyBuffer.erase(hb_it--);
+ cpu->deactivateStage(FullCPU::RenameIdx);
+ }
}
+}
- // Finally free up the previous register of the finished instruction
- // itself.
- if (!(*hb_it).placeHolder) {
- freeList->addReg(hb_it->prevPhysReg);
- ++renameCommittedMaps;
+template <class Impl>
+bool
+DefaultRename<Impl>::block(unsigned tid)
+{
+ DPRINTF(Rename, "[tid:%u]: Blocking.\n", tid);
+
+ // Add the current inputs onto the skid buffer, so they can be
+ // reprocessed when this stage unblocks.
+ skidInsert(tid);
+
+ // Only signal backwards to block if the previous stages do not think
+ // rename is already blocked.
+ if (renameStatus[tid] != Blocked) {
+ if (renameStatus[tid] != Unblocking) {
+ toDecode->renameBlock[tid] = true;
+ toDecode->renameUnblock[tid] = false;
+ wroteToTimeBuffer = true;
+ }
+
+ // Rename can not go from SerializeStall to Blocked, otherwise
+ // it would not know to complete the serialize stall.
+ if (renameStatus[tid] != SerializeStall) {
+ // Set status to Blocked.
+ renameStatus[tid] = Blocked;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <class Impl>
+bool
+DefaultRename<Impl>::unblock(unsigned tid)
+{
+ DPRINTF(Rename, "[tid:%u]: Trying to unblock.\n", tid);
+
+ // Rename is done unblocking if the skid buffer is empty.
+ if (skidBuffer[tid].empty() && renameStatus[tid] != SerializeStall) {
+
+ DPRINTF(Rename, "[tid:%u]: Done unblocking.\n", tid);
+
+ toDecode->renameUnblock[tid] = true;
+ wroteToTimeBuffer = true;
+
+ renameStatus[tid] = Running;
+ return true;
}
- historyBuffer.erase(hb_it);
+ return false;
+}
+
+template <class Impl>
+void
+DefaultRename<Impl>::doSquash(unsigned tid)
+{
+ typename list<RenameHistory>::iterator hb_it = historyBuffer[tid].begin();
+
+ InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum;
+
+ // After a syscall squashes everything, the history buffer may be empty
+ // but the ROB may still be squashing instructions.
+ if (historyBuffer[tid].empty()) {
+ return;
+ }
+
+ // Go through the most recent instructions, undoing the mappings
+ // they did and freeing up the registers.
+ while (!historyBuffer[tid].empty() &&
+ (*hb_it).instSeqNum > squashed_seq_num) {
+ 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);
+
+ historyBuffer[tid].erase(hb_it++);
+
+ ++renameUndoneMaps;
+ }
+}
+
+template<class Impl>
+void
+DefaultRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num, unsigned tid)
+{
+ DPRINTF(Rename, "[tid:%u]: Removing a committed instruction from the "
+ "history buffer %u (size=%i), until [sn:%lli].\n",
+ tid, tid, historyBuffer[tid].size(), inst_seq_num);
+
+ typename list<RenameHistory>::iterator hb_it = historyBuffer[tid].end();
+
+ --hb_it;
+
+ if (historyBuffer[tid].empty()) {
+ DPRINTF(Rename, "[tid:%u]: History buffer is empty.\n", tid);
+ return;
+ } else if (hb_it->instSeqNum > inst_seq_num) {
+ DPRINTF(Rename, "[tid:%u]: Old sequence number encountered. Ensure "
+ "that a syscall happened recently.\n", tid);
+ return;
+ }
+
+ // Commit all the renames up until (and including) the committed sequence
+ // number. Some or even all of the committed instructions may not have
+ // rename histories if they did not have destination registers that were
+ // renamed.
+ while (!historyBuffer[tid].empty() &&
+ hb_it != historyBuffer[tid].end() &&
+ (*hb_it).instSeqNum <= inst_seq_num) {
+
+ DPRINTF(Rename, "[tid:%u]: Freeing up older rename of reg %i, "
+ "[sn:%lli].\n",
+ tid, (*hb_it).prevPhysReg, (*hb_it).instSeqNum);
+
+ freeList->addReg((*hb_it).prevPhysReg);
+ ++renameCommittedMaps;
+
+ historyBuffer[tid].erase(hb_it--);
+ }
}
template <class Impl>
inline void
-SimpleRename<Impl>::renameSrcRegs(DynInstPtr &inst)
+DefaultRename<Impl>::renameSrcRegs(DynInstPtr &inst,unsigned tid)
{
+ assert(renameMap[tid] != 0);
+
unsigned num_src_regs = inst->numSrcRegs();
// Get the architectual register numbers from the source and
// destination operands, and redirect them to the right register.
// Will need to mark dependencies though.
- for (int src_idx = 0; src_idx < num_src_regs; src_idx++)
- {
+ for (int src_idx = 0; src_idx < num_src_regs; src_idx++) {
RegIndex src_reg = inst->srcRegIdx(src_idx);
// Look up the source registers to get the phys. register they've
// been renamed to, and set the sources to those registers.
- PhysRegIndex renamed_reg = renameMap->lookup(src_reg);
+ PhysRegIndex renamed_reg = renameMap[tid]->lookup(src_reg);
- DPRINTF(Rename, "Rename: Looking up arch reg %i, got "
- "physical reg %i.\n", (int)src_reg, (int)renamed_reg);
+ DPRINTF(Rename, "[tid:%u]: Looking up arch reg %i, got "
+ "physical reg %i.\n", tid, (int)src_reg,
+ (int)renamed_reg);
inst->renameSrcReg(src_idx, renamed_reg);
- // Either incorporate it into the info passed back,
- // or make another function call to see if that register is
- // ready or not.
- if (renameMap->isReady(renamed_reg)) {
- DPRINTF(Rename, "Rename: Register is ready.\n");
+ // See if the register is ready or not.
+ if (scoreboard->getReg(renamed_reg) == true) {
+ DPRINTF(Rename, "[tid:%u]: Register is ready.\n", tid);
inst->markSrcRegReady(src_idx);
}
@@ -376,379 +934,341 @@ SimpleRename<Impl>::renameSrcRegs(DynInstPtr &inst)
template <class Impl>
inline void
-SimpleRename<Impl>::renameDestRegs(DynInstPtr &inst)
+DefaultRename<Impl>::renameDestRegs(DynInstPtr &inst,unsigned tid)
{
- typename SimpleRenameMap::RenameInfo rename_result;
+ typename RenameMap::RenameInfo rename_result;
unsigned num_dest_regs = inst->numDestRegs();
- // If it's an instruction with no destination registers, then put
- // a placeholder within the history buffer. It might be better
- // to not put it in the history buffer at all (other than branches,
- // which always need at least a place holder), and differentiate
- // between instructions with and without destination registers
- // when getting from commit the instructions that committed.
- if (num_dest_regs == 0) {
- RenameHistory hb_entry(inst->seqNum);
+ // Rename the destination registers.
+ for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++) {
+ RegIndex dest_reg = inst->destRegIdx(dest_idx);
- historyBuffer.push_front(hb_entry);
+ // Get the physical register that the destination will be
+ // renamed to.
+ rename_result = renameMap[tid]->rename(dest_reg);
- DPRINTF(Rename, "Rename: Adding placeholder instruction to "
- "history buffer, sequence number %lli.\n",
- inst->seqNum);
+ //Mark Scoreboard entry as not ready
+ scoreboard->unsetReg(rename_result.first);
- ++renameHBPlaceHolders;
- } else {
+ DPRINTF(Rename, "[tid:%u]: Renaming arch reg %i to physical "
+ "reg %i.\n", tid, (int)dest_reg,
+ (int)rename_result.first);
- // Rename the destination registers.
- for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++)
- {
- RegIndex dest_reg = inst->destRegIdx(dest_idx);
+ // Record the rename information so that a history can be kept.
+ RenameHistory hb_entry(inst->seqNum, dest_reg,
+ rename_result.first,
+ rename_result.second);
- // Get the physical register that the destination will be
- // renamed to.
- rename_result = renameMap->rename(dest_reg);
+ historyBuffer[tid].push_front(hb_entry);
- DPRINTF(Rename, "Rename: Renaming arch reg %i to physical "
- "reg %i.\n", (int)dest_reg,
- (int)rename_result.first);
+ DPRINTF(Rename, "[tid:%u]: Adding instruction to history buffer, "
+ "[sn:%lli].\n",tid,
+ (*historyBuffer[tid].begin()).instSeqNum);
- // Record the rename information so that a history can be kept.
- RenameHistory hb_entry(inst->seqNum, dest_reg,
- rename_result.first,
- rename_result.second);
+ // Tell the instruction to rename the appropriate destination
+ // register (dest_idx) to the new physical register
+ // (rename_result.first), and record the previous physical
+ // register that the same logical register was renamed to
+ // (rename_result.second).
+ inst->renameDestReg(dest_idx,
+ rename_result.first,
+ rename_result.second);
- historyBuffer.push_front(hb_entry);
+ ++renameRenamedOperands;
+ }
+}
- DPRINTF(Rename, "Rename: Adding instruction to history buffer, "
- "sequence number %lli.\n",
- (*historyBuffer.begin()).instSeqNum);
+template <class Impl>
+inline int
+DefaultRename<Impl>::calcFreeROBEntries(unsigned tid)
+{
+ int num_free = freeEntries[tid].robEntries -
+ (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched);
- // Tell the instruction to rename the appropriate destination
- // register (dest_idx) to the new physical register
- // (rename_result.first), and record the previous physical
- // register that the same logical register was renamed to
- // (rename_result.second).
- inst->renameDestReg(dest_idx,
- rename_result.first,
- rename_result.second);
+ //DPRINTF(Rename,"[tid:%i]: %i rob free\n",tid,num_free);
- ++renameRenamedOperands;
- }
- }
+ return num_free;
}
template <class Impl>
inline int
-SimpleRename<Impl>::calcFreeROBEntries()
+DefaultRename<Impl>::calcFreeIQEntries(unsigned tid)
{
- return fromCommit->commitInfo.freeROBEntries -
- renameWidth * iewToRenameDelay;
+ int num_free = freeEntries[tid].iqEntries -
+ (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched);
+
+ //DPRINTF(Rename,"[tid:%i]: %i iq free\n",tid,num_free);
+
+ return num_free;
}
template <class Impl>
inline int
-SimpleRename<Impl>::calcFreeIQEntries()
+DefaultRename<Impl>::calcFreeLSQEntries(unsigned tid)
{
- return fromIEW->iewInfo.freeIQEntries - renameWidth * iewToRenameDelay;
-}
+ int num_free = freeEntries[tid].lsqEntries -
+ (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatchedToLSQ);
-template<class Impl>
-void
-SimpleRename<Impl>::tick()
-{
- // Rename will need to try to rename as many instructions as it
- // has bandwidth, unless it is blocked.
-
- // Check if _status is BarrierStall. If so, then check if the number
- // of free ROB entries is equal to the number of total ROB entries.
- // Once equal then wake this stage up. Set status to unblocking maybe.
-
- if (_status != Blocked && _status != Squashing) {
- DPRINTF(Rename, "Rename: Status is not blocked, will attempt to "
- "run stage.\n");
- // Make sure that the skid buffer has something in it if the
- // status is unblocking.
- assert(_status == Unblocking ? !skidBuffer.empty() : 1);
-
- rename();
-
- // If the status was unblocking, then instructions from the skid
- // buffer were used. Remove those instructions and handle
- // the rest of unblocking.
- if (_status == Unblocking) {
- ++renameUnblockCycles;
-
- if (fromDecode->size > 0) {
- // Add the current inputs onto the skid buffer, so they can be
- // reprocessed when this stage unblocks.
- skidBuffer.push(*fromDecode);
- }
+ //DPRINTF(Rename,"[tid:%i]: %i lsq free\n",tid,num_free);
- unblock();
- }
- } else if (_status == Blocked) {
- ++renameBlockCycles;
+ return num_free;
+}
- // If stage is blocked and still receiving valid instructions,
- // make sure to store them in the skid buffer.
- if (fromDecode->size > 0) {
+template <class Impl>
+unsigned
+DefaultRename<Impl>::validInsts()
+{
+ unsigned inst_count = 0;
- block();
+ for (int i=0; i<fromDecode->size; i++) {
+ if (!fromDecode->insts[i]->squashed)
+ inst_count++;
+ }
- // Continue to tell previous stage to stall.
- toDecode->renameInfo.stall = true;
- }
+ return inst_count;
+}
- if (!fromIEW->iewInfo.stall &&
- !fromCommit->commitInfo.stall &&
- calcFreeROBEntries() > 0 &&
- calcFreeIQEntries() > 0 &&
- renameMap->numFreeEntries() > 0) {
-
- // Need to be sure to check all blocking conditions above.
- // If they have cleared, then start unblocking.
- DPRINTF(Rename, "Rename: Stall signals cleared, going to "
- "unblock.\n");
- _status = Unblocking;
-
- // Continue to tell previous stage to block until this stage
- // is done unblocking.
- toDecode->renameInfo.stall = true;
- } else {
- // Otherwise no conditions have changed. Tell previous
- // stage to continue blocking.
- toDecode->renameInfo.stall = true;
- }
+template <class Impl>
+void
+DefaultRename<Impl>::readStallSignals(unsigned tid)
+{
+ if (fromIEW->iewBlock[tid]) {
+ stalls[tid].iew = true;
+ }
- if (fromCommit->commitInfo.squash ||
- fromCommit->commitInfo.robSquashing) {
- squash();
- return;
- }
- } else if (_status == Squashing) {
- ++renameSquashCycles;
+ if (fromIEW->iewUnblock[tid]) {
+ assert(stalls[tid].iew);
+ stalls[tid].iew = false;
+ }
- if (fromCommit->commitInfo.squash) {
- squash();
- } else if (!fromCommit->commitInfo.squash &&
- !fromCommit->commitInfo.robSquashing) {
+ if (fromCommit->commitBlock[tid]) {
+ stalls[tid].commit = true;
+ }
- DPRINTF(Rename, "Rename: Done squashing, going to running.\n");
- _status = Running;
- rename();
- } else {
- doSquash();
- }
+ if (fromCommit->commitUnblock[tid]) {
+ assert(stalls[tid].commit);
+ stalls[tid].commit = false;
}
+}
- // Ugly code, revamp all of the tick() functions eventually.
- if (fromCommit->commitInfo.doneSeqNum != 0 && _status != Squashing) {
-#if !FULL_SYSTEM
- if (!fromCommit->commitInfo.squash) {
- removeFromHistory(fromCommit->commitInfo.doneSeqNum);
- }
-#else
- removeFromHistory(fromCommit->commitInfo.doneSeqNum);
-#endif
+template <class Impl>
+bool
+DefaultRename<Impl>::checkStall(unsigned tid)
+{
+ bool ret_val = false;
+
+ if (stalls[tid].iew) {
+ DPRINTF(Rename,"[tid:%i]: Stall from IEW stage detected.\n", tid);
+ ret_val = true;
+ } else if (stalls[tid].commit) {
+ DPRINTF(Rename,"[tid:%i]: Stall from Commit stage detected.\n", tid);
+ ret_val = true;
+ } else if (calcFreeROBEntries(tid) <= 0) {
+ DPRINTF(Rename,"[tid:%i]: Stall: ROB has 0 free entries.\n", tid);
+ ret_val = true;
+ } else if (calcFreeIQEntries(tid) <= 0) {
+ DPRINTF(Rename,"[tid:%i]: Stall: IQ has 0 free entries.\n", tid);
+ ret_val = true;
+ } else if (calcFreeLSQEntries(tid) <= 0) {
+ DPRINTF(Rename,"[tid:%i]: Stall: LSQ has 0 free entries.\n", tid);
+ ret_val = true;
+ } else if (renameMap[tid]->numFreeEntries() <= 0) {
+ DPRINTF(Rename,"[tid:%i]: Stall: RenameMap has 0 free entries.\n", tid);
+ ret_val = true;
+ } else if (renameStatus[tid] == SerializeStall &&
+ (!emptyROB[tid] || instsInProgress[tid])) {
+ DPRINTF(Rename,"[tid:%i]: Stall: Serialize stall and ROB is not "
+ "empty.\n",
+ tid);
+ ret_val = true;
}
+ return ret_val;
}
-template<class Impl>
+template <class Impl>
void
-SimpleRename<Impl>::rename()
-{
- // Check if any of the stages ahead of rename are telling rename
- // to squash. The squash() function will also take care of fixing up
- // the rename map and the free list.
- if (fromCommit->commitInfo.squash ||
- fromCommit->commitInfo.robSquashing) {
- DPRINTF(Rename, "Rename: Receiving signal from Commit to squash.\n");
- squash();
- return;
+DefaultRename<Impl>::readFreeEntries(unsigned tid)
+{
+ bool updated = false;
+ if (fromIEW->iewInfo[tid].usedIQ) {
+ freeEntries[tid].iqEntries =
+ fromIEW->iewInfo[tid].freeIQEntries;
+ updated = true;
}
- // Check if time buffer is telling this stage to stall.
- if (fromIEW->iewInfo.stall ||
- fromCommit->commitInfo.stall) {
- DPRINTF(Rename, "Rename: Receiving signal from IEW/Commit to "
- "stall.\n");
- block();
- return;
+ if (fromIEW->iewInfo[tid].usedLSQ) {
+ freeEntries[tid].lsqEntries =
+ fromIEW->iewInfo[tid].freeLSQEntries;
+ updated = true;
}
- // Check if the current status is squashing. If so, set its status
- // to running and resume execution the next cycle.
- if (_status == Squashing) {
- DPRINTF(Rename, "Rename: Done squashing.\n");
- _status = Running;
- return;
+ if (fromCommit->commitInfo[tid].usedROB) {
+ freeEntries[tid].robEntries =
+ fromCommit->commitInfo[tid].freeROBEntries;
+ emptyROB[tid] = fromCommit->commitInfo[tid].emptyROB;
+ updated = true;
}
- // Check the decode queue to see if instructions are available.
- // If there are no available instructions to rename, then do nothing.
- // Or, if the stage is currently unblocking, then go ahead and run it.
- if (fromDecode->size == 0 && _status != Unblocking) {
- DPRINTF(Rename, "Rename: Nothing to do, breaking out early.\n");
- // Should I change status to idle?
- return;
- }
-
- ////////////////////////////////////
- // Actual rename part.
- ////////////////////////////////////
-
- DynInstPtr inst;
+ DPRINTF(Rename, "[tid:%i]: Free IQ: %i, Free ROB: %i, Free LSQ: %i\n",
+ tid,
+ freeEntries[tid].iqEntries,
+ freeEntries[tid].robEntries,
+ freeEntries[tid].lsqEntries);
- // If we're unblocking, then we may be in the middle of an instruction
- // group. Subtract off numInst to get the proper number of instructions
- // left.
- int insts_available = _status == Unblocking ?
- skidBuffer.front().size - numInst :
- fromDecode->size;
+ DPRINTF(Rename, "[tid:%i]: %i instructions not yet in ROB\n",
+ tid, instsInProgress[tid]);
+}
- bool block_this_cycle = false;
+template <class Impl>
+bool
+DefaultRename<Impl>::checkSignalsAndUpdate(unsigned tid)
+{
+ // Check if there's a squash signal, squash if there is
+ // Check stall signals, block if necessary.
+ // If status was blocked
+ // check if stall conditions have passed
+ // if so then go to unblocking
+ // If status was Squashing
+ // check if squashing is not high. Switch to running this cycle.
+ // If status was serialize stall
+ // check if ROB is empty and no insts are in flight to the ROB
+
+ readFreeEntries(tid);
+ readStallSignals(tid);
+
+ if (fromCommit->commitInfo[tid].squash) {
+ DPRINTF(Rename, "[tid:%u]: Squashing instructions due to squash from "
+ "commit.\n", tid);
+
+ squash(tid);
+
+ return true;
+ }
- // Will have to do a different calculation for the number of free
- // entries. Number of free entries recorded on this cycle -
- // renameWidth * renameToDecodeDelay
- int free_rob_entries = calcFreeROBEntries();
- int free_iq_entries = calcFreeIQEntries();
- int min_iq_rob = min(free_rob_entries, free_iq_entries);
+ if (fromCommit->commitInfo[tid].robSquashing) {
+ DPRINTF(Rename, "[tid:%u]: ROB is still squashing.\n", tid);
- unsigned to_iew_index = 0;
+ renameStatus[tid] = Squashing;
- // Check if there's any space left.
- if (min_iq_rob <= 0) {
- DPRINTF(Rename, "Rename: Blocking due to no free ROB or IQ "
- "entries.\n"
- "Rename: ROB has %d free entries.\n"
- "Rename: IQ has %d free entries.\n",
- free_rob_entries,
- free_iq_entries);
- block();
- // Tell previous stage to stall.
- toDecode->renameInfo.stall = true;
+ return true;
+ }
- if (free_rob_entries <= 0) {
- ++renameROBFullEvents;
- } else {
- ++renameIQFullEvents;
- }
+ if (checkStall(tid)) {
+ return block(tid);
+ }
- return;
- } else if (min_iq_rob < insts_available) {
- DPRINTF(Rename, "Rename: Will have to block this cycle. Only "
- "%i insts can be renamed due to IQ/ROB limits.\n",
- min_iq_rob);
+ if (renameStatus[tid] == Blocked) {
+ DPRINTF(Rename, "[tid:%u]: Done blocking, switching to unblocking.\n",
+ tid);
- insts_available = min_iq_rob;
+ renameStatus[tid] = Unblocking;
- block_this_cycle = true;
+ unblock(tid);
- if (free_rob_entries < free_iq_entries) {
- ++renameROBFullEvents;
- } else {
- ++renameIQFullEvents;
- }
+ return true;
}
- while (insts_available > 0) {
- DPRINTF(Rename, "Rename: Sending instructions to iew.\n");
+ if (renameStatus[tid] == Squashing) {
+ // Switch status to running if rename isn't being told to block or
+ // squash this cycle.
+ DPRINTF(Rename, "[tid:%u]: Done squashing, switching to running.\n",
+ tid);
- // Get the next instruction either from the skid buffer or the
- // decode queue.
- inst = _status == Unblocking ? skidBuffer.front().insts[numInst] :
- fromDecode->insts[numInst];
+ renameStatus[tid] = Running;
- if (inst->isSquashed()) {
- DPRINTF(Rename, "Rename: instruction %i with PC %#x is "
- "squashed, skipping.\n",
- inst->seqNum, inst->readPC());
+ return false;
+ }
- // Go to the next instruction.
- ++numInst;
+ if (renameStatus[tid] == SerializeStall) {
+ // Stall ends once the ROB is free.
+ DPRINTF(Rename, "[tid:%u]: Done with serialize stall, switching to "
+ "unblocking.\n", tid);
- ++renameSquashedInsts;
+ DynInstPtr serial_inst = serializeInst[tid];
- // Decrement how many instructions are available.
- --insts_available;
+ renameStatus[tid] = Unblocking;
- continue;
- }
+ unblock(tid);
- DPRINTF(Rename, "Rename: Processing instruction %i with PC %#x.\n",
- inst->seqNum, inst->readPC());
-
- // If it's a trap instruction, then it needs to wait here within
- // rename until the ROB is empty. Needs a way to detect that the
- // ROB is empty. Maybe an event?
- // Would be nice if it could be avoided putting this into a
- // specific stage and instead just put it into the AlphaFullCPU.
- // Might not really be feasible though...
- // (EXCB, TRAPB)
- if (inst->isSerializing()) {
- panic("Rename: Serializing instruction encountered.\n");
- DPRINTF(Rename, "Rename: Serializing instruction "
- "encountered.\n");
-
- // Change status over to BarrierStall so that other stages know
- // what this is blocked on.
- _status = BarrierStall;
+ DPRINTF(Rename, "[tid:%u]: Processing instruction [%lli] with "
+ "PC %#x.\n",
+ tid, serial_inst->seqNum, serial_inst->readPC());
- block_this_cycle = true;
+ // Put instruction into queue here.
+ serial_inst->clearSerializeBefore();
- break;
+ if (!skidBuffer[tid].empty()) {
+ skidBuffer[tid].push_front(serial_inst);
+ } else {
+ insts[tid].push_front(serial_inst);
}
- // Check here to make sure there are enough destination registers
- // to rename to. Otherwise block.
- if (renameMap->numFreeEntries() < inst->numDestRegs())
- {
- DPRINTF(Rename, "Rename: Blocking due to lack of free "
- "physical registers to rename to.\n");
- // Need some sort of event based on a register being freed.
-
- block_this_cycle = true;
+ DPRINTF(Rename, "[tid:%u]: Instruction must be processed by rename."
+ " Adding to front of list.", tid);
- ++renameFullRegistersEvents;
+ serializeInst[tid] = NULL;
- break;
- }
+ return true;
+ }
- renameSrcRegs(inst);
+ // If we've reached this point, we have not gotten any signals that
+ // cause rename to change its status. Rename remains the same as before.
+ return false;
+}
- renameDestRegs(inst);
+template<class Impl>
+void
+DefaultRename<Impl>::serializeAfter(InstQueue &inst_list,
+ unsigned tid)
+{
+ if (inst_list.empty()) {
+ // Mark a bit to say that I must serialize on the next instruction.
+ serializeOnNextInst[tid] = true;
+ return;
+ }
- // Put instruction in rename queue.
- toIEW->insts[to_iew_index] = inst;
- ++(toIEW->size);
+ // Set the next instruction as serializing.
+ inst_list.front()->setSerializeBefore();
+}
- // Decrease the number of free ROB and IQ entries.
- --free_rob_entries;
- --free_iq_entries;
+template <class Impl>
+inline void
+DefaultRename<Impl>::incrFullStat(const FullSource &source)
+{
+ switch (source) {
+ case ROB:
+ ++renameROBFullEvents;
+ break;
+ case IQ:
+ ++renameIQFullEvents;
+ break;
+ case LSQ:
+ ++renameLSQFullEvents;
+ break;
+ default:
+ panic("Rename full stall stat should be incremented for a reason!");
+ break;
+ }
+}
- // Increment which instruction we're on.
- ++to_iew_index;
- ++numInst;
+template <class Impl>
+void
+DefaultRename<Impl>::dumpHistory()
+{
+ typename list<RenameHistory>::iterator buf_it;
- ++renameRenamedInsts;
+ for (int i = 0; i < numThreads; i++) {
- // Decrement how many instructions are available.
- --insts_available;
- }
+ buf_it = historyBuffer[i].begin();
- // Check if there's any instructions left that haven't yet been renamed.
- // If so then block.
- if (block_this_cycle) {
- block();
+ while (buf_it != historyBuffer[i].end()) {
+ cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys "
+ "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg,
+ (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg);
- toDecode->renameInfo.stall = true;
- } else {
- // If we had a successful rename and didn't have to exit early, then
- // reset numInst so it will refer to the correct instruction on next
- // run.
- numInst = 0;
+ buf_it++;
+ }
}
}
diff --git a/src/cpu/o3/rename_map.cc b/src/cpu/o3/rename_map.cc
index 10963f7de..befbc3e8a 100644
--- a/src/cpu/o3/rename_map.cc
+++ b/src/cpu/o3/rename_map.cc
@@ -24,6 +24,8 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include <vector>
@@ -32,118 +34,105 @@
using namespace std;
-// Todo: Consider making functions inline. Avoid having things that are
-// using the zero register or misc registers from adding on the registers
-// to the free list. Possibly remove the direct communication between
-// this and the freelist. Considering making inline bool functions that
-// determine if the register is a logical int, logical fp, physical int,
-// physical fp, etc.
-
-SimpleRenameMap::SimpleRenameMap(unsigned _numLogicalIntRegs,
- unsigned _numPhysicalIntRegs,
- unsigned _numLogicalFloatRegs,
- unsigned _numPhysicalFloatRegs,
- unsigned _numMiscRegs,
- RegIndex _intZeroReg,
- RegIndex _floatZeroReg)
- : numLogicalIntRegs(_numLogicalIntRegs),
- numPhysicalIntRegs(_numPhysicalIntRegs),
- numLogicalFloatRegs(_numLogicalFloatRegs),
- numPhysicalFloatRegs(_numPhysicalFloatRegs),
- numMiscRegs(_numMiscRegs),
- intZeroReg(_intZeroReg),
- floatZeroReg(_floatZeroReg)
+// @todo: Consider making inline bool functions that determine if the
+// register is a logical int, logical fp, physical int, physical fp,
+// etc.
+
+SimpleRenameMap::~SimpleRenameMap()
+{
+}
+
+void
+SimpleRenameMap::init(unsigned _numLogicalIntRegs,
+ unsigned _numPhysicalIntRegs,
+ PhysRegIndex &ireg_idx,
+
+ unsigned _numLogicalFloatRegs,
+ unsigned _numPhysicalFloatRegs,
+ PhysRegIndex &freg_idx,
+
+ unsigned _numMiscRegs,
+
+ RegIndex _intZeroReg,
+ RegIndex _floatZeroReg,
+
+ int map_id,
+ bool bindRegs)
{
- DPRINTF(Rename, "Rename: Creating rename map. Phys: %i / %i, Float: "
- "%i / %i.\n", numLogicalIntRegs, numPhysicalIntRegs,
+ id = map_id;
+
+ numLogicalIntRegs = _numLogicalIntRegs;
+
+ numLogicalFloatRegs = _numLogicalFloatRegs;
+
+ numPhysicalIntRegs = _numPhysicalIntRegs;
+
+ numPhysicalFloatRegs = _numPhysicalFloatRegs;
+
+ numMiscRegs = _numMiscRegs;
+
+ intZeroReg = _intZeroReg;
+ floatZeroReg = _floatZeroReg;
+
+ DPRINTF(Rename, "Creating rename map %i. Phys: %i / %i, Float: "
+ "%i / %i.\n", id, numLogicalIntRegs, numPhysicalIntRegs,
numLogicalFloatRegs, numPhysicalFloatRegs);
numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs;
numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs;
- //Create the rename maps, and their scoreboards.
- intRenameMap = new RenameEntry[numLogicalIntRegs];
- floatRenameMap = new RenameEntry[numLogicalRegs];
-
- // Should combine this into one scoreboard.
- intScoreboard.resize(numPhysicalIntRegs);
- floatScoreboard.resize(numPhysicalRegs);
- miscScoreboard.resize(numPhysicalRegs + numMiscRegs);
-
- // Initialize the entries in the integer rename map to point to the
- // physical registers of the same index, and consider each register
- // ready until the first rename occurs.
- for (RegIndex index = 0; index < numLogicalIntRegs; ++index)
- {
- intRenameMap[index].physical_reg = index;
- intScoreboard[index] = 1;
- }
+ //Create the rename maps
+ intRenameMap.resize(numLogicalIntRegs);
+ floatRenameMap.resize(numLogicalRegs);
- // Initialize the rest of the physical registers (the ones that don't
- // directly map to a logical register) as unready.
- for (PhysRegIndex index = numLogicalIntRegs;
- index < numPhysicalIntRegs;
- ++index)
- {
- intScoreboard[index] = 0;
- }
+ if (bindRegs) {
+ DPRINTF(Rename, "Binding registers into rename map %i",id);
- int float_reg_idx = numPhysicalIntRegs;
-
- // Initialize the entries in the floating point rename map to point to
- // the physical registers of the same index, and consider each register
- // ready until the first rename occurs.
- // Although the index refers purely to architected registers, because
- // the floating reg indices come after the integer reg indices, they
- // may exceed the size of a normal RegIndex (short).
- for (PhysRegIndex index = numLogicalIntRegs;
- index < numLogicalRegs; ++index)
- {
- floatRenameMap[index].physical_reg = float_reg_idx++;
- }
+ // Initialize the entries in the integer rename map to point to the
+ // physical registers of the same index
+ for (RegIndex index = 0; index < numLogicalIntRegs; ++index)
+ {
+ intRenameMap[index].physical_reg = ireg_idx++;
+ }
- for (PhysRegIndex index = numPhysicalIntRegs;
- index < numPhysicalIntRegs + numLogicalFloatRegs; ++index)
- {
- floatScoreboard[index] = 1;
- }
+ // Initialize the entries in the floating point rename map to point to
+ // the physical registers of the same index
+ // Although the index refers purely to architected registers, because
+ // the floating reg indices come after the integer reg indices, they
+ // may exceed the size of a normal RegIndex (short).
+ for (PhysRegIndex index = numLogicalIntRegs;
+ index < numLogicalRegs; ++index)
+ {
+ floatRenameMap[index].physical_reg = freg_idx++;
+ }
+ } else {
+ DPRINTF(Rename, "Binding registers into rename map %i",id);
- // Initialize the rest of the physical registers (the ones that don't
- // directly map to a logical register) as unready.
- for (PhysRegIndex index = numPhysicalIntRegs + numLogicalFloatRegs;
- index < numPhysicalRegs;
- ++index)
- {
- floatScoreboard[index] = 0;
- }
+ PhysRegIndex temp_ireg = ireg_idx;
- // Initialize the entries in the misc register scoreboard to be ready.
- for (PhysRegIndex index = numPhysicalRegs;
- index < numPhysicalRegs + numMiscRegs; ++index)
- {
- miscScoreboard[index] = 1;
- }
-}
+ for (RegIndex index = 0; index < numLogicalIntRegs; ++index)
+ {
+ intRenameMap[index].physical_reg = temp_ireg++;
+ }
-SimpleRenameMap::~SimpleRenameMap()
-{
- // Delete the rename maps as they were allocated with new.
- delete [] intRenameMap;
- delete [] floatRenameMap;
+ PhysRegIndex temp_freg = freg_idx;
+
+ for (PhysRegIndex index = numLogicalIntRegs;
+ index < numLogicalRegs; ++index)
+ {
+ floatRenameMap[index].physical_reg = temp_freg++;
+ }
+ }
}
void
SimpleRenameMap::setFreeList(SimpleFreeList *fl_ptr)
{
- //Setup the interface to the freelist.
freeList = fl_ptr;
}
-// Don't allow this stage to fault; force that check to the rename stage.
-// Simply ask to rename a logical register and get back a new physical
-// register index.
SimpleRenameMap::RenameInfo
SimpleRenameMap::rename(RegIndex arch_reg)
{
@@ -156,45 +145,33 @@ SimpleRenameMap::rename(RegIndex arch_reg)
// requested architected register.
prev_reg = intRenameMap[arch_reg].physical_reg;
- // If it's not referencing the zero register, then mark the register
- // as not ready.
+ // If it's not referencing the zero register, then rename the
+ // register.
if (arch_reg != intZeroReg) {
- // Get a free physical register to rename to.
renamed_reg = freeList->getIntReg();
- // Update the integer rename map.
intRenameMap[arch_reg].physical_reg = renamed_reg;
assert(renamed_reg >= 0 && renamed_reg < numPhysicalIntRegs);
- // Mark register as not ready.
- intScoreboard[renamed_reg] = false;
} else {
// Otherwise return the zero register so nothing bad happens.
renamed_reg = intZeroReg;
}
} else if (arch_reg < numLogicalRegs) {
- // Subtract off the base offset for floating point registers.
-// arch_reg = arch_reg - numLogicalIntRegs;
-
// Record the current physical register that is renamed to the
// requested architected register.
prev_reg = floatRenameMap[arch_reg].physical_reg;
- // If it's not referencing the zero register, then mark the register
- // as not ready.
+ // If it's not referencing the zero register, then rename the
+ // register.
if (arch_reg != floatZeroReg) {
- // Get a free floating point register to rename to.
renamed_reg = freeList->getFloatReg();
- // Update the floating point rename map.
floatRenameMap[arch_reg].physical_reg = renamed_reg;
assert(renamed_reg < numPhysicalRegs &&
renamed_reg >= numPhysicalIntRegs);
-
- // Mark register as not ready.
- floatScoreboard[renamed_reg] = false;
} else {
// Otherwise return the zero register so nothing bad happens.
renamed_reg = floatZeroReg;
@@ -203,10 +180,10 @@ SimpleRenameMap::rename(RegIndex arch_reg)
// Subtract off the base offset for miscellaneous registers.
arch_reg = arch_reg - numLogicalRegs;
- // No renaming happens to the misc. registers. They are simply the
- // registers that come after all the physical registers; thus
- // take the base architected register and add the physical registers
- // to it.
+ // No renaming happens to the misc. registers. They are
+ // simply the registers that come after all the physical
+ // registers; thus take the base architected register and add
+ // the physical registers to it.
renamed_reg = arch_reg + numPhysicalRegs;
// Set the previous register to the same register; mainly it must be
@@ -215,24 +192,17 @@ SimpleRenameMap::rename(RegIndex arch_reg)
prev_reg = renamed_reg;
assert(renamed_reg < numPhysicalRegs + numMiscRegs);
-
- miscScoreboard[renamed_reg] = false;
}
return RenameInfo(renamed_reg, prev_reg);
}
-//Perhaps give this a pair as a return value, of the physical register
-//and whether or not it's ready.
PhysRegIndex
SimpleRenameMap::lookup(RegIndex arch_reg)
{
if (arch_reg < numLogicalIntRegs) {
return intRenameMap[arch_reg].physical_reg;
} else if (arch_reg < numLogicalRegs) {
- // Subtract off the base FP offset.
-// arch_reg = arch_reg - numLogicalIntRegs;
-
return floatRenameMap[arch_reg].physical_reg;
} else {
// Subtract off the misc registers offset.
@@ -244,38 +214,18 @@ SimpleRenameMap::lookup(RegIndex arch_reg)
}
}
-bool
-SimpleRenameMap::isReady(PhysRegIndex phys_reg)
-{
- if (phys_reg < numPhysicalIntRegs) {
- return intScoreboard[phys_reg];
- } else if (phys_reg < numPhysicalRegs) {
-
- // Subtract off the base FP offset.
-// phys_reg = phys_reg - numPhysicalIntRegs;
-
- return floatScoreboard[phys_reg];
- } else {
- // Subtract off the misc registers offset.
-// phys_reg = phys_reg - numPhysicalRegs;
-
- return miscScoreboard[phys_reg];
- }
-}
-
-// In this implementation the miscellaneous registers do not actually rename,
-// so this function does not allow you to try to change their mappings.
void
SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg)
{
+ // In this implementation the miscellaneous registers do not
+ // actually rename, so this function does not allow you to try to
+ // change their mappings.
if (arch_reg < numLogicalIntRegs) {
DPRINTF(Rename, "Rename Map: Integer register %i being set to %i.\n",
(int)arch_reg, renamed_reg);
intRenameMap[arch_reg].physical_reg = renamed_reg;
- } else {
- assert(arch_reg < (numLogicalIntRegs + numLogicalFloatRegs));
-
+ } else if (arch_reg < numLogicalIntRegs + numLogicalFloatRegs) {
DPRINTF(Rename, "Rename Map: Float register %i being set to %i.\n",
(int)arch_reg - numLogicalIntRegs, renamed_reg);
@@ -283,55 +233,6 @@ SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg)
}
}
-void
-SimpleRenameMap::squash(vector<RegIndex> freed_regs,
- vector<UnmapInfo> unmaps)
-{
- panic("Not sure this function should be called.");
-
- // Not sure the rename map should be able to access the free list
- // like this.
- while (!freed_regs.empty()) {
- RegIndex free_register = freed_regs.back();
-
- if (free_register < numPhysicalIntRegs) {
- freeList->addIntReg(free_register);
- } else {
- // Subtract off the base FP dependence tag.
- free_register = free_register - numPhysicalIntRegs;
- freeList->addFloatReg(free_register);
- }
-
- freed_regs.pop_back();
- }
-
- // Take unmap info and roll back the rename map.
-}
-
-void
-SimpleRenameMap::markAsReady(PhysRegIndex ready_reg)
-{
- DPRINTF(Rename, "Rename map: Marking register %i as ready.\n",
- (int)ready_reg);
-
- if (ready_reg < numPhysicalIntRegs) {
- assert(ready_reg >= 0);
-
- intScoreboard[ready_reg] = 1;
- } else if (ready_reg < numPhysicalRegs) {
-
- // Subtract off the base FP offset.
-// ready_reg = ready_reg - numPhysicalIntRegs;
-
- floatScoreboard[ready_reg] = 1;
- } else {
- //Subtract off the misc registers offset.
-// ready_reg = ready_reg - numPhysicalRegs;
-
- miscScoreboard[ready_reg] = 1;
- }
-}
-
int
SimpleRenameMap::numFreeEntries()
{
diff --git a/src/cpu/o3/rename_map.hh b/src/cpu/o3/rename_map.hh
index 57be4a64a..c4c90c99a 100644
--- a/src/cpu/o3/rename_map.hh
+++ b/src/cpu/o3/rename_map.hh
@@ -24,14 +24,16 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
// Todo: Create destructor.
// Have it so that there's a more meaningful name given to the variable
// that marks the beginning of the FP registers.
-#ifndef __CPU_O3_CPU_RENAME_MAP_HH__
-#define __CPU_O3_CPU_RENAME_MAP_HH__
+#ifndef __CPU_O3_RENAME_MAP_HH__
+#define __CPU_O3_RENAME_MAP_HH__
#include <iostream>
#include <utility>
@@ -62,18 +64,30 @@ class SimpleRenameMap
typedef std::pair<PhysRegIndex, PhysRegIndex> RenameInfo;
public:
- //Constructor
- SimpleRenameMap(unsigned _numLogicalIntRegs,
- unsigned _numPhysicalIntRegs,
- unsigned _numLogicalFloatRegs,
- unsigned _numPhysicalFloatRegs,
- unsigned _numMiscRegs,
- RegIndex _intZeroReg,
- RegIndex _floatZeroReg);
+ /** Default constructor. init() must be called prior to use. */
+ SimpleRenameMap() {};
/** Destructor. */
~SimpleRenameMap();
+ /** Initializes rename map with given parameters. */
+ void init(unsigned _numLogicalIntRegs,
+ unsigned _numPhysicalIntRegs,
+ PhysRegIndex &_int_reg_start,
+
+ unsigned _numLogicalFloatRegs,
+ unsigned _numPhysicalFloatRegs,
+ PhysRegIndex &_float_reg_start,
+
+ unsigned _numMiscRegs,
+
+ RegIndex _intZeroReg,
+ RegIndex _floatZeroReg,
+
+ int id,
+ bool bindRegs);
+
+ /** Sets the free list used with this rename map. */
void setFreeList(SimpleFreeList *fl_ptr);
//Tell rename map to get a free physical register for a given
@@ -84,23 +98,19 @@ class SimpleRenameMap
PhysRegIndex lookup(RegIndex phys_reg);
- bool isReady(PhysRegIndex arch_reg);
-
/**
* Marks the given register as ready, meaning that its value has been
* calculated and written to the register file.
* @param ready_reg The index of the physical register that is now ready.
*/
- void markAsReady(PhysRegIndex ready_reg);
-
void setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg);
- void squash(std::vector<RegIndex> freed_regs,
- std::vector<UnmapInfo> unmaps);
-
int numFreeEntries();
private:
+ /** Rename Map ID */
+ int id;
+
/** Number of logical integer registers. */
int numLogicalIntRegs;
@@ -143,31 +153,16 @@ class SimpleRenameMap
{ }
};
+ private:
/** Integer rename map. */
- RenameEntry *intRenameMap;
+ std::vector<RenameEntry> intRenameMap;
/** Floating point rename map. */
- RenameEntry *floatRenameMap;
+ std::vector<RenameEntry> floatRenameMap;
+ private:
/** Free list interface. */
SimpleFreeList *freeList;
-
- // Might want to make all these scoreboards into one large scoreboard.
-
- /** Scoreboard of physical integer registers, saying whether or not they
- * are ready.
- */
- std::vector<bool> intScoreboard;
-
- /** Scoreboard of physical floating registers, saying whether or not they
- * are ready.
- */
- std::vector<bool> floatScoreboard;
-
- /** Scoreboard of miscellaneous registers, saying whether or not they
- * are ready.
- */
- std::vector<bool> miscScoreboard;
};
-#endif //__CPU_O3_CPU_RENAME_MAP_HH__
+#endif //__CPU_O3_RENAME_MAP_HH__
diff --git a/src/cpu/o3/rob.cc b/src/cpu/o3/rob.cc
index c10f782fd..f99e5ccfd 100644
--- a/src/cpu/o3/rob.cc
+++ b/src/cpu/o3/rob.cc
@@ -24,6 +24,9 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ * Nathan Binkert
*/
#include "cpu/o3/alpha_dyn_inst.hh"
diff --git a/src/cpu/o3/rob.hh b/src/cpu/o3/rob.hh
index 1185564ad..6d1402531 100644
--- a/src/cpu/o3/rob.hh
+++ b/src/cpu/o3/rob.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,25 +24,19 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-// Todo: Probably add in support for scheduling events (more than one as
-// well) on the case of the ROB being empty or full. Considering tracking
-// free entries instead of insts in ROB. Differentiate between squashing
-// all instructions after the instruction, and all instructions after *and*
-// including that instruction.
-
-#ifndef __CPU_O3_CPU_ROB_HH__
-#define __CPU_O3_CPU_ROB_HH__
+#ifndef __CPU_O3_ROB_HH__
+#define __CPU_O3_ROB_HH__
+#include <string>
#include <utility>
#include <vector>
/**
- * ROB class. Uses the instruction list that exists within the CPU to
- * represent the ROB. This class doesn't contain that list, but instead
- * a pointer to the CPU to get access to the list. The ROB, in this first
- * implementation, is largely what drives squashing.
+ * ROB class. The ROB is largely what drives squashing.
*/
template <class Impl>
class ROB
@@ -54,16 +48,43 @@ class ROB
typedef typename Impl::FullCPU FullCPU;
typedef typename Impl::DynInstPtr DynInstPtr;
- typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo_t;
- typedef typename list<DynInstPtr>::iterator InstIt_t;
+ typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo;
+ typedef typename std::list<DynInstPtr>::iterator InstIt;
+
+ /** Possible ROB statuses. */
+ enum Status {
+ Running,
+ Idle,
+ ROBSquashing
+ };
+
+ /** SMT ROB Sharing Policy */
+ enum ROBPolicy{
+ Dynamic,
+ Partitioned,
+ Threshold
+ };
+
+ private:
+ /** Per-thread ROB status. */
+ Status robStatus[Impl::MaxThreads];
+
+ /** ROB resource sharing policy for SMT mode. */
+ ROBPolicy robPolicy;
public:
/** ROB constructor.
- * @param _numEntries Number of entries in ROB.
- * @param _squashWidth Number of instructions that can be squashed in a
- * single cycle.
+ * @param _numEntries Number of entries in ROB.
+ * @param _squashWidth Number of instructions that can be squashed in a
+ * single cycle.
+ * @param _smtROBPolicy ROB Partitioning Scheme for SMT.
+ * @param _smtROBThreshold Max Resources(by %) a thread can have in the ROB.
+ * @param _numThreads The number of active threads.
*/
- ROB(unsigned _numEntries, unsigned _squashWidth);
+ ROB(unsigned _numEntries, unsigned _squashWidth, std::string smtROBPolicy,
+ unsigned _smtROBThreshold, unsigned _numThreads);
+
+ std::string name() const;
/** Function to set the CPU pointer, necessary due to which object the ROB
* is created within.
@@ -71,12 +92,21 @@ class ROB
*/
void setCPU(FullCPU *cpu_ptr);
- /** Function to insert an instruction into the ROB. The parameter inst is
- * not truly required, but is useful for checking correctness. Note
- * that whatever calls this function must ensure that there is enough
- * space within the ROB for the new instruction.
+ /** Sets pointer to the list of active threads.
+ * @param at_ptr Pointer to the list of active threads.
+ */
+ void setActiveThreads(std::list<unsigned>* at_ptr);
+
+ /** Switches out the ROB. */
+ void switchOut();
+
+ /** Takes over another CPU's thread. */
+ void takeOverFrom();
+
+ /** Function to insert an instruction into the ROB. Note that whatever
+ * calls this function must ensure that there is enough space within the
+ * ROB for the new instruction.
* @param inst The instruction being inserted into the ROB.
- * @todo Remove the parameter once correctness is ensured.
*/
void insertInst(DynInstPtr &inst);
@@ -84,40 +114,134 @@ class ROB
* no guarantee as to the return value if the ROB is empty.
* @retval Pointer to the DynInst that is at the head of the ROB.
*/
- DynInstPtr readHeadInst() { return cpu->instList.front(); }
+// DynInstPtr readHeadInst();
+
+ /** Returns a pointer to the head instruction of a specific thread within
+ * the ROB.
+ * @return Pointer to the DynInst that is at the head of the ROB.
+ */
+ DynInstPtr readHeadInst(unsigned tid);
- DynInstPtr readTailInst() { return (*tail); }
+ /** Returns pointer to the tail instruction within the ROB. There is
+ * no guarantee as to the return value if the ROB is empty.
+ * @retval Pointer to the DynInst that is at the tail of the ROB.
+ */
+// DynInstPtr readTailInst();
+
+ /** Returns a pointer to the tail instruction of a specific thread within
+ * the ROB.
+ * @return Pointer to the DynInst that is at the tail of the ROB.
+ */
+ DynInstPtr readTailInst(unsigned tid);
+
+ /** Retires the head instruction, removing it from the ROB. */
+// void retireHead();
+
+ /** Retires the head instruction of a specific thread, removing it from the
+ * ROB.
+ */
+ void retireHead(unsigned tid);
- void retireHead();
+ /** Is the oldest instruction across all threads ready. */
+// bool isHeadReady();
- bool isHeadReady();
+ /** Is the oldest instruction across a particular thread ready. */
+ bool isHeadReady(unsigned tid);
+ /** Is there any commitable head instruction across all threads ready. */
+ bool canCommit();
+
+ /** Re-adjust ROB partitioning. */
+ void resetEntries();
+
+ /** Number of entries needed For 'num_threads' amount of threads. */
+ int entryAmount(int num_threads);
+
+ /** Returns the number of total free entries in the ROB. */
unsigned numFreeEntries();
+ /** Returns the number of free entries in a specific ROB paritition. */
+ unsigned numFreeEntries(unsigned tid);
+
+ /** Returns the maximum number of entries for a specific thread. */
+ unsigned getMaxEntries(unsigned tid)
+ { return maxEntries[tid]; }
+
+ /** Returns the number of entries being used by a specific thread. */
+ unsigned getThreadEntries(unsigned tid)
+ { return threadEntries[tid]; }
+
+ /** Returns if the ROB is full. */
bool isFull()
{ return numInstsInROB == numEntries; }
+ /** Returns if a specific thread's partition is full. */
+ bool isFull(unsigned tid)
+ { return threadEntries[tid] == numEntries; }
+
+ /** Returns if the ROB is empty. */
bool isEmpty()
{ return numInstsInROB == 0; }
- void doSquash();
+ /** Returns if a specific thread's partition is empty. */
+ bool isEmpty(unsigned tid)
+ { return threadEntries[tid] == 0; }
- void squash(InstSeqNum squash_num);
+ /** Executes the squash, marking squashed instructions. */
+ void doSquash(unsigned tid);
- uint64_t readHeadPC();
+ /** Squashes all instructions younger than the given sequence number for
+ * the specific thread.
+ */
+ void squash(InstSeqNum squash_num, unsigned tid);
+
+ /** Updates the head instruction with the new oldest instruction. */
+ void updateHead();
+
+ /** Updates the tail instruction with the new youngest instruction. */
+ void updateTail();
+
+ /** Reads the PC of the oldest head instruction. */
+// uint64_t readHeadPC();
+
+ /** Reads the PC of the head instruction of a specific thread. */
+// uint64_t readHeadPC(unsigned tid);
+
+ /** Reads the next PC of the oldest head instruction. */
+// uint64_t readHeadNextPC();
+
+ /** Reads the next PC of the head instruction of a specific thread. */
+// uint64_t readHeadNextPC(unsigned tid);
- uint64_t readHeadNextPC();
+ /** Reads the sequence number of the oldest head instruction. */
+// InstSeqNum readHeadSeqNum();
- InstSeqNum readHeadSeqNum();
+ /** Reads the sequence number of the head instruction of a specific thread.
+ */
+// InstSeqNum readHeadSeqNum(unsigned tid);
+
+ /** Reads the PC of the youngest tail instruction. */
+// uint64_t readTailPC();
- uint64_t readTailPC();
+ /** Reads the PC of the tail instruction of a specific thread. */
+// uint64_t readTailPC(unsigned tid);
- InstSeqNum readTailSeqNum();
+ /** Reads the sequence number of the youngest tail instruction. */
+// InstSeqNum readTailSeqNum();
+
+ /** Reads the sequence number of tail instruction of a specific thread. */
+// InstSeqNum readTailSeqNum(unsigned tid);
/** Checks if the ROB is still in the process of squashing instructions.
* @retval Whether or not the ROB is done squashing.
*/
- bool isDoneSquashing() const { return doneSquashing; }
+ bool isDoneSquashing(unsigned tid) const
+ { return doneSquashing[tid]; }
+
+ /** Checks if the ROB is still in the process of squashing instructions for
+ * any thread.
+ */
+ bool isDoneSquashing();
/** This is more of a debugging function than anything. Use
* numInstsInROB to get the instructions in the ROB unless you are
@@ -125,23 +249,46 @@ class ROB
*/
int countInsts();
- private:
+ /** This is more of a debugging function than anything. Use
+ * threadEntries to get the instructions in the ROB unless you are
+ * double checking that variable.
+ */
+ int countInsts(unsigned tid);
+ private:
/** Pointer to the CPU. */
FullCPU *cpu;
+ /** Active Threads in CPU */
+ std::list<unsigned>* activeThreads;
+
/** Number of instructions in the ROB. */
unsigned numEntries;
+ /** Entries Per Thread */
+ unsigned threadEntries[Impl::MaxThreads];
+
+ /** Max Insts a Thread Can Have in the ROB */
+ unsigned maxEntries[Impl::MaxThreads];
+
+ /** ROB List of Instructions */
+ std::list<DynInstPtr> instList[Impl::MaxThreads];
+
/** Number of instructions that can be squashed in a single cycle. */
unsigned squashWidth;
+ public:
/** Iterator pointing to the instruction which is the last instruction
* in the ROB. This may at times be invalid (ie when the ROB is empty),
* however it should never be incorrect.
*/
- InstIt_t tail;
+ InstIt tail;
+
+ /** Iterator pointing to the instruction which is the first instruction in
+ * in the ROB*/
+ InstIt head;
+ private:
/** Iterator used for walking through the list of instructions when
* squashing. Used so that there is persistent state between cycles;
* when squashing, the instructions are marked as squashed but not
@@ -149,16 +296,24 @@ class ROB
* and after a squash.
* This will always be set to cpu->instList.end() if it is invalid.
*/
- InstIt_t squashIt;
+ InstIt squashIt[Impl::MaxThreads];
+ public:
/** Number of instructions in the ROB. */
int numInstsInROB;
+ /** Dummy instruction returned if there are no insts left. */
+ DynInstPtr dummyInst;
+
+ private:
/** The sequence number of the squashed instruction. */
InstSeqNum squashedSeqNum;
/** Is the ROB done squashing. */
- bool doneSquashing;
+ bool doneSquashing[Impl::MaxThreads];
+
+ /** Number of active threads. */
+ unsigned numThreads;
};
-#endif //__CPU_O3_CPU_ROB_HH__
+#endif //__CPU_O3_ROB_HH__
diff --git a/src/cpu/o3/rob_impl.hh b/src/cpu/o3/rob_impl.hh
index e7a5671d9..97694e371 100644
--- a/src/cpu/o3/rob_impl.hh
+++ b/src/cpu/o3/rob_impl.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,22 +24,78 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_ROB_IMPL_HH__
-#define __CPU_O3_CPU_ROB_IMPL_HH__
-
#include "config/full_system.hh"
#include "cpu/o3/rob.hh"
+using namespace std;
+
template <class Impl>
-ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth)
+ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth,
+ string _smtROBPolicy, unsigned _smtROBThreshold,
+ unsigned _numThreads)
: numEntries(_numEntries),
squashWidth(_squashWidth),
numInstsInROB(0),
- squashedSeqNum(0)
+ squashedSeqNum(0),
+ numThreads(_numThreads)
+{
+ for (int tid=0; tid < numThreads; tid++) {
+ doneSquashing[tid] = true;
+ threadEntries[tid] = 0;
+ }
+
+ string policy = _smtROBPolicy;
+
+ //Convert string to lowercase
+ std::transform(policy.begin(), policy.end(), policy.begin(),
+ (int(*)(int)) tolower);
+
+ //Figure out rob policy
+ if (policy == "dynamic") {
+ robPolicy = Dynamic;
+
+ //Set Max Entries to Total ROB Capacity
+ for (int i = 0; i < numThreads; i++) {
+ maxEntries[i]=numEntries;
+ }
+
+ } else if (policy == "partitioned") {
+ robPolicy = Partitioned;
+ DPRINTF(Fetch, "ROB sharing policy set to Partitioned\n");
+
+ //@todo:make work if part_amt doesnt divide evenly.
+ int part_amt = numEntries / numThreads;
+
+ //Divide ROB up evenly
+ for (int i = 0; i < numThreads; i++) {
+ maxEntries[i]=part_amt;
+ }
+
+ } else if (policy == "threshold") {
+ robPolicy = Threshold;
+ DPRINTF(Fetch, "ROB sharing policy set to Threshold\n");
+
+ int threshold = _smtROBThreshold;;
+
+ //Divide up by threshold amount
+ for (int i = 0; i < numThreads; i++) {
+ maxEntries[i]=threshold;
+ }
+ } else {
+ assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic,"
+ "Partitioned, Threshold}");
+ }
+}
+
+template <class Impl>
+std::string
+ROB<Impl>::name() const
{
- doneSquashing = true;
+ return cpu->name() + ".rob";
}
template <class Impl>
@@ -48,124 +104,227 @@ ROB<Impl>::setCPU(FullCPU *cpu_ptr)
{
cpu = cpu_ptr;
- // Set the tail to the beginning of the CPU instruction list so that
- // upon the first instruction being inserted into the ROB, the tail
- // iterator can simply be incremented.
- tail = cpu->instList.begin();
+ // Set the per-thread iterators to the end of the instruction list.
+ for (int i=0; i < numThreads;i++) {
+ squashIt[i] = instList[i].end();
+ }
- // Set the squash iterator to the end of the instruction list.
- squashIt = cpu->instList.end();
+ // Initialize the "universal" ROB head & tail point to invalid
+ // pointers
+ head = instList[0].end();
+ tail = instList[0].end();
}
template <class Impl>
-int
-ROB<Impl>::countInsts()
+void
+ROB<Impl>::setActiveThreads(list<unsigned> *at_ptr)
{
- // Start at 1; if the tail matches cpu->instList.begin(), then there is
- // one inst in the ROB.
- int return_val = 1;
+ DPRINTF(ROB, "Setting active threads list pointer.\n");
+ activeThreads = at_ptr;
+}
- // There are quite a few special cases. Do not use this function other
- // than for debugging purposes.
- if (cpu->instList.begin() == cpu->instList.end()) {
- // In this case there are no instructions in the list. The ROB
- // must be empty.
- return 0;
- } else if (tail == cpu->instList.end()) {
- // In this case, the tail is not yet pointing to anything valid.
- // The ROB must be empty.
- return 0;
+template <class Impl>
+void
+ROB<Impl>::switchOut()
+{
+ for (int tid = 0; tid < numThreads; tid++) {
+ instList[tid].clear();
}
+}
- // Iterate through the ROB from the head to the tail, counting the
- // entries.
- for (InstIt_t i = cpu->instList.begin(); i != tail; ++i)
- {
- assert(i != cpu->instList.end());
- ++return_val;
+template <class Impl>
+void
+ROB<Impl>::takeOverFrom()
+{
+ for (int tid=0; tid < numThreads; tid++) {
+ doneSquashing[tid] = true;
+ threadEntries[tid] = 0;
+ squashIt[tid] = instList[tid].end();
}
+ numInstsInROB = 0;
- return return_val;
+ // Initialize the "universal" ROB head & tail point to invalid
+ // pointers
+ head = instList[0].end();
+ tail = instList[0].end();
+}
- // Because the head won't be tracked properly until the ROB gets the
- // first instruction, and any time that the ROB is empty and has not
- // yet gotten the instruction, this function doesn't work.
-// return numInstsInROB;
+template <class Impl>
+void
+ROB<Impl>::resetEntries()
+{
+ if (robPolicy != Dynamic || numThreads > 1) {
+ int active_threads = (*activeThreads).size();
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+ list<unsigned>::iterator list_end = (*activeThreads).end();
+
+ while (threads != list_end) {
+ if (robPolicy == Partitioned) {
+ maxEntries[*threads++] = numEntries / active_threads;
+ } else if (robPolicy == Threshold && active_threads == 1) {
+ maxEntries[*threads++] = numEntries;
+ }
+ }
+ }
+}
+
+template <class Impl>
+int
+ROB<Impl>::entryAmount(int num_threads)
+{
+ if (robPolicy == Partitioned) {
+ return numEntries / num_threads;
+ } else {
+ return 0;
+ }
+}
+
+template <class Impl>
+int
+ROB<Impl>::countInsts()
+{
+ int total=0;
+
+ for (int i=0;i < numThreads;i++)
+ total += countInsts(i);
+
+ return total;
+}
+
+template <class Impl>
+int
+ROB<Impl>::countInsts(unsigned tid)
+{
+ return instList[tid].size();
}
template <class Impl>
void
ROB<Impl>::insertInst(DynInstPtr &inst)
{
- // Make sure we have the right number of instructions.
- assert(numInstsInROB == countInsts());
- // Make sure the instruction is valid.
+ //assert(numInstsInROB == countInsts());
assert(inst);
- DPRINTF(ROB, "ROB: Adding inst PC %#x to the ROB.\n", inst->readPC());
+ DPRINTF(ROB, "Adding inst PC %#x to the ROB.\n", inst->readPC());
- // If the ROB is full then exit.
assert(numInstsInROB != numEntries);
- ++numInstsInROB;
+ int tid = inst->threadNumber;
- // Increment the tail iterator, moving it one instruction back.
- // There is a special case if the ROB was empty prior to this insertion,
- // in which case the tail will be pointing at instList.end(). If that
- // happens, then reset the tail to the beginning of the list.
- if (tail != cpu->instList.end()) {
- ++tail;
- } else {
- tail = cpu->instList.begin();
+ instList[tid].push_back(inst);
+
+ //Set Up head iterator if this is the 1st instruction in the ROB
+ if (numInstsInROB == 0) {
+ head = instList[tid].begin();
+ assert((*head) == inst);
}
- // Make sure the tail iterator is actually pointing at the instruction
- // added.
- assert((*tail) == inst);
+ //Must Decrement for iterator to actually be valid since __.end()
+ //actually points to 1 after the last inst
+ tail = instList[tid].end();
+ tail--;
+
+ inst->setInROB();
- DPRINTF(ROB, "ROB: Now has %d instructions.\n", numInstsInROB);
+ ++numInstsInROB;
+ ++threadEntries[tid];
+
+ assert((*tail) == inst);
+ DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]);
}
// Whatever calls this function needs to ensure that it properly frees up
// registers prior to this function.
+/*
template <class Impl>
void
ROB<Impl>::retireHead()
{
- assert(numInstsInROB == countInsts());
+ //assert(numInstsInROB == countInsts());
+ assert(numInstsInROB > 0);
+
+ int tid = (*head)->threadNumber;
+
+ retireHead(tid);
+
+ if (numInstsInROB == 0) {
+ tail = instList[tid].end();
+ }
+}
+*/
+
+template <class Impl>
+void
+ROB<Impl>::retireHead(unsigned tid)
+{
+ //assert(numInstsInROB == countInsts());
assert(numInstsInROB > 0);
// Get the head ROB instruction.
- DynInstPtr head_inst = cpu->instList.front();
+ InstIt head_it = instList[tid].begin();
+
+ DynInstPtr head_inst = (*head_it);
- // Make certain this can retire.
assert(head_inst->readyToCommit());
- DPRINTF(ROB, "ROB: Retiring head instruction of the ROB, "
- "instruction PC %#x, seq num %i\n", head_inst->readPC(),
+ DPRINTF(ROB, "[tid:%u]: Retiring head instruction, "
+ "instruction PC %#x,[sn:%lli]\n", tid, head_inst->readPC(),
head_inst->seqNum);
- // Keep track of how many instructions are in the ROB.
--numInstsInROB;
+ --threadEntries[tid];
+
+ head_inst->removeInROB();
+ head_inst->setCommitted();
- // Tell CPU to remove the instruction from the list of instructions.
- // A special case is needed if the instruction being retired is the
- // only instruction in the ROB; otherwise the tail iterator will become
- // invalidated.
+ instList[tid].erase(head_it);
+
+ //Update "Global" Head of ROB
+ updateHead();
+
+ // @todo: A special case is needed if the instruction being
+ // retired is the only instruction in the ROB; otherwise the tail
+ // iterator will become invalidated.
cpu->removeFrontInst(head_inst);
+}
+/*
+template <class Impl>
+bool
+ROB<Impl>::isHeadReady()
+{
+ if (numInstsInROB != 0) {
+ return (*head)->readyToCommit();
+ }
- if (numInstsInROB == 0) {
- tail = cpu->instList.end();
+ return false;
+}
+*/
+template <class Impl>
+bool
+ROB<Impl>::isHeadReady(unsigned tid)
+{
+ if (threadEntries[tid] != 0) {
+ return instList[tid].front()->readyToCommit();
}
+
+ return false;
}
template <class Impl>
bool
-ROB<Impl>::isHeadReady()
+ROB<Impl>::canCommit()
{
- if (numInstsInROB != 0) {
- return cpu->instList.front()->readyToCommit();
+ //@todo: set ActiveThreads through ROB or CPU
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (isHeadReady(tid)) {
+ return true;
+ }
}
return false;
@@ -175,131 +334,339 @@ template <class Impl>
unsigned
ROB<Impl>::numFreeEntries()
{
- assert(numInstsInROB == countInsts());
+ //assert(numInstsInROB == countInsts());
return numEntries - numInstsInROB;
}
template <class Impl>
+unsigned
+ROB<Impl>::numFreeEntries(unsigned tid)
+{
+ return maxEntries[tid] - threadEntries[tid];
+}
+
+template <class Impl>
void
-ROB<Impl>::doSquash()
+ROB<Impl>::doSquash(unsigned tid)
{
- DPRINTF(ROB, "ROB: Squashing instructions.\n");
+ DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n",
+ tid, squashedSeqNum);
+
+ assert(squashIt[tid] != instList[tid].end());
- assert(squashIt != cpu->instList.end());
+ if ((*squashIt[tid])->seqNum < squashedSeqNum) {
+ DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n",
+ tid);
+
+ squashIt[tid] = instList[tid].end();
+
+ doneSquashing[tid] = true;
+ return;
+ }
+
+ bool robTailUpdate = false;
for (int numSquashed = 0;
- numSquashed < squashWidth && (*squashIt)->seqNum != squashedSeqNum;
+ numSquashed < squashWidth &&
+ squashIt[tid] != instList[tid].end() &&
+ (*squashIt[tid])->seqNum > squashedSeqNum;
++numSquashed)
{
- // Ensure that the instruction is younger.
- assert((*squashIt)->seqNum > squashedSeqNum);
-
- DPRINTF(ROB, "ROB: Squashing instruction PC %#x, seq num %i.\n",
- (*squashIt)->readPC(), (*squashIt)->seqNum);
+ DPRINTF(ROB, "[tid:%u]: Squashing instruction PC %#x, seq num %i.\n",
+ (*squashIt[tid])->threadNumber,
+ (*squashIt[tid])->readPC(),
+ (*squashIt[tid])->seqNum);
// Mark the instruction as squashed, and ready to commit so that
// it can drain out of the pipeline.
- (*squashIt)->setSquashed();
-
- (*squashIt)->setCanCommit();
-
- // Special case for when squashing due to a syscall. It's possible
- // that the squash happened after the head instruction was already
- // committed, meaning that (*squashIt)->seqNum != squashedSeqNum
- // will never be false. Normally the squash would never be able
- // to go past the head of the ROB; in this case it might, so it
- // must be handled otherwise it will segfault.
-#if !FULL_SYSTEM
- if (squashIt == cpu->instList.begin()) {
- DPRINTF(ROB, "ROB: Reached head of instruction list while "
+ (*squashIt[tid])->setSquashed();
+
+ (*squashIt[tid])->setCanCommit();
+
+
+ if (squashIt[tid] == instList[tid].begin()) {
+ DPRINTF(ROB, "Reached head of instruction list while "
"squashing.\n");
- squashIt = cpu->instList.end();
+ squashIt[tid] = instList[tid].end();
- doneSquashing = true;
+ doneSquashing[tid] = true;
return;
}
-#endif
- // Move the tail iterator to the next instruction.
- squashIt--;
+ InstIt tail_thread = instList[tid].end();
+ tail_thread--;
+
+ if ((*squashIt[tid]) == (*tail_thread))
+ robTailUpdate = true;
+
+ squashIt[tid]--;
}
// Check if ROB is done squashing.
- if ((*squashIt)->seqNum == squashedSeqNum) {
- DPRINTF(ROB, "ROB: Done squashing instructions.\n");
+ if ((*squashIt[tid])->seqNum <= squashedSeqNum) {
+ DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n",
+ tid);
- squashIt = cpu->instList.end();
+ squashIt[tid] = instList[tid].end();
+
+ doneSquashing[tid] = true;
+ }
- doneSquashing = true;
+ if (robTailUpdate) {
+ updateTail();
}
}
+
template <class Impl>
void
-ROB<Impl>::squash(InstSeqNum squash_num)
+ROB<Impl>::updateHead()
{
- DPRINTF(ROB, "ROB: Starting to squash within the ROB.\n");
- doneSquashing = false;
+ DynInstPtr head_inst;
+ InstSeqNum lowest_num = 0;
+ bool first_valid = true;
+
+ // @todo: set ActiveThreads through ROB or CPU
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned thread_num = *threads++;
+
+ if (instList[thread_num].empty())
+ continue;
+
+ if (first_valid) {
+ head = instList[thread_num].begin();
+ lowest_num = (*head)->seqNum;
+ first_valid = false;
+ continue;
+ }
+
+ InstIt head_thread = instList[thread_num].begin();
+
+ DynInstPtr head_inst = (*head_thread);
+
+ assert(head_inst != 0);
+
+ if (head_inst->seqNum < lowest_num) {
+ head = head_thread;
+ lowest_num = head_inst->seqNum;
+ }
+ }
+
+ if (first_valid) {
+ head = instList[0].end();
+ }
+
+}
+
+template <class Impl>
+void
+ROB<Impl>::updateTail()
+{
+ tail = instList[0].end();
+ bool first_valid = true;
+
+ list<unsigned>::iterator threads = (*activeThreads).begin();
+
+ while (threads != (*activeThreads).end()) {
+ unsigned tid = *threads++;
+
+ if (instList[tid].empty()) {
+ continue;
+ }
+
+ // If this is the first valid then assign w/out
+ // comparison
+ if (first_valid) {
+ tail = instList[tid].end();
+ tail--;
+ first_valid = false;
+ continue;
+ }
+
+ // Assign new tail if this thread's tail is younger
+ // than our current "tail high"
+ InstIt tail_thread = instList[tid].end();
+ tail_thread--;
+
+ if ((*tail_thread)->seqNum > (*tail)->seqNum) {
+ tail = tail_thread;
+ }
+ }
+}
+
+
+template <class Impl>
+void
+ROB<Impl>::squash(InstSeqNum squash_num,unsigned tid)
+{
+ if (isEmpty()) {
+ DPRINTF(ROB, "Does not need to squash due to being empty "
+ "[sn:%i]\n",
+ squash_num);
+
+ return;
+ }
+
+ DPRINTF(ROB, "Starting to squash within the ROB.\n");
+
+ robStatus[tid] = ROBSquashing;
+
+ doneSquashing[tid] = false;
squashedSeqNum = squash_num;
- assert(tail != cpu->instList.end());
+ if (!instList[tid].empty()) {
+ InstIt tail_thread = instList[tid].end();
+ tail_thread--;
- squashIt = tail;
+ squashIt[tid] = tail_thread;
- doSquash();
+ doSquash(tid);
+ }
}
+/*
+template <class Impl>
+typename Impl::DynInstPtr
+ROB<Impl>::readHeadInst()
+{
+ if (numInstsInROB != 0) {
+ assert((*head)->isInROB()==true);
+ return *head;
+ } else {
+ return dummyInst;
+ }
+}
+*/
+template <class Impl>
+typename Impl::DynInstPtr
+ROB<Impl>::readHeadInst(unsigned tid)
+{
+ if (threadEntries[tid] != 0) {
+ InstIt head_thread = instList[tid].begin();
+ assert((*head_thread)->isInROB()==true);
+
+ return *head_thread;
+ } else {
+ return dummyInst;
+ }
+}
+/*
template <class Impl>
uint64_t
ROB<Impl>::readHeadPC()
{
- assert(numInstsInROB == countInsts());
+ //assert(numInstsInROB == countInsts());
- DynInstPtr head_inst = cpu->instList.front();
+ DynInstPtr head_inst = *head;
return head_inst->readPC();
}
template <class Impl>
uint64_t
+ROB<Impl>::readHeadPC(unsigned tid)
+{
+ //assert(numInstsInROB == countInsts());
+ InstIt head_thread = instList[tid].begin();
+
+ return (*head_thread)->readPC();
+}
+
+
+template <class Impl>
+uint64_t
ROB<Impl>::readHeadNextPC()
{
- assert(numInstsInROB == countInsts());
+ //assert(numInstsInROB == countInsts());
- DynInstPtr head_inst = cpu->instList.front();
+ DynInstPtr head_inst = *head;
return head_inst->readNextPC();
}
template <class Impl>
+uint64_t
+ROB<Impl>::readHeadNextPC(unsigned tid)
+{
+ //assert(numInstsInROB == countInsts());
+ InstIt head_thread = instList[tid].begin();
+
+ return (*head_thread)->readNextPC();
+}
+
+template <class Impl>
InstSeqNum
ROB<Impl>::readHeadSeqNum()
{
- // Return the last sequence number that has not been squashed. Other
- // stages can use it to squash any instructions younger than the current
- // tail.
- DynInstPtr head_inst = cpu->instList.front();
+ //assert(numInstsInROB == countInsts());
+ DynInstPtr head_inst = *head;
return head_inst->seqNum;
}
template <class Impl>
+InstSeqNum
+ROB<Impl>::readHeadSeqNum(unsigned tid)
+{
+ InstIt head_thread = instList[tid].begin();
+
+ return ((*head_thread)->seqNum);
+}
+
+template <class Impl>
+typename Impl::DynInstPtr
+ROB<Impl>::readTailInst()
+{
+ //assert(numInstsInROB == countInsts());
+ //assert(tail != instList[0].end());
+
+ return (*tail);
+}
+*/
+template <class Impl>
+typename Impl::DynInstPtr
+ROB<Impl>::readTailInst(unsigned tid)
+{
+ //assert(tail_thread[tid] != instList[tid].end());
+
+ InstIt tail_thread = instList[tid].end();
+ tail_thread--;
+
+ return *tail_thread;
+}
+
+/*
+template <class Impl>
uint64_t
ROB<Impl>::readTailPC()
{
- assert(numInstsInROB == countInsts());
+ //assert(numInstsInROB == countInsts());
- assert(tail != cpu->instList.end());
+ //assert(tail != instList[0].end());
return (*tail)->readPC();
}
template <class Impl>
+uint64_t
+ROB<Impl>::readTailPC(unsigned tid)
+{
+ //assert(tail_thread[tid] != instList[tid].end());
+
+ InstIt tail_thread = instList[tid].end();
+ tail_thread--;
+
+ return (*tail_thread)->readPC();
+}
+
+template <class Impl>
InstSeqNum
ROB<Impl>::readTailSeqNum()
{
@@ -309,4 +676,18 @@ ROB<Impl>::readTailSeqNum()
return (*tail)->seqNum;
}
-#endif // __CPU_O3_CPU_ROB_IMPL_HH__
+template <class Impl>
+InstSeqNum
+ROB<Impl>::readTailSeqNum(unsigned tid)
+{
+ // Return the last sequence number that has not been squashed. Other
+ // stages can use it to squash any instructions younger than the current
+ // tail.
+ // assert(tail_thread[tid] != instList[tid].end());
+
+ InstIt tail_thread = instList[tid].end();
+ tail_thread--;
+
+ return (*tail_thread)->seqNum;
+}
+*/
diff --git a/src/cpu/o3/sat_counter.cc b/src/cpu/o3/sat_counter.cc
index d20fff650..68d3ef627 100644
--- a/src/cpu/o3/sat_counter.cc
+++ b/src/cpu/o3/sat_counter.cc
@@ -24,27 +24,29 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
#include "base/misc.hh"
#include "cpu/o3/sat_counter.hh"
SatCounter::SatCounter()
- : maxVal(0), counter(0)
+ : initialVal(0), counter(0)
{
}
SatCounter::SatCounter(unsigned bits)
- : maxVal((1 << bits) - 1), counter(0)
+ : initialVal(0), maxVal((1 << bits) - 1), counter(0)
{
}
-SatCounter::SatCounter(unsigned bits, unsigned initial_val)
- : maxVal((1 << bits) - 1), counter(initial_val)
+SatCounter::SatCounter(unsigned bits, uint8_t initial_val)
+ : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val)
{
// Check to make sure initial value doesn't exceed the max counter value.
if (initial_val > maxVal) {
- panic("BP: Initial counter value exceeds max size.");
+ fatal("BP: Initial counter value exceeds max size.");
}
}
@@ -53,19 +55,3 @@ SatCounter::setBits(unsigned bits)
{
maxVal = (1 << bits) - 1;
}
-
-void
-SatCounter::increment()
-{
- if(counter < maxVal) {
- ++counter;
- }
-}
-
-void
-SatCounter::decrement()
-{
- if(counter > 0) {
- --counter;
- }
-}
diff --git a/src/cpu/o3/sat_counter.hh b/src/cpu/o3/sat_counter.hh
index b7cfe6423..7e15119b0 100644
--- a/src/cpu/o3/sat_counter.hh
+++ b/src/cpu/o3/sat_counter.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005 The Regents of The University of Michigan
+ * Copyright (c) 2005-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,11 +24,14 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_SAT_COUNTER_HH__
-#define __CPU_O3_CPU_SAT_COUNTER_HH__
+#ifndef __CPU_O3_SAT_COUNTER_HH__
+#define __CPU_O3_SAT_COUNTER_HH__
+#include "base/misc.hh"
#include "sim/host.hh"
/**
@@ -44,47 +47,70 @@ class SatCounter
/**
* Constructor for the counter.
*/
- SatCounter();
+ SatCounter()
+ : initialVal(0), counter(0)
+ { }
/**
* Constructor for the counter.
* @param bits How many bits the counter will have.
*/
- SatCounter(unsigned bits);
+ SatCounter(unsigned bits)
+ : initialVal(0), maxVal((1 << bits) - 1), counter(0)
+ { }
/**
* Constructor for the counter.
* @param bits How many bits the counter will have.
* @param initial_val Starting value for each counter.
*/
- SatCounter(unsigned bits, unsigned initial_val);
+ SatCounter(unsigned bits, uint8_t initial_val)
+ : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val)
+ {
+ // Check to make sure initial value doesn't exceed the max
+ // counter value.
+ if (initial_val > maxVal) {
+ fatal("BP: Initial counter value exceeds max size.");
+ }
+ }
/**
* Sets the number of bits.
*/
- void setBits(unsigned bits);
+ void setBits(unsigned bits) { maxVal = (1 << bits) - 1; }
+
+ void reset() { counter = initialVal; }
/**
* Increments the counter's current value.
*/
- void increment();
+ void increment()
+ {
+ if (counter < maxVal) {
+ ++counter;
+ }
+ }
/**
* Decrements the counter's current value.
*/
- void decrement();
+ void decrement()
+ {
+ if (counter > 0) {
+ --counter;
+ }
+ }
/**
* Read the counter's value.
*/
const uint8_t read() const
- {
- return counter;
- }
+ { return counter; }
private:
+ uint8_t initialVal;
uint8_t maxVal;
uint8_t counter;
};
-#endif // __CPU_O3_CPU_SAT_COUNTER_HH__
+#endif // __CPU_O3_SAT_COUNTER_HH__
diff --git a/src/cpu/o3/scoreboard.cc b/src/cpu/o3/scoreboard.cc
new file mode 100644
index 000000000..1859b35a4
--- /dev/null
+++ b/src/cpu/o3/scoreboard.cc
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2005-2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Korey Sewell
+ * Kevin Lim
+ */
+
+#include "cpu/o3/scoreboard.hh"
+
+Scoreboard::Scoreboard(unsigned activeThreads,
+ unsigned _numLogicalIntRegs,
+ unsigned _numPhysicalIntRegs,
+ unsigned _numLogicalFloatRegs,
+ unsigned _numPhysicalFloatRegs,
+ unsigned _numMiscRegs,
+ unsigned _zeroRegIdx)
+ : numLogicalIntRegs(_numLogicalIntRegs),
+ numPhysicalIntRegs(_numPhysicalIntRegs),
+ numLogicalFloatRegs(_numLogicalFloatRegs),
+ numPhysicalFloatRegs(_numPhysicalFloatRegs),
+ numMiscRegs(_numMiscRegs),
+ zeroRegIdx(_zeroRegIdx)
+{
+ //Get Register Sizes
+ numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs;
+ numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs;
+
+ //Resize scoreboard appropriately
+ regScoreBoard.resize(numPhysicalRegs + (numMiscRegs * activeThreads));
+
+ //Initialize values
+ for (int i=0; i < numLogicalIntRegs * activeThreads; i++) {
+ regScoreBoard[i] = 1;
+ }
+
+ for (int i= numPhysicalIntRegs;
+ i < numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads);
+ i++) {
+ regScoreBoard[i] = 1;
+ }
+
+ for (int i = numPhysicalRegs;
+ i < numPhysicalRegs + (numMiscRegs * activeThreads);
+ i++) {
+ regScoreBoard[i] = 1;
+ }
+}
+
+std::string
+Scoreboard::name() const
+{
+ return "cpu.scoreboard";
+}
+
+bool
+Scoreboard::getReg(PhysRegIndex phys_reg)
+{
+ // Always ready if int or fp zero reg.
+ if (phys_reg == zeroRegIdx ||
+ phys_reg == (zeroRegIdx + numPhysicalIntRegs)) {
+ return 1;
+ }
+
+ return regScoreBoard[phys_reg];
+}
+
+void
+Scoreboard::setReg(PhysRegIndex phys_reg)
+{
+ DPRINTF(Scoreboard, "Setting reg %i as ready\n", phys_reg);
+
+ regScoreBoard[phys_reg] = 1;
+}
+
+void
+Scoreboard::unsetReg(PhysRegIndex ready_reg)
+{
+ if (ready_reg == zeroRegIdx ||
+ ready_reg == (zeroRegIdx + numPhysicalIntRegs)) {
+ // Don't do anything if int or fp zero reg.
+ return;
+ }
+
+ regScoreBoard[ready_reg] = 0;
+}
diff --git a/src/cpu/o3/scoreboard.hh b/src/cpu/o3/scoreboard.hh
new file mode 100644
index 000000000..f8e4df3b7
--- /dev/null
+++ b/src/cpu/o3/scoreboard.hh
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2005-2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Korey Sewell
+ * Kevin Lim
+ */
+
+#ifndef __CPU_O3_SCOREBOARD_HH__
+#define __CPU_O3_SCOREBOARD_HH__
+
+#include <iostream>
+#include <utility>
+#include <vector>
+#include "arch/alpha/isa_traits.hh"
+#include "base/trace.hh"
+#include "base/traceflags.hh"
+#include "cpu/o3/comm.hh"
+
+/**
+ * Implements a simple scoreboard to track which registers are ready.
+ * This class assumes that the fp registers start, index wise, right after
+ * the integer registers. The misc. registers start, index wise, right after
+ * the fp registers.
+ * @todo: Fix up handling of the zero register in case the decoder does not
+ * automatically make insts that write the zero register into nops.
+ */
+class Scoreboard
+{
+ public:
+ /** Constructs a scoreboard.
+ * @param activeThreads The number of active threads.
+ * @param _numLogicalIntRegs Number of logical integer registers.
+ * @param _numPhysicalIntRegs Number of physical integer registers.
+ * @param _numLogicalFloatRegs Number of logical fp registers.
+ * @param _numPhysicalFloatRegs Number of physical fp registers.
+ * @param _numMiscRegs Number of miscellaneous registers.
+ * @param _zeroRegIdx Index of the zero register.
+ */
+ Scoreboard(unsigned activeThreads,
+ unsigned _numLogicalIntRegs,
+ unsigned _numPhysicalIntRegs,
+ unsigned _numLogicalFloatRegs,
+ unsigned _numPhysicalFloatRegs,
+ unsigned _numMiscRegs,
+ unsigned _zeroRegIdx);
+
+ /** Destructor. */
+ ~Scoreboard() {}
+
+ /** Returns the name of the scoreboard. */
+ std::string name() const;
+
+ /** Checks if the register is ready. */
+ bool getReg(PhysRegIndex ready_reg);
+
+ /** Sets the register as ready. */
+ void setReg(PhysRegIndex phys_reg);
+
+ /** Sets the register as not ready. */
+ void unsetReg(PhysRegIndex ready_reg);
+
+ private:
+ /** Scoreboard of physical integer registers, saying whether or not they
+ * are ready.
+ */
+ std::vector<bool> regScoreBoard;
+
+ /** Number of logical integer registers. */
+ int numLogicalIntRegs;
+
+ /** Number of physical integer registers. */
+ int numPhysicalIntRegs;
+
+ /** Number of logical floating point registers. */
+ int numLogicalFloatRegs;
+
+ /** Number of physical floating point registers. */
+ int numPhysicalFloatRegs;
+
+ /** Number of miscellaneous registers. */
+ int numMiscRegs;
+
+ /** Number of logical integer + float registers. */
+ int numLogicalRegs;
+
+ /** Number of physical integer + float registers. */
+ int numPhysicalRegs;
+
+ /** The logical index of the zero register. */
+ int zeroRegIdx;
+};
+
+#endif
diff --git a/src/cpu/o3/store_set.cc b/src/cpu/o3/store_set.cc
index 11023f4a8..2d28b617f 100644
--- a/src/cpu/o3/store_set.cc
+++ b/src/cpu/o3/store_set.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,49 +24,94 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
+#include "base/intmath.hh"
+#include "base/misc.hh"
#include "base/trace.hh"
#include "cpu/o3/store_set.hh"
StoreSet::StoreSet(int _SSIT_size, int _LFST_size)
- : SSIT_size(_SSIT_size), LFST_size(_LFST_size)
+ : SSITSize(_SSIT_size), LFSTSize(_LFST_size)
{
DPRINTF(StoreSet, "StoreSet: Creating store set object.\n");
DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n",
- SSIT_size, LFST_size);
+ SSITSize, LFSTSize);
+
+ if (!isPowerOf2(SSITSize)) {
+ fatal("Invalid SSIT size!\n");
+ }
- SSIT = new SSID[SSIT_size];
+ SSIT.resize(SSITSize);
- validSSIT.resize(SSIT_size);
+ validSSIT.resize(SSITSize);
- for (int i = 0; i < SSIT_size; ++i)
+ for (int i = 0; i < SSITSize; ++i)
validSSIT[i] = false;
- LFST = new InstSeqNum[LFST_size];
+ if (!isPowerOf2(LFSTSize)) {
+ fatal("Invalid LFST size!\n");
+ }
+
+ LFST.resize(LFSTSize);
+
+ validLFST.resize(LFSTSize);
+
+ for (int i = 0; i < LFSTSize; ++i) {
+ validLFST[i] = false;
+ LFST[i] = 0;
+ }
+
+ indexMask = SSITSize - 1;
+
+ offsetBits = 2;
+}
+
+StoreSet::~StoreSet()
+{
+}
- validLFST.resize(LFST_size);
+void
+StoreSet::init(int _SSIT_size, int _LFST_size)
+{
+ SSITSize = _SSIT_size;
+ LFSTSize = _LFST_size;
+
+ DPRINTF(StoreSet, "StoreSet: Creating store set object.\n");
+ DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n",
+ SSITSize, LFSTSize);
- SSCounters = new int[LFST_size];
+ SSIT.resize(SSITSize);
- for (int i = 0; i < LFST_size; ++i)
- {
+ validSSIT.resize(SSITSize);
+
+ for (int i = 0; i < SSITSize; ++i)
+ validSSIT[i] = false;
+
+ LFST.resize(LFSTSize);
+
+ validLFST.resize(LFSTSize);
+
+ for (int i = 0; i < LFSTSize; ++i) {
validLFST[i] = false;
- SSCounters[i] = 0;
+ LFST[i] = 0;
}
- index_mask = SSIT_size - 1;
+ indexMask = SSITSize - 1;
- offset_bits = 2;
+ offsetBits = 2;
}
+
void
StoreSet::violation(Addr store_PC, Addr load_PC)
{
int load_index = calcIndex(load_PC);
int store_index = calcIndex(store_PC);
- assert(load_index < SSIT_size && store_index < SSIT_size);
+ assert(load_index < SSITSize && store_index < SSITSize);
bool valid_load_SSID = validSSIT[load_index];
bool valid_store_SSID = validSSIT[store_index];
@@ -83,10 +128,7 @@ StoreSet::violation(Addr store_PC, Addr load_PC)
SSIT[store_index] = new_set;
- assert(new_set < LFST_size);
-
- SSCounters[new_set]++;
-
+ assert(new_set < LFSTSize);
DPRINTF(StoreSet, "StoreSet: Neither load nor store had a valid "
"storeset, creating a new one: %i for load %#x, store %#x\n",
@@ -98,9 +140,7 @@ StoreSet::violation(Addr store_PC, Addr load_PC)
SSIT[store_index] = load_SSID;
- assert(load_SSID < LFST_size);
-
- SSCounters[load_SSID]++;
+ assert(load_SSID < LFSTSize);
DPRINTF(StoreSet, "StoreSet: Load had a valid store set. Adding "
"store to that set: %i for load %#x, store %#x\n",
@@ -112,9 +152,6 @@ StoreSet::violation(Addr store_PC, Addr load_PC)
SSIT[load_index] = store_SSID;
- // Because we are having a load point to an already existing set,
- // the size of the store set is not incremented.
-
DPRINTF(StoreSet, "StoreSet: Store had a valid store set: %i for "
"load %#x, store %#x\n",
store_SSID, load_PC, store_PC);
@@ -122,29 +159,19 @@ StoreSet::violation(Addr store_PC, Addr load_PC)
SSID load_SSID = SSIT[load_index];
SSID store_SSID = SSIT[store_index];
- assert(load_SSID < LFST_size && store_SSID < LFST_size);
+ assert(load_SSID < LFSTSize && store_SSID < LFSTSize);
- int load_SS_size = SSCounters[load_SSID];
- int store_SS_size = SSCounters[store_SSID];
-
- // If the load has the bigger store set, then assign the store
- // to the same store set as the load. Otherwise vice-versa.
- if (load_SS_size > store_SS_size) {
+ // The store set with the lower number wins
+ if (store_SSID > load_SSID) {
SSIT[store_index] = load_SSID;
- SSCounters[load_SSID]++;
- SSCounters[store_SSID]--;
-
- DPRINTF(StoreSet, "StoreSet: Load had bigger store set: %i; "
+ DPRINTF(StoreSet, "StoreSet: Load had smaller store set: %i; "
"for load %#x, store %#x\n",
load_SSID, load_PC, store_PC);
} else {
SSIT[load_index] = store_SSID;
- SSCounters[store_SSID]++;
- SSCounters[load_SSID]--;
-
- DPRINTF(StoreSet, "StoreSet: Store had bigger store set: %i; "
+ DPRINTF(StoreSet, "StoreSet: Store had smaller store set: %i; "
"for load %#x, store %#x\n",
store_SSID, load_PC, store_PC);
}
@@ -159,13 +186,14 @@ StoreSet::insertLoad(Addr load_PC, InstSeqNum load_seq_num)
}
void
-StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num)
+StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num,
+ unsigned tid)
{
int index = calcIndex(store_PC);
int store_SSID;
- assert(index < SSIT_size);
+ assert(index < SSITSize);
if (!validSSIT[index]) {
// Do nothing if there's no valid entry.
@@ -173,13 +201,15 @@ StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num)
} else {
store_SSID = SSIT[index];
- assert(store_SSID < LFST_size);
+ assert(store_SSID < LFSTSize);
// Update the last store that was fetched with the current one.
LFST[store_SSID] = store_seq_num;
validLFST[store_SSID] = 1;
+ storeList[store_seq_num] = store_SSID;
+
DPRINTF(StoreSet, "Store %#x updated the LFST, SSID: %i\n",
store_PC, store_SSID);
}
@@ -192,7 +222,7 @@ StoreSet::checkInst(Addr PC)
int inst_SSID;
- assert(index < SSIT_size);
+ assert(index < SSITSize);
if (!validSSIT[index]) {
DPRINTF(StoreSet, "Inst %#x with index %i had no SSID\n",
@@ -203,7 +233,7 @@ StoreSet::checkInst(Addr PC)
} else {
inst_SSID = SSIT[index];
- assert(inst_SSID < LFST_size);
+ assert(inst_SSID < LFSTSize);
if (!validLFST[inst_SSID]) {
@@ -232,7 +262,13 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store)
int store_SSID;
- assert(index < SSIT_size);
+ assert(index < SSITSize);
+
+ SeqNumMapIt store_list_it = storeList.find(issued_seq_num);
+
+ if (store_list_it != storeList.end()) {
+ storeList.erase(store_list_it);
+ }
// Make sure the SSIT still has a valid entry for the issued store.
if (!validSSIT[index]) {
@@ -241,7 +277,7 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store)
store_SSID = SSIT[index];
- assert(store_SSID < LFST_size);
+ assert(store_SSID < LFSTSize);
// If the last fetched store in the store set refers to the store that
// was just issued, then invalidate the entry.
@@ -252,18 +288,31 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store)
}
void
-StoreSet::squash(InstSeqNum squashed_num)
+StoreSet::squash(InstSeqNum squashed_num, unsigned tid)
{
- // Not really sure how to do this well.
- // Generally this is small enough that it should be okay; short circuit
- // evaluation should take care of invalid entries.
-
DPRINTF(StoreSet, "StoreSet: Squashing until inum %i\n",
squashed_num);
- for (int i = 0; i < LFST_size; ++i) {
- if (validLFST[i] && LFST[i] < squashed_num) {
- validLFST[i] = false;
+ int idx;
+ SeqNumMapIt store_list_it = storeList.begin();
+
+ //@todo:Fix to only delete from correct thread
+ while (!storeList.empty()) {
+ idx = (*store_list_it).second;
+
+ if ((*store_list_it).first <= squashed_num) {
+ break;
+ }
+
+ bool younger = LFST[idx] > squashed_num;
+
+ if (validLFST[idx] && younger) {
+ DPRINTF(StoreSet, "Squashed [sn:%lli]\n", LFST[idx]);
+ validLFST[idx] = false;
+
+ storeList.erase(store_list_it++);
+ } else if (!validLFST[idx] && younger) {
+ storeList.erase(store_list_it++);
}
}
}
@@ -271,12 +320,29 @@ StoreSet::squash(InstSeqNum squashed_num)
void
StoreSet::clear()
{
- for (int i = 0; i < SSIT_size; ++i) {
+ for (int i = 0; i < SSITSize; ++i) {
validSSIT[i] = false;
}
- for (int i = 0; i < LFST_size; ++i) {
+ for (int i = 0; i < LFSTSize; ++i) {
validLFST[i] = false;
}
+
+ storeList.clear();
}
+void
+StoreSet::dump()
+{
+ cprintf("storeList.size(): %i\n", storeList.size());
+ SeqNumMapIt store_list_it = storeList.begin();
+
+ int num = 0;
+
+ while (store_list_it != storeList.end()) {
+ cprintf("%i: [sn:%lli] SSID:%i\n",
+ num, (*store_list_it).first, (*store_list_it).second);
+ num++;
+ store_list_it++;
+ }
+}
diff --git a/src/cpu/o3/store_set.hh b/src/cpu/o3/store_set.hh
index 5a885d838..f5a44a1ac 100644
--- a/src/cpu/o3/store_set.hh
+++ b/src/cpu/o3/store_set.hh
@@ -24,63 +24,124 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_STORE_SET_HH__
-#define __CPU_O3_CPU_STORE_SET_HH__
+#ifndef __CPU_O3_STORE_SET_HH__
+#define __CPU_O3_STORE_SET_HH__
+#include <list>
+#include <map>
+#include <utility>
#include <vector>
#include "arch/isa_traits.hh"
#include "cpu/inst_seq.hh"
+struct ltseqnum {
+ bool operator()(const InstSeqNum &lhs, const InstSeqNum &rhs) const
+ {
+ return lhs > rhs;
+ }
+};
+
+/**
+ * Implements a store set predictor for determining if memory
+ * instructions are dependent upon each other. See paper "Memory
+ * Dependence Prediction using Store Sets" by Chrysos and Emer. SSID
+ * stands for Store Set ID, SSIT stands for Store Set ID Table, and
+ * LFST is Last Fetched Store Table.
+ */
class StoreSet
{
public:
typedef unsigned SSID;
public:
+ /** Default constructor. init() must be called prior to use. */
+ StoreSet() { };
+
+ /** Creates store set predictor with given table sizes. */
StoreSet(int SSIT_size, int LFST_size);
+ /** Default destructor. */
+ ~StoreSet();
+
+ /** Initializes the store set predictor with the given table sizes. */
+ void init(int SSIT_size, int LFST_size);
+
+ /** Records a memory ordering violation between the younger load
+ * and the older store. */
void violation(Addr store_PC, Addr load_PC);
+ /** Inserts a load into the store set predictor. This does nothing but
+ * is included in case other predictors require a similar function.
+ */
void insertLoad(Addr load_PC, InstSeqNum load_seq_num);
- void insertStore(Addr store_PC, InstSeqNum store_seq_num);
+ /** Inserts a store into the store set predictor. Updates the
+ * LFST if the store has a valid SSID. */
+ void insertStore(Addr store_PC, InstSeqNum store_seq_num,
+ unsigned tid);
+ /** Checks if the instruction with the given PC is dependent upon
+ * any store. @return Returns the sequence number of the store
+ * instruction this PC is dependent upon. Returns 0 if none.
+ */
InstSeqNum checkInst(Addr PC);
+ /** Records this PC/sequence number as issued. */
void issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store);
- void squash(InstSeqNum squashed_num);
+ /** Squashes for a specific thread until the given sequence number. */
+ void squash(InstSeqNum squashed_num, unsigned tid);
+ /** Resets all tables. */
void clear();
+ /** Debug function to dump the contents of the store list. */
+ void dump();
+
private:
+ /** Calculates the index into the SSIT based on the PC. */
inline int calcIndex(Addr PC)
- { return (PC >> offset_bits) & index_mask; }
+ { return (PC >> offsetBits) & indexMask; }
+ /** Calculates a Store Set ID based on the PC. */
inline SSID calcSSID(Addr PC)
- { return ((PC ^ (PC >> 10)) % LFST_size); }
+ { return ((PC ^ (PC >> 10)) % LFSTSize); }
- SSID *SSIT;
+ /** The Store Set ID Table. */
+ std::vector<SSID> SSIT;
+ /** Bit vector to tell if the SSIT has a valid entry. */
std::vector<bool> validSSIT;
- InstSeqNum *LFST;
+ /** Last Fetched Store Table. */
+ std::vector<InstSeqNum> LFST;
+ /** Bit vector to tell if the LFST has a valid entry. */
std::vector<bool> validLFST;
- int *SSCounters;
+ /** Map of stores that have been inserted into the store set, but
+ * not yet issued or squashed.
+ */
+ std::map<InstSeqNum, int, ltseqnum> storeList;
+
+ typedef std::map<InstSeqNum, int, ltseqnum>::iterator SeqNumMapIt;
- int SSIT_size;
+ /** Store Set ID Table size, in entries. */
+ int SSITSize;
- int LFST_size;
+ /** Last Fetched Store Table size, in entries. */
+ int LFSTSize;
- int index_mask;
+ /** Mask to obtain the index. */
+ int indexMask;
// HACK: Hardcoded for now.
- int offset_bits;
+ int offsetBits;
};
-#endif // __CPU_O3_CPU_STORE_SET_HH__
+#endif // __CPU_O3_STORE_SET_HH__
diff --git a/src/cpu/o3/thread_state.hh b/src/cpu/o3/thread_state.hh
new file mode 100644
index 000000000..b6535baa1
--- /dev/null
+++ b/src/cpu/o3/thread_state.hh
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2006 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
+ */
+
+#ifndef __CPU_O3_THREAD_STATE_HH__
+#define __CPU_O3_THREAD_STATE_HH__
+
+#include "arch/faults.hh"
+#include "arch/isa_traits.hh"
+#include "cpu/thread_context.hh"
+#include "cpu/thread_state.hh"
+
+class Event;
+class Process;
+
+#if FULL_SYSTEM
+class EndQuiesceEvent;
+class FunctionProfile;
+class ProfileNode;
+#else
+class FunctionalMemory;
+class Process;
+#endif
+
+/**
+ * Class that has various thread state, such as the status, the
+ * current instruction being processed, whether or not the thread has
+ * a trap pending or is being externally updated, the ThreadContext
+ * pointer, etc. It also handles anything related to a specific
+ * thread's process, such as syscalls and checking valid addresses.
+ */
+template <class Impl>
+struct O3ThreadState : public ThreadState {
+ typedef ThreadContext::Status Status;
+ typedef typename Impl::FullCPU FullCPU;
+
+ private:
+ /** Pointer to the CPU. */
+ FullCPU *cpu;
+ public:
+ /** Whether or not the thread is currently in syscall mode, and
+ * thus able to be externally updated without squashing.
+ */
+ bool inSyscall;
+
+ /** Whether or not the thread is currently waiting on a trap, and
+ * thus able to be externally updated without squashing.
+ */
+ bool trapPending;
+
+#if FULL_SYSTEM
+ O3ThreadState(FullCPU *_cpu, int _thread_num)
+ : ThreadState(-1, _thread_num),
+ inSyscall(0), trapPending(0)
+ { }
+#else
+ O3ThreadState(FullCPU *_cpu, int _thread_num, Process *_process, int _asid,
+ MemObject *mem)
+ : ThreadState(-1, _thread_num, mem, _process, _asid),
+ cpu(_cpu), inSyscall(0), trapPending(0)
+ { }
+#endif
+
+ /** Pointer to the ThreadContext of this thread. */
+ ThreadContext *tc;
+
+ /** Returns a pointer to the TC of this thread. */
+ ThreadContext *getTC() { return tc; }
+
+#if !FULL_SYSTEM
+ /** Handles the syscall. */
+ void syscall(int64_t callnum) { process->syscall(callnum, tc); }
+#endif
+};
+
+#endif // __CPU_O3_THREAD_STATE_HH__
diff --git a/src/cpu/o3/tournament_pred.cc b/src/cpu/o3/tournament_pred.cc
index 3fb580510..7cf78dcb1 100644
--- a/src/cpu/o3/tournament_pred.cc
+++ b/src/cpu/o3/tournament_pred.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,41 +24,50 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
+#include "base/intmath.hh"
#include "cpu/o3/tournament_pred.hh"
-TournamentBP::TournamentBP(unsigned _local_predictor_size,
- unsigned _local_ctr_bits,
- unsigned _local_history_table_size,
- unsigned _local_history_bits,
- unsigned _global_predictor_size,
- unsigned _global_ctr_bits,
- unsigned _global_history_bits,
- unsigned _choice_predictor_size,
- unsigned _choice_ctr_bits,
+TournamentBP::TournamentBP(unsigned _localPredictorSize,
+ unsigned _localCtrBits,
+ unsigned _localHistoryTableSize,
+ unsigned _localHistoryBits,
+ unsigned _globalPredictorSize,
+ unsigned _globalCtrBits,
+ unsigned _globalHistoryBits,
+ unsigned _choicePredictorSize,
+ unsigned _choiceCtrBits,
unsigned _instShiftAmt)
- : localPredictorSize(_local_predictor_size),
- localCtrBits(_local_ctr_bits),
- localHistoryTableSize(_local_history_table_size),
- localHistoryBits(_local_history_bits),
- globalPredictorSize(_global_predictor_size),
- globalCtrBits(_global_ctr_bits),
- globalHistoryBits(_global_history_bits),
- choicePredictorSize(_global_predictor_size),
- choiceCtrBits(_choice_ctr_bits),
+ : localPredictorSize(_localPredictorSize),
+ localCtrBits(_localCtrBits),
+ localHistoryTableSize(_localHistoryTableSize),
+ localHistoryBits(_localHistoryBits),
+ globalPredictorSize(_globalPredictorSize),
+ globalCtrBits(_globalCtrBits),
+ globalHistoryBits(_globalHistoryBits),
+ choicePredictorSize(_globalPredictorSize),
+ choiceCtrBits(_choiceCtrBits),
instShiftAmt(_instShiftAmt)
{
- //Should do checks here to make sure sizes are correct (powers of 2)
+ if (!isPowerOf2(localPredictorSize)) {
+ fatal("Invalid local predictor size!\n");
+ }
//Setup the array of counters for the local predictor
- localCtrs = new SatCounter[localPredictorSize];
+ localCtrs.resize(localPredictorSize);
for (int i = 0; i < localPredictorSize; ++i)
localCtrs[i].setBits(localCtrBits);
+ if (!isPowerOf2(localHistoryTableSize)) {
+ fatal("Invalid local history table size!\n");
+ }
+
//Setup the history table for the local table
- localHistoryTable = new unsigned[localHistoryTableSize];
+ localHistoryTable.resize(localHistoryTableSize);
for (int i = 0; i < localHistoryTableSize; ++i)
localHistoryTable[i] = 0;
@@ -66,8 +75,12 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size,
// Setup the local history mask
localHistoryMask = (1 << localHistoryBits) - 1;
+ if (!isPowerOf2(globalPredictorSize)) {
+ fatal("Invalid global predictor size!\n");
+ }
+
//Setup the array of counters for the global predictor
- globalCtrs = new SatCounter[globalPredictorSize];
+ globalCtrs.resize(globalPredictorSize);
for (int i = 0; i < globalPredictorSize; ++i)
globalCtrs[i].setBits(globalCtrBits);
@@ -77,12 +90,17 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size,
// Setup the global history mask
globalHistoryMask = (1 << globalHistoryBits) - 1;
+ if (!isPowerOf2(choicePredictorSize)) {
+ fatal("Invalid choice predictor size!\n");
+ }
+
//Setup the array of counters for the choice predictor
- choiceCtrs = new SatCounter[choicePredictorSize];
+ choiceCtrs.resize(choicePredictorSize);
for (int i = 0; i < choicePredictorSize; ++i)
choiceCtrs[i].setBits(choiceCtrBits);
+ // @todo: Allow for different thresholds between the predictors.
threshold = (1 << (localCtrBits - 1)) - 1;
threshold = threshold / 2;
}
@@ -91,166 +109,185 @@ inline
unsigned
TournamentBP::calcLocHistIdx(Addr &branch_addr)
{
+ // Get low order bits after removing instruction offset.
return (branch_addr >> instShiftAmt) & (localHistoryTableSize - 1);
}
inline
void
-TournamentBP::updateHistoriesTaken(unsigned local_history_idx)
+TournamentBP::updateGlobalHistTaken()
{
globalHistory = (globalHistory << 1) | 1;
globalHistory = globalHistory & globalHistoryMask;
-
- localHistoryTable[local_history_idx] =
- (localHistoryTable[local_history_idx] << 1) | 1;
}
inline
void
-TournamentBP::updateHistoriesNotTaken(unsigned local_history_idx)
+TournamentBP::updateGlobalHistNotTaken()
{
globalHistory = (globalHistory << 1);
globalHistory = globalHistory & globalHistoryMask;
+}
+inline
+void
+TournamentBP::updateLocalHistTaken(unsigned local_history_idx)
+{
+ localHistoryTable[local_history_idx] =
+ (localHistoryTable[local_history_idx] << 1) | 1;
+}
+
+inline
+void
+TournamentBP::updateLocalHistNotTaken(unsigned local_history_idx)
+{
localHistoryTable[local_history_idx] =
(localHistoryTable[local_history_idx] << 1);
}
bool
-TournamentBP::lookup(Addr &branch_addr)
+TournamentBP::lookup(Addr &branch_addr, void * &bp_history)
{
- uint8_t local_prediction;
+ bool local_prediction;
unsigned local_history_idx;
unsigned local_predictor_idx;
- uint8_t global_prediction;
- uint8_t choice_prediction;
+ bool global_prediction;
+ bool choice_prediction;
//Lookup in the local predictor to get its branch prediction
local_history_idx = calcLocHistIdx(branch_addr);
local_predictor_idx = localHistoryTable[local_history_idx]
& localHistoryMask;
- local_prediction = localCtrs[local_predictor_idx].read();
+ local_prediction = localCtrs[local_predictor_idx].read() > threshold;
//Lookup in the global predictor to get its branch prediction
- global_prediction = globalCtrs[globalHistory].read();
+ global_prediction = globalCtrs[globalHistory].read() > threshold;
//Lookup in the choice predictor to see which one to use
- choice_prediction = choiceCtrs[globalHistory].read();
-
- //@todo Put a threshold value in for the three predictors that can
- // be set through the constructor (so this isn't hard coded).
- //Also should put some of this code into functions.
- if (choice_prediction > threshold) {
- if (global_prediction > threshold) {
- updateHistoriesTaken(local_history_idx);
-
- assert(globalHistory < globalPredictorSize &&
- local_history_idx < localPredictorSize);
-
- globalCtrs[globalHistory].increment();
- localCtrs[local_history_idx].increment();
-
+ choice_prediction = choiceCtrs[globalHistory].read() > threshold;
+
+ // Create BPHistory and pass it back to be recorded.
+ BPHistory *history = new BPHistory;
+ history->globalHistory = globalHistory;
+ history->localPredTaken = local_prediction;
+ history->globalPredTaken = global_prediction;
+ history->globalUsed = choice_prediction;
+ bp_history = (void *)history;
+
+ assert(globalHistory < globalPredictorSize &&
+ local_history_idx < localPredictorSize);
+
+ // Commented code is for doing speculative update of counters and
+ // all histories.
+ if (choice_prediction) {
+ if (global_prediction) {
+// updateHistoriesTaken(local_history_idx);
+// globalCtrs[globalHistory].increment();
+// localCtrs[local_history_idx].increment();
+ updateGlobalHistTaken();
return true;
} else {
- updateHistoriesNotTaken(local_history_idx);
-
- assert(globalHistory < globalPredictorSize &&
- local_history_idx < localPredictorSize);
-
- globalCtrs[globalHistory].decrement();
- localCtrs[local_history_idx].decrement();
-
+// updateHistoriesNotTaken(local_history_idx);
+// globalCtrs[globalHistory].decrement();
+// localCtrs[local_history_idx].decrement();
+ updateGlobalHistNotTaken();
return false;
}
} else {
- if (local_prediction > threshold) {
- updateHistoriesTaken(local_history_idx);
-
- assert(globalHistory < globalPredictorSize &&
- local_history_idx < localPredictorSize);
-
- globalCtrs[globalHistory].increment();
- localCtrs[local_history_idx].increment();
-
+ if (local_prediction) {
+// updateHistoriesTaken(local_history_idx);
+// globalCtrs[globalHistory].increment();
+// localCtrs[local_history_idx].increment();
+ updateGlobalHistTaken();
return true;
} else {
- updateHistoriesNotTaken(local_history_idx);
-
- assert(globalHistory < globalPredictorSize &&
- local_history_idx < localPredictorSize);
-
- globalCtrs[globalHistory].decrement();
- localCtrs[local_history_idx].decrement();
-
+// updateHistoriesNotTaken(local_history_idx);
+// globalCtrs[globalHistory].decrement();
+// localCtrs[local_history_idx].decrement();
+ updateGlobalHistNotTaken();
return false;
}
}
}
-// Update the branch predictor if it predicted a branch wrong.
void
-TournamentBP::update(Addr &branch_addr, unsigned correct_gh, bool taken)
+TournamentBP::uncondBr(void * &bp_history)
{
+ // Create BPHistory and pass it back to be recorded.
+ BPHistory *history = new BPHistory;
+ history->globalHistory = globalHistory;
+ history->localPredTaken = true;
+ history->globalPredTaken = true;
+ bp_history = static_cast<void *>(history);
+
+ updateGlobalHistTaken();
+}
- uint8_t local_prediction;
+void
+TournamentBP::update(Addr &branch_addr, bool taken, void *bp_history)
+{
unsigned local_history_idx;
unsigned local_predictor_idx;
- bool local_pred_taken;
+ unsigned local_predictor_hist;
- uint8_t global_prediction;
- bool global_pred_taken;
-
- // Load the correct global history into the register.
- globalHistory = correct_gh;
-
- // Get the local predictor's current prediction, remove the incorrect
- // update, and update the local predictor
+ // Get the local predictor's current prediction
local_history_idx = calcLocHistIdx(branch_addr);
- local_predictor_idx = localHistoryTable[local_history_idx];
- local_predictor_idx = (local_predictor_idx >> 1) & localHistoryMask;
-
- local_prediction = localCtrs[local_predictor_idx].read();
- local_pred_taken = local_prediction > threshold;
-
- //Get the global predictor's current prediction, and update the
- //global predictor
- global_prediction = globalCtrs[globalHistory].read();
- global_pred_taken = global_prediction > threshold;
-
- //Update the choice predictor to tell it which one was correct
- if (local_pred_taken != global_pred_taken) {
- //If the local prediction matches the actual outcome, decerement
- //the counter. Otherwise increment the counter.
- if (local_pred_taken == taken) {
- choiceCtrs[globalHistory].decrement();
- } else {
- choiceCtrs[globalHistory].increment();
+ local_predictor_hist = localHistoryTable[local_history_idx];
+ local_predictor_idx = local_predictor_hist & localHistoryMask;
+
+ // Update the choice predictor to tell it which one was correct if
+ // there was a prediction.
+ if (bp_history) {
+ BPHistory *history = static_cast<BPHistory *>(bp_history);
+ if (history->localPredTaken != history->globalPredTaken) {
+ // If the local prediction matches the actual outcome,
+ // decerement the counter. Otherwise increment the
+ // counter.
+ if (history->localPredTaken == taken) {
+ choiceCtrs[globalHistory].decrement();
+ } else if (history->globalPredTaken == taken){
+ choiceCtrs[globalHistory].increment();
+ }
}
+
+ // We're done with this history, now delete it.
+ delete history;
}
- if (taken) {
- assert(globalHistory < globalPredictorSize &&
- local_predictor_idx < localPredictorSize);
+ assert(globalHistory < globalPredictorSize &&
+ local_predictor_idx < localPredictorSize);
+ // Update the counters and local history with the proper
+ // resolution of the branch. Global history is updated
+ // speculatively and restored upon squash() calls, so it does not
+ // need to be updated.
+ if (taken) {
localCtrs[local_predictor_idx].increment();
globalCtrs[globalHistory].increment();
- globalHistory = (globalHistory << 1) | 1;
- globalHistory = globalHistory & globalHistoryMask;
+ updateLocalHistTaken(local_history_idx);
+ } else {
+ localCtrs[local_predictor_idx].decrement();
+ globalCtrs[globalHistory].decrement();
- localHistoryTable[local_history_idx] |= 1;
+ updateLocalHistNotTaken(local_history_idx);
}
- else {
- assert(globalHistory < globalPredictorSize &&
- local_predictor_idx < localPredictorSize);
+}
- localCtrs[local_predictor_idx].decrement();
- globalCtrs[globalHistory].decrement();
+void
+TournamentBP::squash(void *bp_history)
+{
+ BPHistory *history = static_cast<BPHistory *>(bp_history);
- globalHistory = (globalHistory << 1);
- globalHistory = globalHistory & globalHistoryMask;
+ // Restore global history to state prior to this branch.
+ globalHistory = history->globalHistory;
- localHistoryTable[local_history_idx] &= ~1;
- }
+ // Delete this BPHistory now that we're done with it.
+ delete history;
}
+
+#ifdef DEBUG
+int
+TournamentBP::BPHistory::newCount = 0;
+#endif
diff --git a/src/cpu/o3/tournament_pred.hh b/src/cpu/o3/tournament_pred.hh
index cb93c2f67..92402adc6 100644
--- a/src/cpu/o3/tournament_pred.hh
+++ b/src/cpu/o3/tournament_pred.hh
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
+ * Copyright (c) 2004-2006 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,61 +24,138 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kevin Lim
*/
-#ifndef __CPU_O3_CPU_TOURNAMENT_PRED_HH__
-#define __CPU_O3_CPU_TOURNAMENT_PRED_HH__
+#ifndef __CPU_O3_TOURNAMENT_PRED_HH__
+#define __CPU_O3_TOURNAMENT_PRED_HH__
// For Addr type.
#include "arch/isa_traits.hh"
#include "cpu/o3/sat_counter.hh"
-
+#include <vector>
+
+/**
+ * Implements a tournament branch predictor, hopefully identical to the one
+ * used in the 21264. It has a local predictor, which uses a local history
+ * table to index into a table of counters, and a global predictor, which
+ * uses a global history to index into a table of counters. A choice
+ * predictor chooses between the two. Only the global history register
+ * is speculatively updated, the rest are updated upon branches committing
+ * or misspeculating.
+ */
class TournamentBP
{
public:
/**
* Default branch predictor constructor.
*/
- TournamentBP(unsigned local_predictor_size,
- unsigned local_ctr_bits,
- unsigned local_history_table_size,
- unsigned local_history_bits,
- unsigned global_predictor_size,
- unsigned global_history_bits,
- unsigned global_ctr_bits,
- unsigned choice_predictor_size,
- unsigned choice_ctr_bits,
+ TournamentBP(unsigned localPredictorSize,
+ unsigned localCtrBits,
+ unsigned localHistoryTableSize,
+ unsigned localHistoryBits,
+ unsigned globalPredictorSize,
+ unsigned globalHistoryBits,
+ unsigned globalCtrBits,
+ unsigned choicePredictorSize,
+ unsigned choiceCtrBits,
unsigned instShiftAmt);
/**
* Looks up the given address in the branch predictor and returns
- * a true/false value as to whether it is taken.
+ * a true/false value as to whether it is taken. Also creates a
+ * BPHistory object to store any state it will need on squash/update.
* @param branch_addr The address of the branch to look up.
+ * @param bp_history Pointer that will be set to the BPHistory object.
* @return Whether or not the branch is taken.
*/
- bool lookup(Addr &branch_addr);
+ bool lookup(Addr &branch_addr, void * &bp_history);
+
+ /**
+ * Records that there was an unconditional branch, and modifies
+ * the bp history to point to an object that has the previous
+ * global history stored in it.
+ * @param bp_history Pointer that will be set to the BPHistory object.
+ */
+ void uncondBr(void * &bp_history);
/**
* Updates the branch predictor with the actual result of a branch.
* @param branch_addr The address of the branch to update.
* @param taken Whether or not the branch was taken.
+ * @param bp_history Pointer to the BPHistory object that was created
+ * when the branch was predicted.
*/
- void update(Addr &branch_addr, unsigned global_history, bool taken);
+ void update(Addr &branch_addr, bool taken, void *bp_history);
+ /**
+ * Restores the global branch history on a squash.
+ * @param bp_history Pointer to the BPHistory object that has the
+ * previous global branch history in it.
+ */
+ void squash(void *bp_history);
+
+ /** Returns the global history. */
inline unsigned readGlobalHist() { return globalHistory; }
private:
-
+ /**
+ * Returns if the branch should be taken or not, given a counter
+ * value.
+ * @param count The counter value.
+ */
inline bool getPrediction(uint8_t &count);
+ /**
+ * Returns the local history index, given a branch address.
+ * @param branch_addr The branch's PC address.
+ */
inline unsigned calcLocHistIdx(Addr &branch_addr);
- inline void updateHistoriesTaken(unsigned local_history_idx);
+ /** Updates global history as taken. */
+ inline void updateGlobalHistTaken();
+
+ /** Updates global history as not taken. */
+ inline void updateGlobalHistNotTaken();
+
+ /**
+ * Updates local histories as taken.
+ * @param local_history_idx The local history table entry that
+ * will be updated.
+ */
+ inline void updateLocalHistTaken(unsigned local_history_idx);
+
+ /**
+ * Updates local histories as not taken.
+ * @param local_history_idx The local history table entry that
+ * will be updated.
+ */
+ inline void updateLocalHistNotTaken(unsigned local_history_idx);
- inline void updateHistoriesNotTaken(unsigned local_history_idx);
+ /**
+ * The branch history information that is created upon predicting
+ * a branch. It will be passed back upon updating and squashing,
+ * when the BP can use this information to update/restore its
+ * state properly.
+ */
+ struct BPHistory {
+#ifdef DEBUG
+ BPHistory()
+ { newCount++; }
+ ~BPHistory()
+ { newCount--; }
+
+ static int newCount;
+#endif
+ unsigned globalHistory;
+ bool localPredTaken;
+ bool globalPredTaken;
+ bool globalUsed;
+ };
/** Local counters. */
- SatCounter *localCtrs;
+ std::vector<SatCounter> localCtrs;
/** Size of the local predictor. */
unsigned localPredictorSize;
@@ -87,7 +164,7 @@ class TournamentBP
unsigned localCtrBits;
/** Array of local history table entries. */
- unsigned *localHistoryTable;
+ std::vector<unsigned> localHistoryTable;
/** Size of the local history table. */
unsigned localHistoryTableSize;
@@ -100,9 +177,8 @@ class TournamentBP
/** Mask to get the proper local history. */
unsigned localHistoryMask;
-
/** Array of counters that make up the global predictor. */
- SatCounter *globalCtrs;
+ std::vector<SatCounter> globalCtrs;
/** Size of the global predictor. */
unsigned globalPredictorSize;
@@ -119,9 +195,8 @@ class TournamentBP
/** Mask to get the proper global history. */
unsigned globalHistoryMask;
-
/** Array of counters that make up the choice predictor. */
- SatCounter *choiceCtrs;
+ std::vector<SatCounter> choiceCtrs;
/** Size of the choice predictor (identical to the global predictor). */
unsigned choicePredictorSize;
@@ -140,4 +215,4 @@ class TournamentBP
unsigned threshold;
};
-#endif // __CPU_O3_CPU_TOURNAMENT_PRED_HH__
+#endif // __CPU_O3_TOURNAMENT_PRED_HH__