📄 issue.cc
字号:
// (b) The oldest instruction is ops-ready // => ISSUE_IQ // unsigned n_ready = IQNumReadyInstructions() + LSQ->ready_count(); if (n_ready == 0) { if ((IQNumInstructions() == 0) && (LSQ->count() == 0)) { floss_state.issue_end_cause[0] = ISSUE_NO_INSN; } else { // Find the oldest instruction.... evil_inst = IQOldestInstruction(); if (evil_inst.notnull()) { if (LSQ->oldest().notnull()) { if (LSQ->oldest()->seq < evil_inst->seq) { evil_inst = LSQ->oldest(); } else { // existing IQ inst is older } } else { // existing IQ inst older than non-existant LSQ } } else { // LSQ oldest since IQ doesn't exist evil_inst = LSQ->oldest(); // since we've already decided that we MUST have at least // one instruction, and it's not in the IQ... assert(evil_inst.notnull()); } if (evil_inst->ops_ready()) { floss_state.issue_end_cause[0] = ISSUE_IQ; } else { // instruction is waiting on dependencies... // make a note of each fu-type that is producing the data // that this instruction is waiting for floss_state.issue_end_cause[0] = ISSUE_DEPS; if (!find_idep_to_blame(evil_inst, 0)) floss_state.issue_end_cause[0] = ISSUE_IQ; } } // If the store-ready-queue is empty, then we're done if (sb_rq_iterator.isnull()) { delete[] iq_rq_iterator; delete[] hp_rq_it_list; return; } } // // Main issue loop // // This loop executes once for each instruction that is considered // for issue. Instructions can issue from the IQ, LSQ, or Storebuffer. // The process is: // // (0) Extract the status of the ready-lists for each IQ // (1) Extract the oldest instruction from each of the three // ready queues // (2) Choose the oldest instruction of these three and indicate // the selection with the "source" variable // (3) Attempt to issue the instruction // (4) Collect statistical data on what happened in #3 // (5) Check the status of the current queue's ready-list, then // rotate to the next queue // (6) Check to see if the issue process is complete for this cycle // // // STEP 0: // // This should help our speed through the IQ-selection logic // IQList done_list(numIQueues); // we don't check BW here, since all should have BW available // at the start of the issue process for (int i = 0; i < numIQueues; ++i) if (IQ[i]->ready_count() == 0) done_list.markDone(i); done_with_iq = done_list.allDone(); bool *hp_done = new bool[numIQueues]; // // Point to the issuable lists // Populate the HP thread lists too... // for (int q = 0; q < numIQueues; ++q) { if (! done_list.done(q)) { if (prioritize_issue) { BaseIQ::rq_iterator i; // Walk the list for each queue, putting the RQ iterators to // the high-priority thread into the list for this queue i = IQ[q]->issuable_list(); while (i.notnull()) { // add RQ iterator to list if it's the HP thread if ((*i)->inst->thread_number == hp_thread) hp_rq_it_list[q].push_back(i); i = i.next(); } } // save this for non-prioritized issue iq_rq_iterator[q] = IQ[q]->issuable_list(); } hp_done[q] = false; } do { InstSeqNum seq_num = 0; DynInst *inst = 0; unsigned thread = 0; Tick pred_issue_cycle = 0; Tick ready_ts = 0; Tick dispatch_ts = 0; OpClass op_class = No_OpClass; bool issued = false; unsigned issue_events = 0; InstSeqNum fetch_seq = 0; BaseIQ::iterator iq_it = 0, lsq_it = 0; StoreBuffer::iterator sb_it = 0; InstSeqNum oldestIQInstSeq = 0; int oldestIQIndex = -1; // // STEP #1: Convert from RQ iterator to Queue iterator // // If the RQ is empty, signal that we don't need to continue // looking into that structure... // // We also catch the case where we are out of instructions // if (!done_with_iq) { // // PRIORITIZED ISSUE: // set iq_rq_iterator[current_iq] to the next HP instruction, // if it exists, otherwise, // if (prioritize_issue) { // if there are elements in the high-priority list if (hp_rq_it_list[current_iq].size()) { // get the next instruction iq_rq_iterator[current_iq] = hp_rq_it_list[current_iq].front(); // remove it from this list (still in RQ) hp_rq_it_list[current_iq].pop_front(); } else { if (!hp_done[current_iq]) { // otherwise, look through the remaining RQ entries... iq_rq_iterator[current_iq] = IQ[current_iq]->issuable_list(); hp_done[current_iq] = true; } else { // we've already set up the iq_rq_iterator[] entry // don't mess with it again } } } // // Try to grab an iterator to the first ready IQ instruction // we can issue // if (iq_rq_iterator[current_iq].notnull()) { if (IQ[current_iq]->issue_bw()) { // This iterator points to the first instruction in the // ready-queue iq_it = *iq_rq_iterator[current_iq]; } else { // this cluster ran out of bandwidth SET_FIRST_FLOSS_CAUSE(floss_state.issue_end_cause[0], ISSUE_BW); done_with_iq = done_list.markDone(current_iq); if (!done_with_iq) { // get the next index... // false return indicates none available#ifndef NDEBUG bool avail = #endif done_list.next(current_iq); assert(avail); // point to the first instruction in this ready-queue iq_it = *iq_rq_iterator[current_iq]; } } } else { // // No ready instructions in this cluster... // if (IQ[current_iq]->issue_bw() == 0) { // we have run out of bandwidth, but there were no // instructions to issue, so... SET_FIRST_FLOSS_CAUSE(floss_state.issue_end_cause[0], ISSUE_NO_INSN); } else { // Ready-List is empty, but BW is available...: // Either: (1) we have no instructions in the // issue window, // (2) or the existing instructions // have dependants if (IQ[current_iq]->iw_count() == 0) { SET_FIRST_FLOSS_CAUSE(floss_state.issue_end_cause[0], ISSUE_NO_INSN); } else { // Empty ready-list, insts available ==> DEPS if (floss_state.issue_end_cause[0] == ISSUE_CAUSE_NOT_SET) { floss_state.issue_end_cause[0] = ISSUE_DEPS; evil_inst = IQ[current_iq]->oldest(); if (!find_idep_to_blame(evil_inst, 0)) floss_state.issue_end_cause[0] = ISSUE_IQ; } } } done_with_iq = done_list.markDone(current_iq); if (!done_with_iq) { // get the next index... // false return indicates none available#ifndef NDEBUG bool avail = #endif done_list.next(current_iq); assert(avail); // point to the first instruction in this ready-queue iq_it = *iq_rq_iterator[current_iq]; } } // // Find the IQ with the oldest instruction that we can issue // if (!done_with_iq) { for (int i = 0; i < numIQueues; ++i) { if (!done_list.done(i)) { BaseIQ::iterator j = *iq_rq_iterator[i]; if (j->seq < oldestIQInstSeq || oldestIQIndex == -1) { oldestIQInstSeq = j->seq; oldestIQIndex = i; } } }#if ISSUE_OLDEST current_iq = oldestIQIndex; // point to the first instruction in this ready-queue iq_it = *iq_rq_iterator[current_iq];#endif } // Handle the case where we need a 1:1 relationship between // IQ's and FU pools if (numIQueues == numFUPools) current_fu_pool = current_iq; } if (!done_with_lsq) { if (lsq_rq_iterator.notnull()) lsq_it = *lsq_rq_iterator; else done_with_lsq = true; } if (!done_with_sb) { if (sb_rq_iterator.notnull()) sb_it = *sb_rq_iterator; else done_with_sb = true; } // // Early out... // if (done_with_iq && done_with_lsq && done_with_sb) break; // // STEP #2: Select the source for our next issued instruction // source = none; if (!done_with_iq) { seq_num = iq_it->seq; source = iq; if (!done_with_lsq && (seq_num > lsq_it->seq)) { seq_num = lsq_it->seq; source = lsq; } if (!done_with_sb && (seq_num > sb_it->seq)) { seq_num = sb_it->seq; source = sb; } } else if (!done_with_lsq) { seq_num = lsq_it->seq; source = lsq; if (!done_with_sb && (seq_num > sb_it->seq)) { seq_num = sb_it->seq; source = sb; } } else if (!done_with_sb) { seq_num = sb_it->seq; source = sb; } if (issue_break && (issue_break == seq_num)) issue_breakpoint(); // // INORDER ISSUE: If seq_num is not the sequence number we expect, // stall issue. // if (inorder_issue) { if ((seq_num != expected_inorder_seq_num) && (source != sb)) { // [DAG] Do sequence numbers wrap around? They're long longs, // so they shouldn't! assert(expected_inorder_seq_num < seq_num); break; } } // // STEP #3: Attempt to issue the instruction // switch (source) { case iq: op_class = iq_it->opClass(); inst = iq_it->inst; thread = iq_it->inst->thread_number; if (issued_by_thread[thread] < issue_bandwidth[thread]) { issued = iq_issue(iq_it, current_fu_pool);#if DUMP_ISSUE string s; iq_it->inst->dump(s); issued_list.push_back(issue_info(iq_it->inst->fetch_seq, current_iq, s));#endif } else { issued = false; SET_FIRST_FLOSS_CAUSE(floss_state.issue_end_cause[0], ISSUE_BW); } if (issued) { pred_issue_cycle = iq_it->pred_issue_cycle; ready_ts = iq_it->ready_timestamp; dispatch_ts = iq_it->dispatch_timestamp; fetch_seq = inst->fetch_seq; if (iq_it->ea_comp) issue_events = PipeTrace::AddressGen; // // We have to let everyone else know that an // instruction has issued // for (unsigned i = 0; i < numIQueues; ++i) if (i != current_iq) IQ[i]->inform_issue(iq_it);#if 0 if (chainGenerator) chainGenerator->removeInstruction(iq_it->rob_entry);#endif // Inform the IQ that this instruction has issued // and update the RQ iterator for this queue iq_rq_iterator[current_iq] = IQ[current_iq]->issue(iq_rq_iterator[current_iq]); // Clear ROB pointer to this inst iq_it->rob_entry->iq_entry = 0; // If this entry has a pointer to an LSQ entry... if (iq_it->lsq_entry.notnull()) { // clear the pointer from the LSQ entry to this IQ entry iq_it->lsq_entry->iq_entry = 0; // clear the pointer to the LSQ entry iq_it->lsq_entry = 0; } ++iq_count; // we've removed one ready instrction from the IQ --n_ready; } else { // this instruction didn't issue, so point to the next one iq_rq_iterator[current_iq] = iq_rq_iterator[current_iq].next(); } break; case lsq: op_class = lsq_it->opClass(); inst = lsq_it->inst; thread = lsq_it->inst->thread_number; if ((lsq_it->seq > oldestIQInstSeq) && (oldestIQInstSeq != 0)) { ++lsqInversion; current_iq = (current_iq + 1) % numIQueues; source = none; break; // don't issue it... // we'll get the ordering right next time... } if (issued_by_thread[thread] < issue_bandwidth[thread]) { // LSQ portions issue into the same pool as the EA-comp if we // have a 1:1 relationship between IQ's and FU Pools if (numIQueues == numFUPools) issued = lsq_issue(lsq_it, lsq_it->rob_entry->queue_num); else issued = lsq_issue(lsq_it, current_fu_pool); } else { issued = false; SET_FIRST_FLOSS_CAUSE(floss_state.issue_end_cause[0], ISSUE_BW); } if (issued) { pred_issue_cycle = lsq_it->pred_issue_cycle; ready_ts = lsq_it->ready_timestamp; dispatch_ts = lsq_it->dispatch_timestamp; fetch_seq = inst->fetch_seq; if (lsq_it->mem_result != MA_HIT) issue_events = PipeTrace::CacheMiss; // Clean up this instruction lsq_rq_iterator = LSQ->issue(lsq_rq_iterator); // // We have to let the IQ's know that an // instruction has issued // for (unsigned i = 0; i < numIQueues; ++i) IQ[i]->inform_issue(lsq_it); // Clear IQ & ROB pointers to this inst lsq_it->rob_entry->lsq_entry = 0; // ROB ++lsq_count; // we've removed one ready instrction from the LSQ --n_ready; } else { // If it was a store (EA comp part) it should have issued // ==> doesn't work if we are limiting issue bandwidth... if (dcacheInterface->isBlocked()) done_with_lsq = true; else lsq_rq_iterator = lsq_rq_iterator.next(); } break; case sb: // If we have a 1:1 correspondence between FU's and // IQ's, then we issue to the FU pool that matches the // EA-Comp portion of this store if (numIQueues == numFUPools) issue_current_fupool_for_sb = sb_it->queue_num; issued = sb_issue(sb_it, issue_current_fupool_for_sb); thread = sb_it->thread_number(); op_class = MemWriteOp; if (issued) { fetch_seq = sb_it->fetch_seq; // Remove current element, and return pointer to next sb_rq_iterator = storebuffer->issue(sb_rq_iterator); // Mark a copy storebuffer as complete, since we don't // need a response currently if (sb_it->isCopy) { storebuffer->completeStore(sb_it); } // each load attempts to issue to the "next" pool issue_current_fupool_for_sb = (++issue_current_fupool_for_sb % numFUPools); ++sb_count; } else { // If we can't issue this one... don't worry about the // rest... done_with_sb = true; } break; case none: // // We get here when NO instruction tries to issue on this pass // through the loop. This can happen when we hit an empty IQ // before we empty all the IQ's // break; } // // STEP #4: // // Do book-keeping only if we tried to issue from IQ or LSQ on this // iteration // if ((source == iq) || (source == lsq)) { if (issued) { // // Pipetrace this instruction if it issued... // // This doesn't quite work the way we might want it: // - The fourth parameter (latency) can't be filled in, // since we don't actually know how long it will take to // service the cache miss... // - The fifth parameter (longest latency event) is designed // to indicate which of several events took the longest... // since our model doesn't generate multiple events, this // isn't used here either. // if (ptrace) { unsigned load_latency = 0; unsigned longest_event = 0; ptrace->moveInst(inst, PipeTrace::Issue, issue_events, load_latency, longest_event); }#ifdef DEBUG_ISSUE std::cerr << "Issued instruction " << fetch_seq; if (inst != 0) { std::cerr << " 0x" << std::hex << inst->PC << std::dec << " (seq " << inst->fetch_seq << " spec mode " << inst->spec_mode << "): "; std::cerr << inst->staticInst->disassemble(inst->xc->PC); } std::cerr << std::endl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -