| /* |
| * 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. |
| */ |
| |
| // Remove this later; used only for debugging. |
| #define OPCODE(X) (X >> 26) & 0x3f |
| |
| |
| #include "arch/alpha/byte_swap.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 "sim/root.hh" |
| |
| template<class Impl> |
| SimpleFetch<Impl>::CacheCompletionEvent |
| ::CacheCompletionEvent(SimpleFetch *_fetch) |
| : Event(&mainEventQueue), |
| fetch(_fetch) |
| { |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::CacheCompletionEvent::process() |
| { |
| fetch->processCacheCompletion(); |
| } |
| |
| template<class Impl> |
| const char * |
| SimpleFetch<Impl>::CacheCompletionEvent::description() |
| { |
| return "SimpleFetch cache completion event"; |
| } |
| |
| template<class Impl> |
| SimpleFetch<Impl>::SimpleFetch(Params ¶ms) |
| : icacheInterface(params.icacheInterface), |
| 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. |
| #ifndef FULL_SYSTEM |
| memReq->asid = 0; |
| #else |
| memReq->asid = 0; |
| #endif // FULL_SYSTEM |
| memReq->data = new uint8_t[64]; |
| |
| // Size of cache block. |
| cacheBlkSize = icacheInterface ? icacheInterface->getBlockSize() : 64; |
| |
| // Create mask to get rid of offset bits. |
| cacheBlkMask = (cacheBlkSize - 1); |
| |
| // Get the size of an instruction. |
| instSize = sizeof(MachInst); |
| |
| // Create space to store a cache line. |
| cacheData = new uint8_t[cacheBlkSize]; |
| } |
| |
| template <class Impl> |
| void |
| SimpleFetch<Impl>::regStats() |
| { |
| icacheStallCycles |
| .name(name() + ".icacheStallCycles") |
| .desc("Number of cycles fetch is stalled on an Icache miss") |
| .prereq(icacheStallCycles); |
| |
| fetchedInsts |
| .name(name() + ".fetchedInsts") |
| .desc("Number of instructions fetch has processed") |
| .prereq(fetchedInsts); |
| predictedBranches |
| .name(name() + ".predictedBranches") |
| .desc("Number of branches that fetch has predicted taken") |
| .prereq(predictedBranches); |
| fetchCycles |
| .name(name() + ".fetchCycles") |
| .desc("Number of cycles fetch has run and was not squashing or" |
| " blocked") |
| .prereq(fetchCycles); |
| fetchSquashCycles |
| .name(name() + ".fetchSquashCycles") |
| .desc("Number of cycles fetch has spent squashing") |
| .prereq(fetchSquashCycles); |
| fetchBlockedCycles |
| .name(name() + ".fetchBlockedCycles") |
| .desc("Number of cycles fetch has spent blocked") |
| .prereq(fetchBlockedCycles); |
| fetchedCacheLines |
| .name(name() + ".fetchedCacheLines") |
| .desc("Number of cache lines fetched") |
| .prereq(fetchedCacheLines); |
| |
| fetch_nisn_dist |
| .init(/* base value */ 0, |
| /* last value */ fetchWidth, |
| /* bucket size */ 1) |
| .name(name() + ".FETCH:rate_dist") |
| .desc("Number of instructions fetched each cycle (Total)") |
| .flags(Stats::pdf) |
| ; |
| |
| branchPred.regStats(); |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::setCPU(FullCPU *cpu_ptr) |
| { |
| DPRINTF(Fetch, "Fetch: Setting the CPU pointer.\n"); |
| cpu = cpu_ptr; |
| // This line will be removed eventually. |
| memReq->xc = cpu->xcBase(); |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) |
| { |
| DPRINTF(Fetch, "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 |
| SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) |
| { |
| DPRINTF(Fetch, "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); |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::processCacheCompletion() |
| { |
| DPRINTF(Fetch, "Fetch: Waking up from cache miss.\n"); |
| |
| // 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; |
| } |
| |
| template <class Impl> |
| bool |
| SimpleFetch<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); |
| |
| if (predict_taken) { |
| ++predictedBranches; |
| } |
| |
| return predict_taken; |
| } |
| |
| template <class Impl> |
| Fault |
| SimpleFetch<Impl>::fetchCacheLine(Addr fetch_PC) |
| { |
| // 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. |
| |
| #ifdef FULL_SYSTEM |
| // Flag to say whether or not address is physical addr. |
| unsigned flags = cpu->inPalMode() ? PHYSICAL : 0; |
| #else |
| unsigned flags = 0; |
| #endif // FULL_SYSTEM |
| |
| Fault fault = No_Fault; |
| |
| // 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. |
| // Set the appropriate read size and flags as well. |
| memReq->cmd = Read; |
| memReq->reset(fetch_PC, cacheBlkSize, flags); |
| |
| // Translate the instruction request. |
| // Should this function be |
| // in the CPU class ? Probably...ITB/DTB should exist within the |
| // CPU. |
| |
| fault = cpu->translateInstReq(memReq); |
| |
| // In the case of faults, the fetch stage may need to stall and wait |
| // on what caused the fetch (ITB or Icache miss). |
| |
| // If translation was successful, attempt to read the first |
| // instruction. |
| if (fault == No_Fault) { |
| 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 == No_Fault) { |
| DPRINTF(Fetch, "Fetch: Doing timing memory access.\n"); |
| memReq->completionEvent = NULL; |
| |
| memReq->time = curTick; |
| |
| MemAccessResult result = icacheInterface->access(memReq); |
| |
| // 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); |
| |
| // 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(Fetch, "Fetch: Stalling due to icache miss.\n"); |
| _status = IcacheMissStall; |
| } |
| } |
| |
| return fault; |
| } |
| |
| template <class Impl> |
| inline void |
| SimpleFetch<Impl>::doSquash(const Addr &new_PC) |
| { |
| DPRINTF(Fetch, "Fetch: Squashing, setting PC to: %#x.\n", new_PC); |
| |
| cpu->setNextPC(new_PC + instSize); |
| cpu->setPC(new_PC); |
| |
| // 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); |
| } |
| |
| _status = Squashing; |
| |
| ++fetchSquashCycles; |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::squashFromDecode(const Addr &new_PC, |
| const InstSeqNum &seq_num) |
| { |
| DPRINTF(Fetch, "Fetch: Squashing from decode.\n"); |
| |
| doSquash(new_PC); |
| |
| // Tell the CPU to remove any instructions that are in flight between |
| // fetch and decode. |
| cpu->removeInstsUntil(seq_num); |
| } |
| |
| template <class Impl> |
| void |
| SimpleFetch<Impl>::squash(const Addr &new_PC) |
| { |
| DPRINTF(Fetch, "Fetch: Squash from commit.\n"); |
| |
| doSquash(new_PC); |
| |
| // Tell the CPU to remove any instructions that are not in the ROB. |
| cpu->removeInstsNotInROB(); |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::tick() |
| { |
| // Check squash signals from commit. |
| if (fromCommit->commitInfo.squash) { |
| DPRINTF(Fetch, "Fetch: Squashing instructions due to squash " |
| "from commit.\n"); |
| |
| // In any case, squash. |
| squash(fromCommit->commitInfo.nextPC); |
| |
| // Also check if there's a mispredict that happened. |
| if (fromCommit->commitInfo.branchMispredict) { |
| branchPred.squash(fromCommit->commitInfo.doneSeqNum, |
| fromCommit->commitInfo.nextPC, |
| fromCommit->commitInfo.branchTaken); |
| } else { |
| branchPred.squash(fromCommit->commitInfo.doneSeqNum); |
| } |
| |
| return; |
| } else if (fromCommit->commitInfo.doneSeqNum) { |
| // Update the branch predictor if it wasn't a squashed instruction |
| // that was braodcasted. |
| branchPred.update(fromCommit->commitInfo.doneSeqNum); |
| } |
| |
| // Check ROB squash signals from commit. |
| if (fromCommit->commitInfo.robSquashing) { |
| DPRINTF(Fetch, "Fetch: ROB is still squashing.\n"); |
| |
| // Continue to squash. |
| _status = Squashing; |
| |
| ++fetchSquashCycles; |
| return; |
| } |
| |
| // Check squash signals from decode. |
| if (fromDecode->decodeInfo.squash) { |
| DPRINTF(Fetch, "Fetch: Squashing instructions due to squash " |
| "from decode.\n"); |
| |
| // Update the branch predictor. |
| if (fromDecode->decodeInfo.branchMispredict) { |
| branchPred.squash(fromDecode->decodeInfo.doneSeqNum, |
| fromDecode->decodeInfo.nextPC, |
| fromDecode->decodeInfo.branchTaken); |
| } else { |
| branchPred.squash(fromDecode->decodeInfo.doneSeqNum); |
| } |
| |
| if (_status != Squashing) { |
| // Squash unless we're already squashing? |
| squashFromDecode(fromDecode->decodeInfo.nextPC, |
| fromDecode->decodeInfo.doneSeqNum); |
| return; |
| } |
| } |
| |
| // 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; |
| |
| ++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; |
| |
| ++fetchBlockedCycles; |
| return; |
| } |
| |
| // 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"); |
| |
| ++fetchCycles; |
| |
| fetch(); |
| } |
| } |
| |
| template<class Impl> |
| void |
| SimpleFetch<Impl>::fetch() |
| { |
| ////////////////////////////////////////// |
| // Start actual fetch |
| ////////////////////////////////////////// |
| |
| // The current PC. |
| Addr fetch_PC = cpu->readPC(); |
| |
| // Fault code for memory access. |
| Fault fault = No_Fault; |
| |
| // 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; |
| } else { |
| DPRINTF(Fetch, "Fetch: Attempting to translate and read " |
| "instruction, starting at PC %08p.\n", |
| fetch_PC); |
| |
| fault = fetchCacheLine(fetch_PC); |
| } |
| |
| // 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) { |
| 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; |
| |
| if (fault == No_Fault) { |
| // 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 |
| ////////////////////////// |
| |
| // Need to keep track of whether or not a predicted branch |
| // ended this fetch block. |
| bool predicted_branch = false; |
| |
| for (fetched = 0; |
| offset < cacheBlkSize && |
| fetched < fetchWidth && |
| !predicted_branch; |
| ++fetched) |
| { |
| |
| // 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 = htoa(*reinterpret_cast<MachInst *> |
| (&cacheData[offset])); |
| |
| // Create a new DynInst from the instruction fetched. |
| DynInstPtr instruction = new DynInst(inst, fetch_PC, next_PC, |
| inst_seq, cpu); |
| |
| DPRINTF(Fetch, "Fetch: Instruction %i created, with PC %#x\n", |
| inst_seq, instruction->readPC()); |
| |
| DPRINTF(Fetch, "Fetch: Instruction opcode is: %03p\n", |
| OPCODE(inst)); |
| |
| instruction->traceData = |
| Trace::getInstRecord(curTick, cpu->xcBase(), cpu, |
| instruction->staticInst, |
| instruction->readPC(), 0); |
| |
| predicted_branch = lookupAndUpdateNextPC(instruction, next_PC); |
| |
| // Add instruction to the CPU's list of instructions. |
| cpu->addInst(instruction); |
| |
| // Write the instruction to the first slot in the queue |
| // that heads to decode. |
| toDecode->insts[fetched] = instruction; |
| |
| toDecode->size++; |
| |
| // Increment stat of fetched instructions. |
| ++fetchedInsts; |
| |
| // Move to the next instruction, unless we have a branch. |
| fetch_PC = next_PC; |
| |
| offset+= instSize; |
| } |
| |
| fetch_nisn_dist.sample(fetched); |
| } |
| |
| // 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). |
| if (fault == No_Fault) { |
| DPRINTF(Fetch, "Fetch: Setting PC to %08p.\n", next_PC); |
| cpu->setPC(next_PC); |
| cpu->setNextPC(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; |
| } |
| |
| // 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. |
| |
| DPRINTF(Fetch, "Fetch: Blocked, need to handle the trap.\n"); |
| |
| _status = Blocked; |
| #ifdef 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. |
| #else // !FULL_SYSTEM |
| fatal("fault (%d) detected @ PC %08p", fault, cpu->readPC()); |
| #endif // FULL_SYSTEM |
| } |
| } |