gem5/cpu/o3/fetch_impl.hh
Kevin Lim 74e8abd37e Switch out fixups for the CPUs.
cpu/cpu_exec_context.cc:
    Be sure to switch over the kernel stats so things don't get messed up.  This may lead to weird stats files for sampling runs (detailed stats should be correct, regardless of which kernel stats this is defined on).
cpu/o3/cpu.cc:
    Updates for switching out.  Also include a bunch of debug info if needed.
cpu/o3/fetch_impl.hh:
    Switch out properly.
cpu/o3/inst_queue.hh:
cpu/o3/inst_queue_impl.hh:
    Comment out unused stats (they made the stats file huge).
cpu/o3/lsq_unit.hh:
cpu/o3/lsq_unit_impl.hh:
    Add in new stat.
cpu/o3/rename.hh:
    Fix up for switching out.
cpu/o3/rename_impl.hh:
    Fix up for switching out.  Be sure to mark any Misc regs as ready if their renamed inst got squashed from being switched out.
cpu/ozone/cpu_impl.hh:
cpu/simple/cpu.cc:
    Switch out fixup.
sim/eventq.hh:
    Make CPU switching more immediate.
    Also comment out the assertion, as it doesn't apply if we're putting it on an inst-based queue.

--HG--
extra : convert_revision : f40ed40604738993f061e0c628810ff37a920562
2006-08-24 17:29:34 -04:00

1233 lines
35 KiB
C++

