#ifndef __CPU_OZONE_LW_BACK_END_HH__ #define __CPU_OZONE_LW_BACK_END_HH__ #include #include #include #include #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/functional/functional.hh" #include "mem/mem_interface.hh" #include "mem/mem_req.hh" #include "sim/eventq.hh" class ExecContext; template class OzoneThreadState; template class LWBackEnd { public: typedef OzoneThreadState 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 d2i; typename TimeBuffer::wire instsToDispatch; TimeBuffer i2e; typename TimeBuffer::wire instsToExecute; TimeBuffer e2c; TimeBuffer numInstsToWB; TimeBuffer *comm; typename TimeBuffer::wire toIEW; typename TimeBuffer::wire fromCommit; class TrapEvent : public Event { private: LWBackEnd *be; public: TrapEvent(LWBackEnd *_be); void process(); const char *description(); }; /** 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. */ LWBackEnd *be; bool dcacheMiss; public: /** Constructs a load writeback event. */ LdWritebackEvent(DynInstPtr &_inst, LWBackEnd *be); /** Processes writeback event. */ virtual void process(); /** Returns the description of the writeback event. */ virtual const char *description(); void setDcacheMiss() { dcacheMiss = true; be->addDcacheMiss(inst); } }; LWBackEnd(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 setXC(ExecContext *xc_ptr) { xc = xc_ptr; } void setThreadState(Thread *thread_ptr) { thread = thread_ptr; } void setCommBuffer(TimeBuffer *_comm); void tick(); void squash(); void generateXCEvent() { xcSquash = true; } void squashFromXC(); void squashFromTrap(); void checkInterrupts(); bool trapSquash; bool xcSquash; template Fault read(MemReqPtr &req, T &data, int load_idx); template Fault write(MemReqPtr &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; } void fetchFault(Fault &fault); 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 addDcacheMiss(DynInstPtr &inst) { waitingMemOps.insert(inst->seqNum); numWaitingMemOps++; DPRINTF(BE, "Adding a Dcache miss mem op [sn:%lli], total %i\n", inst->seqNum, numWaitingMemOps); } void removeDcacheMiss(DynInstPtr &inst) { assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); waitingMemOps.erase(inst->seqNum); numWaitingMemOps--; DPRINTF(BE, "Removing a Dcache miss mem op [sn:%lli], total %i\n", inst->seqNum, numWaitingMemOps); } void addWaitingMemOp(DynInstPtr &inst) { waitingMemOps.insert(inst->seqNum); numWaitingMemOps++; DPRINTF(BE, "Adding a waiting mem op [sn:%lli], total %i\n", inst->seqNum, numWaitingMemOps); } void removeWaitingMemOp(DynInstPtr &inst) { assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); waitingMemOps.erase(inst->seqNum); numWaitingMemOps--; DPRINTF(BE, "Removing a waiting mem op [sn:%lli], total %i\n", inst->seqNum, numWaitingMemOps); } void instToCommit(DynInstPtr &inst); private: void generateTrapEvent(Tick latency = 0); void handleFault(Fault &fault, Tick latency = 0); void updateStructures(); void dispatchInsts(); void dispatchStall(); void checkDispatchStatus(); void executeInsts(); void commitInsts(); void addToLSQ(DynInstPtr &inst); void writebackInsts(); bool commitInst(int inst_num); void squash(const InstSeqNum &sn); void squashDueToBranch(DynInstPtr &inst); void squashDueToMemViolation(DynInstPtr &inst); void squashDueToMemBlocked(DynInstPtr &inst); void updateExeInstStats(DynInstPtr &inst); void updateComInstStats(DynInstPtr &inst); public: FullCPU *cpu; FrontEnd *frontEnd; ExecContext *xc; Thread *thread; enum Status { Running, Idle, DcacheMissStall, DcacheMissComplete, Blocked, TrapPending }; Status status; Status dispatchStatus; Status commitStatus; Counter funcExeInst; private: // typedef typename Impl::InstQueue InstQueue; // InstQueue IQ; typedef typename Impl::LdstQueue LdstQueue; LdstQueue LSQ; public: RenameTable commitRenameTable; RenameTable renameTable; private: class DCacheCompletionEvent : public Event { private: LWBackEnd *be; public: DCacheCompletionEvent(LWBackEnd *_be); virtual void process(); virtual const char *description(); }; friend class DCacheCompletionEvent; DCacheCompletionEvent cacheCompletionEvent; MemInterface *dcacheInterface; MemReqPtr memReq; // General back end width. Used if the more specific isn't given. int width; // Dispatch width. int dispatchWidth; int numDispatchEntries; int dispatchSize; int waitingInsts; 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; std::set waitingMemOps; typedef std::set::iterator MemIt; int numWaitingMemOps; unsigned maxOutstandingMemOps; bool squashPending; InstSeqNum squashSeqNum; Addr squashNextPC; Fault faultFromFetch; bool fetchHasFault; private: struct pqCompare { bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const { return lhs->seqNum > rhs->seqNum; } }; typedef typename std::priority_queue, pqCompare> ReadyInstQueue; ReadyInstQueue exeList; typedef typename std::list::iterator InstListIt; std::list instList; std::list waitingList; std::list replayList; std::list 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 template Fault LWBackEnd::read(MemReqPtr &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 template Fault LWBackEnd::write(MemReqPtr &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_LW_BACK_END_HH__