| /* |
| * Copyright (c) 2013-2014 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. |
| * |
| * 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: Andrew Bardsley |
| */ |
| |
| #include "cpu/minor/fetch1.hh" |
| |
| #include <cstring> |
| #include <iomanip> |
| #include <sstream> |
| |
| #include "base/cast.hh" |
| #include "cpu/minor/pipeline.hh" |
| #include "debug/Drain.hh" |
| #include "debug/Fetch.hh" |
| #include "debug/MinorTrace.hh" |
| |
| namespace Minor |
| { |
| |
| Fetch1::Fetch1(const std::string &name_, |
| MinorCPU &cpu_, |
| MinorCPUParams ¶ms, |
| Latch<BranchData>::Output inp_, |
| Latch<ForwardLineData>::Input out_, |
| Latch<BranchData>::Output prediction_, |
| std::vector<InputBuffer<ForwardLineData>> &next_stage_input_buffer) : |
| Named(name_), |
| cpu(cpu_), |
| inp(inp_), |
| out(out_), |
| prediction(prediction_), |
| nextStageReserve(next_stage_input_buffer), |
| icachePort(name_ + ".icache_port", *this, cpu_), |
| lineSnap(params.fetch1LineSnapWidth), |
| maxLineWidth(params.fetch1LineWidth), |
| fetchLimit(params.fetch1FetchLimit), |
| fetchInfo(params.numThreads), |
| threadPriority(0), |
| requests(name_ + ".requests", "lines", params.fetch1FetchLimit), |
| transfers(name_ + ".transfers", "lines", params.fetch1FetchLimit), |
| icacheState(IcacheRunning), |
| lineSeqNum(InstId::firstLineSeqNum), |
| numFetchesInMemorySystem(0), |
| numFetchesInITLB(0) |
| { |
| if (lineSnap == 0) { |
| lineSnap = cpu.cacheLineSize(); |
| DPRINTF(Fetch, "lineSnap set to cache line size of: %d\n", |
| lineSnap); |
| } |
| |
| if (maxLineWidth == 0) { |
| maxLineWidth = cpu.cacheLineSize(); |
| DPRINTF(Fetch, "maxLineWidth set to cache line size of: %d\n", |
| maxLineWidth); |
| } |
| |
| /* These assertions should be copied to the Python config. as well */ |
| if ((lineSnap % sizeof(TheISA::MachInst)) != 0) { |
| fatal("%s: fetch1LineSnapWidth must be a multiple " |
| "of sizeof(TheISA::MachInst) (%d)\n", name_, |
| sizeof(TheISA::MachInst)); |
| } |
| |
| if (!(maxLineWidth >= lineSnap && |
| (maxLineWidth % sizeof(TheISA::MachInst)) == 0)) |
| { |
| fatal("%s: fetch1LineWidth must be a multiple of" |
| " sizeof(TheISA::MachInst)" |
| " (%d), and >= fetch1LineSnapWidth (%d)\n", |
| name_, sizeof(TheISA::MachInst), lineSnap); |
| } |
| |
| if (fetchLimit < 1) { |
| fatal("%s: fetch1FetchLimit must be >= 1 (%d)\n", name_, |
| fetchLimit); |
| } |
| } |
| |
| inline ThreadID |
| Fetch1::getScheduledThread() |
| { |
| /* Select thread via policy. */ |
| std::vector<ThreadID> priority_list; |
| |
| switch (cpu.threadPolicy) { |
| case Enums::SingleThreaded: |
| priority_list.push_back(0); |
| break; |
| case Enums::RoundRobin: |
| priority_list = cpu.roundRobinPriority(threadPriority); |
| break; |
| case Enums::Random: |
| priority_list = cpu.randomPriority(); |
| break; |
| default: |
| panic("Unknown fetch policy"); |
| } |
| |
| for (auto tid : priority_list) { |
| if (cpu.getContext(tid)->status() == ThreadContext::Active && |
| !fetchInfo[tid].blocked && |
| fetchInfo[tid].state == FetchRunning) { |
| threadPriority = tid; |
| return tid; |
| } |
| } |
| |
| return InvalidThreadID; |
| } |
| |
| void |
| Fetch1::fetchLine(ThreadID tid) |
| { |
| /* Reference the currently used thread state. */ |
| Fetch1ThreadInfo &thread = fetchInfo[tid]; |
| |
| /* If line_offset != 0, a request is pushed for the remainder of the |
| * line. */ |
| /* Use a lower, sizeof(MachInst) aligned address for the fetch */ |
| Addr aligned_pc = thread.pc.instAddr() & ~((Addr) lineSnap - 1); |
| unsigned int line_offset = aligned_pc % lineSnap; |
| unsigned int request_size = maxLineWidth - line_offset; |
| |
| /* Fill in the line's id */ |
| InstId request_id(tid, |
| thread.streamSeqNum, thread.predictionSeqNum, |
| lineSeqNum); |
| |
| FetchRequestPtr request = new FetchRequest(*this, request_id, thread.pc); |
| |
| DPRINTF(Fetch, "Inserting fetch into the fetch queue " |
| "%s addr: 0x%x pc: %s line_offset: %d request_size: %d\n", |
| request_id, aligned_pc, thread.pc, line_offset, request_size); |
| |
| request->request.setContext(cpu.threads[tid]->getTC()->contextId()); |
| request->request.setVirt(0 /* asid */, |
| aligned_pc, request_size, Request::INST_FETCH, cpu.instMasterId(), |
| /* I've no idea why we need the PC, but give it */ |
| thread.pc.instAddr()); |
| |
| DPRINTF(Fetch, "Submitting ITLB request\n"); |
| numFetchesInITLB++; |
| |
| request->state = FetchRequest::InTranslation; |
| |
| /* Reserve space in the queues upstream of requests for results */ |
| transfers.reserve(); |
| requests.push(request); |
| |
| /* Submit the translation request. The response will come |
| * through finish/markDelayed on this request as it bears |
| * the Translation interface */ |
| cpu.threads[request->id.threadId]->itb->translateTiming( |
| &request->request, |
| cpu.getContext(request->id.threadId), |
| request, BaseTLB::Execute); |
| |
| lineSeqNum++; |
| |
| /* Step the PC for the next line onto the line aligned next address. |
| * Note that as instructions can span lines, this PC is only a |
| * reliable 'new' PC if the next line has a new stream sequence number. */ |
| #if THE_ISA == ALPHA_ISA |
| /* Restore the low bits of the PC used as address space flags */ |
| Addr pc_low_bits = thread.pc.instAddr() & |
| ((Addr) (1 << sizeof(TheISA::MachInst)) - 1); |
| |
| thread.pc.set(aligned_pc + request_size + pc_low_bits); |
| #else |
| thread.pc.set(aligned_pc + request_size); |
| #endif |
| } |
| |
| std::ostream & |
| operator <<(std::ostream &os, Fetch1::IcacheState state) |
| { |
| switch (state) { |
| case Fetch1::IcacheRunning: |
| os << "IcacheRunning"; |
| break; |
| case Fetch1::IcacheNeedsRetry: |
| os << "IcacheNeedsRetry"; |
| break; |
| default: |
| os << "IcacheState-" << static_cast<int>(state); |
| break; |
| } |
| return os; |
| } |
| |
| void |
| Fetch1::FetchRequest::makePacket() |
| { |
| /* Make the necessary packet for a memory transaction */ |
| packet = new Packet(&request, MemCmd::ReadReq); |
| packet->allocate(); |
| |
| /* This FetchRequest becomes SenderState to allow the response to be |
| * identified */ |
| packet->pushSenderState(this); |
| } |
| |
| void |
| Fetch1::FetchRequest::finish(const Fault &fault_, RequestPtr request_, |
| ThreadContext *tc, BaseTLB::Mode mode) |
| { |
| fault = fault_; |
| |
| state = Translated; |
| fetch.handleTLBResponse(this); |
| |
| /* Let's try and wake up the processor for the next cycle */ |
| fetch.cpu.wakeupOnEvent(Pipeline::Fetch1StageId); |
| } |
| |
| void |
| Fetch1::handleTLBResponse(FetchRequestPtr response) |
| { |
| numFetchesInITLB--; |
| |
| if (response->fault != NoFault) { |
| DPRINTF(Fetch, "Fault in address ITLB translation: %s, " |
| "paddr: 0x%x, vaddr: 0x%x\n", |
| response->fault->name(), |
| (response->request.hasPaddr() ? response->request.getPaddr() : 0), |
| response->request.getVaddr()); |
| |
| if (DTRACE(MinorTrace)) |
| minorTraceResponseLine(name(), response); |
| } else { |
| DPRINTF(Fetch, "Got ITLB response\n"); |
| } |
| |
| response->state = FetchRequest::Translated; |
| |
| tryToSendToTransfers(response); |
| } |
| |
| Fetch1::FetchRequest::~FetchRequest() |
| { |
| if (packet) |
| delete packet; |
| } |
| |
| void |
| Fetch1::tryToSendToTransfers(FetchRequestPtr request) |
| { |
| if (!requests.empty() && requests.front() != request) { |
| DPRINTF(Fetch, "Fetch not at front of requests queue, can't" |
| " issue to memory\n"); |
| return; |
| } |
| |
| if (request->state == FetchRequest::InTranslation) { |
| DPRINTF(Fetch, "Fetch still in translation, not issuing to" |
| " memory\n"); |
| return; |
| } |
| |
| if (request->isDiscardable() || request->fault != NoFault) { |
| /* Discarded and faulting requests carry on through transfers |
| * as Complete/packet == NULL */ |
| |
| request->state = FetchRequest::Complete; |
| moveFromRequestsToTransfers(request); |
| |
| /* Wake up the pipeline next cycle as there will be no event |
| * for this queue->queue transfer */ |
| cpu.wakeupOnEvent(Pipeline::Fetch1StageId); |
| } else if (request->state == FetchRequest::Translated) { |
| if (!request->packet) |
| request->makePacket(); |
| |
| /* Ensure that the packet won't delete the request */ |
| assert(request->packet->needsResponse()); |
| |
| if (tryToSend(request)) |
| moveFromRequestsToTransfers(request); |
| } else { |
| DPRINTF(Fetch, "Not advancing line fetch\n"); |
| } |
| } |
| |
| void |
| Fetch1::moveFromRequestsToTransfers(FetchRequestPtr request) |
| { |
| assert(!requests.empty() && requests.front() == request); |
| |
| requests.pop(); |
| transfers.push(request); |
| } |
| |
| bool |
| Fetch1::tryToSend(FetchRequestPtr request) |
| { |
| bool ret = false; |
| |
| if (icachePort.sendTimingReq(request->packet)) { |
| /* Invalidate the fetch_requests packet so we don't |
| * accidentally fail to deallocate it (or use it!) |
| * later by overwriting it */ |
| request->packet = NULL; |
| request->state = FetchRequest::RequestIssuing; |
| numFetchesInMemorySystem++; |
| |
| ret = true; |
| |
| DPRINTF(Fetch, "Issued fetch request to memory: %s\n", |
| request->id); |
| } else { |
| /* Needs to be resent, wait for that */ |
| icacheState = IcacheNeedsRetry; |
| |
| DPRINTF(Fetch, "Line fetch needs to retry: %s\n", |
| request->id); |
| } |
| |
| return ret; |
| } |
| |
| void |
| Fetch1::stepQueues() |
| { |
| IcacheState old_icache_state = icacheState; |
| |
| switch (icacheState) { |
| case IcacheRunning: |
| /* Move ITLB results on to the memory system */ |
| if (!requests.empty()) { |
| tryToSendToTransfers(requests.front()); |
| } |
| break; |
| case IcacheNeedsRetry: |
| break; |
| } |
| |
| if (icacheState != old_icache_state) { |
| DPRINTF(Fetch, "Step in state %s moving to state %s\n", |
| old_icache_state, icacheState); |
| } |
| } |
| |
| void |
| Fetch1::popAndDiscard(FetchQueue &queue) |
| { |
| if (!queue.empty()) { |
| delete queue.front(); |
| queue.pop(); |
| } |
| } |
| |
| unsigned int |
| Fetch1::numInFlightFetches() |
| { |
| return requests.occupiedSpace() + |
| transfers.occupiedSpace(); |
| } |
| |
| /** Print the appropriate MinorLine line for a fetch response */ |
| void |
| Fetch1::minorTraceResponseLine(const std::string &name, |
| Fetch1::FetchRequestPtr response) const |
| { |
| Request &request M5_VAR_USED = response->request; |
| |
| if (response->packet && response->packet->isError()) { |
| MINORLINE(this, "id=F;%s vaddr=0x%x fault=\"error packet\"\n", |
| response->id, request.getVaddr()); |
| } else if (response->fault != NoFault) { |
| MINORLINE(this, "id=F;%s vaddr=0x%x fault=\"%s\"\n", |
| response->id, request.getVaddr(), response->fault->name()); |
| } else { |
| MINORLINE(this, "id=%s size=%d vaddr=0x%x paddr=0x%x\n", |
| response->id, request.getSize(), |
| request.getVaddr(), request.getPaddr()); |
| } |
| } |
| |
| bool |
| Fetch1::recvTimingResp(PacketPtr response) |
| { |
| DPRINTF(Fetch, "recvTimingResp %d\n", numFetchesInMemorySystem); |
| |
| /* Only push the response if we didn't change stream? No, all responses |
| * should hit the responses queue. It's the job of 'step' to throw them |
| * away. */ |
| FetchRequestPtr fetch_request = safe_cast<FetchRequestPtr> |
| (response->popSenderState()); |
| |
| /* Fixup packet in fetch_request as this may have changed */ |
| assert(!fetch_request->packet); |
| fetch_request->packet = response; |
| |
| numFetchesInMemorySystem--; |
| fetch_request->state = FetchRequest::Complete; |
| |
| if (DTRACE(MinorTrace)) |
| minorTraceResponseLine(name(), fetch_request); |
| |
| if (response->isError()) { |
| DPRINTF(Fetch, "Received error response packet: %s\n", |
| fetch_request->id); |
| } |
| |
| /* We go to idle even if there are more things to do on the queues as |
| * it's the job of step to actually step us on to the next transaction */ |
| |
| /* Let's try and wake up the processor for the next cycle to move on |
| * queues */ |
| cpu.wakeupOnEvent(Pipeline::Fetch1StageId); |
| |
| /* Never busy */ |
| return true; |
| } |
| |
| void |
| Fetch1::recvReqRetry() |
| { |
| DPRINTF(Fetch, "recvRetry\n"); |
| assert(icacheState == IcacheNeedsRetry); |
| assert(!requests.empty()); |
| |
| FetchRequestPtr retryRequest = requests.front(); |
| |
| icacheState = IcacheRunning; |
| |
| if (tryToSend(retryRequest)) |
| moveFromRequestsToTransfers(retryRequest); |
| } |
| |
| std::ostream & |
| operator <<(std::ostream &os, Fetch1::FetchState state) |
| { |
| switch (state) { |
| case Fetch1::FetchHalted: |
| os << "FetchHalted"; |
| break; |
| case Fetch1::FetchWaitingForPC: |
| os << "FetchWaitingForPC"; |
| break; |
| case Fetch1::FetchRunning: |
| os << "FetchRunning"; |
| break; |
| default: |
| os << "FetchState-" << static_cast<int>(state); |
| break; |
| } |
| return os; |
| } |
| |
| void |
| Fetch1::changeStream(const BranchData &branch) |
| { |
| Fetch1ThreadInfo &thread = fetchInfo[branch.threadId]; |
| |
| updateExpectedSeqNums(branch); |
| |
| /* Start fetching again if we were stopped */ |
| switch (branch.reason) { |
| case BranchData::SuspendThread: |
| { |
| if (thread.wakeupGuard) { |
| DPRINTF(Fetch, "Not suspending fetch due to guard: %s\n", |
| branch); |
| } else { |
| DPRINTF(Fetch, "Suspending fetch: %s\n", branch); |
| thread.state = FetchWaitingForPC; |
| } |
| } |
| break; |
| case BranchData::HaltFetch: |
| DPRINTF(Fetch, "Halting fetch\n"); |
| thread.state = FetchHalted; |
| break; |
| default: |
| DPRINTF(Fetch, "Changing stream on branch: %s\n", branch); |
| thread.state = FetchRunning; |
| break; |
| } |
| thread.pc = branch.target; |
| } |
| |
| void |
| Fetch1::updateExpectedSeqNums(const BranchData &branch) |
| { |
| Fetch1ThreadInfo &thread = fetchInfo[branch.threadId]; |
| |
| DPRINTF(Fetch, "Updating streamSeqNum from: %d to %d," |
| " predictionSeqNum from: %d to %d\n", |
| thread.streamSeqNum, branch.newStreamSeqNum, |
| thread.predictionSeqNum, branch.newPredictionSeqNum); |
| |
| /* Change the stream */ |
| thread.streamSeqNum = branch.newStreamSeqNum; |
| /* Update the prediction. Note that it's possible for this to |
| * actually set the prediction to an *older* value if new |
| * predictions have been discarded by execute */ |
| thread.predictionSeqNum = branch.newPredictionSeqNum; |
| } |
| |
| void |
| Fetch1::processResponse(Fetch1::FetchRequestPtr response, |
| ForwardLineData &line) |
| { |
| Fetch1ThreadInfo &thread = fetchInfo[response->id.threadId]; |
| PacketPtr packet = response->packet; |
| |
| /* Pass the prefetch abort (if any) on to Fetch2 in a ForwardLineData |
| * structure */ |
| line.setFault(response->fault); |
| /* Make sequence numbers valid in return */ |
| line.id = response->id; |
| /* Set PC to virtual address */ |
| line.pc = response->pc; |
| /* Set the lineBase, which is a sizeof(MachInst) aligned address <= |
| * pc.instAddr() */ |
| line.lineBaseAddr = response->request.getVaddr(); |
| |
| if (response->fault != NoFault) { |
| /* Stop fetching if there was a fault */ |
| /* Should probably try to flush the queues as well, but we |
| * can't be sure that this fault will actually reach Execute, and we |
| * can't (currently) selectively remove this stream from the queues */ |
| DPRINTF(Fetch, "Stopping line fetch because of fault: %s\n", |
| response->fault->name()); |
| thread.state = Fetch1::FetchWaitingForPC; |
| } else { |
| line.adoptPacketData(packet); |
| /* Null the response's packet to prevent the response from trying to |
| * deallocate the packet */ |
| response->packet = NULL; |
| } |
| } |
| |
| void |
| Fetch1::evaluate() |
| { |
| const BranchData &execute_branch = *inp.outputWire; |
| const BranchData &fetch2_branch = *prediction.outputWire; |
| ForwardLineData &line_out = *out.inputWire; |
| |
| assert(line_out.isBubble()); |
| |
| for (ThreadID tid = 0; tid < cpu.numThreads; tid++) |
| fetchInfo[tid].blocked = !nextStageReserve[tid].canReserve(); |
| |
| /** Are both branches from later stages valid and for the same thread? */ |
| if (execute_branch.threadId != InvalidThreadID && |
| execute_branch.threadId == fetch2_branch.threadId) { |
| |
| Fetch1ThreadInfo &thread = fetchInfo[execute_branch.threadId]; |
| |
| /* Are we changing stream? Look to the Execute branches first, then |
| * to predicted changes of stream from Fetch2 */ |
| if (execute_branch.isStreamChange()) { |
| if (thread.state == FetchHalted) { |
| DPRINTF(Fetch, "Halted, ignoring branch: %s\n", execute_branch); |
| } else { |
| changeStream(execute_branch); |
| } |
| |
| if (!fetch2_branch.isBubble()) { |
| DPRINTF(Fetch, "Ignoring simultaneous prediction: %s\n", |
| fetch2_branch); |
| } |
| |
| /* The streamSeqNum tagging in request/response ->req should handle |
| * discarding those requests when we get to them. */ |
| } else if (thread.state != FetchHalted && fetch2_branch.isStreamChange()) { |
| /* Handle branch predictions by changing the instruction source |
| * if we're still processing the same stream (as set by streamSeqNum) |
| * as the one of the prediction. |
| */ |
| if (fetch2_branch.newStreamSeqNum != thread.streamSeqNum) { |
| DPRINTF(Fetch, "Not changing stream on prediction: %s," |
| " streamSeqNum mismatch\n", |
| fetch2_branch); |
| } else { |
| changeStream(fetch2_branch); |
| } |
| } |
| } else { |
| /* Fetch2 and Execute branches are for different threads */ |
| if (execute_branch.threadId != InvalidThreadID && |
| execute_branch.isStreamChange()) { |
| |
| if (fetchInfo[execute_branch.threadId].state == FetchHalted) { |
| DPRINTF(Fetch, "Halted, ignoring branch: %s\n", execute_branch); |
| } else { |
| changeStream(execute_branch); |
| } |
| } |
| |
| if (fetch2_branch.threadId != InvalidThreadID && |
| fetch2_branch.isStreamChange()) { |
| |
| if (fetchInfo[fetch2_branch.threadId].state == FetchHalted) { |
| DPRINTF(Fetch, "Halted, ignoring branch: %s\n", fetch2_branch); |
| } else if (fetch2_branch.newStreamSeqNum != fetchInfo[fetch2_branch.threadId].streamSeqNum) { |
| DPRINTF(Fetch, "Not changing stream on prediction: %s," |
| " streamSeqNum mismatch\n", fetch2_branch); |
| } else { |
| changeStream(fetch2_branch); |
| } |
| } |
| } |
| |
| if (numInFlightFetches() < fetchLimit) { |
| ThreadID fetch_tid = getScheduledThread(); |
| |
| if (fetch_tid != InvalidThreadID) { |
| DPRINTF(Fetch, "Fetching from thread %d\n", fetch_tid); |
| |
| /* Generate fetch to selected thread */ |
| fetchLine(fetch_tid); |
| /* Take up a slot in the fetch queue */ |
| nextStageReserve[fetch_tid].reserve(); |
| } else { |
| DPRINTF(Fetch, "No active threads available to fetch from\n"); |
| } |
| } |
| |
| |
| /* Halting shouldn't prevent fetches in flight from being processed */ |
| /* Step fetches through the icachePort queues and memory system */ |
| stepQueues(); |
| |
| /* As we've thrown away early lines, if there is a line, it must |
| * be from the right stream */ |
| if (!transfers.empty() && |
| transfers.front()->isComplete()) |
| { |
| Fetch1::FetchRequestPtr response = transfers.front(); |
| |
| if (response->isDiscardable()) { |
| nextStageReserve[response->id.threadId].freeReservation(); |
| |
| DPRINTF(Fetch, "Discarding translated fetch as it's for" |
| " an old stream\n"); |
| |
| /* Wake up next cycle just in case there was some other |
| * action to do */ |
| cpu.wakeupOnEvent(Pipeline::Fetch1StageId); |
| } else { |
| DPRINTF(Fetch, "Processing fetched line: %s\n", |
| response->id); |
| |
| processResponse(response, line_out); |
| } |
| |
| popAndDiscard(transfers); |
| } |
| |
| /* If we generated output, and mark the stage as being active |
| * to encourage that output on to the next stage */ |
| if (!line_out.isBubble()) |
| cpu.activityRecorder->activity(); |
| |
| /* Fetch1 has no inputBuffer so the only activity we can have is to |
| * generate a line output (tested just above) or to initiate a memory |
| * fetch which will signal activity when it returns/needs stepping |
| * between queues */ |
| |
| |
| /* This looks hackish. And it is, but there doesn't seem to be a better |
| * way to do this. The signal from commit to suspend fetch takes 1 |
| * clock cycle to propagate to fetch. However, a legitimate wakeup |
| * may occur between cycles from the memory system. Thus wakeup guard |
| * prevents us from suspending in that case. */ |
| |
| for (auto& thread : fetchInfo) { |
| thread.wakeupGuard = false; |
| } |
| } |
| |
| void |
| Fetch1::wakeupFetch(ThreadID tid) |
| { |
| ThreadContext *thread_ctx = cpu.getContext(tid); |
| Fetch1ThreadInfo &thread = fetchInfo[tid]; |
| thread.pc = thread_ctx->pcState(); |
| thread.state = FetchRunning; |
| thread.wakeupGuard = true; |
| DPRINTF(Fetch, "[tid:%d]: Changing stream wakeup %s\n", |
| tid, thread_ctx->pcState()); |
| |
| cpu.wakeupOnEvent(Pipeline::Fetch1StageId); |
| } |
| |
| bool |
| Fetch1::isDrained() |
| { |
| bool drained = numInFlightFetches() == 0 && (*out.inputWire).isBubble(); |
| for (ThreadID tid = 0; tid < cpu.numThreads; tid++) { |
| Fetch1ThreadInfo &thread = fetchInfo[tid]; |
| DPRINTF(Drain, "isDrained[tid:%d]: %s %s%s\n", |
| tid, |
| thread.state == FetchHalted, |
| (numInFlightFetches() == 0 ? "" : "inFlightFetches "), |
| ((*out.inputWire).isBubble() ? "" : "outputtingLine")); |
| |
| drained = drained && (thread.state != FetchRunning); |
| } |
| |
| return drained; |
| } |
| |
| void |
| Fetch1::FetchRequest::reportData(std::ostream &os) const |
| { |
| os << id; |
| } |
| |
| bool Fetch1::FetchRequest::isDiscardable() const |
| { |
| Fetch1ThreadInfo &thread = fetch.fetchInfo[id.threadId]; |
| |
| /* Can't discard lines in TLB/memory */ |
| return state != InTranslation && state != RequestIssuing && |
| (id.streamSeqNum != thread.streamSeqNum || |
| id.predictionSeqNum != thread.predictionSeqNum); |
| } |
| |
| void |
| Fetch1::minorTrace() const |
| { |
| // TODO: Un-bork minorTrace for THREADS |
| // bork bork bork |
| const Fetch1ThreadInfo &thread = fetchInfo[0]; |
| |
| std::ostringstream data; |
| |
| if (thread.blocked) |
| data << 'B'; |
| else |
| (*out.inputWire).reportData(data); |
| |
| MINORTRACE("state=%s icacheState=%s in_tlb_mem=%s/%s" |
| " streamSeqNum=%d lines=%s\n", thread.state, icacheState, |
| numFetchesInITLB, numFetchesInMemorySystem, |
| thread.streamSeqNum, data.str()); |
| requests.minorTrace(); |
| transfers.minorTrace(); |
| } |
| |
| } |