| /* |
| * 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<ForwardInstData>::Output inp; |
| |
| /** Input port carrying stream changes to Fetch1 */ |
| Latch<BranchData>::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> scoreboard; |
| |
| /** The execution functional units */ |
| std::vector<FUPipeline *> funcUnits; |
| |
| public: /* Public for Pipeline to be able to pass it to Decode */ |
| std::vector<InputBuffer<ForwardInstData>> 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<QueuedInst, ReportTraitsAdaptor<QueuedInst> > *inFlightInsts; |
| |
| /** Memory ref instructions still in the FUs */ |
| Queue<QueuedInst, ReportTraitsAdaptor<QueuedInst> > *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<ExecuteThreadInfo> 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<ForwardInstData>::Output inp_, |
| Latch<BranchData>::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__ */ |