diff options
Diffstat (limited to 'src/cpu/minor/execute.cc')
-rw-r--r-- | src/cpu/minor/execute.cc | 668 |
1 files changed, 398 insertions, 270 deletions
diff --git a/src/cpu/minor/execute.cc b/src/cpu/minor/execute.cc index 4298e1dcc..b13e0c020 100644 --- a/src/cpu/minor/execute.cc +++ b/src/cpu/minor/execute.cc @@ -86,15 +86,10 @@ Execute::Execute(const std::string &name_, params.executeLSQTransfersQueueSize, params.executeLSQStoreBufferSize, params.executeLSQMaxStoreBufferStoresPerCycle), - scoreboard(name_ + ".scoreboard"), - inputBuffer(name_ + ".inputBuffer", "insts", - params.executeInputBufferSize), - inputIndex(0), - lastCommitWasEndOfMacroop(true), - instsBeingCommitted(params.executeCommitLimit), - streamSeqNum(InstId::firstStreamSeqNum), - lastPredictionSeqNum(InstId::firstPredictionSeqNum), - drainState(NotDraining) + executeInfo(params.numThreads, ExecuteThreadInfo(params.executeCommitLimit)), + interruptPriority(0), + issuePriority(0), + commitPriority(0) { if (commitLimit < 1) { fatal("%s: executeCommitLimit must be >= 1 (%d)\n", name_, @@ -170,35 +165,50 @@ Execute::Execute(const std::string &name_, } } - inFlightInsts = new Queue<QueuedInst, - ReportTraitsAdaptor<QueuedInst> >( - name_ + ".inFlightInsts", "insts", total_slots); + /* Per-thread structures */ + for (ThreadID tid = 0; tid < params.numThreads; tid++) { + std::string tid_str = std::to_string(tid); - inFUMemInsts = new Queue<QueuedInst, - ReportTraitsAdaptor<QueuedInst> >( - name_ + ".inFUMemInsts", "insts", total_slots); + /* Input Buffers */ + inputBuffer.push_back( + InputBuffer<ForwardInstData>( + name_ + ".inputBuffer" + tid_str, "insts", + params.executeInputBufferSize)); + + /* Scoreboards */ + scoreboard.push_back(Scoreboard(name_ + ".scoreboard" + tid_str)); + + /* In-flight instruction records */ + executeInfo[tid].inFlightInsts = new Queue<QueuedInst, + ReportTraitsAdaptor<QueuedInst> >( + name_ + ".inFlightInsts" + tid_str, "insts", total_slots); + + executeInfo[tid].inFUMemInsts = new Queue<QueuedInst, + ReportTraitsAdaptor<QueuedInst> >( + name_ + ".inFUMemInsts" + tid_str, "insts", total_slots); + } } const ForwardInstData * -Execute::getInput() +Execute::getInput(ThreadID tid) { /* Get a line from the inputBuffer to work with */ - if (!inputBuffer.empty()) { - const ForwardInstData &head = inputBuffer.front(); + if (!inputBuffer[tid].empty()) { + const ForwardInstData &head = inputBuffer[tid].front(); - return (head.isBubble() ? NULL : &(inputBuffer.front())); + return (head.isBubble() ? NULL : &(inputBuffer[tid].front())); } else { return NULL; } } void -Execute::popInput() +Execute::popInput(ThreadID tid) { - if (!inputBuffer.empty()) - inputBuffer.pop(); + if (!inputBuffer[tid].empty()) + inputBuffer[tid].pop(); - inputIndex = 0; + executeInfo[tid].inputIndex = 0; } void @@ -276,11 +286,12 @@ Execute::tryToBranch(MinorDynInstPtr inst, Fault fault, BranchData &branch) reason = BranchData::NoBranch; } - updateBranchData(reason, inst, target, branch); + updateBranchData(inst->id.threadId, reason, inst, target, branch); } void Execute::updateBranchData( + ThreadID tid, BranchData::Reason reason, MinorDynInstPtr inst, const TheISA::PCState &target, BranchData &branch) @@ -288,14 +299,15 @@ Execute::updateBranchData( if (reason != BranchData::NoBranch) { /* Bump up the stream sequence number on a real branch*/ if (BranchData::isStreamChange(reason)) - streamSeqNum++; + executeInfo[tid].streamSeqNum++; /* Branches (even mis-predictions) don't change the predictionSeqNum, * just the streamSeqNum */ - branch = BranchData(reason, streamSeqNum, + branch = BranchData(reason, tid, + executeInfo[tid].streamSeqNum, /* Maintaining predictionSeqNum if there's no inst is just a * courtesy and looks better on minorview */ - (inst->isBubble() ? lastPredictionSeqNum + (inst->isBubble() ? executeInfo[tid].lastPredictionSeqNum : inst->id.predictionSeqNum), target, inst); @@ -419,8 +431,9 @@ Execute::takeInterrupt(ThreadID thread_id, BranchData &branch) /* Assume that an interrupt *must* cause a branch. Assert this? */ - updateBranchData(BranchData::Interrupt, MinorDynInst::bubble(), - cpu.getContext(thread_id)->pcState(), branch); + updateBranchData(thread_id, BranchData::Interrupt, + MinorDynInst::bubble(), cpu.getContext(thread_id)->pcState(), + branch); } return interrupt != NoFault; @@ -506,9 +519,10 @@ cyclicIndexDec(unsigned int index, unsigned int cycle_size) } unsigned int -Execute::issue(bool only_issue_microops) +Execute::issue(ThreadID thread_id) { - const ForwardInstData *insts_in = getInput(); + const ForwardInstData *insts_in = getInput(thread_id); + ExecuteThreadInfo &thread = executeInfo[thread_id]; /* Early termination if we have no instructions */ if (!insts_in) @@ -534,8 +548,7 @@ Execute::issue(bool only_issue_microops) unsigned num_insts_discarded = 0; do { - MinorDynInstPtr inst = insts_in->insts[inputIndex]; - ThreadID thread_id = inst->id.threadId; + MinorDynInstPtr inst = insts_in->insts[thread.inputIndex]; Fault fault = inst->fault; bool discarded = false; bool issued_mem_ref = false; @@ -550,21 +563,12 @@ Execute::issue(bool only_issue_microops) " thread\n", *inst); issued = false; - } else if (inst->id.streamSeqNum != streamSeqNum) { + } else if (inst->id.streamSeqNum != thread.streamSeqNum) { DPRINTF(MinorExecute, "Discarding inst: %s as its stream" " state was unexpected, expected: %d\n", - *inst, streamSeqNum); + *inst, thread.streamSeqNum); issued = true; discarded = true; - } else if (fault == NoFault && only_issue_microops && - /* Is this anything other than a non-first microop */ - (!inst->staticInst->isMicroop() || - !inst->staticInst->isFirstMicroop())) - { - DPRINTF(MinorExecute, "Not issuing new non-microop inst: %s\n", - *inst); - - issued = false; } else { /* Try and issue an instruction into an FU, assume we didn't and * fix that in the loop */ @@ -598,9 +602,10 @@ Execute::issue(bool only_issue_microops) /* Mark the destinations for this instruction as * busy */ - scoreboard.markupInstDests(inst, cpu.curCycle() + + scoreboard[thread_id].markupInstDests(inst, cpu.curCycle() + Cycles(0), cpu.getContext(thread_id), false); + DPRINTF(MinorExecute, "Issuing %s to %d\n", inst->id, noCostFUIndex); inst->fuIndex = noCostFUIndex; inst->extraCommitDelay = Cycles(0); inst->extraCommitDelayExpr = NULL; @@ -608,7 +613,7 @@ Execute::issue(bool only_issue_microops) /* Push the instruction onto the inFlight queue so * it can be committed in order */ QueuedInst fu_inst(inst); - inFlightInsts->push(fu_inst); + thread.inFlightInsts->push(fu_inst); issued = true; @@ -644,8 +649,8 @@ Execute::issue(bool only_issue_microops) DPRINTF(MinorExecute, "Can't issue inst: %s as extra" " decoding is suppressing it\n", *inst); - } else if (!scoreboard.canInstIssue(inst, src_latencies, - cant_forward_from_fu_indices, + } else if (!scoreboard[thread_id].canInstIssue(inst, + src_latencies, cant_forward_from_fu_indices, cpu.curCycle(), cpu.getContext(thread_id))) { DPRINTF(MinorExecute, "Can't issue inst: %s yet\n", @@ -687,20 +692,20 @@ Execute::issue(bool only_issue_microops) * early */ if (allowEarlyMemIssue) { inst->instToWaitFor = - scoreboard.execSeqNumToWaitFor(inst, + scoreboard[thread_id].execSeqNumToWaitFor(inst, cpu.getContext(thread_id)); - if (lsq.getLastMemBarrier() > + if (lsq.getLastMemBarrier(thread_id) > inst->instToWaitFor) { DPRINTF(MinorExecute, "A barrier will" " cause a delay in mem ref issue of" " inst: %s until after inst" " %d(exec)\n", *inst, - lsq.getLastMemBarrier()); + lsq.getLastMemBarrier(thread_id)); inst->instToWaitFor = - lsq.getLastMemBarrier(); + lsq.getLastMemBarrier(thread_id); } else { DPRINTF(MinorExecute, "Memory ref inst:" " %s must wait for inst %d(exec)" @@ -714,7 +719,7 @@ Execute::issue(bool only_issue_microops) * queue to ensure in-order issue to the LSQ */ DPRINTF(MinorExecute, "Pushing mem inst: %s\n", *inst); - inFUMemInsts->push(fu_inst); + thread.inFUMemInsts->push(fu_inst); } /* Issue to FU */ @@ -725,7 +730,7 @@ Execute::issue(bool only_issue_microops) /* Mark the destinations for this instruction as * busy */ - scoreboard.markupInstDests(inst, cpu.curCycle() + + scoreboard[thread_id].markupInstDests(inst, cpu.curCycle() + fu->description.opLat + extra_dest_retire_lat + extra_assumed_lat, @@ -734,7 +739,7 @@ Execute::issue(bool only_issue_microops) /* Push the instruction onto the inFlight queue so * it can be committed in order */ - inFlightInsts->push(fu_inst); + thread.inFlightInsts->push(fu_inst); issued = true; } @@ -777,24 +782,24 @@ Execute::issue(bool only_issue_microops) DPRINTF(MinorExecute, "Reached inst issue limit\n"); } - inputIndex++; + thread.inputIndex++; DPRINTF(MinorExecute, "Stepping to next inst inputIndex: %d\n", - inputIndex); + thread.inputIndex); } /* Got to the end of a line */ - if (inputIndex == insts_in->width()) { - popInput(); + if (thread.inputIndex == insts_in->width()) { + popInput(thread_id); /* Set insts_in to null to force us to leave the surrounding * loop */ insts_in = NULL; if (processMoreThanOneInput) { DPRINTF(MinorExecute, "Wrapping\n"); - insts_in = getInput(); + insts_in = getInput(thread_id); } } - } while (insts_in && inputIndex < insts_in->width() && + } while (insts_in && thread.inputIndex < insts_in->width() && /* We still have instructions */ fu_index != numFuncUnits && /* Not visited all FUs */ issued && /* We've not yet failed to issue an instruction */ @@ -805,9 +810,9 @@ Execute::issue(bool only_issue_microops) } bool -Execute::tryPCEvents() +Execute::tryPCEvents(ThreadID thread_id) { - ThreadContext *thread = cpu.getContext(0); + ThreadContext *thread = cpu.getContext(thread_id); unsigned int num_pc_event_checks = 0; /* Handle PC events on instructions */ @@ -934,6 +939,11 @@ Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue, " there isn't space in the store buffer\n", *inst); completed_inst = false; + } else if (inst->isInst() && inst->staticInst->isQuiesce() + && !branch.isBubble()){ + /* This instruction can suspend, need to be able to communicate + * backwards, so no other branches may evaluate this cycle*/ + completed_inst = false; } else { ExecContext context(cpu, *cpu.threads[thread_id], *this, inst); @@ -962,7 +972,7 @@ Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue, /* Keep a copy of this instruction's predictionSeqNum just in case * we need to issue a branch without an instruction (such as an * interrupt) */ - lastPredictionSeqNum = inst->id.predictionSeqNum; + executeInfo[thread_id].lastPredictionSeqNum = inst->id.predictionSeqNum; /* Check to see if this instruction suspended the current thread. */ if (!inst->isFault() && @@ -971,17 +981,17 @@ Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue, !isInterrupted(thread_id)) /* Don't suspend if we have interrupts */ { - TheISA::PCState resume_pc = cpu.getContext(0)->pcState(); + TheISA::PCState resume_pc = cpu.getContext(thread_id)->pcState(); assert(resume_pc.microPC() == 0); DPRINTF(MinorInterrupt, "Suspending thread: %d from Execute" - " inst: %s\n", inst->id.threadId, *inst); + " inst: %s\n", thread_id, *inst); cpu.stats.numFetchSuspends++; - updateBranchData(BranchData::SuspendThread, inst, resume_pc, - branch); + updateBranchData(thread_id, BranchData::SuspendThread, inst, + resume_pc, branch); } } @@ -989,10 +999,12 @@ Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue, } void -Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) +Execute::commit(ThreadID thread_id, bool only_commit_microops, bool discard, + BranchData &branch) { Fault fault = NoFault; Cycles now = cpu.curCycle(); + ExecuteThreadInfo &ex_info = executeInfo[thread_id]; /** * Try and execute as many instructions from the end of FU pipelines as @@ -1030,13 +1042,13 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) * memCommitLimit */ unsigned int num_mem_refs_committed = 0; - if (only_commit_microops && !inFlightInsts->empty()) { + if (only_commit_microops && !ex_info.inFlightInsts->empty()) { DPRINTF(MinorInterrupt, "Only commit microops %s %d\n", - *(inFlightInsts->front().inst), - lastCommitWasEndOfMacroop); + *(ex_info.inFlightInsts->front().inst), + ex_info.lastCommitWasEndOfMacroop); } - while (!inFlightInsts->empty() && /* Some more instructions to process */ + while (!ex_info.inFlightInsts->empty() && /* Some more instructions to process */ !branch.isStreamChange() && /* No real branch */ fault == NoFault && /* No faults */ completed_inst && /* Still finding instructions to execute */ @@ -1046,10 +1058,10 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) if (only_commit_microops) { DPRINTF(MinorInterrupt, "Committing tail of insts before" " interrupt: %s\n", - *(inFlightInsts->front().inst)); + *(ex_info.inFlightInsts->front().inst)); } - QueuedInst *head_inflight_inst = &(inFlightInsts->front()); + QueuedInst *head_inflight_inst = &(ex_info.inFlightInsts->front()); InstSeqNum head_exec_seq_num = head_inflight_inst->inst->id.execSeqNum; @@ -1071,8 +1083,8 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) /* If we're just completing a macroop before an interrupt or drain, * can we stil commit another microop (rather than a memory response) * without crosing into the next full instruction? */ - bool can_commit_insts = !inFlightInsts->empty() && - !(only_commit_microops && lastCommitWasEndOfMacroop); + bool can_commit_insts = !ex_info.inFlightInsts->empty() && + !(only_commit_microops && ex_info.lastCommitWasEndOfMacroop); /* Can we find a mem response for this inst */ LSQ::LSQRequestPtr mem_response = @@ -1082,18 +1094,18 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) can_commit_insts); /* Test for PC events after every instruction */ - if (isInbetweenInsts() && tryPCEvents()) { - ThreadContext *thread = cpu.getContext(0); + if (isInbetweenInsts(thread_id) && tryPCEvents(thread_id)) { + ThreadContext *thread = cpu.getContext(thread_id); /* Branch as there was a change in PC */ - updateBranchData(BranchData::UnpredictedBranch, + updateBranchData(thread_id, BranchData::UnpredictedBranch, MinorDynInst::bubble(), thread->pcState(), branch); } else if (mem_response && num_mem_refs_committed < memoryCommitLimit) { /* Try to commit from the memory responses next */ - discard_inst = inst->id.streamSeqNum != streamSeqNum || - discard; + discard_inst = inst->id.streamSeqNum != + ex_info.streamSeqNum || discard; DPRINTF(MinorExecute, "Trying to commit mem response: %s\n", *inst); @@ -1102,7 +1114,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) if (discard_inst) { DPRINTF(MinorExecute, "Discarding mem inst: %s as its" " stream state was unexpected, expected: %d\n", - *inst, streamSeqNum); + *inst, ex_info.streamSeqNum); lsq.popResponse(mem_response); } else { @@ -1128,11 +1140,11 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) * For any other case, leave it to the normal instruction * issue below to handle them. */ - if (!inFUMemInsts->empty() && lsq.canRequest()) { + if (!ex_info.inFUMemInsts->empty() && lsq.canRequest()) { DPRINTF(MinorExecute, "Trying to commit from mem FUs\n"); const MinorDynInstPtr head_mem_ref_inst = - inFUMemInsts->front().inst; + ex_info.inFUMemInsts->front().inst; FUPipeline *fu = funcUnits[head_mem_ref_inst->fuIndex]; const MinorDynInstPtr &fu_inst = fu->front().inst; @@ -1141,7 +1153,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) if (!fu_inst->isBubble() && !fu_inst->inLSQ && fu_inst->canEarlyIssue && - streamSeqNum == fu_inst->id.streamSeqNum && + ex_info.streamSeqNum == fu_inst->id.streamSeqNum && head_exec_seq_num > fu_inst->instToWaitFor) { DPRINTF(MinorExecute, "Issuing mem ref early" @@ -1184,7 +1196,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) * actually at the end of its pipeline * Future instruction: handled above and only for * mem refs on their way to the LSQ */ - } else /* if (fu_inst_seq_num == head_exec_seq_num) */ { + } else if (fu_inst.inst->id == inst->id) { /* All instructions can be committed if they have the * right execSeqNum and there are no in-flight * mem insts before us */ @@ -1194,8 +1206,8 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) } if (try_to_commit) { - discard_inst = inst->id.streamSeqNum != streamSeqNum || - discard; + discard_inst = inst->id.streamSeqNum != + ex_info.streamSeqNum || discard; /* Is this instruction discardable as its streamSeqNum * doesn't match? */ @@ -1209,8 +1221,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) DPRINTF(MinorExecute, "Evaluating expression for" " extra commit delay inst: %s\n", *inst); - ThreadContext *thread = - cpu.getContext(inst->id.threadId); + ThreadContext *thread = cpu.getContext(thread_id); TimingExprEvalContext context(inst->staticInst, thread, NULL); @@ -1241,9 +1252,9 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) /* @todo Think about making lastMemBarrier be * MAX_UINT_64 to avoid using 0 as a marker value */ if (!inst->isFault() && inst->isMemRef() && - lsq.getLastMemBarrier() < + lsq.getLastMemBarrier(thread_id) < inst->id.execSeqNum && - lsq.getLastMemBarrier() != 0) + lsq.getLastMemBarrier(thread_id) != 0) { DPRINTF(MinorExecute, "Not committing inst: %s yet" " as there are incomplete barriers in flight\n", @@ -1269,8 +1280,10 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) * instruction wasn't the inFlightInsts head * but had already been committed, it would have * unstalled the pipeline before here */ - if (inst->fuIndex != noCostFUIndex) + if (inst->fuIndex != noCostFUIndex) { + DPRINTF(MinorExecute, "Unstalling %d for inst %s\n", inst->fuIndex, inst->id); funcUnits[inst->fuIndex]->stalled = false; + } } } } else { @@ -1286,7 +1299,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) if (discard_inst) { DPRINTF(MinorExecute, "Discarding inst: %s as its stream" " state was unexpected, expected: %d\n", - *inst, streamSeqNum); + *inst, ex_info.streamSeqNum); if (fault == NoFault) cpu.stats.numDiscardedOps++; @@ -1303,10 +1316,10 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) if (completed_inst && inst->isMemRef()) { /* The MemRef could have been discarded from the FU or the memory * queue, so just check an FU instruction */ - if (!inFUMemInsts->empty() && - inFUMemInsts->front().inst == inst) + if (!ex_info.inFUMemInsts->empty() && + ex_info.inFUMemInsts->front().inst == inst) { - inFUMemInsts->pop(); + ex_info.inFUMemInsts->pop(); } } @@ -1315,16 +1328,16 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) DPRINTF(MinorExecute, "Completed inst: %s\n", *inst); /* Got to the end of a full instruction? */ - lastCommitWasEndOfMacroop = inst->isFault() || + ex_info.lastCommitWasEndOfMacroop = inst->isFault() || inst->isLastOpInInst(); /* lastPredictionSeqNum is kept as a convenience to prevent its * value from changing too much on the minorview display */ - lastPredictionSeqNum = inst->id.predictionSeqNum; + ex_info.lastPredictionSeqNum = inst->id.predictionSeqNum; /* Finished with the inst, remove it from the inst queue and * clear its dependencies */ - inFlightInsts->pop(); + ex_info.inFlightInsts->pop(); /* Complete barriers in the LSQ/move to store buffer */ if (inst->isInst() && inst->staticInst->isMemBarrier()) { @@ -1333,7 +1346,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) lsq.completeMemBarrierInst(inst, committed_inst); } - scoreboard.clearInstDests(inst, inst->isMemRef()); + scoreboard[thread_id].clearInstDests(inst, inst->isMemRef()); } /* Handle per-cycle instruction counting */ @@ -1343,7 +1356,7 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) /* Don't show no cost instructions as having taken a commit * slot */ if (DTRACE(MinorTrace) && !is_no_cost_inst) - instsBeingCommitted.insts[num_insts_committed] = inst; + ex_info.instsBeingCommitted.insts[num_insts_committed] = inst; if (!is_no_cost_inst) num_insts_committed++; @@ -1369,124 +1382,112 @@ Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) } bool -Execute::isInbetweenInsts() const +Execute::isInbetweenInsts(ThreadID thread_id) const { - return lastCommitWasEndOfMacroop && + return executeInfo[thread_id].lastCommitWasEndOfMacroop && !lsq.accessesInFlight(); } void Execute::evaluate() { - inputBuffer.setTail(*inp.outputWire); + if (!inp.outputWire->isBubble()) + inputBuffer[inp.outputWire->threadId].setTail(*inp.outputWire); + BranchData &branch = *out.inputWire; - const ForwardInstData *insts_in = getInput(); + unsigned int num_issued = 0; /* Do all the cycle-wise activities for dcachePort here to potentially * free up input spaces in the LSQ's requests queue */ lsq.step(); - /* Has an interrupt been signalled? This may not be acted on - * straighaway so this is different from took_interrupt below */ + /* Check interrupts first. Will halt commit if interrupt found */ bool interrupted = false; - /* If there was an interrupt signalled, was it acted on now? */ - bool took_interrupt = false; - - if (cpu.getInterruptController(0)) { - /* This is here because it seems that after drainResume the - * interrupt controller isn't always set */ - interrupted = drainState == NotDraining && isInterrupted(0); - } else { - DPRINTF(MinorInterrupt, "No interrupt controller\n"); - } + ThreadID interrupt_tid = checkInterrupts(branch, interrupted); - unsigned int num_issued = 0; - - if (DTRACE(MinorTrace)) { - /* Empty the instsBeingCommitted for MinorTrace */ - instsBeingCommitted.bubbleFill(); - } - - /* THREAD threadId on isInterrupted */ - /* Act on interrupts */ - if (interrupted && isInbetweenInsts()) { - took_interrupt = takeInterrupt(0, branch); - /* Clear interrupted if no interrupt was actually waiting */ - interrupted = took_interrupt; - } - - if (took_interrupt) { - /* Do no commit/issue this cycle */ + if (interrupt_tid != InvalidThreadID) { + /* Signalling an interrupt this cycle, not issuing/committing from + * any other threads */ } else if (!branch.isBubble()) { /* It's important that this is here to carry Fetch1 wakeups to Fetch1 * without overwriting them */ DPRINTF(MinorInterrupt, "Execute skipping a cycle to allow old" " branch to complete\n"); } else { - if (interrupted) { - if (inFlightInsts->empty()) { - DPRINTF(MinorInterrupt, "Waiting but no insts\n"); + ThreadID commit_tid = getCommittingThread(); + + if (commit_tid != InvalidThreadID) { + ExecuteThreadInfo& commit_info = executeInfo[commit_tid]; + + DPRINTF(MinorExecute, "Attempting to commit [tid:%d]\n", + commit_tid); + /* commit can set stalled flags observable to issue and so *must* be + * called first */ + if (commit_info.drainState != NotDraining) { + if (commit_info.drainState == DrainCurrentInst) { + /* Commit only micro-ops, don't kill anything else */ + commit(commit_tid, true, false, branch); + + if (isInbetweenInsts(commit_tid)) + setDrainState(commit_tid, DrainHaltFetch); + + /* Discard any generated branch */ + branch = BranchData::bubble(); + } else if (commit_info.drainState == DrainAllInsts) { + /* Kill all instructions */ + while (getInput(commit_tid)) + popInput(commit_tid); + commit(commit_tid, false, true, branch); + } } else { - DPRINTF(MinorInterrupt, "Waiting for end of inst before" - " signalling interrupt\n"); + /* Commit micro-ops only if interrupted. Otherwise, commit + * anything you like */ + DPRINTF(MinorExecute, "Committing micro-ops for interrupt[tid:%d]\n", + commit_tid); + bool only_commit_microops = interrupted && + hasInterrupt(commit_tid); + commit(commit_tid, only_commit_microops, false, branch); } - } - /* commit can set stalled flags observable to issue and so *must* be - * called first */ - if (drainState != NotDraining) { - if (drainState == DrainCurrentInst) { - /* Commit only micro-ops, don't kill anything else */ - commit(true, false, branch); - - if (isInbetweenInsts()) - setDrainState(DrainHaltFetch); - - /* Discard any generated branch */ - branch = BranchData::bubble(); - } else if (drainState == DrainAllInsts) { - /* Kill all instructions */ - while (getInput()) - popInput(); - commit(false, true, branch); + /* Halt fetch, but don't do it until we have the current instruction in + * the bag */ + if (commit_info.drainState == DrainHaltFetch) { + updateBranchData(commit_tid, BranchData::HaltFetch, + MinorDynInst::bubble(), TheISA::PCState(0), branch); + + cpu.wakeupOnEvent(Pipeline::ExecuteStageId); + setDrainState(commit_tid, DrainAllInsts); } - } else { - /* Commit micro-ops only if interrupted. Otherwise, commit - * anything you like */ - commit(interrupted, false, branch); } - + ThreadID issue_tid = getIssuingThread(); /* This will issue merrily even when interrupted in the sure and * certain knowledge that the interrupt with change the stream */ - if (insts_in) - num_issued = issue(false); - } - - /* Halt fetch, but don't do it until we have the current instruction in - * the bag */ - if (drainState == DrainHaltFetch) { - updateBranchData(BranchData::HaltFetch, MinorDynInst::bubble(), - TheISA::PCState(0), branch); + if (issue_tid != InvalidThreadID) { + DPRINTF(MinorExecute, "Attempting to issue [tid:%d]\n", + issue_tid); + num_issued = issue(issue_tid); + } - cpu.wakeupOnEvent(Pipeline::ExecuteStageId); - setDrainState(DrainAllInsts); } - MinorDynInstPtr next_issuable_inst = NULL; + /* Run logic to step functional units + decide if we are active on the next + * clock cycle */ + std::vector<MinorDynInstPtr> next_issuable_insts; bool can_issue_next = false; - /* Find the next issuable instruction and see if it can be issued */ - if (getInput()) { - MinorDynInstPtr inst = getInput()->insts[inputIndex]; - - if (inst->isFault()) { - can_issue_next = true; - } else if (!inst->isBubble()) { - if (cpu.getContext(inst->id.threadId)->status() != - ThreadContext::Suspended) - { - next_issuable_inst = inst; + for (ThreadID tid = 0; tid < cpu.numThreads; tid++) { + /* Find the next issuable instruction for each thread and see if it can + be issued */ + if (getInput(tid)) { + unsigned int input_index = executeInfo[tid].inputIndex; + MinorDynInstPtr inst = getInput(tid)->insts[input_index]; + if (inst->isFault()) { + can_issue_next = true; + } else if (!inst->isBubble()) { + if (cpu.getContext(tid)->status() != ThreadContext::Suspended) { + next_issuable_insts.push_back(inst); + } } } } @@ -1494,53 +1495,56 @@ Execute::evaluate() bool becoming_stalled = true; /* Advance the pipelines and note whether they still need to be - * advanced */ + * advanced */ for (unsigned int i = 0; i < numFuncUnits; i++) { FUPipeline *fu = funcUnits[i]; - fu->advance(); - /* If we need to go again, the pipeline will have been left or set - * to be unstalled */ - if (fu->occupancy != 0 && !fu->stalled) + /* If we need to tick again, the pipeline will have been left or set + * to be unstalled */ + if (fu->occupancy !=0 && !fu->stalled) becoming_stalled = false; - /* Could we possibly issue the next instruction? This is quite - * an expensive test */ - if (next_issuable_inst && !fu->stalled && - scoreboard.canInstIssue(next_issuable_inst, - NULL, NULL, cpu.curCycle() + Cycles(1), - cpu.getContext(next_issuable_inst->id.threadId)) && - fu->provides(next_issuable_inst->staticInst->opClass())) - { - can_issue_next = true; + /* Could we possibly issue the next instruction from any thread? + * This is quite an expensive test and is only used to determine + * if the CPU should remain active, only run it if we aren't sure + * we are active next cycle yet */ + for (auto inst : next_issuable_insts) { + if (!fu->stalled && fu->provides(inst->staticInst->opClass()) && + scoreboard[inst->id.threadId].canInstIssue(inst, + NULL, NULL, cpu.curCycle() + Cycles(1), + cpu.getContext(inst->id.threadId))) { + can_issue_next = true; + break; + } } } bool head_inst_might_commit = false; /* Could the head in flight insts be committed */ - if (!inFlightInsts->empty()) { - const QueuedInst &head_inst = inFlightInsts->front(); + for (auto const &info : executeInfo) { + if (!info.inFlightInsts->empty()) { + const QueuedInst &head_inst = info.inFlightInsts->front(); - if (head_inst.inst->isNoCostInst()) { - head_inst_might_commit = true; - } else { - FUPipeline *fu = funcUnits[head_inst.inst->fuIndex]; - - /* Head inst is commitable */ - if ((fu->stalled && - fu->front().inst->id == head_inst.inst->id) || - lsq.findResponse(head_inst.inst)) - { + if (head_inst.inst->isNoCostInst()) { head_inst_might_commit = true; + } else { + FUPipeline *fu = funcUnits[head_inst.inst->fuIndex]; + if ((fu->stalled && + fu->front().inst->id == head_inst.inst->id) || + lsq.findResponse(head_inst.inst)) + { + head_inst_might_commit = true; + break; + } } } } DPRINTF(Activity, "Need to tick num issued insts: %s%s%s%s%s%s\n", (num_issued != 0 ? " (issued some insts)" : ""), - (becoming_stalled ? " (becoming stalled)" : "(not becoming stalled)"), + (becoming_stalled ? "(becoming stalled)" : "(not becoming stalled)"), (can_issue_next ? " (can issued next inst)" : ""), (head_inst_might_commit ? "(head inst might commit)" : ""), (lsq.needsToTick() ? " (LSQ needs to tick)" : ""), @@ -1568,36 +1572,54 @@ Execute::evaluate() cpu.activityRecorder->activity(); /* Make sure the input (if any left) is pushed */ - inputBuffer.pushTail(); + if (!inp.outputWire->isBubble()) + inputBuffer[inp.outputWire->threadId].pushTail(); } -void -Execute::wakeupFetch(BranchData::Reason reason) +ThreadID +Execute::checkInterrupts(BranchData& branch, bool& interrupted) { - BranchData branch; - assert(branch.isBubble()); - - /* THREAD thread id */ - ThreadContext *thread = cpu.getContext(0); - - /* Force a branch to the current PC (which should be the next inst.) to - * wake up Fetch1 */ - if (!branch.isStreamChange() /* No real branch already happened */) { - DPRINTF(MinorInterrupt, "Waking up Fetch (via Execute) by issuing" - " a branch: %s\n", thread->pcState()); + ThreadID tid = interruptPriority; + /* Evaluate interrupts in round-robin based upon service */ + do { + /* Has an interrupt been signalled? This may not be acted on + * straighaway so this is different from took_interrupt */ + bool thread_interrupted = false; + + if (FullSystem && cpu.getInterruptController(tid)) { + /* This is here because it seems that after drainResume the + * interrupt controller isn't always set */ + thread_interrupted = executeInfo[tid].drainState == NotDraining && + isInterrupted(tid); + interrupted = interrupted || thread_interrupted; + } else { + DPRINTF(MinorInterrupt, "No interrupt controller\n"); + } + DPRINTF(MinorInterrupt, "[tid:%d] thread_interrupted?=%d isInbetweenInsts?=%d\n", + tid, thread_interrupted, isInbetweenInsts(tid)); + /* Act on interrupts */ + if (thread_interrupted && isInbetweenInsts(tid)) { + if (takeInterrupt(tid, branch)) { + interruptPriority = tid; + return tid; + } + } else { + tid = (tid + 1) % cpu.numThreads; + } + } while (tid != interruptPriority); - assert(thread->pcState().microPC() == 0); + return InvalidThreadID; +} - updateBranchData(reason, - MinorDynInst::bubble(), thread->pcState(), branch); - } else { - DPRINTF(MinorInterrupt, "Already branching, no need for wakeup\n"); +bool +Execute::hasInterrupt(ThreadID thread_id) +{ + if (FullSystem && cpu.getInterruptController(thread_id)) { + return executeInfo[thread_id].drainState == NotDraining && + isInterrupted(thread_id); } - *out.inputWire = branch; - - /* Make sure we get ticked */ - cpu.wakeupOnEvent(Pipeline::ExecuteStageId); + return false; } void @@ -1606,10 +1628,10 @@ Execute::minorTrace() const std::ostringstream insts; std::ostringstream stalled; - instsBeingCommitted.reportData(insts); + executeInfo[0].instsBeingCommitted.reportData(insts); lsq.minorTrace(); - inputBuffer.minorTrace(); - scoreboard.minorTrace(); + inputBuffer[0].minorTrace(); + scoreboard[0].minorTrace(); /* Report functional unit stalling in one string */ unsigned int i = 0; @@ -1623,14 +1645,110 @@ Execute::minorTrace() const MINORTRACE("insts=%s inputIndex=%d streamSeqNum=%d" " stalled=%s drainState=%d isInbetweenInsts=%d\n", - insts.str(), inputIndex, streamSeqNum, stalled.str(), drainState, - isInbetweenInsts()); + insts.str(), executeInfo[0].inputIndex, executeInfo[0].streamSeqNum, + stalled.str(), executeInfo[0].drainState, isInbetweenInsts(0)); std::for_each(funcUnits.begin(), funcUnits.end(), std::mem_fun(&FUPipeline::minorTrace)); - inFlightInsts->minorTrace(); - inFUMemInsts->minorTrace(); + executeInfo[0].inFlightInsts->minorTrace(); + executeInfo[0].inFUMemInsts->minorTrace(); +} + +inline ThreadID +Execute::getCommittingThread() +{ + std::vector<ThreadID> priority_list; + + switch (cpu.threadPolicy) { + case Enums::SingleThreaded: + return 0; + case Enums::RoundRobin: + priority_list = cpu.roundRobinPriority(commitPriority); + break; + case Enums::Random: + priority_list = cpu.randomPriority(); + break; + default: + panic("Invalid thread policy"); + } + + for (auto tid : priority_list) { + ExecuteThreadInfo &ex_info = executeInfo[tid]; + bool can_commit_insts = !ex_info.inFlightInsts->empty(); + if (can_commit_insts) { + QueuedInst *head_inflight_inst = &(ex_info.inFlightInsts->front()); + MinorDynInstPtr inst = head_inflight_inst->inst; + + can_commit_insts = can_commit_insts && + (!inst->inLSQ || (lsq.findResponse(inst) != NULL)); + + if (!inst->inLSQ) { + bool can_transfer_mem_inst = false; + if (!ex_info.inFUMemInsts->empty() && lsq.canRequest()) { + const MinorDynInstPtr head_mem_ref_inst = + ex_info.inFUMemInsts->front().inst; + FUPipeline *fu = funcUnits[head_mem_ref_inst->fuIndex]; + const MinorDynInstPtr &fu_inst = fu->front().inst; + can_transfer_mem_inst = + !fu_inst->isBubble() && + fu_inst->id.threadId == tid && + !fu_inst->inLSQ && + fu_inst->canEarlyIssue && + inst->id.execSeqNum > fu_inst->instToWaitFor; + } + + bool can_execute_fu_inst = inst->fuIndex == noCostFUIndex; + if (can_commit_insts && !can_transfer_mem_inst && + inst->fuIndex != noCostFUIndex) + { + QueuedInst& fu_inst = funcUnits[inst->fuIndex]->front(); + can_execute_fu_inst = !fu_inst.inst->isBubble() && + fu_inst.inst->id == inst->id; + } + + can_commit_insts = can_commit_insts && + (can_transfer_mem_inst || can_execute_fu_inst); + } + } + + + if (can_commit_insts) { + commitPriority = tid; + return tid; + } + } + + return InvalidThreadID; +} + +inline ThreadID +Execute::getIssuingThread() +{ + std::vector<ThreadID> priority_list; + + switch (cpu.threadPolicy) { + case Enums::SingleThreaded: + return 0; + case Enums::RoundRobin: + priority_list = cpu.roundRobinPriority(issuePriority); + break; + case Enums::Random: + priority_list = cpu.randomPriority(); + break; + default: + panic("Invalid thread scheduling policy."); + } + + for (auto tid : priority_list) { + if (cpu.getContext(tid)->status() == ThreadContext::Active && + getInput(tid)) { + issuePriority = tid; + return tid; + } + } + + return InvalidThreadID; } void @@ -1638,11 +1756,10 @@ Execute::drainResume() { DPRINTF(Drain, "MinorExecute drainResume\n"); - setDrainState(NotDraining); + for (ThreadID tid = 0; tid < cpu.numThreads; tid++) { + setDrainState(tid, NotDraining); + } - /* Wakeup fetch and keep the pipeline running until that branch takes - * effect */ - wakeupFetch(BranchData::WakeupFetch); cpu.wakeupOnEvent(Pipeline::ExecuteStageId); } @@ -1671,10 +1788,10 @@ std::ostream &operator <<(std::ostream &os, Execute::DrainState state) } void -Execute::setDrainState(DrainState state) +Execute::setDrainState(ThreadID thread_id, DrainState state) { - DPRINTF(Drain, "setDrainState: %s\n", state); - drainState = state; + DPRINTF(Drain, "setDrainState[%d]: %s\n", thread_id, state); + executeInfo[thread_id].drainState = state; } unsigned int @@ -1682,29 +1799,39 @@ Execute::drain() { DPRINTF(Drain, "MinorExecute drain\n"); - if (drainState == NotDraining) { - cpu.wakeupOnEvent(Pipeline::ExecuteStageId); + for (ThreadID tid = 0; tid < cpu.numThreads; tid++) { + if (executeInfo[tid].drainState == NotDraining) { + cpu.wakeupOnEvent(Pipeline::ExecuteStageId); - /* Go to DrainCurrentInst if we're between microops - * or waiting on an unbufferable memory operation. - * Otherwise we can go straight to DrainHaltFetch - */ - if (isInbetweenInsts()) - setDrainState(DrainHaltFetch); - else - setDrainState(DrainCurrentInst); + /* Go to DrainCurrentInst if we're between microops + * or waiting on an unbufferable memory operation. + * Otherwise we can go straight to DrainHaltFetch + */ + if (isInbetweenInsts(tid)) + setDrainState(tid, DrainHaltFetch); + else + setDrainState(tid, DrainCurrentInst); + } } - return (isDrained() ? 0 : 1); } bool Execute::isDrained() { - return drainState == DrainAllInsts && - inputBuffer.empty() && - inFlightInsts->empty() && - lsq.isDrained(); + if (!lsq.isDrained()) + return false; + + for (ThreadID tid = 0; tid < cpu.numThreads; tid++) { + if (executeInfo[tid].drainState != DrainAllInsts || + !inputBuffer[tid].empty() || + !executeInfo[tid].inFlightInsts->empty()) { + + return false; + } + } + + return true; } Execute::~Execute() @@ -1712,13 +1839,14 @@ Execute::~Execute() for (unsigned int i = 0; i < numFuncUnits; i++) delete funcUnits[i]; - delete inFlightInsts; + for (ThreadID tid = 0; tid < cpu.numThreads; tid++) + delete executeInfo[tid].inFlightInsts; } bool Execute::instIsRightStream(MinorDynInstPtr inst) { - return inst->id.streamSeqNum == streamSeqNum; + return inst->id.streamSeqNum == executeInfo[inst->id.threadId].streamSeqNum; } bool @@ -1726,8 +1854,8 @@ Execute::instIsHeadInst(MinorDynInstPtr inst) { bool ret = false; - if (!inFlightInsts->empty()) - ret = inFlightInsts->front().inst->id == inst->id; + if (!executeInfo[inst->id.threadId].inFlightInsts->empty()) + ret = executeInfo[inst->id.threadId].inFlightInsts->front().inst->id == inst->id; return ret; } |