| /* |
| * 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. |
| */ |
| |
| #include "arch/faults.hh" |
| #include "arch/isa_traits.hh" |
| #include "base/statistics.hh" |
| #include "cpu/thread_context.hh" |
| #include "cpu/exetrace.hh" |
| #include "cpu/ozone/front_end.hh" |
| #include "mem/mem_interface.hh" |
| #include "sim/byte_swap.hh" |
| |
| using namespace TheISA; |
| |
| template <class Impl> |
| FrontEnd<Impl>::FrontEnd(Params *params) |
| : branchPred(params), |
| icacheInterface(params->icacheInterface), |
| instBufferSize(0), |
| maxInstBufferSize(params->maxInstBufferSize), |
| width(params->frontEndWidth), |
| freeRegs(params->numPhysicalRegs), |
| numPhysRegs(params->numPhysicalRegs), |
| serializeNext(false), |
| interruptPending(false) |
| { |
| switchedOut = false; |
| |
| status = Idle; |
| |
| memReq = NULL; |
| // Size of cache block. |
| cacheBlkSize = icacheInterface ? icacheInterface->getBlockSize() : 64; |
| |
| assert(isPowerOf2(cacheBlkSize)); |
| |
| // Create mask to get rid of offset bits. |
| cacheBlkMask = (cacheBlkSize - 1); |
| |
| // Create space to store a cache line. |
| cacheData = new uint8_t[cacheBlkSize]; |
| |
| fetchCacheLineNextCycle = true; |
| |
| cacheBlkValid = false; |
| |
| #if !FULL_SYSTEM |
| // pTable = params->pTable; |
| #endif |
| fetchFault = NoFault; |
| } |
| |
| template <class Impl> |
| std::string |
| FrontEnd<Impl>::name() const |
| { |
| return cpu->name() + ".frontend"; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) |
| { |
| comm = _comm; |
| // @todo: Hardcoded for now. Allow this to be set by a latency. |
| fromCommit = comm->getWire(-1); |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::setTC(ThreadContext *tc_ptr) |
| { |
| tc = tc_ptr; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<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); |
| |
| fetchedBranches |
| .name(name() + ".fetchedBranches") |
| .desc("Number of fetched branches") |
| .prereq(fetchedBranches); |
| |
| 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); |
| |
| fetchIdleCycles |
| .name(name() + ".fetchIdleCycles") |
| .desc("Number of cycles fetch was idle") |
| .prereq(fetchIdleCycles); |
| |
| 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); |
| |
| fetchIcacheSquashes |
| .name(name() + ".fetchIcacheSquashes") |
| .desc("Number of outstanding Icache misses that were squashed") |
| .prereq(fetchIcacheSquashes); |
| |
| fetchNisnDist |
| .init(/* base value */ 0, |
| /* last value */ width, |
| /* 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; |
| |
| IFQCount |
| .name(name() + ".IFQ:count") |
| .desc("cumulative IFQ occupancy") |
| ; |
| |
| IFQFcount |
| .name(name() + ".IFQ:fullCount") |
| .desc("cumulative IFQ full count") |
| .flags(Stats::total) |
| ; |
| |
| IFQOccupancy |
| .name(name() + ".IFQ:occupancy") |
| .desc("avg IFQ occupancy (inst's)") |
| ; |
| IFQOccupancy = IFQCount / cpu->numCycles; |
| |
| IFQLatency |
| .name(name() + ".IFQ:latency") |
| .desc("avg IFQ occupant latency (cycle's)") |
| .flags(Stats::total) |
| ; |
| |
| IFQFullRate |
| .name(name() + ".IFQ:fullRate") |
| .desc("fraction of time (cycles) IFQ was full") |
| .flags(Stats::total); |
| ; |
| IFQFullRate = IFQFcount * Stats::constant(100) / cpu->numCycles; |
| |
| dispatchCountStat |
| .name(name() + ".DIS:count") |
| .desc("cumulative count of dispatched insts") |
| .flags(Stats::total) |
| ; |
| |
| dispatchedSerializing |
| .name(name() + ".DIS:serializingInsts") |
| .desc("count of serializing insts dispatched") |
| .flags(Stats::total) |
| ; |
| |
| dispatchedTempSerializing |
| .name(name() + ".DIS:tempSerializingInsts") |
| .desc("count of temporary serializing insts dispatched") |
| .flags(Stats::total) |
| ; |
| |
| dispatchSerializeStallCycles |
| .name(name() + ".DIS:serializeStallCycles") |
| .desc("count of cycles dispatch stalled for serializing inst") |
| .flags(Stats::total) |
| ; |
| |
| dispatchRate |
| .name(name() + ".DIS:rate") |
| .desc("dispatched insts per cycle") |
| .flags(Stats::total) |
| ; |
| dispatchRate = dispatchCountStat / cpu->numCycles; |
| |
| regIntFull |
| .name(name() + ".REG:int:full") |
| .desc("number of cycles where there were no INT registers") |
| ; |
| |
| regFpFull |
| .name(name() + ".REG:fp:full") |
| .desc("number of cycles where there were no FP registers") |
| ; |
| IFQLatency = IFQOccupancy / dispatchRate; |
| |
| branchPred.regStats(); |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::tick() |
| { |
| if (switchedOut) |
| return; |
| |
| // @todo: Maybe I want to just have direct communication... |
| if (fromCommit->doneSeqNum) { |
| branchPred.update(fromCommit->doneSeqNum, 0); |
| } |
| |
| IFQCount += instBufferSize; |
| IFQFcount += instBufferSize == maxInstBufferSize; |
| |
| // Fetch cache line |
| if (status == IcacheMissComplete) { |
| cacheBlkValid = true; |
| |
| status = Running; |
| if (barrierInst) |
| status = SerializeBlocked; |
| if (freeRegs <= 0) |
| status = RenameBlocked; |
| checkBE(); |
| } else if (status == IcacheMissStall) { |
| DPRINTF(FE, "Still in Icache miss stall.\n"); |
| icacheStallCycles++; |
| return; |
| } |
| |
| if (status == RenameBlocked || status == SerializeBlocked || |
| status == TrapPending || status == BEBlocked) { |
| // Will cause a one cycle bubble between changing state and |
| // restarting. |
| DPRINTF(FE, "In blocked status.\n"); |
| |
| fetchBlockedCycles++; |
| |
| if (status == SerializeBlocked) { |
| dispatchSerializeStallCycles++; |
| } |
| updateStatus(); |
| return; |
| } else if (status == QuiescePending) { |
| DPRINTF(FE, "Waiting for quiesce to execute or get squashed.\n"); |
| return; |
| } else if (status != IcacheMissComplete) { |
| if (fetchCacheLineNextCycle) { |
| Fault fault = fetchCacheLine(); |
| if (fault != NoFault) { |
| handleFault(fault); |
| fetchFault = fault; |
| return; |
| } |
| fetchCacheLineNextCycle = false; |
| } |
| // If miss, stall until it returns. |
| if (status == IcacheMissStall) { |
| // Tell CPU to not tick me for now. |
| return; |
| } |
| } |
| |
| fetchCycles++; |
| |
| int num_inst = 0; |
| |
| // Otherwise loop and process instructions. |
| // One way to hack infinite width is to set width and maxInstBufferSize |
| // both really high. Inelegant, but probably will work. |
| while (num_inst < width && |
| instBufferSize < maxInstBufferSize) { |
| // Get instruction from cache line. |
| DynInstPtr inst = getInstFromCacheline(); |
| |
| if (!inst) { |
| // PC is no longer in the cache line, end fetch. |
| // Might want to check this at the end of the cycle so that |
| // there's no cycle lost to checking for a new cache line. |
| DPRINTF(FE, "Need to get new cache line\n"); |
| fetchCacheLineNextCycle = true; |
| break; |
| } |
| |
| processInst(inst); |
| |
| if (status == SerializeBlocked) { |
| break; |
| } |
| |
| // Possibly push into a time buffer that estimates the front end |
| // latency |
| instBuffer.push_back(inst); |
| ++instBufferSize; |
| ++num_inst; |
| |
| #if FULL_SYSTEM |
| if (inst->isQuiesce()) { |
| warn("%lli: Quiesce instruction encountered, halting fetch!", curTick); |
| status = QuiescePending; |
| break; |
| } |
| #endif |
| |
| if (inst->predTaken()) { |
| // Start over with tick? |
| break; |
| } else if (freeRegs <= 0) { |
| DPRINTF(FE, "Ran out of free registers to rename to!\n"); |
| status = RenameBlocked; |
| break; |
| } else if (serializeNext) { |
| break; |
| } |
| } |
| |
| fetchNisnDist.sample(num_inst); |
| checkBE(); |
| |
| DPRINTF(FE, "Num insts processed: %i, Inst Buffer size: %i, Free " |
| "Regs %i\n", num_inst, instBufferSize, freeRegs); |
| } |
| |
| template <class Impl> |
| Fault |
| FrontEnd<Impl>::fetchCacheLine() |
| { |
| // Read a cache line, based on the current PC. |
| #if FULL_SYSTEM |
| // Flag to say whether or not address is physical addr. |
| unsigned flags = cpu->inPalMode(PC) ? PHYSICAL : 0; |
| #else |
| unsigned flags = 0; |
| #endif // FULL_SYSTEM |
| Fault fault = NoFault; |
| |
| if (interruptPending && flags == 0) { |
| return fault; |
| } |
| |
| // Align the fetch PC so it's at the start of a cache block. |
| Addr fetch_PC = icacheBlockAlignPC(PC); |
| |
| DPRINTF(FE, "Fetching cache line starting at %#x.\n", 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 = new MemReq(); |
| |
| memReq->asid = 0; |
| memReq->thread_num = 0; |
| memReq->data = new uint8_t[64]; |
| memReq->tc = tc; |
| memReq->cmd = Read; |
| memReq->reset(fetch_PC, cacheBlkSize, flags); |
| |
| // Translate the instruction request. |
| fault = cpu->translateInstReq(memReq); |
| |
| // Now do the timing access to see whether or not the instruction |
| // exists within the cache. |
| if (icacheInterface && fault == NoFault) { |
| #if FULL_SYSTEM |
| if (cpu->system->memctrl->badaddr(memReq->paddr) || |
| memReq->flags & UNCACHEABLE) { |
| DPRINTF(FE, "Fetch: Bad address %#x (hopefully on a " |
| "misspeculating path!", |
| memReq->paddr); |
| return TheISA::genMachineCheckFault(); |
| } |
| #endif |
| |
| memReq->completionEvent = NULL; |
| |
| memReq->time = curTick; |
| fault = cpu->mem->read(memReq, cacheData); |
| |
| MemAccessResult res = icacheInterface->access(memReq); |
| |
| // If the cache missed then schedule an event to wake |
| // up this stage once the cache miss completes. |
| if (icacheInterface->doEvents() && res != MA_HIT) { |
| memReq->completionEvent = new ICacheCompletionEvent(memReq, this); |
| |
| status = IcacheMissStall; |
| |
| cacheBlkValid = false; |
| |
| DPRINTF(FE, "Cache miss.\n"); |
| } else { |
| DPRINTF(FE, "Cache hit.\n"); |
| |
| cacheBlkValid = true; |
| |
| // memcpy(cacheData, memReq->data, memReq->size); |
| } |
| } |
| |
| // Note that this will set the cache block PC a bit earlier than it should |
| // be set. |
| cacheBlkPC = fetch_PC; |
| |
| ++fetchedCacheLines; |
| |
| DPRINTF(FE, "Done fetching cache line.\n"); |
| |
| return fault; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::processInst(DynInstPtr &inst) |
| { |
| if (processBarriers(inst)) { |
| return; |
| } |
| |
| Addr inst_PC = inst->readPC(); |
| |
| if (!inst->isControl()) { |
| inst->setPredTarg(inst->readNextPC()); |
| } else { |
| fetchedBranches++; |
| if (branchPred.predict(inst, inst_PC, inst->threadNumber)) { |
| predictedBranches++; |
| } |
| } |
| |
| Addr next_PC = inst->readPredTarg(); |
| |
| DPRINTF(FE, "[sn:%lli] Predicted and processed inst PC %#x, next PC " |
| "%#x\n", inst->seqNum, inst_PC, next_PC); |
| |
| // inst->setNextPC(next_PC); |
| |
| // Not sure where I should set this |
| PC = next_PC; |
| |
| renameInst(inst); |
| } |
| |
| template <class Impl> |
| bool |
| FrontEnd<Impl>::processBarriers(DynInstPtr &inst) |
| { |
| if (serializeNext) { |
| inst->setSerializeBefore(); |
| serializeNext = false; |
| } else if (!inst->isSerializing() && |
| !inst->isIprAccess() && |
| !inst->isStoreConditional()) { |
| return false; |
| } |
| |
| if ((inst->isIprAccess() || inst->isSerializeBefore()) && |
| !inst->isSerializeHandled()) { |
| DPRINTF(FE, "Serialize before instruction encountered.\n"); |
| |
| if (!inst->isTempSerializeBefore()) { |
| dispatchedSerializing++; |
| inst->setSerializeHandled(); |
| } else { |
| dispatchedTempSerializing++; |
| } |
| |
| // Change status over to SerializeBlocked so that other stages know |
| // what this is blocked on. |
| status = SerializeBlocked; |
| |
| barrierInst = inst; |
| return true; |
| } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) |
| && !inst->isSerializeHandled()) { |
| DPRINTF(FE, "Serialize after instruction encountered.\n"); |
| |
| inst->setSerializeHandled(); |
| |
| dispatchedSerializing++; |
| |
| serializeNext = true; |
| return false; |
| } |
| return false; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::handleFault(Fault &fault) |
| { |
| DPRINTF(FE, "Fault at fetch, telling commit\n"); |
| |
| // We're blocked on the back end until it handles this fault. |
| status = TrapPending; |
| |
| // Get a sequence number. |
| InstSeqNum inst_seq = getAndIncrementInstSeq(); |
| // We will use a nop in order to carry the fault. |
| ExtMachInst ext_inst = TheISA::NoopMachInst; |
| |
| // Create a new DynInst from the dummy nop. |
| DynInstPtr instruction = new DynInst(ext_inst, PC, |
| PC+sizeof(MachInst), |
| inst_seq, cpu); |
| instruction->setPredTarg(instruction->readNextPC()); |
| // instruction->setThread(tid); |
| |
| // instruction->setASID(tid); |
| |
| instruction->setState(thread); |
| |
| instruction->traceData = NULL; |
| |
| instruction->fault = fault; |
| instruction->setCanIssue(); |
| instBuffer.push_back(instruction); |
| ++instBufferSize; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC, |
| const bool is_branch, const bool branch_taken) |
| { |
| DPRINTF(FE, "Squashing from [sn:%lli], setting PC to %#x\n", |
| squash_num, next_PC); |
| |
| if (fetchFault != NoFault) |
| fetchFault = NoFault; |
| |
| while (!instBuffer.empty() && |
| instBuffer.back()->seqNum > squash_num) { |
| DynInstPtr inst = instBuffer.back(); |
| |
| DPRINTF(FE, "Squashing instruction [sn:%lli] PC %#x\n", |
| inst->seqNum, inst->readPC()); |
| |
| inst->clearDependents(); |
| |
| instBuffer.pop_back(); |
| --instBufferSize; |
| |
| freeRegs+= inst->numDestRegs(); |
| } |
| |
| // Copy over rename table from the back end. |
| renameTable.copyFrom(backEnd->renameTable); |
| |
| PC = next_PC; |
| |
| // Update BP with proper information. |
| if (is_branch) { |
| branchPred.squash(squash_num, next_PC, branch_taken, 0); |
| } else { |
| branchPred.squash(squash_num, 0); |
| } |
| |
| // Clear the icache miss if it's outstanding. |
| if (status == IcacheMissStall && icacheInterface) { |
| DPRINTF(FE, "Squashing outstanding Icache miss.\n"); |
| memReq = NULL; |
| } |
| |
| if (status == SerializeBlocked) { |
| assert(barrierInst->seqNum > squash_num); |
| barrierInst = NULL; |
| } |
| |
| // Unless this squash originated from the front end, we're probably |
| // in running mode now. |
| // Actually might want to make this latency dependent. |
| status = Running; |
| fetchCacheLineNextCycle = true; |
| } |
| |
| template <class Impl> |
| typename Impl::DynInstPtr |
| FrontEnd<Impl>::getInst() |
| { |
| if (instBufferSize == 0) { |
| return NULL; |
| } |
| |
| DynInstPtr inst = instBuffer.front(); |
| |
| instBuffer.pop_front(); |
| |
| --instBufferSize; |
| |
| dispatchCountStat++; |
| |
| return inst; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::processCacheCompletion(MemReqPtr &req) |
| { |
| DPRINTF(FE, "Processing cache completion\n"); |
| |
| // Do something here. |
| if (status != IcacheMissStall || |
| req != memReq || |
| switchedOut) { |
| DPRINTF(FE, "Previous fetch was squashed.\n"); |
| fetchIcacheSquashes++; |
| return; |
| } |
| |
| status = IcacheMissComplete; |
| |
| /* if (checkStall(tid)) { |
| fetchStatus[tid] = Blocked; |
| } else { |
| fetchStatus[tid] = IcacheMissComplete; |
| } |
| */ |
| // memcpy(cacheData, memReq->data, memReq->size); |
| |
| // Reset the completion event to NULL. |
| // memReq->completionEvent = NULL; |
| memReq = NULL; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::addFreeRegs(int num_freed) |
| { |
| if (status == RenameBlocked && freeRegs + num_freed > 0) { |
| status = Running; |
| } |
| |
| DPRINTF(FE, "Adding %i freed registers\n", num_freed); |
| |
| freeRegs+= num_freed; |
| |
| // assert(freeRegs <= numPhysRegs); |
| if (freeRegs > numPhysRegs) |
| freeRegs = numPhysRegs; |
| } |
| |
| template <class Impl> |
| bool |
| FrontEnd<Impl>::updateStatus() |
| { |
| bool serialize_block = !backEnd->robEmpty() || instBufferSize; |
| bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); |
| bool ret_val = false; |
| |
| if (status == SerializeBlocked && !serialize_block) { |
| status = SerializeComplete; |
| ret_val = true; |
| } |
| |
| if (status == BEBlocked && !be_block) { |
| if (barrierInst) { |
| status = SerializeBlocked; |
| } else { |
| status = Running; |
| } |
| ret_val = true; |
| } |
| return ret_val; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::checkBE() |
| { |
| bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); |
| if (be_block) { |
| if (status == Running || status == Idle) { |
| status = BEBlocked; |
| } |
| } |
| } |
| |
| template <class Impl> |
| typename Impl::DynInstPtr |
| FrontEnd<Impl>::getInstFromCacheline() |
| { |
| if (status == SerializeComplete) { |
| DynInstPtr inst = barrierInst; |
| status = Running; |
| barrierInst = NULL; |
| inst->clearSerializeBefore(); |
| return inst; |
| } |
| |
| InstSeqNum inst_seq; |
| MachInst inst; |
| // @todo: Fix this magic number used here to handle word offset (and |
| // getting rid of PAL bit) |
| unsigned offset = (PC & cacheBlkMask) & ~3; |
| |
| // PC of inst is not in this cache block |
| if (PC >= (cacheBlkPC + cacheBlkSize) || PC < cacheBlkPC || !cacheBlkValid) { |
| return NULL; |
| } |
| |
| ////////////////////////// |
| // Fetch one instruction |
| ////////////////////////// |
| |
| // Get a sequence number. |
| inst_seq = getAndIncrementInstSeq(); |
| |
| // Make sure this is a valid index. |
| assert(offset <= cacheBlkSize - sizeof(MachInst)); |
| |
| // Get the instruction from the array of the cache line. |
| inst = htog(*reinterpret_cast<MachInst *>(&cacheData[offset])); |
| |
| ExtMachInst decode_inst = TheISA::makeExtMI(inst, PC); |
| |
| // Create a new DynInst from the instruction fetched. |
| DynInstPtr instruction = new DynInst(decode_inst, PC, PC+sizeof(MachInst), |
| inst_seq, cpu); |
| |
| instruction->setState(thread); |
| |
| DPRINTF(FE, "Instruction [sn:%lli] created, with PC %#x\n%s\n", |
| inst_seq, instruction->readPC(), |
| instruction->staticInst->disassemble(PC)); |
| |
| instruction->traceData = |
| Trace::getInstRecord(curTick, tc, cpu, |
| instruction->staticInst, |
| instruction->readPC(), 0); |
| |
| // Increment stat of fetched instructions. |
| ++fetchedInsts; |
| |
| return instruction; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::renameInst(DynInstPtr &inst) |
| { |
| DynInstPtr src_inst = NULL; |
| int num_src_regs = inst->numSrcRegs(); |
| if (num_src_regs == 0) { |
| inst->setCanIssue(); |
| } else { |
| for (int i = 0; i < num_src_regs; ++i) { |
| src_inst = renameTable[inst->srcRegIdx(i)]; |
| |
| inst->setSrcInst(src_inst, i); |
| |
| DPRINTF(FE, "[sn:%lli]: Src reg %i is inst [sn:%lli]\n", |
| inst->seqNum, (int)inst->srcRegIdx(i), src_inst->seqNum); |
| |
| if (src_inst->isResultReady()) { |
| DPRINTF(FE, "Reg ready.\n"); |
| inst->markSrcRegReady(i); |
| } else { |
| DPRINTF(FE, "Adding to dependent list.\n"); |
| src_inst->addDependent(inst); |
| } |
| } |
| } |
| |
| for (int i = 0; i < inst->numDestRegs(); ++i) { |
| RegIndex idx = inst->destRegIdx(i); |
| |
| DPRINTF(FE, "Dest reg %i is now inst [sn:%lli], was previously " |
| "[sn:%lli]\n", |
| (int)inst->destRegIdx(i), inst->seqNum, |
| renameTable[idx]->seqNum); |
| |
| inst->setPrevDestInst(renameTable[idx], i); |
| |
| renameTable[idx] = inst; |
| --freeRegs; |
| } |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::wakeFromQuiesce() |
| { |
| DPRINTF(FE, "Waking up from quiesce\n"); |
| // Hopefully this is safe |
| status = Running; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::switchOut() |
| { |
| switchedOut = true; |
| cpu->signalSwitched(); |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::doSwitchOut() |
| { |
| memReq = NULL; |
| squash(0, 0); |
| instBuffer.clear(); |
| instBufferSize = 0; |
| status = Idle; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::takeOverFrom(ThreadContext *old_tc) |
| { |
| assert(freeRegs == numPhysRegs); |
| fetchCacheLineNextCycle = true; |
| |
| cacheBlkValid = false; |
| |
| #if !FULL_SYSTEM |
| // pTable = params->pTable; |
| #endif |
| fetchFault = NoFault; |
| serializeNext = false; |
| barrierInst = NULL; |
| status = Running; |
| switchedOut = false; |
| interruptPending = false; |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::dumpInsts() |
| { |
| cprintf("instBuffer size: %i\n", instBuffer.size()); |
| |
| InstBuffIt buff_it = instBuffer.begin(); |
| |
| for (int num = 0; buff_it != instBuffer.end(); num++) { |
| cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" |
| "Squashed:%i\n\n", |
| num, (*buff_it)->readPC(), (*buff_it)->threadNumber, |
| (*buff_it)->seqNum, (*buff_it)->isIssued(), |
| (*buff_it)->isSquashed()); |
| buff_it++; |
| } |
| } |
| |
| template <class Impl> |
| FrontEnd<Impl>::ICacheCompletionEvent::ICacheCompletionEvent(MemReqPtr &_req, FrontEnd *fe) |
| : Event(&mainEventQueue, Delayed_Writeback_Pri), req(_req), frontEnd(fe) |
| { |
| this->setFlags(Event::AutoDelete); |
| } |
| |
| template <class Impl> |
| void |
| FrontEnd<Impl>::ICacheCompletionEvent::process() |
| { |
| frontEnd->processCacheCompletion(req); |
| } |
| |
| template <class Impl> |
| const char * |
| FrontEnd<Impl>::ICacheCompletionEvent::description() |
| { |
| return "ICache completion event"; |
| } |