| |
| #ifndef __CPU_OZONE_BACK_END_HH__ |
| #define __CPU_OZONE_BACK_END_HH__ |
| |
| #include <list> |
| #include <queue> |
| #include <string> |
| |
| #include "arch/faults.hh" |
| #include "base/timebuf.hh" |
| #include "cpu/inst_seq.hh" |
| #include "cpu/ozone/rename_table.hh" |
| #include "cpu/ozone/thread_state.hh" |
| #include "mem/request.hh" |
| #include "sim/eventq.hh" |
| |
| class ThreadContext; |
| |
| template <class Impl> |
| class OzoneThreadState; |
| |
| template <class Impl> |
| class BackEnd |
| { |
| public: |
| typedef OzoneThreadState<Impl> Thread; |
| |
| typedef typename Impl::Params Params; |
| typedef typename Impl::DynInst DynInst; |
| typedef typename Impl::DynInstPtr DynInstPtr; |
| typedef typename Impl::FullCPU FullCPU; |
| typedef typename Impl::FrontEnd FrontEnd; |
| typedef typename Impl::FullCPU::CommStruct CommStruct; |
| |
| struct SizeStruct { |
| int size; |
| }; |
| |
| typedef SizeStruct DispatchToIssue; |
| typedef SizeStruct IssueToExec; |
| typedef SizeStruct ExecToCommit; |
| typedef SizeStruct Writeback; |
| |
| TimeBuffer<DispatchToIssue> d2i; |
| typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; |
| TimeBuffer<IssueToExec> i2e; |
| typename TimeBuffer<IssueToExec>::wire instsToExecute; |
| TimeBuffer<ExecToCommit> e2c; |
| TimeBuffer<Writeback> numInstsToWB; |
| |
| TimeBuffer<CommStruct> *comm; |
| typename TimeBuffer<CommStruct>::wire toIEW; |
| typename TimeBuffer<CommStruct>::wire fromCommit; |
| |
| class InstQueue { |
| enum queue { |
| NonSpec, |
| IQ, |
| ToBeScheduled, |
| ReadyList, |
| ReplayList |
| }; |
| struct pqCompare { |
| bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const |
| { |
| return lhs->seqNum > rhs->seqNum; |
| } |
| }; |
| public: |
| InstQueue(Params *params); |
| |
| std::string name() const; |
| |
| void regStats(); |
| |
| void setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue); |
| |
| void setBE(BackEnd *_be) { be = _be; } |
| |
| void insert(DynInstPtr &inst); |
| |
| void scheduleReadyInsts(); |
| |
| void scheduleNonSpec(const InstSeqNum &sn); |
| |
| DynInstPtr getReadyInst(); |
| |
| void commit(const InstSeqNum &sn) {} |
| |
| void squash(const InstSeqNum &sn); |
| |
| int wakeDependents(DynInstPtr &inst); |
| |
| /** Tells memory dependence unit that a memory instruction needs to be |
| * rescheduled. It will re-execute once replayMemInst() is called. |
| */ |
| void rescheduleMemInst(DynInstPtr &inst); |
| |
| /** Re-executes all rescheduled memory instructions. */ |
| void replayMemInst(DynInstPtr &inst); |
| |
| /** Completes memory instruction. */ |
| void completeMemInst(DynInstPtr &inst); |
| |
| void violation(DynInstPtr &inst, DynInstPtr &violation) { } |
| |
| bool isFull() { return numInsts >= size; } |
| |
| void dumpInsts(); |
| |
| private: |
| bool find(queue q, typename std::list<DynInstPtr>::iterator it); |
| BackEnd *be; |
| TimeBuffer<IssueToExec> *i2e; |
| typename TimeBuffer<IssueToExec>::wire numIssued; |
| typedef typename std::list<DynInstPtr> InstList; |
| typedef typename std::list<DynInstPtr>::iterator InstListIt; |
| typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; |
| // Not sure I need the IQ list; it just needs to be a count. |
| InstList iq; |
| InstList toBeScheduled; |
| InstList readyList; |
| InstList nonSpec; |
| InstList replayList; |
| ReadyInstQueue readyQueue; |
| public: |
| int size; |
| int numInsts; |
| int width; |
| |
| Stats::VectorDistribution<> occ_dist; |
| |
| Stats::Vector<> inst_count; |
| Stats::Vector<> peak_inst_count; |
| Stats::Scalar<> empty_count; |
| Stats::Scalar<> current_count; |
| Stats::Scalar<> fullCount; |
| |
| Stats::Formula occ_rate; |
| Stats::Formula avg_residency; |
| Stats::Formula empty_rate; |
| Stats::Formula full_rate; |
| }; |
| |
| /** LdWriteback event for a load completion. */ |
| class LdWritebackEvent : public Event { |
| private: |
| /** Instruction that is writing back data to the register file. */ |
| DynInstPtr inst; |
| /** Pointer to IEW stage. */ |
| BackEnd *be; |
| |
| public: |
| /** Constructs a load writeback event. */ |
| LdWritebackEvent(DynInstPtr &_inst, BackEnd *be); |
| |
| /** Processes writeback event. */ |
| virtual void process(); |
| /** Returns the description of the writeback event. */ |
| virtual const char *description(); |
| }; |
| |
| BackEnd(Params *params); |
| |
| std::string name() const; |
| |
| void regStats(); |
| |
| void setCPU(FullCPU *cpu_ptr) |
| { cpu = cpu_ptr; } |
| |
| void setFrontEnd(FrontEnd *front_end_ptr) |
| { frontEnd = front_end_ptr; } |
| |
| void setTC(ThreadContext *tc_ptr) |
| { tc = tc_ptr; } |
| |
| void setThreadState(Thread *thread_ptr) |
| { thread = thread_ptr; } |
| |
| void setCommBuffer(TimeBuffer<CommStruct> *_comm); |
| |
| void tick(); |
| void squash(); |
| void squashFromTC(); |
| bool tcSquash; |
| |
| template <class T> |
| Fault read(RequestPtr req, T &data, int load_idx); |
| |
| template <class T> |
| Fault write(RequestPtr req, T &data, int store_idx); |
| |
| Addr readCommitPC() { return commitPC; } |
| |
| Addr commitPC; |
| |
| bool robEmpty() { return instList.empty(); } |
| |
| bool isFull() { return numInsts >= numROBEntries; } |
| bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } |
| |
| /** Tells memory dependence unit that a memory instruction needs to be |
| * rescheduled. It will re-execute once replayMemInst() is called. |
| */ |
| void rescheduleMemInst(DynInstPtr &inst) |
| { IQ.rescheduleMemInst(inst); } |
| |
| /** Re-executes all rescheduled memory instructions. */ |
| void replayMemInst(DynInstPtr &inst) |
| { IQ.replayMemInst(inst); } |
| |
| /** Completes memory instruction. */ |
| void completeMemInst(DynInstPtr &inst) |
| { IQ.completeMemInst(inst); } |
| |
| void fetchFault(Fault &fault); |
| |
| private: |
| void updateStructures(); |
| void dispatchInsts(); |
| void dispatchStall(); |
| void checkDispatchStatus(); |
| void scheduleReadyInsts(); |
| void executeInsts(); |
| void commitInsts(); |
| void addToIQ(DynInstPtr &inst); |
| void addToLSQ(DynInstPtr &inst); |
| void instToCommit(DynInstPtr &inst); |
| void writebackInsts(); |
| bool commitInst(int inst_num); |
| void squash(const InstSeqNum &sn); |
| void squashDueToBranch(DynInstPtr &inst); |
| void squashDueToMemBlocked(DynInstPtr &inst); |
| void updateExeInstStats(DynInstPtr &inst); |
| void updateComInstStats(DynInstPtr &inst); |
| |
| public: |
| FullCPU *cpu; |
| |
| FrontEnd *frontEnd; |
| |
| ThreadContext *tc; |
| |
| Thread *thread; |
| |
| enum Status { |
| Running, |
| Idle, |
| DcacheMissStall, |
| DcacheMissComplete, |
| Blocked |
| }; |
| |
| Status status; |
| |
| Status dispatchStatus; |
| |
| Counter funcExeInst; |
| |
| private: |
| // typedef typename Impl::InstQueue InstQueue; |
| |
| InstQueue IQ; |
| |
| typedef typename Impl::LdstQueue LdstQueue; |
| |
| LdstQueue LSQ; |
| public: |
| RenameTable<Impl> commitRenameTable; |
| |
| RenameTable<Impl> renameTable; |
| private: |
| class DCacheCompletionEvent : public Event |
| { |
| private: |
| BackEnd *be; |
| |
| public: |
| DCacheCompletionEvent(BackEnd *_be); |
| |
| virtual void process(); |
| virtual const char *description(); |
| }; |
| |
| friend class DCacheCompletionEvent; |
| |
| DCacheCompletionEvent cacheCompletionEvent; |
| |
| MemInterface *dcacheInterface; |
| |
| Request *memReq; |
| |
| // General back end width. Used if the more specific isn't given. |
| int width; |
| |
| // Dispatch width. |
| int dispatchWidth; |
| int numDispatchEntries; |
| int dispatchSize; |
| |
| int issueWidth; |
| |
| // Writeback width |
| int wbWidth; |
| |
| // Commit width |
| int commitWidth; |
| |
| /** Index into queue of instructions being written back. */ |
| unsigned wbNumInst; |
| |
| /** Cycle number within the queue of instructions being written |
| * back. Used in case there are too many instructions writing |
| * back at the current cycle and writesbacks need to be scheduled |
| * for the future. See comments in instToCommit(). |
| */ |
| unsigned wbCycle; |
| |
| int numROBEntries; |
| int numInsts; |
| |
| bool squashPending; |
| InstSeqNum squashSeqNum; |
| Addr squashNextPC; |
| |
| Fault faultFromFetch; |
| |
| private: |
| typedef typename std::list<DynInstPtr>::iterator InstListIt; |
| |
| std::list<DynInstPtr> instList; |
| std::list<DynInstPtr> dispatch; |
| std::list<DynInstPtr> writeback; |
| |
| int latency; |
| |
| int squashLatency; |
| |
| bool exactFullStall; |
| |
| bool fetchRedirect[Impl::MaxThreads]; |
| |
| // number of cycles stalled for D-cache misses |
| /* Stats::Scalar<> dcacheStallCycles; |
| Counter lastDcacheStall; |
| */ |
| Stats::Vector<> rob_cap_events; |
| Stats::Vector<> rob_cap_inst_count; |
| Stats::Vector<> iq_cap_events; |
| Stats::Vector<> iq_cap_inst_count; |
| // total number of instructions executed |
| Stats::Vector<> exe_inst; |
| Stats::Vector<> exe_swp; |
| Stats::Vector<> exe_nop; |
| Stats::Vector<> exe_refs; |
| Stats::Vector<> exe_loads; |
| Stats::Vector<> exe_branches; |
| |
| Stats::Vector<> issued_ops; |
| |
| // total number of loads forwaded from LSQ stores |
| Stats::Vector<> lsq_forw_loads; |
| |
| // total number of loads ignored due to invalid addresses |
| Stats::Vector<> inv_addr_loads; |
| |
| // total number of software prefetches ignored due to invalid addresses |
| Stats::Vector<> inv_addr_swpfs; |
| // ready loads blocked due to memory disambiguation |
| Stats::Vector<> lsq_blocked_loads; |
| |
| Stats::Scalar<> lsqInversion; |
| |
| Stats::Vector<> n_issued_dist; |
| Stats::VectorDistribution<> issue_delay_dist; |
| |
| Stats::VectorDistribution<> queue_res_dist; |
| /* |
| Stats::Vector<> stat_fu_busy; |
| Stats::Vector2d<> stat_fuBusy; |
| Stats::Vector<> dist_unissued; |
| Stats::Vector2d<> stat_issued_inst_type; |
| |
| Stats::Formula misspec_cnt; |
| Stats::Formula misspec_ipc; |
| Stats::Formula issue_rate; |
| Stats::Formula issue_stores; |
| Stats::Formula issue_op_rate; |
| Stats::Formula fu_busy_rate; |
| Stats::Formula commit_stores; |
| Stats::Formula commit_ipc; |
| Stats::Formula commit_ipb; |
| Stats::Formula lsq_inv_rate; |
| */ |
| Stats::Vector<> writeback_count; |
| Stats::Vector<> producer_inst; |
| Stats::Vector<> consumer_inst; |
| Stats::Vector<> wb_penalized; |
| |
| Stats::Formula wb_rate; |
| Stats::Formula wb_fanout; |
| Stats::Formula wb_penalized_rate; |
| |
| // total number of instructions committed |
| Stats::Vector<> stat_com_inst; |
| Stats::Vector<> stat_com_swp; |
| Stats::Vector<> stat_com_refs; |
| Stats::Vector<> stat_com_loads; |
| Stats::Vector<> stat_com_membars; |
| Stats::Vector<> stat_com_branches; |
| |
| Stats::Distribution<> n_committed_dist; |
| |
| Stats::Scalar<> commit_eligible_samples; |
| Stats::Vector<> commit_eligible; |
| |
| Stats::Scalar<> ROB_fcount; |
| Stats::Formula ROB_full_rate; |
| |
| Stats::Vector<> ROB_count; // cumulative ROB occupancy |
| Stats::Formula ROB_occ_rate; |
| Stats::VectorDistribution<> ROB_occ_dist; |
| public: |
| void dumpInsts(); |
| }; |
| |
| template <class Impl> |
| template <class T> |
| Fault |
| BackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) |
| { |
| /* memReq->reset(addr, sizeof(T), flags); |
| |
| // translate to physical address |
| Fault fault = cpu->translateDataReadReq(memReq); |
| |
| // if we have a cache, do cache access too |
| if (fault == NoFault && dcacheInterface) { |
| memReq->cmd = Read; |
| memReq->completionEvent = NULL; |
| memReq->time = curTick; |
| memReq->flags &= ~INST_READ; |
| MemAccessResult result = dcacheInterface->access(memReq); |
| |
| // Ugly hack to get an event scheduled *only* if the access is |
| // a miss. We really should add first-class support for this |
| // at some point. |
| if (result != MA_HIT && dcacheInterface->doEvents()) { |
| // Fix this hack for keeping funcExeInst correct with loads that |
| // are executed twice. |
| --funcExeInst; |
| |
| memReq->completionEvent = &cacheCompletionEvent; |
| lastDcacheStall = curTick; |
| // unscheduleTickEvent(); |
| // status = DcacheMissStall; |
| DPRINTF(OzoneCPU, "Dcache miss stall!\n"); |
| } else { |
| // do functional access |
| fault = thread->mem->read(memReq, data); |
| |
| } |
| } |
| */ |
| /* |
| if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) |
| recordEvent("Uncached Read"); |
| */ |
| return LSQ.read(req, data, load_idx); |
| } |
| |
| template <class Impl> |
| template <class T> |
| Fault |
| BackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) |
| { |
| /* |
| memReq->reset(addr, sizeof(T), flags); |
| |
| // translate to physical address |
| Fault fault = cpu->translateDataWriteReq(memReq); |
| |
| if (fault == NoFault && dcacheInterface) { |
| memReq->cmd = Write; |
| memcpy(memReq->data,(uint8_t *)&data,memReq->size); |
| memReq->completionEvent = NULL; |
| memReq->time = curTick; |
| memReq->flags &= ~INST_READ; |
| MemAccessResult result = dcacheInterface->access(memReq); |
| |
| // Ugly hack to get an event scheduled *only* if the access is |
| // a miss. We really should add first-class support for this |
| // at some point. |
| if (result != MA_HIT && dcacheInterface->doEvents()) { |
| memReq->completionEvent = &cacheCompletionEvent; |
| lastDcacheStall = curTick; |
| // unscheduleTickEvent(); |
| // status = DcacheMissStall; |
| DPRINTF(OzoneCPU, "Dcache miss stall!\n"); |
| } |
| } |
| |
| if (res && (fault == NoFault)) |
| *res = memReq->result; |
| */ |
| /* |
| if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) |
| recordEvent("Uncached Write"); |
| */ |
| return LSQ.write(req, data, store_idx); |
| } |
| |
| #endif // __CPU_OZONE_BACK_END_HH__ |