| /* | |
| * Copyright (c) 2013-2014, 2016 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/scoreboard.hh" | |
| #include "arch/registers.hh" | |
| #include "cpu/reg_class.hh" | |
| #include "debug/MinorScoreboard.hh" | |
| #include "debug/MinorTiming.hh" | |
| namespace Minor | |
| { | |
| bool | |
| Scoreboard::findIndex(const RegId& reg, Index &scoreboard_index) | |
| { | |
| bool ret = false; | |
| if (reg.isZeroReg()) { | |
| /* Don't bother with the zero register */ | |
| ret = false; | |
| } else { | |
| switch (reg.classValue()) | |
| { | |
| case IntRegClass: | |
| scoreboard_index = reg.index(); | |
| ret = true; | |
| break; | |
| case FloatRegClass: | |
| scoreboard_index = TheISA::NumIntRegs + TheISA::NumCCRegs + | |
| reg.index(); | |
| ret = true; | |
| break; | |
| case VecRegClass: | |
| scoreboard_index = TheISA::NumIntRegs + TheISA::NumCCRegs + | |
| TheISA::NumFloatRegs + reg.index(); | |
| ret = true; | |
| break; | |
| case VecElemClass: | |
| scoreboard_index = TheISA::NumIntRegs + TheISA::NumCCRegs + | |
| TheISA::NumFloatRegs + TheISA::NumVecRegs + reg.index(); | |
| ret = true; | |
| break; | |
| case CCRegClass: | |
| scoreboard_index = TheISA::NumIntRegs + reg.index(); | |
| ret = true; | |
| break; | |
| case MiscRegClass: | |
| /* Don't bother with Misc registers */ | |
| ret = false; | |
| break; | |
| default: | |
| panic("Unknown register class: %d", | |
| static_cast<int>(reg.classValue())); | |
| } | |
| } | |
| return ret; | |
| } | |
| /** Flatten a RegId, irrespective of what reg type it's pointing to */ | |
| static RegId | |
| flattenRegIndex(const RegId& reg, ThreadContext *thread_context) | |
| { | |
| return thread_context->flattenRegId(reg); | |
| } | |
| void | |
| Scoreboard::markupInstDests(MinorDynInstPtr inst, Cycles retire_time, | |
| ThreadContext *thread_context, bool mark_unpredictable) | |
| { | |
| if (inst->isFault()) | |
| return; | |
| StaticInstPtr staticInst = inst->staticInst; | |
| unsigned int num_dests = staticInst->numDestRegs(); | |
| /** Mark each destination register */ | |
| for (unsigned int dest_index = 0; dest_index < num_dests; | |
| dest_index++) | |
| { | |
| RegId reg = flattenRegIndex( | |
| staticInst->destRegIdx(dest_index), thread_context); | |
| Index index; | |
| if (findIndex(reg, index)) { | |
| if (mark_unpredictable) | |
| numUnpredictableResults[index]++; | |
| inst->flatDestRegIdx[dest_index] = reg; | |
| numResults[index]++; | |
| returnCycle[index] = retire_time; | |
| /* We should be able to rely on only being given accending | |
| * execSeqNums, but sanity check */ | |
| if (inst->id.execSeqNum > writingInst[index]) { | |
| writingInst[index] = inst->id.execSeqNum; | |
| fuIndices[index] = inst->fuIndex; | |
| } | |
| DPRINTF(MinorScoreboard, "Marking up inst: %s" | |
| " regIndex: %d final numResults: %d returnCycle: %d\n", | |
| *inst, index, numResults[index], returnCycle[index]); | |
| } else { | |
| /* Use ZeroReg to mark invalid/untracked dests */ | |
| inst->flatDestRegIdx[dest_index] = RegId(IntRegClass, | |
| TheISA::ZeroReg); | |
| } | |
| } | |
| } | |
| InstSeqNum | |
| Scoreboard::execSeqNumToWaitFor(MinorDynInstPtr inst, | |
| ThreadContext *thread_context) | |
| { | |
| InstSeqNum ret = 0; | |
| if (inst->isFault()) | |
| return ret; | |
| StaticInstPtr staticInst = inst->staticInst; | |
| unsigned int num_srcs = staticInst->numSrcRegs(); | |
| for (unsigned int src_index = 0; src_index < num_srcs; src_index++) { | |
| RegId reg = flattenRegIndex(staticInst->srcRegIdx(src_index), | |
| thread_context); | |
| unsigned short int index; | |
| if (findIndex(reg, index)) { | |
| if (writingInst[index] > ret) | |
| ret = writingInst[index]; | |
| } | |
| } | |
| DPRINTF(MinorScoreboard, "Inst: %s depends on execSeqNum: %d\n", | |
| *inst, ret); | |
| return ret; | |
| } | |
| void | |
| Scoreboard::clearInstDests(MinorDynInstPtr inst, bool clear_unpredictable) | |
| { | |
| if (inst->isFault()) | |
| return; | |
| StaticInstPtr staticInst = inst->staticInst; | |
| unsigned int num_dests = staticInst->numDestRegs(); | |
| /** Mark each destination register */ | |
| for (unsigned int dest_index = 0; dest_index < num_dests; | |
| dest_index++) | |
| { | |
| const RegId& reg = inst->flatDestRegIdx[dest_index]; | |
| Index index; | |
| if (findIndex(reg, index)) { | |
| if (clear_unpredictable && numUnpredictableResults[index] != 0) | |
| numUnpredictableResults[index] --; | |
| numResults[index] --; | |
| if (numResults[index] == 0) { | |
| returnCycle[index] = Cycles(0); | |
| writingInst[index] = 0; | |
| fuIndices[index] = -1; | |
| } | |
| DPRINTF(MinorScoreboard, "Clearing inst: %s" | |
| " regIndex: %d final numResults: %d\n", | |
| *inst, index, numResults[index]); | |
| } | |
| } | |
| } | |
| bool | |
| Scoreboard::canInstIssue(MinorDynInstPtr inst, | |
| const std::vector<Cycles> *src_reg_relative_latencies, | |
| const std::vector<bool> *cant_forward_from_fu_indices, | |
| Cycles now, ThreadContext *thread_context) | |
| { | |
| /* Always allow fault to be issued */ | |
| if (inst->isFault()) | |
| return true; | |
| StaticInstPtr staticInst = inst->staticInst; | |
| unsigned int num_srcs = staticInst->numSrcRegs(); | |
| /* Default to saying you can issue */ | |
| bool ret = true; | |
| unsigned int num_relative_latencies = 0; | |
| Cycles default_relative_latency = Cycles(0); | |
| /* Where relative latencies are given, the default is the last | |
| * one as that allows the rel. lat. list to be shorted than the | |
| * number of src. regs */ | |
| if (src_reg_relative_latencies && | |
| src_reg_relative_latencies->size() != 0) | |
| { | |
| num_relative_latencies = src_reg_relative_latencies->size(); | |
| default_relative_latency = (*src_reg_relative_latencies) | |
| [num_relative_latencies-1]; | |
| } | |
| /* For each source register, find the latest result */ | |
| unsigned int src_index = 0; | |
| while (src_index < num_srcs && /* More registers */ | |
| ret /* Still possible */) | |
| { | |
| RegId reg = flattenRegIndex(staticInst->srcRegIdx(src_index), | |
| thread_context); | |
| unsigned short int index; | |
| if (findIndex(reg, index)) { | |
| bool cant_forward = fuIndices[index] != 1 && | |
| cant_forward_from_fu_indices && | |
| index < cant_forward_from_fu_indices->size() && | |
| (*cant_forward_from_fu_indices)[index]; | |
| Cycles relative_latency = (cant_forward ? Cycles(0) : | |
| (src_index >= num_relative_latencies ? | |
| default_relative_latency : | |
| (*src_reg_relative_latencies)[src_index])); | |
| if (returnCycle[index] > (now + relative_latency) || | |
| numUnpredictableResults[index] != 0) | |
| { | |
| ret = false; | |
| } | |
| } | |
| src_index++; | |
| } | |
| if (DTRACE(MinorTiming)) { | |
| if (ret && num_srcs > num_relative_latencies && | |
| num_relative_latencies != 0) | |
| { | |
| DPRINTF(MinorTiming, "Warning, inst: %s timing extra decode has" | |
| " more src. regs: %d than relative latencies: %d\n", | |
| staticInst->disassemble(0), num_srcs, num_relative_latencies); | |
| } | |
| } | |
| return ret; | |
| } | |
| void | |
| Scoreboard::minorTrace() const | |
| { | |
| std::ostringstream result_stream; | |
| bool printed_element = false; | |
| unsigned int i = 0; | |
| while (i < numRegs) { | |
| unsigned short int num_results = numResults[i]; | |
| unsigned short int num_unpredictable_results = | |
| numUnpredictableResults[i]; | |
| if (!(num_results == 0 && num_unpredictable_results == Cycles(0))) { | |
| if (printed_element) | |
| result_stream << ','; | |
| result_stream << '(' << i << ',' | |
| << num_results << '/' | |
| << num_unpredictable_results << '/' | |
| << returnCycle[i] << '/' | |
| << writingInst[i] << ')'; | |
| printed_element = true; | |
| } | |
| i++; | |
| } | |
| MINORTRACE("busy=%s\n", result_stream.str()); | |
| } | |
| } |