blob: 98a6972e9e98b4b3b91b114c247e0ad79e52da2b [file] [log] [blame]
/*
* Copyright (c) 2011-2013 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Copyright (c) 2004-2005 The Regents of The University of Michigan
* Copyright (c) 2011 Regents of the University of California
* 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
* Rick Strong
*/
#ifndef __CPU_O3_CPU_HH__
#define __CPU_O3_CPU_HH__
#include <iostream>
#include <list>
#include <queue>
#include <set>
#include <vector>
#include "arch/types.hh"
#include "base/statistics.hh"
#include "config/the_isa.hh"
#include "cpu/o3/comm.hh"
#include "cpu/o3/cpu_policy.hh"
#include "cpu/o3/scoreboard.hh"
#include "cpu/o3/thread_state.hh"
#include "cpu/activity.hh"
#include "cpu/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/timebuf.hh"
//#include "cpu/o3/thread_context.hh"
#include "params/DerivO3CPU.hh"
#include "sim/process.hh"
template <class>
class Checker;
class ThreadContext;
template <class>
class O3ThreadContext;
class Checkpoint;
class MemObject;
class Process;
struct BaseCPUParams;
class BaseO3CPU : public BaseCPU
{
//Stuff that's pretty ISA independent will go here.
public:
BaseO3CPU(BaseCPUParams *params);
void regStats();
};
/**
* 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 BaseO3CPU
{
public:
// Typedefs from the Impl here.
typedef typename Impl::CPUPol CPUPolicy;
typedef typename Impl::DynInstPtr DynInstPtr;
typedef typename Impl::O3CPU O3CPU;
typedef O3ThreadState<Impl> ImplState;
typedef O3ThreadState<Impl> Thread;
typedef typename std::list<DynInstPtr>::iterator ListIt;
friend class O3ThreadContext<Impl>;
public:
enum Status {
Running,
Idle,
Halted,
Blocked,
SwitchedOut
};
TheISA::TLB * itb;
TheISA::TLB * dtb;
/** Overall CPU status. */
Status _status;
private:
/**
* IcachePort class for instruction fetch.
*/
class IcachePort : public MasterPort
{
protected:
/** Pointer to fetch. */
DefaultFetch<Impl> *fetch;
public:
/** Default constructor. */
IcachePort(DefaultFetch<Impl> *_fetch, FullO3CPU<Impl>* _cpu)
: MasterPort(_cpu->name() + ".icache_port", _cpu), fetch(_fetch)
{ }
protected:
/** Timing version of receive. Handles setting fetch to the
* proper status to start fetching. */
virtual bool recvTimingResp(PacketPtr pkt);
virtual void recvTimingSnoopReq(PacketPtr pkt) { }
/** Handles doing a retry of a failed fetch. */
virtual void recvRetry();
};
/**
* DcachePort class for the load/store queue.
*/
class DcachePort : public MasterPort
{
protected:
/** Pointer to LSQ. */
LSQ<Impl> *lsq;
public:
/** Default constructor. */
DcachePort(LSQ<Impl> *_lsq, FullO3CPU<Impl>* _cpu)
: MasterPort(_cpu->name() + ".dcache_port", _cpu), lsq(_lsq)
{ }
protected:
/** Timing version of receive. Handles writing back and
* completing the load or store that has returned from
* memory. */
virtual bool recvTimingResp(PacketPtr pkt);
virtual void recvTimingSnoopReq(PacketPtr pkt);
virtual void recvFunctionalSnoop(PacketPtr pkt)
{
// @todo: Is there a need for potential invalidation here?
}
/** Handles doing a retry of the previous send. */
virtual void recvRetry();
/**
* As this CPU requires snooping to maintain the load store queue
* change the behaviour from the base CPU port.
*
* @return true since we have to snoop
*/
virtual bool isSnooping() const { return true; }
};
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() const;
};
/** The tick event used for scheduling CPU ticks. */
TickEvent tickEvent;
/** Schedule tick event, regardless of its current state. */
void scheduleTickEvent(Cycles delay)
{
if (tickEvent.squashed())
reschedule(tickEvent, clockEdge(delay));
else if (!tickEvent.scheduled())
schedule(tickEvent, clockEdge(delay));
}
/** Unschedule tick event, regardless of its current state. */
void unscheduleTickEvent()
{
if (tickEvent.scheduled())
tickEvent.squash();
}
class ActivateThreadEvent : public Event
{
private:
/** Number of Thread to Activate */
ThreadID tid;
/** Pointer to the CPU. */
FullO3CPU<Impl> *cpu;
public:
/** Constructs the event. */
ActivateThreadEvent();
/** Initialize Event */
void init(int thread_num, FullO3CPU<Impl> *thread_cpu);
/** Processes the event, calling activateThread() on the CPU. */
void process();
/** Returns the description of the event. */
const char *description() const;
};
/** Schedule thread to activate , regardless of its current state. */
void
scheduleActivateThreadEvent(ThreadID tid, Cycles delay)
{
// Schedule thread to activate, regardless of its current state.
if (activateThreadEvent[tid].squashed())
reschedule(activateThreadEvent[tid],
clockEdge(delay));
else if (!activateThreadEvent[tid].scheduled()) {
Tick when = clockEdge(delay);
// Check if the deallocateEvent is also scheduled, and make
// sure they do not happen at same time causing a sleep that
// is never woken from.
if (deallocateContextEvent[tid].scheduled() &&
deallocateContextEvent[tid].when() == when) {
when++;
}
schedule(activateThreadEvent[tid], when);
}
}
/** Unschedule actiavte thread event, regardless of its current state. */
void
unscheduleActivateThreadEvent(ThreadID tid)
{
if (activateThreadEvent[tid].scheduled())
activateThreadEvent[tid].squash();
}
/** The tick event used for scheduling CPU ticks. */
ActivateThreadEvent activateThreadEvent[Impl::MaxThreads];
class DeallocateContextEvent : public Event
{
private:
/** Number of Thread to deactivate */
ThreadID tid;
/** Should the thread be removed from the CPU? */
bool remove;
/** Pointer to the CPU. */
FullO3CPU<Impl> *cpu;
public:
/** Constructs the event. */
DeallocateContextEvent();
/** Initialize Event */
void init(int thread_num, FullO3CPU<Impl> *thread_cpu);
/** Processes the event, calling activateThread() on the CPU. */
void process();
/** Sets whether the thread should also be removed from the CPU. */
void setRemove(bool _remove) { remove = _remove; }
/** Returns the description of the event. */
const char *description() const;
};
/** Schedule cpu to deallocate thread context.*/
void
scheduleDeallocateContextEvent(ThreadID tid, bool remove, Cycles delay)
{
// Schedule thread to activate, regardless of its current state.
if (deallocateContextEvent[tid].squashed())
reschedule(deallocateContextEvent[tid],
clockEdge(delay));
else if (!deallocateContextEvent[tid].scheduled())
schedule(deallocateContextEvent[tid],
clockEdge(delay));
}
/** Unschedule thread deallocation in CPU */
void
unscheduleDeallocateContextEvent(ThreadID tid)
{
if (deallocateContextEvent[tid].scheduled())
deallocateContextEvent[tid].squash();
}
/** The tick event used for scheduling CPU ticks. */
DeallocateContextEvent deallocateContextEvent[Impl::MaxThreads];
/**
* Check if the pipeline has drained and signal the DrainManager.
*
* This method checks if a drain has been requested and if the CPU
* has drained successfully (i.e., there are no instructions in
* the pipeline). If the CPU has drained, it deschedules the tick
* event and signals the drain manager.
*
* @return False if a drain hasn't been requested or the CPU
* hasn't drained, true otherwise.
*/
bool tryDrain();
/**
* Perform sanity checks after a drain.
*
* This method is called from drain() when it has determined that
* the CPU is fully drained when gem5 is compiled with the NDEBUG
* macro undefined. The intention of this method is to do more
* extensive tests than the isDrained() method to weed out any
* draining bugs.
*/
void drainSanityCheck() const;
/** Check if a system is in a drained state. */
bool isDrained() const;
public:
/** Constructs a CPU with the given parameters. */
FullO3CPU(DerivO3CPUParams *params);
/** Destructor. */
~FullO3CPU();
/** Registers statistics. */
void regStats();
void demapPage(Addr vaddr, uint64_t asn)
{
this->itb->demapPage(vaddr, asn);
this->dtb->demapPage(vaddr, asn);
}
void demapInstPage(Addr vaddr, uint64_t asn)
{
this->itb->demapPage(vaddr, asn);
}
void demapDataPage(Addr vaddr, uint64_t asn)
{
this->dtb->demapPage(vaddr, asn);
}
/** 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 startup();
/** Returns the Number of Active Threads in the CPU */
int numActiveThreads()
{ return activeThreads.size(); }
/** Add Thread to Active Threads List */
void activateThread(ThreadID tid);
/** Remove Thread from Active Threads List */
void deactivateThread(ThreadID tid);
/** Setup CPU to insert a thread's context */
void insertThread(ThreadID tid);
/** Remove all of a thread's context from CPU */
void removeThread(ThreadID tid);
/** Count the Total Instructions Committed in the CPU. */
virtual Counter totalInsts() const;
/** Count the Total Ops (including micro ops) committed in the CPU. */
virtual Counter totalOps() const;
/** Add Thread to Active Threads List. */
void activateContext(ThreadID tid, Cycles delay);
/** Remove Thread from Active Threads List */
void suspendContext(ThreadID tid);
/** Remove Thread from Active Threads List &&
* Possibly Remove Thread Context from CPU.
*/
bool scheduleDeallocateContext(ThreadID tid, bool remove,
Cycles delay = Cycles(1));
/** Remove Thread from Active Threads List &&
* Remove Thread Context from CPU.
*/
void haltContext(ThreadID tid);
/** Activate a Thread When CPU Resources are Available. */
void activateWhenReady(ThreadID tid);
/** Add or Remove a Thread Context in the CPU. */
void doContextSwitch();
/** Update The Order In Which We Process Threads. */
void updateThreadPriority();
/** Is the CPU draining? */
bool isDraining() const { return getDrainState() == Drainable::Draining; }
void serializeThread(std::ostream &os, ThreadID tid);
void unserializeThread(Checkpoint *cp, const std::string &section,
ThreadID tid);
public:
/** Executes a syscall.
* @todo: Determine if this needs to be virtual.
*/
void syscall(int64_t callnum, ThreadID tid);
/** Starts draining the CPU's pipeline of all instructions in
* order to stop all memory accesses. */
unsigned int drain(DrainManager *drain_manager);
/** Resumes execution after a drain. */
void drainResume();
/**
* Commit has reached a safe point to drain a thread.
*
* Commit calls this method to inform the pipeline that it has
* reached a point where it is not executed microcode and is about
* to squash uncommitted instructions to fully drain the pipeline.
*/
void commitDrained(ThreadID tid);
/** Switches out this CPU. */
virtual void switchOut();
/** Takes over from another CPU. */
virtual void takeOverFrom(BaseCPU *oldCPU);
void verifyMemoryMode() const;
/** Get the current instruction sequence number, and increment it. */
InstSeqNum getAndIncrementInstSeq()
{ return globalSeqNum++; }
/** Traps to handle given fault. */
void trap(Fault fault, ThreadID tid, StaticInstPtr inst);
/** HW return from error interrupt. */
Fault hwrei(ThreadID tid);
bool simPalCheck(int palFunc, ThreadID tid);
/** Returns the Fault for any valid interrupt. */
Fault getInterrupts();
/** Processes any an interrupt fault. */
void processInterrupts(Fault interrupt);
/** Halts the CPU. */
void halt() { panic("Halt not implemented!\n"); }
/** Check if this address is a valid instruction address. */
bool validInstAddr(Addr addr) { return true; }
/** Check if this address is a valid data address. */
bool validDataAddr(Addr addr) { return true; }
/** Register accessors. Index refers to the physical register index. */
/** Reads a miscellaneous register. */
TheISA::MiscReg readMiscRegNoEffect(int misc_reg, ThreadID tid);
/** Reads a misc. register, including any side effects the read
* might have as defined by the architecture.
*/
TheISA::MiscReg readMiscReg(int misc_reg, ThreadID tid);
/** Sets a miscellaneous register. */
void setMiscRegNoEffect(int misc_reg, const TheISA::MiscReg &val,
ThreadID tid);
/** Sets a misc. register, including any side effects the write
* might have as defined by the architecture.
*/
void setMiscReg(int misc_reg, const TheISA::MiscReg &val,
ThreadID tid);
uint64_t readIntReg(int reg_idx);
TheISA::FloatReg readFloatReg(int reg_idx);
TheISA::FloatRegBits readFloatRegBits(int reg_idx);
void setIntReg(int reg_idx, uint64_t val);
void setFloatReg(int reg_idx, TheISA::FloatReg val);
void setFloatRegBits(int reg_idx, TheISA::FloatRegBits val);
uint64_t readArchIntReg(int reg_idx, ThreadID tid);
float readArchFloatReg(int reg_idx, ThreadID tid);
uint64_t readArchFloatRegInt(int reg_idx, ThreadID 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, ThreadID tid);
void setArchFloatReg(int reg_idx, float val, ThreadID tid);
void setArchFloatRegInt(int reg_idx, uint64_t val, ThreadID tid);
/** Sets the commit PC state of a specific thread. */
void pcState(const TheISA::PCState &newPCState, ThreadID tid);
/** Reads the commit PC state of a specific thread. */
TheISA::PCState pcState(ThreadID tid);
/** Reads the commit PC of a specific thread. */
Addr instAddr(ThreadID tid);
/** Reads the commit micro PC of a specific thread. */
MicroPC microPC(ThreadID tid);
/** Reads the next PC of a specific thread. */
Addr nextInstAddr(ThreadID 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(ThreadID tid);
/** Function to add instruction onto the head of the list of the
* instructions. Used when new instructions are fetched.
*/
ListIt addInst(DynInstPtr &inst);
/** Function to tell the CPU that an instruction has completed. */
void instDone(ThreadID tid, 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.
* There's also an option to not squash delay slot instructions.*/
void removeInstsNotInROB(ThreadID tid);
/** Remove all instructions younger than the given sequence number. */
void removeInstsUntil(const InstSeqNum &seq_num, ThreadID tid);
/** Removes the instruction pointed to by the iterator. */
inline void squashInstIt(const ListIt &instIt, ThreadID tid);
/** Cleans up all instructions on the remove list. */
void cleanUpRemovedInsts();
/** Debug function to print all instructions on the list. */
void dumpInsts();
public:
#ifndef NDEBUG
/** Count of total number of dynamic instructions in flight. */
int instcount;
#endif
/** List of all the instructions in flight. */
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;
protected:
/** The fetch stage. */
typename CPUPolicy::Fetch fetch;
/** The decode stage. */
typename CPUPolicy::Decode decode;
/** The dispatch stage. */
typename CPUPolicy::Rename rename;
/** The issue/execute/writeback stages. */
typename CPUPolicy::IEW iew;
/** The commit stage. */
typename CPUPolicy::Commit commit;
/** The register file. */
typename CPUPolicy::RegFile regFile;
/** The free list. */
typename CPUPolicy::FreeList freeList;
/** The rename map. */
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<ThreadID> activeThreads;
/** Integer Register Scoreboard */
Scoreboard scoreboard;
std::vector<TheISA::ISA *> isa;
/** Instruction port. Note that it has to appear after the fetch stage. */
IcachePort icachePort;
/** Data port. Note that it has to appear after the iew stages */
DcachePort dcachePort;
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.
*/
typedef typename CPUPolicy::TimeStruct TimeStruct;
typedef typename CPUPolicy::FetchStruct FetchStruct;
typedef typename CPUPolicy::DecodeStruct DecodeStruct;
typedef typename CPUPolicy::RenameStruct RenameStruct;
typedef typename CPUPolicy::IEWStruct IEWStruct;
/** The main time buffer to do backwards communication. */
TimeBuffer<TimeStruct> timeBuffer;
/** The fetch stage's instruction queue. */
TimeBuffer<FetchStruct> fetchQueue;
/** The decode stage's instruction queue. */
TimeBuffer<DecodeStruct> decodeQueue;
/** The rename stage's instruction queue. */
TimeBuffer<RenameStruct> renameQueue;
/** 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:
/** Records that there was time buffer activity this cycle. */
void activityThisCycle() { activityRec.activity(); }
/** 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); }
/** Wakes the CPU, rescheduling the CPU if it's not already active. */
void wakeCPU();
virtual void wakeup();
/** Gets a free thread id. Use if thread ids change across system. */
ThreadID getFreeTid();
public:
/** Returns a pointer to a thread context. */
ThreadContext *
tcBase(ThreadID tid)
{
return thread[tid]->getTC();
}
/** The global sequence number counter. */
InstSeqNum globalSeqNum;//[Impl::MaxThreads];
/** 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<Impl> *checker;
/** Pointer to the system. */
System *system;
/** DrainManager to notify when draining has completed. */
DrainManager *drainManager;
/** Pointers to all of the threads in the CPU. */
std::vector<Thread *> thread;
/** 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. */
Cycles lastRunningCycle;
/** The cycle that the CPU was last activated by a new thread*/
Tick lastActivatedCycle;
/** Mapping for system thread id to cpu id */
std::map<ThreadID, unsigned> threadMap;
/** Available thread ids in the cpu*/
std::vector<ThreadID> tids;
/** CPU read function, forwards read to LSQ. */
Fault read(RequestPtr &req, RequestPtr &sreqLow, RequestPtr &sreqHigh,
uint8_t *data, int load_idx)
{
return this->iew.ldstQueue.read(req, sreqLow, sreqHigh,
data, load_idx);
}
/** CPU write function, forwards write to LSQ. */
Fault write(RequestPtr &req, RequestPtr &sreqLow, RequestPtr &sreqHigh,
uint8_t *data, int store_idx)
{
return this->iew.ldstQueue.write(req, sreqLow, sreqHigh,
data, store_idx);
}
/** Used by the fetch unit to get a hold of the instruction port. */
virtual MasterPort &getInstPort() { return icachePort; }
/** Get the dcache port (used to find block size for translations). */
virtual MasterPort &getDataPort() { return dcachePort; }
/** 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 total number of cycles the CPU spends descheduled due to a
* quiesce operation or waiting for an interrupt. */
Stats::Scalar quiesceCycles;
/** Stat for the number of committed instructions per thread. */
Stats::Vector committedInsts;
/** Stat for the number of committed ops (including micro ops) per thread. */
Stats::Vector committedOps;
/** 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;
//number of integer register file accesses
Stats::Scalar intRegfileReads;
Stats::Scalar intRegfileWrites;
//number of float register file accesses
Stats::Scalar fpRegfileReads;
Stats::Scalar fpRegfileWrites;
//number of misc
Stats::Scalar miscRegfileReads;
Stats::Scalar miscRegfileWrites;
};
#endif // __CPU_O3_CPU_HH__