/* * 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 */ /** * @file * * All the fun of executing instructions from Decode and sending branch/new * instruction stream info. to Fetch1. */ #ifndef __CPU_MINOR_EXECUTE_HH__ #define __CPU_MINOR_EXECUTE_HH__ #include "cpu/minor/buffers.hh" #include "cpu/minor/cpu.hh" #include "cpu/minor/func_unit.hh" #include "cpu/minor/lsq.hh" #include "cpu/minor/pipe_data.hh" #include "cpu/minor/scoreboard.hh" namespace Minor { /** Execute stage. Everything apart from fetching and decoding instructions. * The LSQ lives here too. */ class Execute : public Named { protected: /** Input port carrying instructions from Decode */ Latch::Output inp; /** Input port carrying stream changes to Fetch1 */ Latch::Input out; /** Pointer back to the containing CPU */ MinorCPU &cpu; /** Number of instructions that can be issued per cycle */ unsigned int issueLimit; /** Number of memory ops that can be issued per cycle */ unsigned int memoryIssueLimit; /** Number of instructions that can be committed per cycle */ unsigned int commitLimit; /** Number of memory instructions that can be committed per cycle */ unsigned int memoryCommitLimit; /** If true, more than one input line can be processed each cycle if * there is room to execute more instructions than taken from the first * line */ bool processMoreThanOneInput; /** Descriptions of the functional units we want to generate */ MinorFUPool &fuDescriptions; /** Number of functional units to produce */ unsigned int numFuncUnits; /** Longest latency of any FU, useful for setting up the activity * recoder */ Cycles longestFuLatency; /** Modify instruction trace times on commit */ bool setTraceTimeOnCommit; /** Modify instruction trace times on issue */ bool setTraceTimeOnIssue; /** Allow mem refs to leave their FUs before reaching the head * of the in flight insts queue if their dependencies are met */ bool allowEarlyMemIssue; /** The FU index of the non-existent costless FU for instructions * which pass the MinorDynInst::isNoCostInst test */ unsigned int noCostFUIndex; /** Dcache port to pass on to the CPU. Execute owns this */ LSQ lsq; /** Scoreboard of instruction dependencies */ std::vector scoreboard; /** The execution functional units */ std::vector funcUnits; public: /* Public for Pipeline to be able to pass it to Decode */ std::vector> inputBuffer; protected: /** Stage cycle-by-cycle state */ /** State that drain passes through (in order). On a drain request, * Execute transitions into either DrainCurrentInst (if between * microops) or DrainHaltFetch. * * Note that Execute doesn't actually have * a 'Drained' state, only * an indication that it's currently draining and isDrained that can't * tell if there are insts still in the pipeline leading up to * Execute */ enum DrainState { NotDraining, /* Not draining, possibly running */ DrainCurrentInst, /* Draining to end of inst/macroop */ DrainHaltFetch, /* Halting Fetch after completing current inst */ DrainAllInsts /* Discarding all remaining insts */ }; struct ExecuteThreadInfo { /** Constructor */ ExecuteThreadInfo(unsigned int insts_committed) : inputIndex(0), lastCommitWasEndOfMacroop(true), instsBeingCommitted(insts_committed), streamSeqNum(InstId::firstStreamSeqNum), lastPredictionSeqNum(InstId::firstPredictionSeqNum), drainState(NotDraining) { } ExecuteThreadInfo(const ExecuteThreadInfo& other) : inputIndex(other.inputIndex), lastCommitWasEndOfMacroop(other.lastCommitWasEndOfMacroop), instsBeingCommitted(other.instsBeingCommitted), streamSeqNum(other.streamSeqNum), lastPredictionSeqNum(other.lastPredictionSeqNum), drainState(other.drainState) { } /** In-order instructions either in FUs or the LSQ */ Queue > *inFlightInsts; /** Memory ref instructions still in the FUs */ Queue > *inFUMemInsts; /** Index that we've completed upto in getInput data. We can say we're * popInput when this equals getInput()->width() */ unsigned int inputIndex; /** The last commit was the end of a full instruction so an interrupt * can safely happen */ bool lastCommitWasEndOfMacroop; /** Structure for reporting insts currently being processed/retired * for MinorTrace */ ForwardInstData instsBeingCommitted; /** Source of sequence number for instuction streams. Increment this and * pass to fetch whenever an instruction stream needs to be changed. * For any more complicated behaviour (e.g. speculation) there'll need * to be another plan. */ InstSeqNum streamSeqNum; /** A prediction number for use where one isn't available from an * instruction. This is harvested from committed instructions. * This isn't really needed as the streamSeqNum will change on * a branch, but it minimises disruption in stream identification */ InstSeqNum lastPredictionSeqNum; /** State progression for draining NotDraining -> ... -> DrainAllInsts */ DrainState drainState; }; std::vector executeInfo; ThreadID interruptPriority; ThreadID issuePriority; ThreadID commitPriority; protected: friend std::ostream &operator <<(std::ostream &os, DrainState state); /** Get a piece of data to work on from the inputBuffer, or 0 if there * is no data. */ const ForwardInstData *getInput(ThreadID tid); /** Pop an element off the input buffer, if there are any */ void popInput(ThreadID tid); /** Generate Branch data based (into branch) on an observed (or not) * change in PC while executing an instruction. * Also handles branch prediction information within the inst. */ void tryToBranch(MinorDynInstPtr inst, Fault fault, BranchData &branch); /** Actually create a branch to communicate to Fetch1/Fetch2 and, * if that is a stream-changing branch update the streamSeqNum */ void updateBranchData(ThreadID tid, BranchData::Reason reason, MinorDynInstPtr inst, const TheISA::PCState &target, BranchData &branch); /** Handle extracting mem ref responses from the memory queues and * completing the associated instructions. * Fault is an output and will contain any fault caused (and already * invoked by the function) * Sets branch to any branch generated by the instruction. */ void handleMemResponse(MinorDynInstPtr inst, LSQ::LSQRequestPtr response, BranchData &branch, Fault &fault); /** Execute a memory reference instruction. This calls initiateAcc on * the instruction which will then call writeMem or readMem to issue a * memory access to the LSQ. * Returns true if the instruction was executed rather than stalled * because of a lack of LSQ resources and false otherwise. * branch is set to any branch raised by the instruction. * failed_predicate is set to false if the instruction passed its * predicate and so will access memory or true if the instruction * *failed* its predicate and is now complete. * fault is set if any non-NoFault fault is raised. * Any faults raised are actually invoke-d by this function. */ bool executeMemRefInst(MinorDynInstPtr inst, BranchData &branch, bool &failed_predicate, Fault &fault); /** Has an interrupt been raised */ bool isInterrupted(ThreadID thread_id) const; /** Are we between instructions? Can we be interrupted? */ bool isInbetweenInsts(ThreadID thread_id) const; /** Act on an interrupt. Returns true if an interrupt was actually * signalled and invoked */ bool takeInterrupt(ThreadID thread_id, BranchData &branch); /** Try and issue instructions from the inputBuffer */ unsigned int issue(ThreadID thread_id); /** Try to act on PC-related events. Returns true if any were * executed */ bool tryPCEvents(ThreadID thread_id); /** Do the stats handling and instruction count and PC event events * related to the new instruction/op counts */ void doInstCommitAccounting(MinorDynInstPtr inst); /** Check all threads for possible interrupts. If interrupt is taken, * returns the tid of the thread. interrupted is set if any thread * has an interrupt, irrespective of if it is taken */ ThreadID checkInterrupts(BranchData& branch, bool& interrupted); /** Checks if a specific thread has an interrupt. No action is taken. * this is used for determining if a thread should only commit microops */ bool hasInterrupt(ThreadID thread_id); /** Commit a single instruction. Returns true if the instruction being * examined was completed (fully executed, discarded, or initiated a * memory access), false if there is still some processing to do. * fu_index is the index of the functional unit this instruction is * being executed in into for funcUnits * If early_memory_issue is true then this is an early execution * of a mem ref and so faults will not be processed. * If the return value is true: * fault is set if a fault happened, * branch is set to indicate any branch that occurs * committed is set to true if this instruction is committed * (and so needs to be traced and accounted for) * completed_mem_issue is set if the instruction was a * memory access that was issued */ bool commitInst(MinorDynInstPtr inst, bool early_memory_issue, BranchData &branch, Fault &fault, bool &committed, bool &completed_mem_issue); /** Try and commit instructions from the ends of the functional unit * pipelines. * If only_commit_microops is true then only commit upto the * end of the currect full instruction. * If discard is true then discard all instructions rather than * committing. * branch is set to any branch raised during commit. */ void commit(ThreadID thread_id, bool only_commit_microops, bool discard, BranchData &branch); /** Set the drain state (with useful debugging messages) */ void setDrainState(ThreadID thread_id, DrainState state); /** Use the current threading policy to determine the next thread to * decode from. */ ThreadID getCommittingThread(); ThreadID getIssuingThread(); public: Execute(const std::string &name_, MinorCPU &cpu_, MinorCPUParams ¶ms, Latch::Output inp_, Latch::Input out_); ~Execute(); public: /** Returns the DcachePort owned by this Execute to pass upwards */ MinorCPU::MinorCPUPort &getDcachePort(); /** To allow ExecContext to find the LSQ */ LSQ &getLSQ() { return lsq; } /** Does the given instruction have the right stream sequence number * to be committed? */ bool instIsRightStream(MinorDynInstPtr inst); /** Returns true if the given instruction is at the head of the * inFlightInsts instruction queue */ bool instIsHeadInst(MinorDynInstPtr inst); /** Pass on input/buffer data to the output if you can */ void evaluate(); void minorTrace() const; /** After thread suspension, has Execute been drained of in-flight * instructions and memory accesses. */ bool isDrained(); /** Like the drain interface on SimObject */ unsigned int drain(); void drainResume(); }; } #endif /* __CPU_MINOR_EXECUTE_HH__ */