/*
* 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.
*/
#include "arch/isa_traits.hh"
#include "cpu/exetrace.hh"
#include "cpu/o3/fetch.hh"
#include "mem/base_mem.hh"
#include "mem/mem_interface.hh"
#include "mem/mem_req.hh"
#include "sim/byteswap.hh"
#include "sim/root.hh"
#if FULL_SYSTEM
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#include "mem/functional/memory_control.hh"
#include "mem/functional/physical.hh"
#include "sim/system.hh"
#else // !FULL_SYSTEM
#include "mem/functional/functional.hh"
#endif // FULL_SYSTEM
#include <algorithm>
using namespace std;
template<class Impl>
DefaultFetch<Impl>::CacheCompletionEvent::CacheCompletionEvent(MemReqPtr &_req,
DefaultFetch *_fetch)
: Event(&mainEventQueue, Delayed_Writeback_Pri),
req(_req),
fetch(_fetch)
{
this->setFlags(Event::AutoDelete);
}
template<class Impl>
void
DefaultFetch<Impl>::CacheCompletionEvent::process()
{
fetch->processCacheCompletion(req);
}
template<class Impl>
const char *
DefaultFetch<Impl>::CacheCompletionEvent::description()
{
return "DefaultFetch cache completion event";
}
template<class Impl>
DefaultFetch<Impl>::DefaultFetch(Params *params)
: icacheInterface(params->icacheInterface),
branchPred(params),
decodeToFetchDelay(params->decodeToFetchDelay),
renameToFetchDelay(params->renameToFetchDelay),
iewToFetchDelay(params->iewToFetchDelay),
commitToFetchDelay(params->commitToFetchDelay),
fetchWidth(params->fetchWidth),
numThreads(params->numberOfThreads),
numFetchingThreads(params->smtNumFetchingThreads),
interruptPending(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;
// 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);
// Create a new memory request.
memReq[tid] = NULL;
// Create space to store a cache line.
cacheData[tid] = new uint8_t[cacheBlkSize];
cacheDataPC[tid] = 0;
cacheDataValid[tid] = false;
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);
}
template <class Impl>
std::string
DefaultFetch<Impl>::name() const
{
return cpu->name() + ".fetch";
}
template <class Impl>
void
DefaultFetch<Impl>::regStats()
{
icacheStallCycles
.name(name() + ".icacheStallCycles")
.desc("Number of cycles fetch is stalled on an Icache miss")
.prereq(icacheStallCycles);
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() + ".Cycles")
.desc("Number of cycles fetch has run and was not squashing or"
" blocked")
.prereq(fetchCycles);
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() + ".BlockedCycles")
.desc("Number of cycles fetch has spent blocked")
.prereq(fetchBlockedCycles);
fetchedCacheLines
.name(name() + ".CacheLines")
.desc("Number of cache lines fetched")
.prereq(fetchedCacheLines);
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() + ".rateDist")
.desc("Number of instructions fetched each cycle (Total)")
.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
DefaultFetch<Impl>::setCPU(FullCPU *cpu_ptr)
{
DPRINTF(Fetch, "Setting the CPU pointer.\n");
cpu = cpu_ptr;
// Fetch needs to start fetching instructions at the very beginning,
// so it must start up in active state.
switchToActive();
}
template<class Impl>
void
DefaultFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer)
{
DPRINTF(Fetch, "Setting the time buffer pointer.\n");
timeBuffer = time_buffer;
// Create wires to get information from proper places in time buffer.
fromDecode = timeBuffer->getWire(-decodeToFetchDelay);
fromRename = timeBuffer->getWire(-renameToFetchDelay);
fromIEW = timeBuffer->getWire(-iewToFetchDelay);
fromCommit = timeBuffer->getWire(-commitToFetchDelay);
}
template<class Impl>
void
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, "Setting the fetch queue pointer.\n");
fetchQueue = fq_ptr;
// Create wire to write information to proper place in fetch queue.
toDecode = fetchQueue->getWire(0);
}
#if 0
template<class Impl>
void
DefaultFetch<Impl>::setPageTable(PageTable *pt_ptr)
{
DPRINTF(Fetch, "Setting the page table pointer.\n");
#if !FULL_SYSTEM
pTable = pt_ptr;
#endif
}
#endif
template<class Impl>
void
DefaultFetch<Impl>::initStage()
{
// 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(MemReqPtr &req)
{
unsigned tid = req->thread_num;
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.
if (fetchStatus[tid] != IcacheMissStall ||
req != memReq[tid] ||
isSwitchedOut()) {
++fetchIcacheSquashes;
return;
}
// Wake up the CPU (if it went to sleep and was waiting on this completion
// event).
cpu->wakeCPU();
cacheDataValid[tid] = true;
DPRINTF(Activity, "[tid:%u] Activating fetch due to cache completion\n",
tid);
switchToActive();
// Only switch to IcacheMissComplete if we're not stalled as well.
if (checkStall(tid)) {
fetchStatus[tid] = Blocked;
} else {
fetchStatus[tid] = IcacheMissComplete;
}
// memcpy(cacheData[tid], memReq[tid]->data, memReq[tid]->size);
// Reset the mem req to NULL.
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;
interruptPending = 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
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
// this function updates it.
bool predict_taken;
if (!inst->isControl()) {
next_PC = next_PC + instSize;
inst->setPredTarg(next_PC);
return false;
}
predict_taken = branchPred.predict(inst, next_PC, inst->threadNumber);
++fetchedBranches;
if (predict_taken) {
++predictedBranches;
}
return predict_taken;
}
template <class Impl>
bool
DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid)
{
Fault fault = NoFault;
#if FULL_SYSTEM
// Flag to say whether or not address is physical addr.
unsigned flags = cpu->inPalMode(fetch_PC) ? PHYSICAL : 0;
#else
unsigned flags = 0;
#endif // FULL_SYSTEM
if (isSwitchedOut() || (interruptPending && flags == 0)) {
// Hold off fetch from getting new instructions while an interrupt
// is pending.
return false;
}
// Align the fetch PC so it's at the start of a cache block.
fetch_PC = icacheBlockAlignPC(fetch_PC);
// If we've already got the block, no need to try to fetch it again.
if (cacheDataValid[tid] && fetch_PC == cacheDataPC[tid]) {
return true;
}
// Setup the memReq to do a read of the first instruction's address.
// Set the appropriate read size and flags as well.
memReq[tid] = new MemReq();
memReq[tid]->asid = tid;
memReq[tid]->thread_num = tid;
memReq[tid]->data = new uint8_t[64];
memReq[tid]->xc = cpu->xcBase(tid);
memReq[tid]->cmd = Read;
memReq[tid]->reset(fetch_PC, cacheBlkSize, flags);
// Translate the instruction request.
//#if FULL_SYSTEM
fault = cpu->translateInstReq(memReq[tid]);
//#else
// fault = pTable->translate(memReq[tid]);
//#endif
// In the case of faults, the fetch stage may need to stall and wait
// for the ITB miss to be handled.
// If translation was successful, attempt to read the first
// instruction.
if (fault == NoFault) {
#if FULL_SYSTEM
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
DPRINTF(Fetch, "Fetch: Doing instruction read.\n");
fault = cpu->mem->read(memReq[tid], cacheData[tid]);
// This read may change when the mem interface changes.
// Now do the timing access to see whether or not the instruction
// exists within the cache.
if (icacheInterface && !icacheInterface->isBlocked()) {
DPRINTF(Fetch, "Doing cache access.\n");
memReq[tid]->completionEvent = NULL;
memReq[tid]->time = curTick;
MemAccessResult result = icacheInterface->access(memReq[tid]);
cacheDataPC[tid] = fetch_PC;
cacheDataValid[tid] = false;
fetchedCacheLines++;
// If the cache missed, then schedule an event to wake
// up this stage once the cache miss completes.
// @todo: Possibly allow for longer than 1 cycle cache hits.
if (result != MA_HIT && icacheInterface->doEvents()) {
memReq[tid]->completionEvent =
new CacheCompletionEvent(memReq[tid], this);
lastIcacheStall[tid] = curTick;
DPRINTF(Activity, "[tid:%i]: Activity: Stalling due to I-cache "
"miss.\n", tid);
fetchStatus[tid] = IcacheMissStall;
} else {
DPRINTF(Fetch, "[tid:%i]: I-Cache hit. Doing Instruction "
"read.\n", tid);
// memcpy(cacheData[tid], memReq[tid]->data, memReq[tid]->size);
}
} else {
DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid);
ret_fault = NoFault;
return false;
}
}
ret_fault = fault;
return true;
}
template <class Impl>
inline void
DefaultFetch<Impl>::doSquash(const Addr &new_PC, unsigned tid)
{
DPRINTF(Fetch, "[tid:%i]: Squashing, setting PC to: %#x.\n",
tid, new_PC);
PC[tid] = new_PC;
nextPC[tid] = new_PC + instSize;
// Clear the icache miss if it's outstanding.
if (fetchStatus[tid] == IcacheMissStall && icacheInterface) {
DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n",
tid);
memReq[tid] = NULL;
}
fetchStatus[tid] = Squashing;
++fetchSquashCycles;
}
template<class Impl>
void
DefaultFetch<Impl>::squashFromDecode(const Addr &new_PC,
const InstSeqNum &seq_num,
unsigned tid)
{
DPRINTF(Fetch, "[tid:%i]: Squashing from decode.\n",tid);
doSquash(new_PC, tid);
// Tell the CPU to remove any instructions that are in flight between
// fetch and decode.
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] == IcacheMissComplete) {
if (_status == Inactive) {
DPRINTF(Activity, "[tid:%i]: Activating stage.\n",tid);
if (fetchStatus[tid] == IcacheMissComplete) {
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
DefaultFetch<Impl>::squash(const Addr &new_PC, unsigned tid)
{
DPRINTF(Fetch, "[tid:%u]: Squash from commit.\n",tid);
doSquash(new_PC, tid);
// Tell the CPU to remove any instructions that are not in the ROB.
cpu->removeInstsNotInROB(tid);
}
template <class Impl>
void
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[tid].squash) {
DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash "
"from commit.\n",tid);
// In any case, squash.
squash(fromCommit->commitInfo[tid].nextPC,tid);
// Also check if there's a mispredict that happened.
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[tid].doneSeqNum,
tid);
}
return true;
} else if (fromCommit->commitInfo[tid].doneSeqNum) {
// Update the branch predictor if it wasn't a squashed instruction
// that was broadcasted.
branchPred.update(fromCommit->commitInfo[tid].doneSeqNum, tid);
}
// Check ROB squash signals from commit.
if (fromCommit->commitInfo[tid].robSquashing) {
DPRINTF(Fetch, "[tid:%u]: ROB is still squashing Thread %u.\n", tid);
// Continue to squash.
fetchStatus[tid] = Squashing;
return true;
}
// Check squash signals from decode.
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[tid].branchMispredict) {
branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum,
fromDecode->decodeInfo[tid].nextPC,
fromDecode->decodeInfo[tid].branchTaken,
tid);
} else {
branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum,
tid);
}
if (fetchStatus[tid] != Squashing) {
// Squash unless we're already squashing
squashFromDecode(fromDecode->decodeInfo[tid].nextPC,
fromDecode->decodeInfo[tid].doneSeqNum,
tid);
return true;
}
}
if (fetchStatus[tid] != IcacheMissStall && checkStall(tid)) {
DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid);
fetchStatus[tid] = Blocked;
return true;
}
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);
fetchStatus[tid] = Running;
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
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 = PC[tid];
// Fault code for memory access.
Fault fault = NoFault;
// 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 (fetchStatus[tid] == IcacheMissComplete) {
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 {
if (fetchStatus[tid] == Idle) {
++fetchIdleCycles;
} else if (fetchStatus[tid] == Blocked) {
++fetchBlockedCycles;
} else if (fetchStatus[tid] == Squashing) {
++fetchSquashCycles;
} else if (fetchStatus[tid] == IcacheMissStall) {
++icacheStallCycles;
}
// Status is Idle, Squashing, Blocked, or IcacheMissStall, so
// fetch should do nothing.
return;
}
++fetchCycles;
// If we had a stall due to an icache miss, then return.
if (fetchStatus[tid] == IcacheMissStall) {
++icacheStallCycles;
status_change = true;
return;
}
Addr next_PC = fetch_PC;
InstSeqNum inst_seq;
MachInst inst;
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, "[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 (;
offset < cacheBlkSize &&
numInst < fetchWidth &&
!predicted_branch;
++numInst) {
// Get a sequence number.
inst_seq = cpu->getAndIncrementInstSeq();
// Make sure this is a valid index.
assert(offset <= cacheBlkSize - instSize);
// Get the instruction from the array of the cache line.
inst = gtoh(*reinterpret_cast<MachInst *>
(&cacheData[tid][offset]));
ext_inst = TheISA::makeExtMI(inst, fetch_PC);
// Create a new DynInst from the instruction fetched.
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, "[tid:%i]: Instruction PC %#x created "
"[sn:%lli]\n",
tid, instruction->readPC(), inst_seq);
DPRINTF(Fetch, "[tid:%i]: Instruction is: %s\n",
tid, instruction->staticInst->disassemble(fetch_PC));
instruction->traceData =
Trace::getInstRecord(curTick, cpu->xcBase(tid), cpu,
instruction->staticInst,
instruction->readPC(),tid);
predicted_branch = lookupAndUpdateNextPC(instruction, next_PC);
// Add instruction to the CPU's list of instructions.
instruction->setInstListIt(cpu->addInst(instruction));
// Write the instruction to the first slot in the queue
// that heads to decode.
toDecode->insts[numInst] = instruction;
toDecode->size++;
// Increment stat of fetched instructions.
++fetchedInsts;
// 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;
}
}
if (numInst > 0) {
wroteToTimeBuffer = true;
}
// Now that fetching is completed, update the PC to signify what the next
// cycle will be.
if (fault == NoFault) {
DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC);
PC[tid] = next_PC;
nextPC[tid] = next_PC + instSize;
} else {
// We shouldn't be in an icache miss and also have a fault (an ITB
// miss)
if (fetchStatus[tid] == IcacheMissStall) {
panic("Fetch should have exited prior to this!");
}
// 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;
// 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);
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, PC[tid]);
#endif // FULL_SYSTEM
}
}
///////////////////////////////////////
// //
// 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] == IcacheMissComplete ||
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] == IcacheMissComplete ||
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] == IcacheMissComplete ||
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] == IcacheMissComplete ||
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;
}