| /* |
| * Copyright (c) 2013-2014, 2016-2017 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 + reg.flatIndex(); |
| ret = true; |
| break; |
| case VecPredRegClass: |
| 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()); |
| } |
| |
| } |