diff --git a/arch/alpha/isa/decoder.isa b/arch/alpha/isa/decoder.isa index e09673269..905ace4e1 100644 --- a/arch/alpha/isa/decoder.isa +++ b/arch/alpha/isa/decoder.isa @@ -73,7 +73,7 @@ decode OPCODE default Unknown::unknown() { uint64_t tmp = write_result; // see stq_c Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; - }}, mem_flags = LOCKED); + }}, mem_flags = LOCKED, inst_flags = IsNonSpeculative); 0x2f: stq_c({{ Mem.uq = Ra; }}, {{ uint64_t tmp = write_result; @@ -85,7 +85,7 @@ decode OPCODE default Unknown::unknown() { // mailbox access, and we don't update the // result register at all. Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; - }}, mem_flags = LOCKED); + }}, mem_flags = LOCKED, inst_flags = IsNonSpeculative); } format IntegerOperate { @@ -591,8 +591,8 @@ decode OPCODE default Unknown::unknown() { 0x02e: fcmovle({{ Fc = (Fa <= 0) ? Fb : Fc; }}); 0x02f: fcmovgt({{ Fc = (Fa > 0) ? Fb : Fc; }}); - 0x024: mt_fpcr({{ FPCR = Fa.uq; }}); - 0x025: mf_fpcr({{ Fa.uq = FPCR; }}); + 0x024: mt_fpcr({{ FPCR = Fa.uq; }}, IsSerializing, IsSerializeBefore); + 0x025: mf_fpcr({{ Fa.uq = FPCR; }}, IsSerializing, IsSerializeBefore); } } @@ -623,7 +623,7 @@ decode OPCODE default Unknown::unknown() { #else Ra = curTick; #endif - }}); + }}, IsNonSpeculative); // All of the barrier instructions below do nothing in // their execute() methods (hence the empty code blocks). @@ -641,8 +641,8 @@ decode OPCODE default Unknown::unknown() { // a barrier on integer and FP traps. "EXCB is thus a // superset of TRAPB." (Alpha ARM, Sec 4.11.4) We treat // them the same though. - 0x0000: trapb({{ }}, IsSerializing, No_OpClass); - 0x0400: excb({{ }}, IsSerializing, No_OpClass); + 0x0000: trapb({{ }}, IsSerializing, IsSerializeBefore, No_OpClass); + 0x0400: excb({{ }}, IsSerializing, IsSerializeBefore, No_OpClass); 0x4000: mb({{ }}, IsMemBarrier, MemReadOp); 0x4400: wmb({{ }}, IsWriteBarrier, MemWriteOp); } @@ -694,11 +694,11 @@ decode OPCODE default Unknown::unknown() { }}, IsNonSpeculative); 0x83: callsys({{ xc->syscall(); - }}, IsNonSpeculative); + }}, IsNonSpeculative, IsSerializeAfter); // Read uniq reg into ABI return value register (r0) - 0x9e: rduniq({{ R0 = Runiq; }}); + 0x9e: rduniq({{ R0 = Runiq; }}, IsSerializing, IsSerializeBefore); // Write uniq reg with value from ABI arg register (r16) - 0x9f: wruniq({{ Runiq = R16; }}); + 0x9f: wruniq({{ Runiq = R16; }}, IsSerializing, IsSerializeBefore); } } #endif @@ -735,7 +735,7 @@ decode OPCODE default Unknown::unknown() { format HwMoveIPR { 1: hw_mfpr({{ Ra = xc->readMiscRegWithEffect(ipr_index, fault); - }}); + }}, IsSerializing, IsSerializeBefore); } } @@ -745,14 +745,14 @@ decode OPCODE default Unknown::unknown() { 1: hw_mtpr({{ xc->setMiscRegWithEffect(ipr_index, Ra); if (traceData) { traceData->setData(Ra); } - }}); + }}, IsSerializing, IsSerializeBefore); } } format BasicOperate { 0x1e: decode PALMODE { 0: OpcdecFault::hw_rei(); - 1:hw_rei({{ xc->hwrei(); }}, IsSerializing); + 1:hw_rei({{ xc->hwrei(); }}, IsSerializing, IsSerializeBefore); } // M5 special opcodes use the reserved 0x01 opcode space @@ -762,13 +762,13 @@ decode OPCODE default Unknown::unknown() { }}, IsNonSpeculative); 0x01: quiesce({{ AlphaPseudo::quiesce(xc->xcBase()); - }}, IsNonSpeculative); + }}, IsNonSpeculative, IsQuiesce); 0x02: quiesceNs({{ AlphaPseudo::quiesceNs(xc->xcBase(), R16); - }}, IsNonSpeculative); + }}, IsNonSpeculative, IsQuiesce); 0x03: quiesceCycles({{ AlphaPseudo::quiesceCycles(xc->xcBase(), R16); - }}, IsNonSpeculative); + }}, IsNonSpeculative, IsQuiesce); 0x04: quiesceTime({{ R0 = AlphaPseudo::quiesceTime(xc->xcBase()); }}, IsNonSpeculative); diff --git a/arch/alpha/isa/pal.isa b/arch/alpha/isa/pal.isa index e07bea5a8..63af56359 100644 --- a/arch/alpha/isa/pal.isa +++ b/arch/alpha/isa/pal.isa @@ -259,9 +259,11 @@ output decoder {{ } }}; -def format HwMoveIPR(code) {{ +def format HwMoveIPR(code, *flags) {{ + all_flags = ['IprAccessOp'] + all_flags += flags iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code), - ['IprAccessOp']) + all_flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) diff --git a/base/traceflags.py b/base/traceflags.py index e814a00fb..bd0f258a0 100644 --- a/base/traceflags.py +++ b/base/traceflags.py @@ -133,15 +133,24 @@ baseFlags = [ 'ROB', 'FreeList', 'RenameMap', - 'LDSTQ', + 'LSQ', + 'LSQUnit', 'StoreSet', 'MemDepUnit', 'DynInst', 'FullCPU', 'CommitRate', - 'OoOCPU', + 'OzoneCPU', + 'FE', + 'IBE', + 'BE', + 'OzoneLSQ', 'HWPrefetch', 'Stack', + 'DependGraph', + 'Activity', + 'Scoreboard', + 'Writeback' ] # @@ -159,7 +168,8 @@ compoundFlagMap = { 'EthernetAll' : [ 'Ethernet', 'EthernetPIO', 'EthernetDMA', 'EthernetData' , 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ], 'EthernetNoData' : [ 'Ethernet', 'EthernetPIO', 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ], 'IdeAll' : [ 'IdeCtrl', 'IdeDisk' ], - 'FullCPUAll' : [ 'Fetch', 'Decode', 'Rename', 'IEW', 'Commit', 'IQ', 'ROB', 'FreeList', 'RenameMap', 'LDSTQ', 'StoreSet', 'MemDepUnit', 'DynInst', 'FullCPU'] + 'FullCPUAll' : [ 'Fetch', 'Decode', 'Rename', 'IEW', 'Commit', 'IQ', 'ROB', 'FreeList', 'RenameMap', 'LSQ', 'LSQUnit', 'StoreSet', 'MemDepUnit', 'DynInst', 'FullCPU', 'Activity','Scoreboard','Writeback'], + 'OzoneCPUAll' : [ 'BE', 'FE', 'IBE', 'OzoneLSQ', 'OzoneCPU'] } ############################################################# diff --git a/cpu/SConscript b/cpu/SConscript index af6bab4eb..888dbdc22 100644 --- a/cpu/SConscript +++ b/cpu/SConscript @@ -53,6 +53,14 @@ exec_sig_template = ''' virtual Fault execute(%s *xc, Trace::InstRecord *traceData) const = 0; ''' +mem_ini_sig_template = ''' +virtual Fault initiateAcc(%s *xc, Trace::InstRecord *traceData) const { panic("Not defined!"); }; +''' + +mem_comp_sig_template = ''' +virtual Fault completeAcc(uint8_t *data, %s *xc, Trace::InstRecord *traceData) const { panic("Not defined!"); return NoFault; }; +''' + # Generate header. def gen_cpu_exec_signatures(target, source, env): f = open(str(target[0]), 'w') @@ -63,6 +71,8 @@ def gen_cpu_exec_signatures(target, source, env): for cpu in env['CPU_MODELS']: xc_type = CpuModel.dict[cpu].strings['CPU_exec_context'] print >> f, exec_sig_template % xc_type + print >> f, mem_ini_sig_template % xc_type + print >> f, mem_comp_sig_template % xc_type print >> f, ''' #endif // __CPU_STATIC_INST_EXEC_SIGS_HH__ ''' @@ -104,20 +114,40 @@ if 'AlphaFullCPU' in env['CPU_MODELS']: o3/decode.cc o3/fetch.cc o3/free_list.cc + o3/fu_pool.cc o3/cpu.cc o3/iew.cc o3/inst_queue.cc - o3/ldstq.cc + o3/lsq_unit.cc + o3/lsq.cc o3/mem_dep_unit.cc o3/ras.cc o3/rename.cc o3/rename_map.cc o3/rob.cc o3/sat_counter.cc + o3/scoreboard.cc o3/store_set.cc o3/tournament_pred.cc ''') +if 'OzoneSimpleCPU' in env['CPU_MODELS']: + sources += Split(''' + ozone/cpu.cc + ozone/cpu_builder.cc + ozone/dyn_inst.cc + ozone/front_end.cc + ozone/inorder_back_end.cc + ozone/inst_queue.cc + ozone/rename_table.cc + ''') + +if 'OzoneCPU' in env['CPU_MODELS']: + sources += Split(''' + ozone/back_end.cc + ozone/lsq_unit.cc + ''') + # FullCPU sources are included from m5/SConscript since they're not # below this point in the file hierarchy. diff --git a/cpu/base_dyn_inst.cc b/cpu/base_dyn_inst.cc index bf7c35cad..6ce9b4455 100644 --- a/cpu/base_dyn_inst.cc +++ b/cpu/base_dyn_inst.cc @@ -26,10 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_BASE_DYN_INST_CC__ -#define __CPU_BASE_DYN_INST_CC__ - #include +#include #include #include @@ -43,6 +41,8 @@ #include "cpu/base_dyn_inst.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/alpha_cpu.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/ozone_impl.hh" using namespace std; using namespace TheISA; @@ -54,21 +54,23 @@ using namespace TheISA; unsigned int MyHashFunc(const BaseDynInst *addr) { - unsigned a = (unsigned)addr; - unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + unsigned a = (unsigned)addr; + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; - return hash; + return hash; } -typedef m5::hash_map my_hash_t; +typedef m5::hash_map +my_hash_t; + my_hash_t thishash; #endif template -BaseDynInst::BaseDynInst(MachInst machInst, Addr inst_PC, +BaseDynInst::BaseDynInst(ExtMachInst machInst, Addr inst_PC, Addr pred_PC, InstSeqNum seq_num, FullCPU *cpu) - : staticInst(machInst), traceData(NULL), cpu(cpu), cpuXC(cpu->cpuXCBase()) + : staticInst(machInst), traceData(NULL), cpu(cpu)/*, xc(cpu->xcBase())*/ { seqNum = seq_num; @@ -83,6 +85,7 @@ template BaseDynInst::BaseDynInst(StaticInstPtr &_staticInst) : staticInst(_staticInst), traceData(NULL) { + seqNum = 0; initVars(); } @@ -90,8 +93,10 @@ template void BaseDynInst::initVars() { + req = NULL; effAddr = MemReq::inval_addr; physEffAddr = MemReq::inval_addr; + storeSize = 0; readyRegs = 0; @@ -100,13 +105,27 @@ BaseDynInst::initVars() issued = false; executed = false; canCommit = false; + committed = false; squashed = false; squashedInIQ = false; + squashedInLSQ = false; + squashedInROB = false; eaCalcDone = false; + memOpDone = false; + lqIdx = -1; + sqIdx = -1; + reachedCommit = false; blockingInst = false; recoverInst = false; + iqEntry = false; + robEntry = false; + + serializeBefore = false; + serializeAfter = false; + serializeHandled = false; + // Eventually make this a parameter. threadNumber = 0; @@ -114,22 +133,63 @@ BaseDynInst::initVars() asid = 0; // Initialize the fault to be unimplemented opcode. - fault = new UnimplementedOpcodeFault; +// fault = new UnimplementedOpcodeFault; + fault = NoFault; ++instcount; - DPRINTF(FullCPU, "DynInst: Instruction created. Instcount=%i\n", - instcount); + if (instcount > 1500) { + cpu->dumpInsts(); +#ifdef DEBUG + dumpSNList(); +#endif + assert(instcount <= 1500); + } + + DPRINTF(DynInst, "DynInst: [sn:%lli] Instruction created. Instcount=%i\n", + seqNum, instcount); + +#ifdef DEBUG + cpu->snList.insert(seqNum); +#endif } template BaseDynInst::~BaseDynInst() { + if (req) { + req = NULL; + } + + if (traceData) { + delete traceData; + } + --instcount; - DPRINTF(FullCPU, "DynInst: Instruction destroyed. Instcount=%i\n", - instcount); + + DPRINTF(DynInst, "DynInst: [sn:%lli] Instruction destroyed. Instcount=%i\n", + seqNum, instcount); +#ifdef DEBUG + cpu->snList.erase(seqNum); +#endif } +#ifdef DEBUG +template +void +BaseDynInst::dumpSNList() +{ + std::set::iterator sn_it = cpu->snList.begin(); + + int count = 0; + while (sn_it != cpu->snList.end()) { + cprintf("%i: [sn:%lli] not destroyed\n", count, (*sn_it)); + count++; + sn_it++; + } +} +#endif + template void BaseDynInst::prefetch(Addr addr, unsigned flags) @@ -139,14 +199,14 @@ BaseDynInst::prefetch(Addr addr, unsigned flags) // state. // Generate a MemReq so we can translate the effective address. - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), 1, flags); + MemReqPtr req = new MemReq(addr, thread->getXCProxy(), 1, flags); req->asid = asid; // Prefetches never cause faults. fault = NoFault; // note this is a local, not BaseDynInst::fault - Fault trans_fault = cpuXC->translateDataReadReq(req); + Fault trans_fault = cpu->translateDataReadReq(req); if (trans_fault == NoFault && !(req->flags & UNCACHEABLE)) { // It's a valid address to cacheable space. Record key MemReq @@ -162,15 +222,6 @@ BaseDynInst::prefetch(Addr addr, unsigned flags) effAddr = physEffAddr = MemReq::inval_addr; } - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif - if (traceData) { traceData->setAddr(addr); } @@ -184,10 +235,10 @@ BaseDynInst::writeHint(Addr addr, int size, unsigned flags) // will casue a TLB miss trap if necessary... not sure whether // that's the best thing to do or not. We don't really need the // MemReq otherwise, since wh64 has no functional effect. - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), size, flags); + MemReqPtr req = new MemReq(addr, thread->getXCProxy(), size, flags); req->asid = asid; - fault = cpuXC->translateDataWriteReq(req); + fault = cpu->translateDataWriteReq(req); if (fault == NoFault && !(req->flags & UNCACHEABLE)) { // Record key MemReq parameters so we can generate another one @@ -212,18 +263,18 @@ template Fault BaseDynInst::copySrcTranslate(Addr src) { - MemReqPtr req = new MemReq(src, cpuXC->getProxy(), 64); + MemReqPtr req = new MemReq(src, thread->getXCProxy(), 64); req->asid = asid; // translate to physical address - Fault fault = cpuXC->translateDataReadReq(req); + Fault fault = cpu->translateDataReadReq(req); if (fault == NoFault) { - cpuXC->copySrcAddr = src; - cpuXC->copySrcPhysAddr = req->paddr; + thread->copySrcAddr = src; + thread->copySrcPhysAddr = req->paddr; } else { - cpuXC->copySrcAddr = 0; - cpuXC->copySrcPhysAddr = 0; + thread->copySrcAddr = 0; + thread->copySrcPhysAddr = 0; } return fault; } @@ -236,18 +287,18 @@ Fault BaseDynInst::copy(Addr dest) { uint8_t data[64]; - FunctionalMemory *mem = cpuXC->mem; - assert(cpuXC->copySrcPhysAddr || cpuXC->misspeculating()); - MemReqPtr req = new MemReq(dest, cpuXC->getProxy(), 64); + FunctionalMemory *mem = thread->mem; + assert(thread->copySrcPhysAddr || thread->misspeculating()); + MemReqPtr req = new MemReq(dest, thread->getXCProxy(), 64); req->asid = asid; // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(req); + Fault fault = cpu->translateDataWriteReq(req); if (fault == NoFault) { Addr dest_addr = req->paddr; // Need to read straight from memory since we have more than 8 bytes. - req->paddr = cpuXC->copySrcPhysAddr; + req->paddr = thread->copySrcPhysAddr; mem->read(req, data); req->paddr = dest_addr; mem->write(req, data); @@ -275,7 +326,6 @@ BaseDynInst::dump(std::string &outstring) outstring = s.str(); } - #if 0 template Fault @@ -337,6 +387,28 @@ BaseDynInst::mem_access(mem_cmd cmd, Addr addr, void *p, int nbytes) #endif +template +void +BaseDynInst::markSrcRegReady() +{ + if (++readyRegs == numSrcRegs()) { + canIssue = true; + } +} + +template +void +BaseDynInst::markSrcRegReady(RegIndex src_idx) +{ + ++readyRegs; + + _readySrcRegIdx[src_idx] = true; + + if (readyRegs == numSrcRegs()) { + canIssue = true; + } +} + template bool BaseDynInst::eaSrcsReady() @@ -345,8 +417,7 @@ BaseDynInst::eaSrcsReady() // EA calc depends on. (i.e. src reg 0 is the source of the data to be // stored) - for (int i = 1; i < numSrcRegs(); ++i) - { + for (int i = 1; i < numSrcRegs(); ++i) { if (!_readySrcRegIdx[i]) return false; } @@ -361,4 +432,16 @@ template <> int BaseDynInst::instcount = 0; -#endif // __CPU_BASE_DYN_INST_CC__ +// Forward declaration +template class BaseDynInst; + +template <> +int +BaseDynInst::instcount = 0; + +// Forward declaration +template class BaseDynInst; + +template <> +int +BaseDynInst::instcount = 0; diff --git a/cpu/base_dyn_inst.hh b/cpu/base_dyn_inst.hh index 3a7852f79..ecad6ad64 100644 --- a/cpu/base_dyn_inst.hh +++ b/cpu/base_dyn_inst.hh @@ -29,21 +29,24 @@ #ifndef __CPU_BASE_DYN_INST_HH__ #define __CPU_BASE_DYN_INST_HH__ +#include #include -#include #include "base/fast_alloc.hh" #include "base/trace.hh" #include "config/full_system.hh" #include "cpu/exetrace.hh" #include "cpu/inst_seq.hh" -#include "cpu/o3/comm.hh" #include "cpu/static_inst.hh" -#include "encumbered/cpu/full/bpred_update.hh" #include "encumbered/cpu/full/op_class.hh" +#include "mem/functional/memory_control.hh" +#include "sim/system.hh" +/* +#include "encumbered/cpu/full/bpred_update.hh" #include "encumbered/cpu/full/spec_memory.hh" #include "encumbered/cpu/full/spec_state.hh" #include "encumbered/mem/functional/main.hh" +*/ /** * @file @@ -59,20 +62,29 @@ class BaseDynInst : public FastAlloc, public RefCounted public: // Typedef for the CPU. typedef typename Impl::FullCPU FullCPU; + typedef typename FullCPU::ImplState ImplState; - /// Binary machine instruction type. + // Binary machine instruction type. typedef TheISA::MachInst MachInst; - /// Logical register index type. + // Extended machine instruction type + typedef TheISA::ExtMachInst ExtMachInst; + // Logical register index type. typedef TheISA::RegIndex RegIndex; - /// Integer register index type. + // Integer register index type. typedef TheISA::IntReg IntReg; + // The DynInstPtr type. + typedef typename Impl::DynInstPtr DynInstPtr; + + // The list of instructions iterator type. + typedef typename std::list::iterator ListIt; + enum { - MaxInstSrcRegs = TheISA::MaxInstSrcRegs, //< Max source regs - MaxInstDestRegs = TheISA::MaxInstDestRegs, //< Max dest regs + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, /// Max source regs + MaxInstDestRegs = TheISA::MaxInstDestRegs, /// Max dest regs }; - /** The static inst used by this dyn inst. */ + /** The StaticInst used by this BaseDynInst. */ StaticInstPtr staticInst; //////////////////////////////////////////// @@ -80,11 +92,27 @@ class BaseDynInst : public FastAlloc, public RefCounted // INSTRUCTION EXECUTION // //////////////////////////////////////////// + /** InstRecord that tracks this instructions. */ Trace::InstRecord *traceData; + /** + * Does a read to a given address. + * @param addr The address to read. + * @param data The read's data is written into this parameter. + * @param flags The request's flags. + * @return Returns any fault due to the read. + */ template Fault read(Addr addr, T &data, unsigned flags); + /** + * Does a write to a given address. + * @param data The data to be written. + * @param addr The address to write to. + * @param flags The request's flags. + * @param res The result of the write (for load locked/store conditionals). + * @return Returns any fault due to the write. + */ template Fault write(T data, Addr addr, unsigned flags, uint64_t *res); @@ -96,14 +124,17 @@ class BaseDynInst : public FastAlloc, public RefCounted /** @todo: Consider making this private. */ public: - /** Is this instruction valid. */ - bool valid; - /** The sequence number of the instruction. */ InstSeqNum seqNum; - /** How many source registers are ready. */ - unsigned readyRegs; + /** Is the instruction in the IQ */ + bool iqEntry; + + /** Is the instruction in the ROB */ + bool robEntry; + + /** Is the instruction in the LSQ */ + bool lsqEntry; /** Is the instruction completed. */ bool completed; @@ -120,12 +151,21 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Can this instruction commit. */ bool canCommit; + /** Is this instruction committed. */ + bool committed; + /** Is this instruction squashed. */ bool squashed; /** Is this instruction squashed in the instruction queue. */ bool squashedInIQ; + /** Is this instruction squashed in the instruction queue. */ + bool squashedInLSQ; + + /** Is this instruction squashed in the instruction queue. */ + bool squashedInROB; + /** Is this a recover instruction. */ bool recoverInst; @@ -141,15 +181,21 @@ class BaseDynInst : public FastAlloc, public RefCounted /** data address space ID, for loads & stores. */ short asid; + /** How many source registers are ready. */ + unsigned readyRegs; + /** Pointer to the FullCPU object. */ FullCPU *cpu; /** Pointer to the exec context. Will not exist in the final version. */ - CPUExecContext *cpuXC; + ImplState *thread; /** The kind of fault this instruction has generated. */ Fault fault; + /** The memory request. */ + MemReqPtr req; + /** The effective virtual address (lds & stores only). */ Addr effAddr; @@ -197,17 +243,29 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Count of total number of dynamic instructions. */ static int instcount; - /** Whether or not the source register is ready. Not sure this should be - * here vs. the derived class. +#ifdef DEBUG + void dumpSNList(); +#endif + + /** Whether or not the source register is ready. + * @todo: Not sure this should be here vs the derived class. */ bool _readySrcRegIdx[MaxInstSrcRegs]; public: - /** BaseDynInst constructor given a binary instruction. */ - BaseDynInst(MachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, + /** BaseDynInst constructor given a binary instruction. + * @param inst The binary instruction. + * @param PC The PC of the instruction. + * @param pred_PC The predicted next PC. + * @param seq_num The sequence number of the instruction. + * @param cpu Pointer to the instruction's CPU. + */ + BaseDynInst(ExtMachInst inst, Addr PC, Addr pred_PC, InstSeqNum seq_num, FullCPU *cpu); - /** BaseDynInst constructor given a static inst pointer. */ + /** BaseDynInst constructor given a StaticInst pointer. + * @param _staticInst The StaticInst for this BaseDynInst. + */ BaseDynInst(StaticInstPtr &_staticInst); /** BaseDynInst destructor. */ @@ -218,12 +276,20 @@ class BaseDynInst : public FastAlloc, public RefCounted void initVars(); public: + /** + * @todo: Make this function work; currently it is a dummy function. + * @param fault Last fault. + * @param cmd Last command. + * @param addr Virtual address of access. + * @param p Memory accessed. + * @param nbytes Access size. + */ void - trace_mem(Fault fault, // last fault - MemCmd cmd, // last command - Addr addr, // virtual address of access - void *p, // memory accessed - int nbytes); // access size + trace_mem(Fault fault, + MemCmd cmd, + Addr addr, + void *p, + int nbytes); /** Dumps out contents of this BaseDynInst. */ void dump(); @@ -237,6 +303,7 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Checks whether or not this instruction has had its branch target * calculated yet. For now it is not utilized and is hacked to be * always false. + * @todo: Actually use this instruction. */ bool doneTargCalc() { return false; } @@ -252,12 +319,10 @@ class BaseDynInst : public FastAlloc, public RefCounted Addr readPredTarg() { return predPC; } /** Returns whether the instruction was predicted taken or not. */ - bool predTaken() { - return( predPC != (PC + sizeof(MachInst) ) ); - } + bool predTaken() { return predPC != (PC + sizeof(MachInst)); } /** Returns whether the instruction mispredicted. */ - bool mispredicted() { return (predPC != nextPC); } + bool mispredicted() { return predPC != nextPC; } // // Instruction types. Forward checks to StaticInst object. @@ -280,9 +345,51 @@ class BaseDynInst : public FastAlloc, public RefCounted bool isUncondCtrl() const { return staticInst->isUncondCtrl(); } bool isThreadSync() const { return staticInst->isThreadSync(); } bool isSerializing() const { return staticInst->isSerializing(); } + bool isSerializeBefore() const + { return staticInst->isSerializeBefore() || serializeBefore; } + bool isSerializeAfter() const + { return staticInst->isSerializeAfter() || serializeAfter; } bool isMemBarrier() const { return staticInst->isMemBarrier(); } bool isWriteBarrier() const { return staticInst->isWriteBarrier(); } bool isNonSpeculative() const { return staticInst->isNonSpeculative(); } + bool isQuiesce() const { return staticInst->isQuiesce(); } + + /** Temporarily sets this instruction as a serialize before instruction. */ + void setSerializeBefore() { serializeBefore = true; } + + /** Clears the serializeBefore part of this instruction. */ + void clearSerializeBefore() { serializeBefore = false; } + + /** Checks if this serializeBefore is only temporarily set. */ + bool isTempSerializeBefore() { return serializeBefore; } + + /** Tracks if instruction has been externally set as serializeBefore. */ + bool serializeBefore; + + /** Temporarily sets this instruction as a serialize after instruction. */ + void setSerializeAfter() { serializeAfter = true; } + + /** Clears the serializeAfter part of this instruction.*/ + void clearSerializeAfter() { serializeAfter = false; } + + /** Checks if this serializeAfter is only temporarily set. */ + bool isTempSerializeAfter() { return serializeAfter; } + + /** Tracks if instruction has been externally set as serializeAfter. */ + bool serializeAfter; + + /** Checks if the serialization part of this instruction has been + * handled. This does not apply to the temporary serializing + * state; it only applies to this instruction's own permanent + * serializing state. + */ + bool isSerializeHandled() { return serializeHandled; } + + /** Sets the serialization part of this instruction as handled. */ + void setSerializeHandled() { serializeHandled = true; } + + /** Whether or not the serialization of this instruction has been handled. */ + bool serializeHandled; /** Returns the opclass of this instruction. */ OpClass opClass() const { return staticInst->opClass(); } @@ -290,10 +397,10 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns the branch target address. */ Addr branchTarget() const { return staticInst->branchTarget(PC); } - /** Number of source registers. */ - int8_t numSrcRegs() const { return staticInst->numSrcRegs(); } + /** Returns the number of source registers. */ + int8_t numSrcRegs() const { return staticInst->numSrcRegs(); } - /** Number of destination registers. */ + /** Returns the number of destination registers. */ int8_t numDestRegs() const { return staticInst->numDestRegs(); } // the following are used to track physical register usage @@ -302,16 +409,10 @@ class BaseDynInst : public FastAlloc, public RefCounted int8_t numIntDestRegs() const { return staticInst->numIntDestRegs(); } /** Returns the logical register index of the i'th destination register. */ - RegIndex destRegIdx(int i) const - { - return staticInst->destRegIdx(i); - } + RegIndex destRegIdx(int i) const { return staticInst->destRegIdx(i); } /** Returns the logical register index of the i'th source register. */ - RegIndex srcRegIdx(int i) const - { - return staticInst->srcRegIdx(i); - } + RegIndex srcRegIdx(int i) const { return staticInst->srcRegIdx(i); } /** Returns the result of an integer instruction. */ uint64_t readIntResult() { return instResult.integer; } @@ -324,27 +425,12 @@ class BaseDynInst : public FastAlloc, public RefCounted //Push to .cc file. /** Records that one of the source registers is ready. */ - void markSrcRegReady() - { - ++readyRegs; - if(readyRegs == numSrcRegs()) { - canIssue = true; - } - } + void markSrcRegReady(); /** Marks a specific register as ready. * @todo: Move this to .cc file. */ - void markSrcRegReady(RegIndex src_idx) - { - ++readyRegs; - - _readySrcRegIdx[src_idx] = 1; - - if(readyRegs == numSrcRegs()) { - canIssue = true; - } - } + void markSrcRegReady(RegIndex src_idx); /** Returns if a source register is ready. */ bool isReadySrcRegIdx(int idx) const @@ -355,7 +441,7 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Sets this instruction as completed. */ void setCompleted() { completed = true; } - /** Returns whethe or not this instruction is completed. */ + /** Returns whether or not this instruction is completed. */ bool isCompleted() const { return completed; } /** Sets this instruction as ready to issue. */ @@ -385,34 +471,94 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns whether or not this instruction is ready to commit. */ bool readyToCommit() const { return canCommit; } + /** Sets this instruction as committed. */ + void setCommitted() { committed = true; } + + /** Returns whether or not this instruction is committed. */ + bool isCommitted() const { return committed; } + /** Sets this instruction as squashed. */ void setSquashed() { squashed = true; } /** Returns whether or not this instruction is squashed. */ bool isSquashed() const { return squashed; } + //Instruction Queue Entry + //----------------------- + /** Sets this instruction as a entry the IQ. */ + void setInIQ() { iqEntry = true; } + + /** Sets this instruction as a entry the IQ. */ + void removeInIQ() { iqEntry = false; } + /** Sets this instruction as squashed in the IQ. */ - void setSquashedInIQ() { squashedInIQ = true; } + void setSquashedInIQ() { squashedInIQ = true; squashed = true;} /** Returns whether or not this instruction is squashed in the IQ. */ bool isSquashedInIQ() const { return squashedInIQ; } + /** Returns whether or not this instruction has issued. */ + bool isInIQ() const { return iqEntry; } + + + //Load / Store Queue Functions + //----------------------- + /** Sets this instruction as a entry the LSQ. */ + void setInLSQ() { lsqEntry = true; } + + /** Sets this instruction as a entry the LSQ. */ + void removeInLSQ() { lsqEntry = false; } + + /** Sets this instruction as squashed in the LSQ. */ + void setSquashedInLSQ() { squashedInLSQ = true;} + + /** Returns whether or not this instruction is squashed in the LSQ. */ + bool isSquashedInLSQ() const { return squashedInLSQ; } + + /** Returns whether or not this instruction is in the LSQ. */ + bool isInLSQ() const { return lsqEntry; } + + + //Reorder Buffer Functions + //----------------------- + /** Sets this instruction as a entry the ROB. */ + void setInROB() { robEntry = true; } + + /** Sets this instruction as a entry the ROB. */ + void removeInROB() { robEntry = false; } + + /** Sets this instruction as squashed in the ROB. */ + void setSquashedInROB() { squashedInROB = true; } + + /** Returns whether or not this instruction is squashed in the ROB. */ + bool isSquashedInROB() const { return squashedInROB; } + + /** Returns whether or not this instruction is in the ROB. */ + bool isInROB() const { return robEntry; } + /** Read the PC of this instruction. */ const Addr readPC() const { return PC; } /** Set the next PC of this instruction (its actual target). */ void setNextPC(uint64_t val) { nextPC = val; } + void setASID(short addr_space_id) { asid = addr_space_id; } + + void setThread(unsigned tid) { threadNumber = tid; } + + void setState(ImplState *state) { thread = state; } + /** Returns the exec context. * @todo: Remove this once the ExecContext is no longer used. */ - ExecContext *xcBase() { return cpuXC->getProxy(); } + ExecContext *xcBase() { return thread->getXCProxy(); } private: /** Instruction effective address. * @todo: Consider if this is necessary or not. */ Addr instEffAddr; + /** Whether or not the effective address calculation is completed. * @todo: Consider if this is necessary or not. */ @@ -423,7 +569,7 @@ class BaseDynInst : public FastAlloc, public RefCounted void setEA(Addr &ea) { instEffAddr = ea; eaCalcDone = true; } /** Returns the effective address. */ - const Addr &getEA() const { return instEffAddr; } + const Addr &getEA() const { return req->vaddr; } /** Returns whether or not the eff. addr. calculation has been completed. */ bool doneEACalc() { return eaCalcDone; } @@ -431,12 +577,26 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns whether or not the eff. addr. source registers are ready. */ bool eaSrcsReady(); + /** Whether or not the memory operation is done. */ + bool memOpDone; + public: /** Load queue index. */ int16_t lqIdx; /** Store queue index. */ int16_t sqIdx; + + bool reachedCommit; + + /** Iterator pointing to this BaseDynInst in the list of all insts. */ + ListIt instListIt; + + /** Returns iterator to this instruction in the list of all insts. */ + ListIt &getInstListIt() { return instListIt; } + + /** Sets iterator for this instruction in the list of all insts. */ + void setInstListIt(ListIt _instListIt) { instListIt = _instListIt; } }; template @@ -444,34 +604,47 @@ template inline Fault BaseDynInst::read(Addr addr, T &data, unsigned flags) { - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), sizeof(T), flags); + if (executed) { + fault = cpu->read(req, data, lqIdx); + return fault; + } + + req = new MemReq(addr, thread->getXCProxy(), sizeof(T), flags); req->asid = asid; + req->thread_num = threadNumber; + req->pc = this->PC; + + if ((req->vaddr & (TheISA::VMPageSize - 1)) + req->size > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } fault = cpu->translateDataReadReq(req); - // Record key MemReq parameters so we can generate another one - // just like it for the timing access without calling translate() - // again (which might mess up the TLB). - // Do I ever really need this? -KTL 3/05 effAddr = req->vaddr; physEffAddr = req->paddr; memReqFlags = req->flags; - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif - if (fault == NoFault) { +#if FULL_SYSTEM + if (cpu->system->memctrl->badaddr(physEffAddr)) { + fault = TheISA::genMachineCheckFault(); + data = (T)-1; + this->setExecuted(); + } else { + fault = cpu->read(req, data, lqIdx); + } +#else fault = cpu->read(req, data, lqIdx); +#endif } else { // Return a fixed value to keep simulation deterministic even // along misspeculated paths. data = (T)-1; + + // Commit will have to clean up whatever happened. Set this + // instruction as executed. + this->setExecuted(); } if (traceData) { @@ -492,30 +665,33 @@ BaseDynInst::write(T data, Addr addr, unsigned flags, uint64_t *res) traceData->setData(data); } - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), sizeof(T), flags); + req = new MemReq(addr, thread->getXCProxy(), sizeof(T), flags); req->asid = asid; + req->thread_num = threadNumber; + req->pc = this->PC; + + if ((req->vaddr & (TheISA::VMPageSize - 1)) + req->size > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } fault = cpu->translateDataWriteReq(req); - // Record key MemReq parameters so we can generate another one - // just like it for the timing access without calling translate() - // again (which might mess up the TLB). effAddr = req->vaddr; physEffAddr = req->paddr; memReqFlags = req->flags; - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif - if (fault == NoFault) { +#if FULL_SYSTEM + if (cpu->system->memctrl->badaddr(physEffAddr)) { + fault = TheISA::genMachineCheckFault(); + } else { + fault = cpu->write(req, data, sqIdx); + } +#else fault = cpu->write(req, data, sqIdx); +#endif } if (res) { diff --git a/cpu/o3/2bit_local_pred.cc b/cpu/o3/2bit_local_pred.cc index d9744eec7..458fbd663 100644 --- a/cpu/o3/2bit_local_pred.cc +++ b/cpu/o3/2bit_local_pred.cc @@ -26,6 +26,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "base/intmath.hh" #include "base/trace.hh" #include "cpu/o3/2bit_local_pred.hh" @@ -36,17 +37,25 @@ DefaultBP::DefaultBP(unsigned _localPredictorSize, localCtrBits(_localCtrBits), instShiftAmt(_instShiftAmt) { - // Should do checks here to make sure sizes are correct (powers of 2). + if (!isPowerOf2(localPredictorSize)) { + fatal("Invalid local predictor size!\n"); + } + + localPredictorSets = localPredictorSize / localCtrBits; + + if (!isPowerOf2(localPredictorSets)) { + fatal("Invalid number of local predictor sets! Check localCtrBits.\n"); + } // Setup the index mask. - indexMask = localPredictorSize - 1; + indexMask = localPredictorSets - 1; DPRINTF(Fetch, "Branch predictor: index mask: %#x\n", indexMask); // Setup the array of counters for the local predictor. - localCtrs = new SatCounter[localPredictorSize]; + localCtrs.resize(localPredictorSets); - for (int i = 0; i < localPredictorSize; ++i) + for (int i = 0; i < localPredictorSets; ++i) localCtrs[i].setBits(_localCtrBits); DPRINTF(Fetch, "Branch predictor: local predictor size: %i\n", @@ -68,8 +77,6 @@ DefaultBP::lookup(Addr &branch_addr) DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n", local_predictor_idx); - assert(local_predictor_idx < localPredictorSize); - local_prediction = localCtrs[local_predictor_idx].read(); DPRINTF(Fetch, "Branch predictor: prediction is %i.\n", @@ -102,8 +109,6 @@ DefaultBP::update(Addr &branch_addr, bool taken) DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n", local_predictor_idx); - assert(local_predictor_idx < localPredictorSize); - if (taken) { DPRINTF(Fetch, "Branch predictor: Branch updated as taken.\n"); localCtrs[local_predictor_idx].increment(); diff --git a/cpu/o3/2bit_local_pred.hh b/cpu/o3/2bit_local_pred.hh index 97433e542..38d3f4842 100644 --- a/cpu/o3/2bit_local_pred.hh +++ b/cpu/o3/2bit_local_pred.hh @@ -26,18 +26,23 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ -#define __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ +#ifndef __CPU_O3_2BIT_LOCAL_PRED_HH__ +#define __CPU_O3_2BIT_LOCAL_PRED_HH__ // For Addr type. #include "arch/isa_traits.hh" #include "cpu/o3/sat_counter.hh" +#include + class DefaultBP { public: /** * Default branch predictor constructor. + * @param localPredictorSize Size of the local predictor. + * @param localCtrBits Number of bits per counter. + * @param instShiftAmt Offset amount for instructions to ignore alignment. */ DefaultBP(unsigned localPredictorSize, unsigned localCtrBits, unsigned instShiftAmt); @@ -59,8 +64,11 @@ class DefaultBP private: - /** Returns the taken/not taken prediction given the value of the + /** + * Returns the taken/not taken prediction given the value of the * counter. + * @param count The value of the counter. + * @return The prediction based on the counter value. */ inline bool getPrediction(uint8_t &count); @@ -68,11 +76,14 @@ class DefaultBP inline unsigned getLocalIndex(Addr &PC); /** Array of counters that make up the local predictor. */ - SatCounter *localCtrs; + std::vector localCtrs; /** Size of the local predictor. */ unsigned localPredictorSize; + /** Number of sets. */ + unsigned localPredictorSets; + /** Number of bits of the local predictor's counters. */ unsigned localCtrBits; @@ -83,4 +94,4 @@ class DefaultBP unsigned indexMask; }; -#endif // __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ +#endif // __CPU_O3_2BIT_LOCAL_PRED_HH__ diff --git a/cpu/o3/alpha_cpu.hh b/cpu/o3/alpha_cpu.hh index 0352e9972..68e149e77 100644 --- a/cpu/o3/alpha_cpu.hh +++ b/cpu/o3/alpha_cpu.hh @@ -26,14 +26,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Todo: Find all the stuff in ExecContext and ev5 that needs to be -// specifically designed for this CPU. +#ifndef __CPU_O3_ALPHA_FULL_CPU_HH__ +#define __CPU_O3_ALPHA_FULL_CPU_HH__ -#ifndef __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ -#define __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ - -#include "cpu/o3/cpu.hh" #include "arch/isa_traits.hh" +#include "cpu/exec_context.hh" +#include "cpu/o3/cpu.hh" #include "sim/byteswap.hh" template @@ -46,17 +44,175 @@ class AlphaFullCPU : public FullO3CPU typedef TheISA::MiscRegFile MiscRegFile; public: + typedef O3ThreadState ImplState; + typedef O3ThreadState Thread; typedef typename Impl::Params Params; - public: - AlphaFullCPU(Params ¶ms); + /** Constructs an AlphaFullCPU with the given parameters. */ + AlphaFullCPU(Params *params); + + class AlphaXC : public ExecContext + { + public: + AlphaFullCPU *cpu; + + O3ThreadState *thread; + + Tick lastActivate; + Tick lastSuspend; + + Event *quiesceEvent; + + virtual BaseCPU *getCpuPtr() { return cpu; } + + virtual void setCpuId(int id) { cpu->cpu_id = id; } + + virtual int readCpuId() { return cpu->cpu_id; } + + virtual FunctionalMemory *getMemPtr() { return thread->mem; } #if FULL_SYSTEM + virtual System *getSystemPtr() { return cpu->system; } + + virtual PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + virtual AlphaITB *getITBPtr() { return cpu->itb; } + + virtual AlphaDTB * getDTBPtr() { return cpu->dtb; } +#else + virtual Process *getProcessPtr() { return thread->process; } +#endif + + virtual Status status() const { return thread->status(); } + + virtual void setStatus(Status new_status) { thread->setStatus(new_status); } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + virtual void activate(int delay = 1); + + /// Set the status to Suspended. + virtual void suspend(); + + /// Set the status to Unallocated. + virtual void deallocate(); + + /// Set the status to Halted. + virtual void halt(); + +#if FULL_SYSTEM + virtual void dumpFuncProfile(); +#endif + + virtual void takeOverFrom(ExecContext *old_context); + + virtual void regStats(const std::string &name); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + virtual Event *getQuiesceEvent(); + + // Not necessarily the best location for these... + // Having an extra function just to read these is obnoxious + virtual Tick readLastActivate(); + virtual Tick readLastSuspend(); + + virtual void profileClear(); + virtual void profileSample(); +#endif + + virtual int getThreadNum() { return thread->tid; } + + // Also somewhat obnoxious. Really only used for the TLB fault. + // However, may be quite useful in SPARC. + virtual TheISA::MachInst getInst(); + + virtual void copyArchRegs(ExecContext *xc); + + virtual void clearArchRegs(); + + // + // New accessors for new decoder. + // + virtual uint64_t readIntReg(int reg_idx); + + virtual float readFloatRegSingle(int reg_idx); + + virtual double readFloatRegDouble(int reg_idx); + + virtual uint64_t readFloatRegInt(int reg_idx); + + virtual void setIntReg(int reg_idx, uint64_t val); + + virtual void setFloatRegSingle(int reg_idx, float val); + + virtual void setFloatRegDouble(int reg_idx, double val); + + virtual void setFloatRegInt(int reg_idx, uint64_t val); + + virtual uint64_t readPC() + { return cpu->readPC(thread->tid); } + + virtual void setPC(uint64_t val); + + virtual uint64_t readNextPC() + { return cpu->readNextPC(thread->tid); } + + virtual void setNextPC(uint64_t val); + + virtual MiscReg readMiscReg(int misc_reg) + { return cpu->readMiscReg(misc_reg, thread->tid); } + + virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return cpu->readMiscRegWithEffect(misc_reg, fault, thread->tid); } + + virtual Fault setMiscReg(int misc_reg, const MiscReg &val); + + virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + // Also not necessarily the best location for these two. + // Hopefully will go away once we decide upon where st cond + // failures goes. + virtual unsigned readStCondFailures() { return thread->storeCondFailures; } + + virtual void setStCondFailures(unsigned sc_failures) { thread->storeCondFailures = sc_failures; } + +#if FULL_SYSTEM + virtual bool inPalMode() { return TheISA::PcPAL(cpu->readPC(thread->tid)); } +#endif + + // Only really makes sense for old CPU model. Still could be useful though. + virtual bool misspeculating() { return false; } + +#if !FULL_SYSTEM + virtual IntReg getSyscallArg(int i); + + // used to shift args for indirect syscall + virtual void setSyscallArg(int i, IntReg val); + + virtual void setSyscallReturn(SyscallReturn return_value); + + virtual void syscall() { return cpu->syscall(thread->tid); } + + // Same with st cond failures. + virtual Counter readFuncExeInst() { return thread->funcExeInst; } +#endif + }; + + friend class AlphaXC; + + std::vector xcProxies; + +#if FULL_SYSTEM + /** ITB pointer. */ AlphaITB *itb; + /** DTB pointer. */ AlphaDTB *dtb; #endif - public: + /** Registers statistics. */ void regStats(); #if FULL_SYSTEM @@ -67,16 +223,19 @@ class AlphaFullCPU : public FullO3CPU // void clear_interrupt(int int_num, int index); // void clear_interrupts(); + /** Translates instruction requestion. */ Fault translateInstReq(MemReqPtr &req) { return itb->translate(req); } + /** Translates data read request. */ Fault translateDataReadReq(MemReqPtr &req) { return dtb->translate(req, false); } + /** Translates data write request. */ Fault translateDataWriteReq(MemReqPtr &req) { return dtb->translate(req, true); @@ -95,16 +254,19 @@ class AlphaFullCPU : public FullO3CPU return NoFault; } + /** Translates instruction requestion in syscall emulation mode. */ Fault translateInstReq(MemReqPtr &req) { return dummyTranslation(req); } + /** Translates data read request in syscall emulation mode. */ Fault translateDataReadReq(MemReqPtr &req) { return dummyTranslation(req); } + /** Translates data write request in syscall emulation mode. */ Fault translateDataWriteReq(MemReqPtr &req) { return dummyTranslation(req); @@ -113,36 +275,36 @@ class AlphaFullCPU : public FullO3CPU #endif // Later on may want to remove this misc stuff from the regfile and - // have it handled at this level. Might prove to be an issue when + // have it handled at this level. This would be similar to moving certain + // IPRs into the devices themselves. Might prove to be an issue when // trying to rename source/destination registers... - MiscReg readMiscReg(int misc_reg) - { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return 0; - } + MiscReg readMiscReg(int misc_reg, unsigned tid); - Fault setMiscReg(int misc_reg, const MiscReg &val) - { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; - } + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, unsigned tid); + + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned tid); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, unsigned tid); + + void squashFromXC(unsigned tid); - // Most of the full system code and syscall emulation is not yet - // implemented. These functions do show what the final interface will - // look like. #if FULL_SYSTEM + void post_interrupt(int int_num, int index); + int readIntrFlag(); + /** Sets the interrupt flags. */ void setIntrFlag(int val); - Fault hwrei(); - bool inPalMode() { return AlphaISA::PcPAL(this->regFile.readPC()); } + /** HW return from error interrupt. */ + Fault hwrei(unsigned tid); + /** Returns if a specific PC is a PAL mode PC. */ bool inPalMode(uint64_t PC) { return AlphaISA::PcPAL(PC); } - void trap(Fault fault); + /** Traps to handle given fault. */ + void trap(Fault fault, unsigned tid); bool simPalCheck(int palFunc); + /** Processes any interrupts. */ void processInterrupts(); #endif @@ -152,84 +314,64 @@ class AlphaFullCPU : public FullO3CPU // register. Actually, these functions should handle most of this // functionality by themselves; should look up the rename and then // set the register. - IntReg getSyscallArg(int i) - { - return this->cpuXC->readIntReg(AlphaISA::ArgumentReg0 + i); - } + /** Gets a syscall argument. */ + IntReg getSyscallArg(int i, int tid); - // used to shift args for indirect syscall - void setSyscallArg(int i, IntReg val) - { - this->cpuXC->setIntReg(AlphaISA::ArgumentReg0 + i, val); - } + /** Used to shift args for indirect syscall. */ + void setSyscallArg(int i, IntReg val, int tid); - void setSyscallReturn(int64_t return_value) - { - // check for error condition. Alpha syscall convention is to - // indicate success/failure in reg a3 (r19) and put the - // return value itself in the standard return value reg (v0). - const int RegA3 = 19; // only place this is used - if (return_value >= 0) { - // no error - this->cpuXC->setIntReg(RegA3, 0); - this->cpuXC->setIntReg(AlphaISA::ReturnValueReg, return_value); - } else { - // got an error, return details - this->cpuXC->setIntReg(RegA3, (IntReg) -1); - this->cpuXC->setIntReg(AlphaISA::ReturnValueReg, -return_value); - } - } + /** Sets the return value of a syscall. */ + void setSyscallReturn(SyscallReturn return_value, int tid); - void syscall(short thread_num); - void squashStages(); + /** Executes a syscall. + * @todo: Determine if this needs to be virtual. + */ + virtual void syscall(int thread_num); #endif - void copyToXC(); - void copyFromXC(); - public: #if FULL_SYSTEM - bool palShadowEnabled; - - // Not sure this is used anywhere. - void intr_post(RegFile *regs, Fault fault, Addr pc); - // Actually used within exec files. Implement properly. - void swapPALShadow(bool use_shadow); - // Called by CPU constructor. Can implement as I please. - void initCPU(RegFile *regs); - // Called by initCPU. Implement as I please. - void initIPRs(RegFile *regs); - + /** Halts the CPU. */ void halt() { panic("Halt not implemented!\n"); } #endif - + /** Old CPU read from memory function. No longer used. */ template Fault read(MemReqPtr &req, T &data) { +// panic("CPU READ NOT IMPLEMENTED W/NEW MEMORY\n"); +#if 0 #if FULL_SYSTEM && defined(TARGET_ALPHA) if (req->flags & LOCKED) { req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); } #endif - +#endif Fault error; + if (req->flags & LOCKED) { + lockAddr = req->paddr; + lockFlag = true; + } + error = this->mem->read(req, data); data = gtoh(data); return error; } + /** CPU read function, forwards read to LSQ. */ template Fault read(MemReqPtr &req, T &data, int load_idx) { return this->iew.ldstQueue.read(req, data, load_idx); } + /** Old CPU write to memory function. No longer used. */ template Fault write(MemReqPtr &req, T &data) { +#if 0 #if FULL_SYSTEM && defined(TARGET_ALPHA) ExecContext *xc; @@ -276,16 +418,32 @@ class AlphaFullCPU : public FullO3CPU } #endif +#endif + + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + req->result = 2; + } else { + if (this->lockFlag/* && this->lockAddr == req->paddr*/) { + req->result=1; + } else { + req->result = 0; + } + } + } return this->mem->write(req, (T)htog(data)); } + /** CPU write function, forwards write to LSQ. */ template Fault write(MemReqPtr &req, T &data, int store_idx) { return this->iew.ldstQueue.write(req, data, store_idx); } + Addr lockAddr; + bool lockFlag; }; -#endif // __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ +#endif // __CPU_O3_ALPHA_FULL_CPU_HH__ diff --git a/cpu/o3/alpha_cpu_builder.cc b/cpu/o3/alpha_cpu_builder.cc index 6025b8ef2..d676a69c1 100644 --- a/cpu/o3/alpha_cpu_builder.cc +++ b/cpu/o3/alpha_cpu_builder.cc @@ -26,39 +26,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "base/inifile.hh" -#include "base/loader/symtab.hh" -#include "base/misc.hh" +#include + #include "cpu/base.hh" -#include "cpu/exetrace.hh" #include "cpu/o3/alpha_cpu.hh" #include "cpu/o3/alpha_impl.hh" -#include "mem/base_mem.hh" +#include "cpu/o3/alpha_params.hh" +#include "cpu/o3/fu_pool.hh" #include "mem/cache/base_cache.hh" -#include "mem/mem_interface.hh" #include "sim/builder.hh" -#include "sim/debug.hh" -#include "sim/host.hh" -#include "sim/process.hh" -#include "sim/sim_events.hh" -#include "sim/sim_object.hh" -#include "sim/stats.hh" - -#if FULL_SYSTEM -#include "base/remote_gdb.hh" -#include "mem/functional/memory_control.hh" -#include "mem/functional/physical.hh" -#include "sim/system.hh" -#include "arch/tlb.hh" -#include "arch/vtophys.hh" -#else // !FULL_SYSTEM -#include "mem/functional/functional.hh" -#endif // FULL_SYSTEM class DerivAlphaFullCPU : public AlphaFullCPU { public: - DerivAlphaFullCPU(AlphaSimpleParams p) + DerivAlphaFullCPU(AlphaSimpleParams *p) : AlphaFullCPU(p) { } }; @@ -75,7 +56,9 @@ SimObjectParam itb; SimObjectParam dtb; #else SimObjectVectorParam workload; +//SimObjectParam page_table; #endif // FULL_SYSTEM + SimObjectParam mem; Param max_insts_any_thread; @@ -86,6 +69,8 @@ Param max_loads_all_threads; SimObjectParam icache; SimObjectParam dcache; +Param cachePorts; + Param decodeToFetchDelay; Param renameToFetchDelay; Param iewToFetchDelay; @@ -112,25 +97,22 @@ Param executeIntWidth; Param executeFloatWidth; Param executeBranchWidth; Param executeMemoryWidth; +SimObjectParam fuPool; Param iewToCommitDelay; Param renameToROBDelay; Param commitWidth; Param squashWidth; -#if 0 Param localPredictorSize; -Param localPredictorCtrBits; -#endif -Param local_predictor_size; -Param local_ctr_bits; -Param local_history_table_size; -Param local_history_bits; -Param global_predictor_size; -Param global_ctr_bits; -Param global_history_bits; -Param choice_predictor_size; -Param choice_ctr_bits; +Param localCtrBits; +Param localHistoryTableSize; +Param localHistoryBits; +Param globalPredictorSize; +Param globalCtrBits; +Param globalHistoryBits; +Param choicePredictorSize; +Param choiceCtrBits; Param BTBEntries; Param BTBTagSize; @@ -147,6 +129,16 @@ Param numPhysFloatRegs; Param numIQEntries; Param numROBEntries; +Param smtNumFetchingThreads; +Param smtFetchPolicy; +Param smtLSQPolicy; +Param smtLSQThreshold; +Param smtIQPolicy; +Param smtIQThreshold; +Param smtROBPolicy; +Param smtROBThreshold; +Param smtCommitPolicy; + Param instShiftAmt; Param defer_registration; @@ -168,6 +160,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(dtb, "Data translation buffer"), #else INIT_PARAM(workload, "Processes to run"), +// INIT_PARAM(page_table, "Page table"), #endif // FULL_SYSTEM INIT_PARAM_DFLT(mem, "Memory", NULL), @@ -190,13 +183,14 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL), INIT_PARAM_DFLT(dcache, "L1 data cache", NULL), + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" "delay"), INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), INIT_PARAM(fetchWidth, "Fetch width"), - INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" "delay"), @@ -222,6 +216,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(executeFloatWidth, "Floating point execute width"), INIT_PARAM(executeBranchWidth, "Branch execute width"), INIT_PARAM(executeMemoryWidth, "Memory execute width"), + INIT_PARAM_DFLT(fuPool, "Functional unit pool", NULL), INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " "delay"), @@ -229,20 +224,15 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(commitWidth, "Commit width"), INIT_PARAM(squashWidth, "Squash width"), -#if 0 - INIT_PARAM(localPredictorSize, "Size of the local predictor in entries. " - "Must be a power of 2."), - INIT_PARAM(localPredictorCtrBits, "Number of bits per counter for bpred"), -#endif - INIT_PARAM(local_predictor_size, "Size of local predictor"), - INIT_PARAM(local_ctr_bits, "Bits per counter"), - INIT_PARAM(local_history_table_size, "Size of local history table"), - INIT_PARAM(local_history_bits, "Bits for the local history"), - INIT_PARAM(global_predictor_size, "Size of global predictor"), - INIT_PARAM(global_ctr_bits, "Bits per counter"), - INIT_PARAM(global_history_bits, "Bits of history"), - INIT_PARAM(choice_predictor_size, "Size of choice predictor"), - INIT_PARAM(choice_ctr_bits, "Bits of choice counters"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), INIT_PARAM(BTBEntries, "Number of BTB entries"), INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), @@ -260,6 +250,16 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(numIQEntries, "Number of instruction queue entries"), INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), INIT_PARAM(defer_registration, "defer system registration (for sampling)"), @@ -287,101 +287,113 @@ CREATE_SIM_OBJECT(DerivAlphaFullCPU) #endif - AlphaSimpleParams params; + AlphaSimpleParams *params = new AlphaSimpleParams; - params.clock = clock; + params->clock = clock; - params.name = getInstanceName(); - params.numberOfThreads = actual_num_threads; + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; #if FULL_SYSTEM - params.system = system; - params.cpu_id = cpu_id; - params.itb = itb; - params.dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; #else - params.workload = workload; + params->workload = workload; + //@todo: change to pageTable +// params->pTable = page_table; #endif // FULL_SYSTEM - params.mem = mem; + params->mem = mem; - params.max_insts_any_thread = max_insts_any_thread; - params.max_insts_all_threads = max_insts_all_threads; - params.max_loads_any_thread = max_loads_any_thread; - params.max_loads_all_threads = max_loads_all_threads; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; // // Caches // - params.icacheInterface = icache ? icache->getInterface() : NULL; - params.dcacheInterface = dcache ? dcache->getInterface() : NULL; + params->icacheInterface = icache ? icache->getInterface() : NULL; + params->dcacheInterface = dcache ? dcache->getInterface() : NULL; + params->cachePorts = cachePorts; - params.decodeToFetchDelay = decodeToFetchDelay; - params.renameToFetchDelay = renameToFetchDelay; - params.iewToFetchDelay = iewToFetchDelay; - params.commitToFetchDelay = commitToFetchDelay; - params.fetchWidth = fetchWidth; + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; - params.renameToDecodeDelay = renameToDecodeDelay; - params.iewToDecodeDelay = iewToDecodeDelay; - params.commitToDecodeDelay = commitToDecodeDelay; - params.fetchToDecodeDelay = fetchToDecodeDelay; - params.decodeWidth = decodeWidth; + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; - params.iewToRenameDelay = iewToRenameDelay; - params.commitToRenameDelay = commitToRenameDelay; - params.decodeToRenameDelay = decodeToRenameDelay; - params.renameWidth = renameWidth; + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; - params.commitToIEWDelay = commitToIEWDelay; - params.renameToIEWDelay = renameToIEWDelay; - params.issueToExecuteDelay = issueToExecuteDelay; - params.issueWidth = issueWidth; - params.executeWidth = executeWidth; - params.executeIntWidth = executeIntWidth; - params.executeFloatWidth = executeFloatWidth; - params.executeBranchWidth = executeBranchWidth; - params.executeMemoryWidth = executeMemoryWidth; + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->issueWidth = issueWidth; + params->executeWidth = executeWidth; + params->executeIntWidth = executeIntWidth; + params->executeFloatWidth = executeFloatWidth; + params->executeBranchWidth = executeBranchWidth; + params->executeMemoryWidth = executeMemoryWidth; + params->fuPool = fuPool; - params.iewToCommitDelay = iewToCommitDelay; - params.renameToROBDelay = renameToROBDelay; - params.commitWidth = commitWidth; - params.squashWidth = squashWidth; -#if 0 - params.localPredictorSize = localPredictorSize; - params.localPredictorCtrBits = localPredictorCtrBits; -#endif - params.local_predictor_size = local_predictor_size; - params.local_ctr_bits = local_ctr_bits; - params.local_history_table_size = local_history_table_size; - params.local_history_bits = local_history_bits; - params.global_predictor_size = global_predictor_size; - params.global_ctr_bits = global_ctr_bits; - params.global_history_bits = global_history_bits; - params.choice_predictor_size = choice_predictor_size; - params.choice_ctr_bits = choice_ctr_bits; + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; - params.BTBEntries = BTBEntries; - params.BTBTagSize = BTBTagSize; - params.RASSize = RASSize; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; - params.LQEntries = LQEntries; - params.SQEntries = SQEntries; - params.SSITSize = SSITSize; - params.LFSTSize = LFSTSize; + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; - params.numPhysIntRegs = numPhysIntRegs; - params.numPhysFloatRegs = numPhysFloatRegs; - params.numIQEntries = numIQEntries; - params.numROBEntries = numROBEntries; + params->RASSize = RASSize; - params.instShiftAmt = 2; + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; - params.defReg = defer_registration; + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; - params.functionTrace = function_trace; - params.functionTraceStart = function_trace_start; + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + params->smtFetchPolicy = smtFetchPolicy; + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; cpu = new DerivAlphaFullCPU(params); diff --git a/cpu/o3/alpha_cpu_impl.hh b/cpu/o3/alpha_cpu_impl.hh index 9f1fa24f6..86f7d9f28 100644 --- a/cpu/o3/alpha_cpu_impl.hh +++ b/cpu/o3/alpha_cpu_impl.hh @@ -30,6 +30,7 @@ #include "base/cprintf.hh" #include "base/statistics.hh" #include "base/timebuf.hh" +#include "cpu/quiesce_event.hh" #include "mem/cache/cache.hh" // for dynamic cast #include "mem/mem_interface.hh" #include "sim/builder.hh" @@ -39,18 +40,79 @@ #include "cpu/o3/alpha_cpu.hh" #include "cpu/o3/alpha_params.hh" #include "cpu/o3/comm.hh" +#include "cpu/o3/thread_state.hh" #if FULL_SYSTEM #include "arch/alpha/osfpal.hh" -#include "arch/alpha/isa_traits.hh" +#include "arch/isa_traits.hh" #endif +using namespace TheISA; + template -AlphaFullCPU::AlphaFullCPU(Params ¶ms) +AlphaFullCPU::AlphaFullCPU(Params *params) +#if FULL_SYSTEM + : FullO3CPU(params), itb(params->itb), dtb(params->dtb) +#else : FullO3CPU(params) +#endif { DPRINTF(FullCPU, "AlphaFullCPU: Creating AlphaFullCPU object.\n"); + this->thread.resize(this->numThreads); + + for (int i = 0; i < this->numThreads; ++i) { +#if FULL_SYSTEM + assert(i == 0); + this->thread[i] = new Thread(this, 0, params->mem); +// this->system->execContexts[i] = this->thread[i]->getXCProxy(); + this->thread[i]->setStatus(ExecContext::Suspended); + +#else + if (i < params->workload.size()) { + DPRINTF(FullCPU, "FullCPU: Workload[%i]'s starting PC is %#x, " + "process is %#x", + i, params->workload[i]->prog_entry, this->thread[i]); + this->thread[i] = new Thread(this, i, params->workload[i], i); + assert(params->workload[i]->getMemory() != NULL); + + this->thread[i]->setStatus(ExecContext::Suspended); + //usedTids[i] = true; + //threadMap[i] = i; + } else { + //Allocate Empty execution context so M5 can use later + //when scheduling threads to CPU + Process* dummy_proc = NULL; + + this->thread[i] = new Thread(this, i, dummy_proc, i); + //usedTids[i] = false; + } +#endif // !FULL_SYSTEM + + this->thread[i]->numInst = 0; + + xcProxies.push_back(new AlphaXC); + + xcProxies[i]->cpu = this; + xcProxies[i]->thread = this->thread[i]; + + xcProxies[i]->quiesceEvent = new EndQuiesceEvent(xcProxies[i]); + xcProxies[i]->lastActivate = 0; + xcProxies[i]->lastSuspend = 0; + + + this->thread[i]->xcProxy = xcProxies[i]; + + this->execContexts.push_back(this->thread[i]->getXCProxy()); + } + + + for (int i=0; i < this->numThreads; i++) { + this->thread[i]->funcExeInst = 0; + } + + // Sets CPU pointers. These must be set at this level because the CPU + // pointers are defined to be the highest level of CPU class. this->fetch.setCPU(this); this->decode.setCPU(this); this->rename.setCPU(this); @@ -58,6 +120,10 @@ AlphaFullCPU::AlphaFullCPU(Params ¶ms) this->commit.setCPU(this); this->rob.setCPU(this); + this->regFile.setCPU(this); + + lockAddr = 0; + lockFlag = false; } template @@ -73,182 +139,436 @@ AlphaFullCPU::regStats() this->commit.regStats(); } -#if !FULL_SYSTEM - -// Will probably need to know which thread is calling syscall -// Will need to pass that information in to the DynInst when it is constructed, -// so that this call can be made with the proper thread number. +#if FULL_SYSTEM template void -AlphaFullCPU::syscall(short thread_num) +AlphaFullCPU::AlphaXC::dumpFuncProfile() { - DPRINTF(FullCPU, "AlphaFullCPU: Syscall() called.\n\n"); +} +#endif - // Commit stage needs to run as well. - this->commit.tick(); +template +void +AlphaFullCPU::AlphaXC::takeOverFrom(ExecContext *old_context) +{ +} - squashStages(); +template +void +AlphaFullCPU::AlphaXC::activate(int delay) +{ + DPRINTF(FullCPU, "Calling activate on AlphaXC\n"); +// warn("Calling activate on AlphaXC"); + if (thread->status() == ExecContext::Active) + return; + + lastActivate = curTick; + + if (thread->status() == ExecContext::Unallocated) { + cpu->activateWhenReady(thread->tid); + return; + } + + thread->setStatus(ExecContext::Active); + + // status() == Suspended + cpu->activateContext(thread->tid, delay); +} + +template +void +AlphaFullCPU::AlphaXC::suspend() +{ + DPRINTF(FullCPU, "Calling suspend on AlphaXC\n"); +// warn("Calling suspend on AlphaXC"); + if (thread->status() == ExecContext::Suspended) + return; + + lastActivate = curTick; + lastSuspend = curTick; +/* +#if FULL_SYSTEM + // Don't change the status from active if there are pending interrupts + if (cpu->check_interrupts()) { + assert(status() == ExecContext::Active); + return; + } +#endif +*/ + thread->setStatus(ExecContext::Suspended); + cpu->suspendContext(thread->tid); +} + +template +void +AlphaFullCPU::AlphaXC::deallocate() +{ + DPRINTF(FullCPU, "Calling deallocate on AlphaXC\n"); +// warn("Calling deallocate on AlphaXC"); + if (thread->status() == ExecContext::Unallocated) + return; + + thread->setStatus(ExecContext::Unallocated); + cpu->deallocateContext(thread->tid); +} + +template +void +AlphaFullCPU::AlphaXC::halt() +{ + DPRINTF(FullCPU, "Calling halt on AlphaXC\n"); +// warn("Calling halt on AlphaXC"); + if (thread->status() == ExecContext::Halted) + return; + + thread->setStatus(ExecContext::Halted); + cpu->haltContext(thread->tid); +} + +template +void +AlphaFullCPU::AlphaXC::regStats(const std::string &name) +{} + +template +void +AlphaFullCPU::AlphaXC::serialize(std::ostream &os) +{} +template +void +AlphaFullCPU::AlphaXC::unserialize(Checkpoint *cp, const std::string §ion) +{} + +#if FULL_SYSTEM +template +Event * +AlphaFullCPU::AlphaXC::getQuiesceEvent() +{ + return quiesceEvent; +} + +template +Tick +AlphaFullCPU::AlphaXC::readLastActivate() +{ + return lastActivate; +} + +template +Tick +AlphaFullCPU::AlphaXC::readLastSuspend() +{ + return lastSuspend; +} + +template +void +AlphaFullCPU::AlphaXC::profileClear() +{} + +template +void +AlphaFullCPU::AlphaXC::profileSample() +{} +#endif + +template +TheISA::MachInst +AlphaFullCPU::AlphaXC:: getInst() +{ + return thread->inst; +} + +template +void +AlphaFullCPU::AlphaXC::copyArchRegs(ExecContext *xc) +{ + // This function will mess things up unless the ROB is empty and + // there are no instructions in the pipeline. + unsigned tid = thread->tid; + PhysRegIndex renamed_reg; + + // First loop through the integer registers. + for (int i = 0; i < AlphaISA::NumIntRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i); + + DPRINTF(FullCPU, "FullCPU: Copying over register %i, had data %lli, " + "now has data %lli.\n", + renamed_reg, cpu->readIntReg(renamed_reg), + xc->readIntReg(i)); + + cpu->setIntReg(renamed_reg, xc->readIntReg(i)); + } + + // Then loop through the floating point registers. + for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i + AlphaISA::FP_Base_DepTag); + cpu->setFloatRegDouble(renamed_reg, + xc->readFloatRegDouble(i)); + cpu->setFloatRegInt(renamed_reg, + xc->readFloatRegInt(i)); + } + + // Copy the misc regs. + cpu->regFile.miscRegs[tid].copyMiscRegs(xc); + + // Then finally set the PC and the next PC. + cpu->setPC(xc->readPC(), tid); + cpu->setNextPC(xc->readNextPC(), tid); +#if !FULL_SYSTEM + this->thread->funcExeInst = xc->readFuncExeInst(); +#endif +} + +template +void +AlphaFullCPU::AlphaXC::clearArchRegs() +{} + +// +// New accessors for new decoder. +// +template +uint64_t +AlphaFullCPU::AlphaXC::readIntReg(int reg_idx) +{ + DPRINTF(Fault, "Reading int register through the XC!\n"); + return cpu->readArchIntReg(reg_idx, thread->tid); +} + +template +float +AlphaFullCPU::AlphaXC::readFloatRegSingle(int reg_idx) +{ + DPRINTF(Fault, "Reading float register through the XC!\n"); + return cpu->readArchFloatRegSingle(reg_idx, thread->tid); +} + +template +double +AlphaFullCPU::AlphaXC::readFloatRegDouble(int reg_idx) +{ + DPRINTF(Fault, "Reading float register through the XC!\n"); + return cpu->readArchFloatRegDouble(reg_idx, thread->tid); +} + +template +uint64_t +AlphaFullCPU::AlphaXC::readFloatRegInt(int reg_idx) +{ + DPRINTF(Fault, "Reading floatint register through the XC!\n"); + return cpu->readArchFloatRegInt(reg_idx, thread->tid); +} + +template +void +AlphaFullCPU::AlphaXC::setIntReg(int reg_idx, uint64_t val) +{ + DPRINTF(Fault, "Setting int register through the XC!\n"); + cpu->setArchIntReg(reg_idx, val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } +} + +template +void +AlphaFullCPU::AlphaXC::setFloatRegSingle(int reg_idx, float val) +{ + DPRINTF(Fault, "Setting float register through the XC!\n"); + cpu->setArchFloatRegSingle(reg_idx, val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } +} + +template +void +AlphaFullCPU::AlphaXC::setFloatRegDouble(int reg_idx, double val) +{ + DPRINTF(Fault, "Setting float register through the XC!\n"); + cpu->setArchFloatRegDouble(reg_idx, val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } +} + +template +void +AlphaFullCPU::AlphaXC::setFloatRegInt(int reg_idx, uint64_t val) +{ + DPRINTF(Fault, "Setting floatint register through the XC!\n"); + cpu->setArchFloatRegInt(reg_idx, val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } +} + +template +void +AlphaFullCPU::AlphaXC::setPC(uint64_t val) +{ + cpu->setPC(val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } +} + +template +void +AlphaFullCPU::AlphaXC::setNextPC(uint64_t val) +{ + cpu->setNextPC(val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } +} + +template +Fault +AlphaFullCPU::AlphaXC::setMiscReg(int misc_reg, const MiscReg &val) +{ + DPRINTF(Fault, "Setting misc register through the XC!\n"); + + Fault ret_fault = cpu->setMiscReg(misc_reg, val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } + + return ret_fault; +} + +template +Fault +AlphaFullCPU::AlphaXC::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + DPRINTF(Fault, "Setting misc register through the XC!\n"); + + Fault ret_fault = cpu->setMiscRegWithEffect(misc_reg, val, thread->tid); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromXC(thread->tid); + } + + return ret_fault; +} + +#if !FULL_SYSTEM + +template +TheISA::IntReg +AlphaFullCPU::AlphaXC::getSyscallArg(int i) +{ + return cpu->getSyscallArg(i, thread->tid); +} + +template +void +AlphaFullCPU::AlphaXC::setSyscallArg(int i, IntReg val) +{ + cpu->setSyscallArg(i, val, thread->tid); +} + +template +void +AlphaFullCPU::AlphaXC::setSyscallReturn(SyscallReturn return_value) +{ + cpu->setSyscallReturn(return_value, thread->tid); +} + +template +void +AlphaFullCPU::syscall(int tid) +{ + DPRINTF(FullCPU, "AlphaFullCPU: [tid:%i] Executing syscall().\n\n", tid); + + DPRINTF(Activity,"Activity: syscall() called.\n"); // Temporarily increase this by one to account for the syscall // instruction. - ++(this->funcExeInst); + ++(this->thread[tid]->funcExeInst); - // Copy over all important state to xc once all the unrolling is done. - copyToXC(); - - // This is hardcoded to thread 0 while the CPU is only single threaded. - this->thread[0]->syscall(); - - // Copy over all important state back to CPU. - copyFromXC(); + // Execute the actual syscall. + this->thread[tid]->syscall(); // Decrease funcExeInst by one as the normal commit will handle - // incrememnting it. - --(this->funcExeInst); -} - -// This is not a pretty function, and should only be used if it is necessary -// to fake having everything squash all at once (ie for non-full system -// syscalls). Maybe put this at the FullCPU level? -template -void -AlphaFullCPU::squashStages() -{ - InstSeqNum rob_head = this->rob.readHeadSeqNum(); - - // Now hack the time buffer to put this sequence number in the places - // where the stages might read it. - for (int i = 0; i < 5; ++i) - { - this->timeBuffer.access(-i)->commitInfo.doneSeqNum = rob_head; - } - - this->fetch.squash(this->rob.readHeadNextPC()); - this->fetchQueue.advance(); - - this->decode.squash(); - this->decodeQueue.advance(); - - this->rename.squash(); - this->renameQueue.advance(); - this->renameQueue.advance(); - - // Be sure to advance the IEW queues so that the commit stage doesn't - // try to set an instruction as completed at the same time that it - // might be deleting it. - this->iew.squash(); - this->iewQueue.advance(); - this->iewQueue.advance(); - // Needs to tell the LSQ to write back all of its data - this->iew.lsqWriteback(); - - this->rob.squash(rob_head); - this->commit.setSquashing(); - - // Now hack the time buffer to clear the sequence numbers in the places - // where the stages might read it.? - for (int i = 0; i < 5; ++i) - { - this->timeBuffer.access(-i)->commitInfo.doneSeqNum = 0; - } - + // incrementing it. + --(this->thread[tid]->funcExeInst); } #endif // FULL_SYSTEM template -void -AlphaFullCPU::copyToXC() +MiscReg +AlphaFullCPU::readMiscReg(int misc_reg, unsigned tid) { - PhysRegIndex renamed_reg; - - // First loop through the integer registers. - for (int i = 0; i < AlphaISA::NumIntRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i); - this->cpuXC->setIntReg(i, this->regFile.readIntReg(renamed_reg)); - DPRINTF(FullCPU, "FullCPU: Copying register %i, has data %lli.\n", - renamed_reg, this->regFile.intRegFile[renamed_reg]); - } - - // Then loop through the floating point registers. - for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i + AlphaISA::FP_Base_DepTag); - this->cpuXC->setFloatRegDouble(i, - this->regFile.readFloatRegDouble(renamed_reg)); - this->cpuXC->setFloatRegInt(i, - this->regFile.readFloatRegInt(renamed_reg)); - } - - this->cpuXC->setMiscReg(AlphaISA::Fpcr_DepTag, - this->regFile.readMiscReg(AlphaISA::Fpcr_DepTag)); - this->cpuXC->setMiscReg(AlphaISA::Uniq_DepTag, - this->regFile.readMiscReg(AlphaISA::Uniq_DepTag)); - this->cpuXC->setMiscReg(AlphaISA::Lock_Flag_DepTag, - this->regFile.readMiscReg(AlphaISA::Lock_Flag_DepTag)); - this->cpuXC->setMiscReg(AlphaISA::Lock_Addr_DepTag, - this->regFile.readMiscReg(AlphaISA::Lock_Addr_DepTag)); - - this->cpuXC->setPC(this->rob.readHeadPC()); - this->cpuXC->setNextPC(this->cpuXC->readPC()+4); - -#if !FULL_SYSTEM - this->cpuXC->setFuncExeInst(this->funcExeInst); -#endif + return this->regFile.readMiscReg(misc_reg, tid); +} + +template +MiscReg +AlphaFullCPU::readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned tid) +{ + return this->regFile.readMiscRegWithEffect(misc_reg, fault, tid); +} + +template +Fault +AlphaFullCPU::setMiscReg(int misc_reg, const MiscReg &val, unsigned tid) +{ + // I think that these registers should always be set, regardless of what + // mode the thread is in. The main difference is if the thread needs to + // squash as a result of the write, which is controlled by the AlphaXC. +// if (!this->thread[tid]->trapPending) { + return this->regFile.setMiscReg(misc_reg, val, tid); +// } else { +// return NoFault; +// } +} + +template +Fault +AlphaFullCPU::setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned tid) +{ +// if (!this->thread[tid]->trapPending) { + return this->regFile.setMiscRegWithEffect(misc_reg, val, tid); +// } else { +// return NoFault; +// } } -// This function will probably mess things up unless the ROB is empty and -// there are no instructions in the pipeline. template void -AlphaFullCPU::copyFromXC() +AlphaFullCPU::squashFromXC(unsigned tid) { - PhysRegIndex renamed_reg; - - // First loop through the integer registers. - for (int i = 0; i < AlphaISA::NumIntRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i); - - DPRINTF(FullCPU, "FullCPU: Copying over register %i, had data %lli, " - "now has data %lli.\n", - renamed_reg, this->regFile.intRegFile[renamed_reg], - this->cpuXC->readIntReg(i)); - - this->regFile.setIntReg(renamed_reg, this->cpuXC->readIntReg(i)); - } - - // Then loop through the floating point registers. - for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i + AlphaISA::FP_Base_DepTag); - this->regFile.setFloatRegDouble(renamed_reg, - this->cpuXC->readFloatRegDouble(i)); - this->regFile.setFloatRegInt(renamed_reg, - this->cpuXC->readFloatRegInt(i)); - } - - // Then loop through the misc registers. - this->regFile.setMiscReg(AlphaISA::Fpcr_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Fpcr_DepTag)); - this->regFile.setMiscReg(AlphaISA::Uniq_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Uniq_DepTag)); - this->regFile.setMiscReg(AlphaISA::Lock_Flag_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Lock_Flag_DepTag)); - this->regFile.setMiscReg(AlphaISA::Lock_Addr_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Lock_Addr_DepTag)); - - // Then finally set the PC and the next PC. -// regFile.pc = cpuXC->regs.pc; -// regFile.npc = cpuXC->regs.npc; -#if !FULL_SYSTEM - this->funcExeInst = this->cpuXC->readFuncExeInst(); -#endif +// this->thread[tid]->trapPending = true; + this->thread[tid]->inSyscall = true; + this->commit.generateXCEvent(tid); } #if FULL_SYSTEM +template +void +AlphaFullCPU::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (this->thread[0]->status() == ExecContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + xcProxies[0]->activate(); + } +} + template int AlphaFullCPU::readIntrFlag() @@ -263,23 +583,26 @@ AlphaFullCPU::setIntrFlag(int val) this->regFile.setIntrFlag(val); } -// Can force commit stage to squash and stuff. template Fault -AlphaFullCPU::hwrei() +AlphaFullCPU::hwrei(unsigned tid) { - if (!inPalMode()) +#if 0 + if (!inPalMode(this->readPC(tid))) return new AlphaISA::UnimplementedOpcodeFault; - this->setNextPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR)); + setNextPC(cpu->readMiscReg(AlphaISA::IPR_EXC_ADDR, tid), tid); -// kernelStats.hwrei(); + cpu->kernelStats->hwrei(); - if ((this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR) & 1) == 0) +// if ((this->regFile.miscRegs[tid].readReg(AlphaISA::IPR_EXC_ADDR) & 1) == 0) // AlphaISA::swap_palshadow(®s, false); - this->checkInterrupts = true; - + cpu->checkInterrupts = true; +#endif +// panic("Do not call this function!"); + // Need to clear the lock flag upon returning from an interrupt. + this->lockFlag = false; // FIXME: XXX check for interrupts? XXX return NoFault; } @@ -312,8 +635,10 @@ AlphaFullCPU::simPalCheck(int palFunc) // stage. template void -AlphaFullCPU::trap(Fault fault) +AlphaFullCPU::trap(Fault fault, unsigned tid) { + + fault->invoke(this->xcProxies[tid]); /* // Keep in mind that a trap may be initiated by fetch if there's a TLB // miss uint64_t PC = this->commit.readCommitPC(); @@ -344,32 +669,93 @@ AlphaFullCPU::trap(Fault fault) swapPALShadow(true); this->regFile.setPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_PAL_BASE) + - (dynamic_cast(fault.get()))->vect()); - this->regFile.setNextPC(PC + sizeof(MachInst));*/ + (dynamic_cast(fault.get()))->vect(), 0); + this->regFile.setNextPC(PC + sizeof(MachInst), 0);*/ } template void AlphaFullCPU::processInterrupts() { - // Check for interrupts here. For now can copy the code that exists - // within isa_fullsys_traits.hh. -} + // Check for interrupts here. For now can copy the code that + // exists within isa_fullsys_traits.hh. Also assume that thread 0 + // is the one that handles the interrupts. -// swap_palshadow swaps in the values of the shadow registers and -// swaps them with the values of the physical registers that map to the -// same logical index. -template -void -AlphaFullCPU::swapPALShadow(bool use_shadow) -{ - if (palShadowEnabled == use_shadow) - panic("swap_palshadow: wrong PAL shadow state"); + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; - palShadowEnabled = use_shadow; + this->checkInterrupts = false; - // Will have to lookup in rename map to get physical registers, then - // swap. + if (this->readMiscReg(IPR_ASTRR, 0)) + panic("asynchronous traps not implemented\n"); + + if (this->readMiscReg(IPR_SIRR, 0)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (this->readMiscReg(IPR_SIRR, 0) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = this->intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > this->readMiscReg(IPR_IPLR, 0)) { + this->setMiscReg(IPR_ISR, summary, 0); + this->setMiscReg(IPR_INTID, ipl, 0); + this->trap(Fault(new InterruptFault), 0); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + this->readMiscReg(IPR_IPLR, 0), ipl, summary); + } } #endif // FULL_SYSTEM + +#if !FULL_SYSTEM +template +TheISA::IntReg +AlphaFullCPU::getSyscallArg(int i, int tid) +{ + return this->readArchIntReg(AlphaISA::ArgumentReg0 + i, tid); +} + +template +void +AlphaFullCPU::setSyscallArg(int i, IntReg val, int tid) +{ + this->setArchIntReg(AlphaISA::ArgumentReg0 + i, val, tid); +} + +template +void +AlphaFullCPU::setSyscallReturn(SyscallReturn return_value, int tid) +{ + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + this->setArchIntReg(SyscallSuccessReg, 0, tid); + this->setArchIntReg(ReturnValueReg, return_value.value(), tid); + } else { + // got an error, return details + this->setArchIntReg(SyscallSuccessReg, (IntReg) -1, tid); + this->setArchIntReg(ReturnValueReg, -return_value.value(), tid); + } +} +#endif diff --git a/cpu/o3/alpha_dyn_inst.hh b/cpu/o3/alpha_dyn_inst.hh index e7f7d3a57..e0b73f17e 100644 --- a/cpu/o3/alpha_dyn_inst.hh +++ b/cpu/o3/alpha_dyn_inst.hh @@ -26,21 +26,24 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_ALPHA_DYN_INST_HH__ -#define __CPU_O3_CPU_ALPHA_DYN_INST_HH__ +#ifndef __CPU_O3_ALPHA_DYN_INST_HH__ +#define __CPU_O3_ALPHA_DYN_INST_HH__ #include "cpu/base_dyn_inst.hh" +#include "cpu/inst_seq.hh" #include "cpu/o3/alpha_cpu.hh" #include "cpu/o3/alpha_impl.hh" -#include "cpu/inst_seq.hh" /** - * Mostly implementation specific AlphaDynInst. It is templated in case there - * are other implementations that are similar enough to be able to use this - * class without changes. This is mainly useful if there are multiple similar - * CPU implementations of the same ISA. + * Mostly implementation & ISA specific AlphaDynInst. As with most other classes + * in the new CPU model, it is templated on the Impl to allow for passing in of + * all types, such as the CPU type and the ISA type. The AlphaDynInst serves + * as the primary interface to the CPU; it plays the role that the ExecContext + * does for the old CPU and the SimpleCPU. The goal is to abstract ExecContext + * purely into an interface, and have it forward calls to the appropriate + * CPU interface, which in the new CPU model's case would be this AlphaDynInst, + * or any other high level implementation specific DynInst. */ - template class AlphaDynInst : public BaseDynInst { @@ -50,6 +53,8 @@ class AlphaDynInst : public BaseDynInst /** Binary machine instruction type. */ typedef TheISA::MachInst MachInst; + /** Extended machine instruction type. */ + typedef TheISA::ExtMachInst ExtMachInst; /** Logical register index type. */ typedef TheISA::RegIndex RegIndex; /** Integer register index type. */ @@ -64,55 +69,60 @@ class AlphaDynInst : public BaseDynInst public: /** BaseDynInst constructor given a binary instruction. */ - AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, + AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, FullCPU *cpu); /** BaseDynInst constructor given a static inst pointer. */ AlphaDynInst(StaticInstPtr &_staticInst); /** Executes the instruction.*/ - Fault execute() - { - return this->fault = this->staticInst->execute(this, this->traceData); - } + Fault execute(); + + Fault initiateAcc(); + + Fault completeAcc(); + + private: + /** Initializes variables. */ + void initVars(); public: MiscReg readMiscReg(int misc_reg) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return 0; + return this->cpu->readMiscReg(misc_reg, this->threadNumber); } MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return 0; + return this->cpu->readMiscRegWithEffect(misc_reg, fault, + this->threadNumber); } Fault setMiscReg(int misc_reg, const MiscReg &val) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; + return this->cpu->setMiscReg(misc_reg, val, this->threadNumber); } Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; + return this->cpu->setMiscRegWithEffect(misc_reg, val, + this->threadNumber); } #if FULL_SYSTEM + /** Calls hardware return from error interrupt. */ Fault hwrei(); + /** Reads interrupt flag. */ int readIntrFlag(); + /** Sets interrupt flag. */ void setIntrFlag(int val); + /** Checks if system is in PAL mode. */ bool inPalMode(); + /** Traps to handle specified fault. */ void trap(Fault fault); bool simPalCheck(int palFunc); #else + /** Calls a syscall. */ void syscall(); #endif @@ -237,16 +247,24 @@ class AlphaDynInst : public BaseDynInst } public: + /** Calculates EA part of a memory instruction. Currently unused, though + * it may be useful in the future when memory instructions aren't + * executed with the EA calculation and the memory access being atomic. + */ Fault calcEA() { return this->staticInst->eaCompInst()->execute(this, this->traceData); } + /** Does the memory access part of a memory instruction. Currently unused, + * though it may be useful in the future when memory instructions aren't + * executed with the EA calculation and the memory access being atomic. + */ Fault memAccess() { return this->staticInst->memAccInst()->execute(this, this->traceData); } }; -#endif // __CPU_O3_CPU_ALPHA_DYN_INST_HH__ +#endif // __CPU_O3_ALPHA_DYN_INST_HH__ diff --git a/cpu/o3/alpha_dyn_inst_impl.hh b/cpu/o3/alpha_dyn_inst_impl.hh index 96b7d3430..b5999f8d1 100644 --- a/cpu/o3/alpha_dyn_inst_impl.hh +++ b/cpu/o3/alpha_dyn_inst_impl.hh @@ -29,57 +29,117 @@ #include "cpu/o3/alpha_dyn_inst.hh" template -AlphaDynInst::AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC, +AlphaDynInst::AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, FullCPU *cpu) : BaseDynInst(inst, PC, Pred_PC, seq_num, cpu) { - // Make sure to have the renamed register entries set to the same - // as the normal register entries. It will allow the IQ to work - // without any modifications. - for (int i = 0; i < this->staticInst->numDestRegs(); i++) - { - _destRegIdx[i] = this->staticInst->destRegIdx(i); - } - - for (int i = 0; i < this->staticInst->numSrcRegs(); i++) - { - _srcRegIdx[i] = this->staticInst->srcRegIdx(i); - this->_readySrcRegIdx[i] = 0; - } - + initVars(); } template AlphaDynInst::AlphaDynInst(StaticInstPtr &_staticInst) : BaseDynInst(_staticInst) +{ + initVars(); +} + +template +void +AlphaDynInst::initVars() { // Make sure to have the renamed register entries set to the same // as the normal register entries. It will allow the IQ to work // without any modifications. - for (int i = 0; i < _staticInst->numDestRegs(); i++) - { - _destRegIdx[i] = _staticInst->destRegIdx(i); + for (int i = 0; i < this->staticInst->numDestRegs(); i++) { + _destRegIdx[i] = this->staticInst->destRegIdx(i); } - for (int i = 0; i < _staticInst->numSrcRegs(); i++) - { - _srcRegIdx[i] = _staticInst->srcRegIdx(i); + for (int i = 0; i < this->staticInst->numSrcRegs(); i++) { + _srcRegIdx[i] = this->staticInst->srcRegIdx(i); + this->_readySrcRegIdx[i] = 0; } } +template +Fault +AlphaDynInst::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening when using + // the XC during an instruction's execution (specifically for instructions + // that have sideeffects that use the XC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->execute(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template +Fault +AlphaDynInst::initiateAcc() +{ + // @todo: Pretty convoluted way to avoid squashing from happening when using + // the XC during an instruction's execution (specifically for instructions + // that have sideeffects that use the XC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->initiateAcc(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template +Fault +AlphaDynInst::completeAcc() +{ + if (this->isLoad()) { + this->fault = this->staticInst->completeAcc(this->req->data, + this, + this->traceData); + } else if (this->isStore()) { + this->fault = this->staticInst->completeAcc((uint8_t*)&this->req->result, + this, + this->traceData); + } else { + panic("Unknown type!"); + } + + return this->fault; +} + #if FULL_SYSTEM template Fault AlphaDynInst::hwrei() { - return this->cpu->hwrei(); + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + this->setNextPC(this->cpu->readMiscReg(AlphaISA::IPR_EXC_ADDR, + this->threadNumber)); + + this->cpu->kernelStats->hwrei(); + + // Tell CPU to clear any state it needs to if a hwrei is taken. + this->cpu->hwrei(this->threadNumber); + + this->cpu->checkInterrupts = true; + + // FIXME: XXX check for interrupts? XXX + return NoFault; } template int AlphaDynInst::readIntrFlag() { -return this->cpu->readIntrFlag(); + return this->cpu->readIntrFlag(); } template @@ -93,14 +153,14 @@ template bool AlphaDynInst::inPalMode() { - return this->cpu->inPalMode(); + return this->cpu->inPalMode(this->PC); } template void AlphaDynInst::trap(Fault fault) { - this->cpu->trap(fault); + this->cpu->trap(fault, this->threadNumber); } template diff --git a/cpu/o3/alpha_impl.hh b/cpu/o3/alpha_impl.hh index 5e39fcb37..f404bd3ec 100644 --- a/cpu/o3/alpha_impl.hh +++ b/cpu/o3/alpha_impl.hh @@ -26,8 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_ALPHA_IMPL_HH__ -#define __CPU_O3_CPU_ALPHA_IMPL_HH__ +#ifndef __CPU_O3_ALPHA_IMPL_HH__ +#define __CPU_O3_ALPHA_IMPL_HH__ #include "arch/alpha/isa_traits.hh" @@ -41,7 +41,7 @@ class AlphaDynInst; template class AlphaFullCPU; -/** Implementation specific struct that defines several key things to the +/** Implementation specific struct that defines several key types to the * CPU, the stages within the CPU, the time buffers, and the DynInst. * The struct defines the ISA, the CPU policy, the specific DynInst, the * specific FullCPU, and all of the structs from the time buffers to do @@ -54,10 +54,10 @@ struct AlphaSimpleImpl /** The type of MachInst. */ typedef TheISA::MachInst MachInst; - /** The CPU policy to be used (ie fetch, decode, etc.). */ + /** The CPU policy to be used, which defines all of the CPU stages. */ typedef SimpleCPUPolicy CPUPol; - /** The DynInst to be used. */ + /** The DynInst type to be used. */ typedef AlphaDynInst DynInst; /** The refcounted DynInst pointer to be used. In most cases this is @@ -65,15 +65,16 @@ struct AlphaSimpleImpl */ typedef RefCountingPtr DynInstPtr; - /** The FullCPU to be used. */ + /** The FullCPU type to be used. */ typedef AlphaFullCPU FullCPU; /** The Params to be passed to each stage. */ typedef AlphaSimpleParams Params; enum { - MaxWidth = 8 + MaxWidth = 8, + MaxThreads = 4 }; }; -#endif // __CPU_O3_CPU_ALPHA_IMPL_HH__ +#endif // __CPU_O3_ALPHA_IMPL_HH__ diff --git a/cpu/o3/alpha_params.hh b/cpu/o3/alpha_params.hh index 79b0937e3..04b790815 100644 --- a/cpu/o3/alpha_params.hh +++ b/cpu/o3/alpha_params.hh @@ -26,18 +26,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_ALPHA_SIMPLE_PARAMS_HH__ -#define __CPU_O3_CPU_ALPHA_SIMPLE_PARAMS_HH__ +#ifndef __CPU_O3_ALPHA_PARAMS_HH__ +#define __CPU_O3_ALPHA_PARAMS_HH__ #include "cpu/o3/cpu.hh" //Forward declarations -class System; -class AlphaITB; class AlphaDTB; +class AlphaITB; +class FUPool; class FunctionalMemory; -class Process; class MemInterface; +class Process; +class System; /** * This file defines the parameters that will be used for the AlphaFullCPU. @@ -56,6 +57,9 @@ class AlphaSimpleParams : public BaseFullCPU::Params Process *process; #endif // FULL_SYSTEM + //Page Table +// PageTable *pTable; + FunctionalMemory *mem; // @@ -64,6 +68,8 @@ class AlphaSimpleParams : public BaseFullCPU::Params MemInterface *icacheInterface; MemInterface *dcacheInterface; + unsigned cachePorts; + // // Fetch // @@ -102,6 +108,7 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned executeFloatWidth; unsigned executeBranchWidth; unsigned executeMemoryWidth; + FUPool *fuPool; // // Commit @@ -114,20 +121,15 @@ class AlphaSimpleParams : public BaseFullCPU::Params // // Branch predictor (BP & BTB) // -/* unsigned localPredictorSize; - unsigned localPredictorCtrBits; -*/ - - unsigned local_predictor_size; - unsigned local_ctr_bits; - unsigned local_history_table_size; - unsigned local_history_bits; - unsigned global_predictor_size; - unsigned global_ctr_bits; - unsigned global_history_bits; - unsigned choice_predictor_size; - unsigned choice_ctr_bits; + unsigned localCtrBits; + unsigned localHistoryTableSize; + unsigned localHistoryBits; + unsigned globalPredictorSize; + unsigned globalCtrBits; + unsigned globalHistoryBits; + unsigned choicePredictorSize; + unsigned choiceCtrBits; unsigned BTBEntries; unsigned BTBTagSize; @@ -154,10 +156,24 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned numIQEntries; unsigned numROBEntries; + //SMT Parameters + unsigned smtNumFetchingThreads; + + std::string smtFetchPolicy; + + std::string smtIQPolicy; + unsigned smtIQThreshold; + + std::string smtLSQPolicy; + unsigned smtLSQThreshold; + + std::string smtCommitPolicy; + + std::string smtROBPolicy; + unsigned smtROBThreshold; + // Probably can get this from somewhere. unsigned instShiftAmt; - - bool defReg; }; -#endif // __CPU_O3_CPU_ALPHA_PARAMS_HH__ +#endif // __CPU_O3_ALPHA_PARAMS_HH__ diff --git a/cpu/o3/bpred_unit.cc b/cpu/o3/bpred_unit.cc index 85bd6f0a6..a78dcf463 100644 --- a/cpu/o3/bpred_unit.cc +++ b/cpu/o3/bpred_unit.cc @@ -29,5 +29,9 @@ #include "cpu/o3/bpred_unit_impl.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" template class TwobitBPredUnit; +template class TwobitBPredUnit; +template class TwobitBPredUnit; diff --git a/cpu/o3/bpred_unit.hh b/cpu/o3/bpred_unit.hh index 2725684f7..67c300989 100644 --- a/cpu/o3/bpred_unit.hh +++ b/cpu/o3/bpred_unit.hh @@ -26,8 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BPRED_UNIT_HH__ -#define __BPRED_UNIT_HH__ +#ifndef __CPU_O3_BPRED_UNIT_HH__ +#define __CPU_O3_BPRED_UNIT_HH__ // For Addr type. #include "arch/isa_traits.hh" @@ -35,9 +35,9 @@ #include "cpu/inst_seq.hh" #include "cpu/o3/2bit_local_pred.hh" -#include "cpu/o3/tournament_pred.hh" #include "cpu/o3/btb.hh" #include "cpu/o3/ras.hh" +#include "cpu/o3/tournament_pred.hh" #include @@ -57,77 +57,171 @@ class TwobitBPredUnit typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; - TwobitBPredUnit(Params ¶ms); + /** + * @param params The params object, that has the size of the BP and BTB. + */ + TwobitBPredUnit(Params *params); + /** + * Registers statistics. + */ void regStats(); - bool predict(DynInstPtr &inst, Addr &PC); + /** + * Predicts whether or not the instruction is a taken branch, and the + * target of the branch if it is taken. + * @param inst The branch instruction. + * @param PC The predicted PC is passed back through this parameter. + * @param tid The thread id. + * @return Returns if the branch is taken or not. + */ + bool predict(DynInstPtr &inst, Addr &PC, unsigned tid); - void update(const InstSeqNum &done_sn); + /** + * Tells the branch predictor to commit any updates until the given + * sequence number. + * @param done_sn The sequence number to commit any older updates up until. + * @param tid The thread id. + */ + void update(const InstSeqNum &done_sn, unsigned tid); - void squash(const InstSeqNum &squashed_sn); + /** + * Squashes all outstanding updates until a given sequence number. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, unsigned tid); + /** + * Squashes all outstanding updates until a given sequence number, and + * corrects that sn's update with the proper address and taken/not taken. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param corr_target The correct branch target. + * @param actually_taken The correct branch direction. + * @param tid The thread id. + */ void squash(const InstSeqNum &squashed_sn, const Addr &corr_target, - bool actually_taken); + bool actually_taken, unsigned tid); + /** + * Looks up a given PC in the BP to see if it is taken or not taken. + * @param inst_PC The PC to look up. + * @return Whether the branch is taken or not taken. + */ bool BPLookup(Addr &inst_PC) { return BP.lookup(inst_PC); } + /** + * Looks up a given PC in the BTB to see if a matching entry exists. + * @param inst_PC The PC to look up. + * @return Whether the BTB contains the given PC. + */ bool BTBValid(Addr &inst_PC) - { return BTB.valid(inst_PC); } + { return BTB.valid(inst_PC, 0); } + /** + * Looks up a given PC in the BTB to get the predicted target. + * @param inst_PC The PC to look up. + * @return The address of the target of the branch. + */ Addr BTBLookup(Addr &inst_PC) - { return BTB.lookup(inst_PC); } + { return BTB.lookup(inst_PC, 0); } - // Will want to include global history. + /** + * Updates the BP with taken/not taken information. + * @param inst_PC The branch's PC that will be updated. + * @param taken Whether the branch was taken or not taken. + * @todo Make this update flexible enough to handle a global predictor. + */ void BPUpdate(Addr &inst_PC, bool taken) { BP.update(inst_PC, taken); } + /** + * Updates the BTB with the target of a branch. + * @param inst_PC The branch's PC that will be updated. + * @param target_PC The branch's target that will be added to the BTB. + */ void BTBUpdate(Addr &inst_PC, Addr &target_PC) - { BTB.update(inst_PC, target_PC); } + { BTB.update(inst_PC, target_PC,0); } private: struct PredictorHistory { + /** + * Makes a predictor history struct that contains a sequence number, + * the PC of its instruction, and whether or not it was predicted + * taken. + */ PredictorHistory(const InstSeqNum &seq_num, const Addr &inst_PC, - const bool pred_taken) - : seqNum(seq_num), PC(inst_PC), predTaken(pred_taken), - globalHistory(0), usedRAS(0), wasCall(0), RASIndex(0), - RASTarget(0) + const bool pred_taken, const unsigned _tid) + : seqNum(seq_num), PC(inst_PC), RASTarget(0), globalHistory(0), + RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0), + wasCall(0) { } + /** The sequence number for the predictor history entry. */ InstSeqNum seqNum; + /** The PC associated with the sequence number. */ Addr PC; - bool predTaken; + /** The RAS target (only valid if a return). */ + Addr RASTarget; + /** The global history at the time this entry was created. */ unsigned globalHistory; - bool usedRAS; - - bool wasCall; - + /** The RAS index of the instruction (only valid if a call). */ unsigned RASIndex; - Addr RASTarget; + /** The thread id. */ + unsigned tid; + + /** Whether or not it was predicted taken. */ + bool predTaken; + + /** Whether or not the RAS was used. */ + bool usedRAS; + + /** Whether or not the instruction was a call. */ + bool wasCall; }; - std::list predHist; + typedef std::list History; + /** + * The per-thread predictor history. This is used to update the predictor + * as instructions are committed, or restore it to the proper state after + * a squash. + */ + History predHist[Impl::MaxThreads]; + + /** The branch predictor. */ DefaultBP BP; + /** The BTB. */ DefaultBTB BTB; - ReturnAddrStack RAS; + /** The per-thread return address stack. */ + ReturnAddrStack RAS[Impl::MaxThreads]; + /** Stat for number of BP lookups. */ Stats::Scalar<> lookups; + /** Stat for number of conditional branches predicted. */ Stats::Scalar<> condPredicted; + /** Stat for number of conditional branches predicted incorrectly. */ Stats::Scalar<> condIncorrect; + /** Stat for number of BTB lookups. */ Stats::Scalar<> BTBLookups; + /** Stat for number of BTB hits. */ Stats::Scalar<> BTBHits; + /** Stat for number of times the BTB is correct. */ Stats::Scalar<> BTBCorrect; + /** Stat for number of times the RAS is used to get a target. */ Stats::Scalar<> usedRAS; + /** Stat for number of times the RAS is incorrect. */ Stats::Scalar<> RASIncorrect; }; -#endif // __BPRED_UNIT_HH__ +#endif // __CPU_O3_BPRED_UNIT_HH__ diff --git a/cpu/o3/bpred_unit_impl.hh b/cpu/o3/bpred_unit_impl.hh index 8d16a0cdf..f79b67b6c 100644 --- a/cpu/o3/bpred_unit_impl.hh +++ b/cpu/o3/bpred_unit_impl.hh @@ -30,16 +30,22 @@ #include "base/traceflags.hh" #include "cpu/o3/bpred_unit.hh" +#include +#include + +using namespace std; + template -TwobitBPredUnit::TwobitBPredUnit(Params ¶ms) - : BP(params.local_predictor_size, - params.local_ctr_bits, - params.instShiftAmt), - BTB(params.BTBEntries, - params.BTBTagSize, - params.instShiftAmt), - RAS(params.RASSize) +TwobitBPredUnit::TwobitBPredUnit(Params *params) + : BP(params->localPredictorSize, + params->localCtrBits, + params->instShiftAmt), + BTB(params->BTBEntries, + params->BTBTagSize, + params->instShiftAmt) { + for (int i=0; i < Impl::MaxThreads; i++) + RAS[i].init(params->RASSize); } template @@ -79,7 +85,7 @@ TwobitBPredUnit::regStats() usedRAS .name(name() + ".BPredUnit.usedRAS") - .desc("Number of times the RAS was used.") + .desc("Number of times the RAS was used to get a target.") ; RASIncorrect @@ -90,7 +96,7 @@ TwobitBPredUnit::regStats() template bool -TwobitBPredUnit::predict(DynInstPtr &inst, Addr &PC) +TwobitBPredUnit::predict(DynInstPtr &inst, Addr &PC, unsigned tid) { // See if branch predictor predicts taken. // If so, get its target addr either from the BTB or the RAS. @@ -106,18 +112,19 @@ TwobitBPredUnit::predict(DynInstPtr &inst, Addr &PC) ++lookups; if (inst->isUncondCtrl()) { - DPRINTF(Fetch, "BranchPred: Unconditional control.\n"); + DPRINTF(Fetch, "BranchPred: [tid:%i] Unconditional control.\n", tid); pred_taken = true; } else { ++condPredicted; pred_taken = BPLookup(PC); - DPRINTF(Fetch, "BranchPred: Branch predictor predicted %i for PC %#x" - "\n", pred_taken, inst->readPC()); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Branch predictor predicted %i " + "for PC %#x\n", + tid, pred_taken, inst->readPC()); } - PredictorHistory predict_record(inst->seqNum, PC, pred_taken); + PredictorHistory predict_record(inst->seqNum, PC, pred_taken, tid); // Now lookup in the BTB or RAS. if (pred_taken) { @@ -126,45 +133,48 @@ TwobitBPredUnit::predict(DynInstPtr &inst, Addr &PC) // If it's a function return call, then look up the address // in the RAS. - target = RAS.top(); + target = RAS[tid].top(); // Record the top entry of the RAS, and its index. predict_record.usedRAS = true; - predict_record.RASIndex = RAS.topIdx(); + predict_record.RASIndex = RAS[tid].topIdx(); predict_record.RASTarget = target; - RAS.pop(); + assert(predict_record.RASIndex < 16); - DPRINTF(Fetch, "BranchPred: Instruction %#x is a return, RAS " - "predicted target: %#x, RAS index: %i.\n", - inst->readPC(), target, predict_record.RASIndex); + RAS[tid].pop(); + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x is a return, " + "RAS predicted target: %#x, RAS index: %i.\n", + tid, inst->readPC(), target, predict_record.RASIndex); } else { ++BTBLookups; if (inst->isCall()) { - RAS.push(PC+sizeof(MachInst)); + RAS[tid].push(PC + sizeof(MachInst)); // Record that it was a call so that the top RAS entry can // be popped off if the speculation is incorrect. predict_record.wasCall = true; - DPRINTF(Fetch, "BranchPred: Instruction %#x was a call, " - "adding %#x to the RAS.\n", - inst->readPC(), PC+sizeof(MachInst)); + DPRINTF(Fetch, "BranchPred: [tid:%i] Instruction %#x was a call" + ", adding %#x to the RAS.\n", + tid, inst->readPC(), PC + sizeof(MachInst)); } - if (BTB.valid(PC)) { + if (BTB.valid(PC, tid)) { ++BTBHits; //If it's anything else, use the BTB to get the target addr. - target = BTB.lookup(PC); + target = BTB.lookup(PC, tid); - DPRINTF(Fetch, "BranchPred: Instruction %#x predicted target " - "is %#x.\n", inst->readPC(), target); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x predicted" + " target is %#x.\n", + tid, inst->readPC(), target); } else { - DPRINTF(Fetch, "BranchPred: BTB doesn't have a valid entry." - "\n"); + DPRINTF(Fetch, "BranchPred: [tid:%i]: BTB doesn't have a " + "valid entry.\n",tid); pred_taken = false; } @@ -180,97 +190,112 @@ TwobitBPredUnit::predict(DynInstPtr &inst, Addr &PC) inst->setPredTarg(PC); } - predHist.push_front(predict_record); + predHist[tid].push_front(predict_record); - assert(!predHist.empty()); + DPRINTF(Fetch, "[tid:%i] predHist.size(): %i\n", tid, predHist[tid].size()); return pred_taken; } template void -TwobitBPredUnit::update(const InstSeqNum &done_sn) +TwobitBPredUnit::update(const InstSeqNum &done_sn, unsigned tid) { - DPRINTF(Fetch, "BranchPred: Commiting branches until sequence number " - "%i.\n", done_sn); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Commiting branches until sequence" + "number %lli.\n", tid, done_sn); - while (!predHist.empty() && predHist.back().seqNum <= done_sn) { - assert(!predHist.empty()); + while (!predHist[tid].empty() && + predHist[tid].back().seqNum <= done_sn) { + // Update the branch predictor with the correct results. + BP.update(predHist[tid].back().PC, + predHist[tid].back().predTaken); - // Update the branch predictor with the correct results of branches. - BP.update(predHist.back().PC, predHist.back().predTaken); - - predHist.pop_back(); + predHist[tid].pop_back(); } } template void -TwobitBPredUnit::squash(const InstSeqNum &squashed_sn) +TwobitBPredUnit::squash(const InstSeqNum &squashed_sn, unsigned tid) { - while (!predHist.empty() && predHist.front().seqNum > squashed_sn) { - if (predHist.front().usedRAS) { - DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, " - "target: %#x.\n", - predHist.front().RASIndex, - predHist.front().RASTarget); + History &pred_hist = predHist[tid]; - RAS.restore(predHist.front().RASIndex, - predHist.front().RASTarget); - } else if (predHist.front().wasCall) { - DPRINTF(Fetch, "BranchPred: Removing speculative entry added " - "to the RAS.\n"); + while (!pred_hist.empty() && + pred_hist.front().seqNum > squashed_sn) { + if (pred_hist.front().usedRAS) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Restoring top of RAS to: %i," + " target: %#x.\n", + tid, + pred_hist.front().RASIndex, + pred_hist.front().RASTarget); - RAS.pop(); + RAS[tid].restore(pred_hist.front().RASIndex, + pred_hist.front().RASTarget); + + } else if (pred_hist.front().wasCall) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Removing speculative entry added " + "to the RAS.\n",tid); + + RAS[tid].pop(); } - predHist.pop_front(); + pred_hist.pop_front(); } + } template void TwobitBPredUnit::squash(const InstSeqNum &squashed_sn, const Addr &corr_target, - const bool actually_taken) + const bool actually_taken, + unsigned tid) { // Now that we know that a branch was mispredicted, we need to undo // all the branches that have been seen up until this branch and // fix up everything. + History &pred_hist = predHist[tid]; + ++condIncorrect; - DPRINTF(Fetch, "BranchPred: Squashing from sequence number %i, " + DPRINTF(Fetch, "BranchPred: [tid:%i]: Squashing from sequence number %i, " "setting target to %#x.\n", - squashed_sn, corr_target); + tid, squashed_sn, corr_target); - while (!predHist.empty() && predHist.front().seqNum > squashed_sn) { - - if (predHist.front().usedRAS) { - DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, " + while (!pred_hist.empty() && + pred_hist.front().seqNum > squashed_sn) { + if (pred_hist.front().usedRAS) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Restoring top of RAS to: %i, " "target: %#x.\n", - predHist.front().RASIndex, - predHist.front().RASTarget); + tid, + pred_hist.front().RASIndex, + pred_hist.front().RASTarget); - RAS.restore(predHist.front().RASIndex, - predHist.front().RASTarget); - } else if (predHist.front().wasCall) { - DPRINTF(Fetch, "BranchPred: Removing speculative entry added " - "to the RAS.\n"); + RAS[tid].restore(pred_hist.front().RASIndex, + pred_hist.front().RASTarget); + } else if (pred_hist.front().wasCall) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Removing speculative entry" + " added to the RAS.\n", tid); - RAS.pop(); + RAS[tid].pop(); } - predHist.pop_front(); + pred_hist.pop_front(); } - predHist.front().predTaken = actually_taken; + // If there's a squash due to a syscall, there may not be an entry + // corresponding to the squash. In that case, don't bother trying to + // fix up the entry. + if (!pred_hist.empty()) { + pred_hist.front().predTaken = actually_taken; - if (predHist.front().usedRAS) { - ++RASIncorrect; + if (pred_hist.front().usedRAS) { + ++RASIncorrect; + } + + BP.update(pred_hist.front().PC, actually_taken); + + BTB.update(pred_hist.front().PC, corr_target, tid); } - - BP.update(predHist.front().PC, actually_taken); - - BTB.update(predHist.front().PC, corr_target); } diff --git a/cpu/o3/btb.cc b/cpu/o3/btb.cc index 2d39c3856..e084142d7 100644 --- a/cpu/o3/btb.cc +++ b/cpu/o3/btb.cc @@ -39,14 +39,15 @@ DefaultBTB::DefaultBTB(unsigned _numEntries, tagBits(_tagBits), instShiftAmt(_instShiftAmt) { - // @todo Check to make sure num_entries is valid (a power of 2) - DPRINTF(Fetch, "BTB: Creating BTB object.\n"); - btb = new BTBEntry[numEntries]; + if (!isPowerOf2(numEntries)) { + fatal("BTB entries is not a power of 2!"); + } - for (int i = 0; i < numEntries; ++i) - { + btb.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) { btb[i].valid = false; } @@ -73,7 +74,7 @@ DefaultBTB::getTag(const Addr &inst_PC) } bool -DefaultBTB::valid(const Addr &inst_PC) +DefaultBTB::valid(const Addr &inst_PC, unsigned tid) { unsigned btb_idx = getIndex(inst_PC); @@ -81,7 +82,9 @@ DefaultBTB::valid(const Addr &inst_PC) assert(btb_idx < numEntries); - if (btb[btb_idx].valid && inst_tag == btb[btb_idx].tag) { + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { return true; } else { return false; @@ -92,7 +95,7 @@ DefaultBTB::valid(const Addr &inst_PC) // address is valid, and also the address. For now will just use addr = 0 to // represent invalid entry. Addr -DefaultBTB::lookup(const Addr &inst_PC) +DefaultBTB::lookup(const Addr &inst_PC, unsigned tid) { unsigned btb_idx = getIndex(inst_PC); @@ -100,7 +103,9 @@ DefaultBTB::lookup(const Addr &inst_PC) assert(btb_idx < numEntries); - if (btb[btb_idx].valid && inst_tag == btb[btb_idx].tag) { + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { return btb[btb_idx].target; } else { return 0; @@ -108,12 +113,13 @@ DefaultBTB::lookup(const Addr &inst_PC) } void -DefaultBTB::update(const Addr &inst_PC, const Addr &target) +DefaultBTB::update(const Addr &inst_PC, const Addr &target, unsigned tid) { unsigned btb_idx = getIndex(inst_PC); assert(btb_idx < numEntries); + btb[btb_idx].tid = tid; btb[btb_idx].valid = true; btb[btb_idx].target = target; btb[btb_idx].tag = getTag(inst_PC); diff --git a/cpu/o3/btb.hh b/cpu/o3/btb.hh index 77bdc32ea..aaa9945f7 100644 --- a/cpu/o3/btb.hh +++ b/cpu/o3/btb.hh @@ -26,8 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_BTB_HH__ -#define __CPU_O3_CPU_BTB_HH__ +#ifndef __CPU_O3_BTB_HH__ +#define __CPU_O3_BTB_HH__ // For Addr type. #include "arch/isa_traits.hh" @@ -42,39 +42,84 @@ class DefaultBTB { } + /** The entry's tag. */ Addr tag; + + /** The entry's target. */ Addr target; + + /** The entry's thread id. */ + unsigned tid; + + /** Whether or not the entry is valid. */ bool valid; }; public: + /** Creates a BTB with the given number of entries, number of bits per + * tag, and instruction offset amount. + * @param numEntries Number of entries for the BTB. + * @param tagBits Number of bits for each tag in the BTB. + * @param instShiftAmt Offset amount for instructions to ignore alignment. + */ DefaultBTB(unsigned numEntries, unsigned tagBits, unsigned instShiftAmt); - Addr lookup(const Addr &inst_PC); + /** Looks up an address in the BTB. Must call valid() first on the address. + * @param inst_PC The address of the branch to look up. + * @param tid The thread id. + * @return Returns the target of the branch. + */ + Addr lookup(const Addr &inst_PC, unsigned tid); - bool valid(const Addr &inst_PC); + /** Checks if a branch is in the BTB. + * @param inst_PC The address of the branch to look up. + * @param tid The thread id. + * @return Whether or not the branch exists in the BTB. + */ + bool valid(const Addr &inst_PC, unsigned tid); - void update(const Addr &inst_PC, const Addr &target_PC); + /** Updates the BTB with the target of a branch. + * @param inst_PC The address of the branch being updated. + * @param target_PC The target address of the branch. + * @param tid The thread id. + */ + void update(const Addr &inst_PC, const Addr &target_PC, + unsigned tid); private: + /** Returns the index into the BTB, based on the branch's PC. + * @param inst_PC The branch to look up. + * @return Returns the index into the BTB. + */ inline unsigned getIndex(const Addr &inst_PC); + /** Returns the tag bits of a given address. + * @param inst_PC The branch's address. + * @return Returns the tag bits. + */ inline Addr getTag(const Addr &inst_PC); - BTBEntry *btb; + /** The actual BTB. */ + std::vector btb; + /** The number of entries in the BTB. */ unsigned numEntries; + /** The index mask. */ unsigned idxMask; + /** The number of tag bits per entry. */ unsigned tagBits; + /** The tag mask. */ unsigned tagMask; + /** Number of bits to shift PC when calculating index. */ unsigned instShiftAmt; + /** Number of bits to shift PC when calculating tag. */ unsigned tagShiftAmt; }; -#endif // __CPU_O3_CPU_BTB_HH__ +#endif // __CPU_O3_BTB_HH__ diff --git a/cpu/o3/comm.hh b/cpu/o3/comm.hh index c74c77ddf..1a8f394ca 100644 --- a/cpu/o3/comm.hh +++ b/cpu/o3/comm.hh @@ -26,21 +26,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_COMM_HH__ -#define __CPU_O3_CPU_COMM_HH__ +#ifndef __CPU_O3_COMM_HH__ +#define __CPU_O3_COMM_HH__ #include +#include "arch/faults.hh" #include "arch/isa_traits.hh" #include "cpu/inst_seq.hh" #include "sim/host.hh" -// Find better place to put this typedef. -// The impl might be the best place for this. +// Typedef for physical register index type. Although the Impl would be the +// most likely location for this, there are a few classes that need this +// typedef yet are not templated on the Impl. For now it will be defined here. typedef short int PhysRegIndex; template -struct SimpleFetchSimpleDecode { +struct DefaultFetchDefaultDecode { + typedef typename Impl::DynInstPtr DynInstPtr; + + int size; + + DynInstPtr insts[Impl::MaxWidth]; + Fault fetchFault; + InstSeqNum fetchFaultSN; + bool clearFetchFault; +}; + +template +struct DefaultDecodeDefaultRename { typedef typename Impl::DynInstPtr DynInstPtr; int size; @@ -49,7 +63,7 @@ struct SimpleFetchSimpleDecode { }; template -struct SimpleDecodeSimpleRename { +struct DefaultRenameDefaultIEW { typedef typename Impl::DynInstPtr DynInstPtr; int size; @@ -58,28 +72,21 @@ struct SimpleDecodeSimpleRename { }; template -struct SimpleRenameSimpleIEW { - typedef typename Impl::DynInstPtr DynInstPtr; - - int size; - - DynInstPtr insts[Impl::MaxWidth]; -}; - -template -struct SimpleIEWSimpleCommit { +struct DefaultIEWDefaultCommit { typedef typename Impl::DynInstPtr DynInstPtr; int size; DynInstPtr insts[Impl::MaxWidth]; - bool squash; - bool branchMispredict; - bool branchTaken; - uint64_t mispredPC; - uint64_t nextPC; - InstSeqNum squashedSeqNum; + bool squash[Impl::MaxThreads]; + bool branchMispredict[Impl::MaxThreads]; + bool branchTaken[Impl::MaxThreads]; + uint64_t mispredPC[Impl::MaxThreads]; + uint64_t nextPC[Impl::MaxThreads]; + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + bool includeSquashInst[Impl::MaxThreads]; }; template @@ -91,63 +98,77 @@ struct IssueStruct { DynInstPtr insts[Impl::MaxWidth]; }; +template struct TimeBufStruct { struct decodeComm { bool squash; - bool stall; bool predIncorrect; uint64_t branchAddr; InstSeqNum doneSeqNum; - // Might want to package this kind of branch stuff into a single + // @todo: Might want to package this kind of branch stuff into a single // struct as it is used pretty frequently. bool branchMispredict; bool branchTaken; uint64_t mispredPC; uint64_t nextPC; + + unsigned branchCount; }; - decodeComm decodeInfo; + decodeComm decodeInfo[Impl::MaxThreads]; // Rename can't actually tell anything to squash or send a new PC back // because it doesn't do anything along those lines. But maybe leave // these fields in here to keep the stages mostly orthagonal. struct renameComm { bool squash; - bool stall; uint64_t nextPC; }; - renameComm renameInfo; + renameComm renameInfo[Impl::MaxThreads]; struct iewComm { - bool stall; - // Also eventually include skid buffer space. + bool usedIQ; unsigned freeIQEntries; + bool usedLSQ; + unsigned freeLSQEntries; + + unsigned iqCount; + unsigned ldstqCount; + + unsigned dispatched; + unsigned dispatchedToLSQ; }; - iewComm iewInfo; + iewComm iewInfo[Impl::MaxThreads]; struct commitComm { - bool squash; - bool stall; + bool usedROB; unsigned freeROBEntries; + bool emptyROB; + + bool squash; + bool robSquashing; bool branchMispredict; bool branchTaken; uint64_t mispredPC; uint64_t nextPC; - bool robSquashing; - // Represents the instruction that has either been retired or // squashed. Similar to having a single bus that broadcasts the // retired or squashed sequence number. InstSeqNum doneSeqNum; + //Just in case we want to do a commit/squash on a cycle + //(necessary for multiple ROBs?) + bool commitInsts; + InstSeqNum squashSeqNum; + // Extra bit of information so that the LDSTQ only updates when it // needs to. bool commitIsLoad; @@ -155,9 +176,26 @@ struct TimeBufStruct { // Communication specifically to the IQ to tell the IQ that it can // schedule a non-speculative instruction. InstSeqNum nonSpecSeqNum; + + // Hack for now to send back an uncached access to the IEW stage. + typedef typename Impl::DynInstPtr DynInstPtr; + bool uncached; + DynInstPtr uncachedLoad; + + bool interruptPending; + bool clearInterrupt; }; - commitComm commitInfo; + commitComm commitInfo[Impl::MaxThreads]; + + bool decodeBlock[Impl::MaxThreads]; + bool decodeUnblock[Impl::MaxThreads]; + bool renameBlock[Impl::MaxThreads]; + bool renameUnblock[Impl::MaxThreads]; + bool iewBlock[Impl::MaxThreads]; + bool iewUnblock[Impl::MaxThreads]; + bool commitBlock[Impl::MaxThreads]; + bool commitUnblock[Impl::MaxThreads]; }; -#endif //__CPU_O3_CPU_COMM_HH__ +#endif //__CPU_O3_COMM_HH__ diff --git a/cpu/o3/commit.cc b/cpu/o3/commit.cc index cf33d7f8b..fe5e9c1de 100644 --- a/cpu/o3/commit.cc +++ b/cpu/o3/commit.cc @@ -30,4 +30,4 @@ #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/commit_impl.hh" -template class SimpleCommit; +template class DefaultCommit; diff --git a/cpu/o3/commit.hh b/cpu/o3/commit.hh index 580c1a316..93b74ebb0 100644 --- a/cpu/o3/commit.hh +++ b/cpu/o3/commit.hh @@ -26,29 +26,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Todo: Maybe have a special method for handling interrupts/traps. -// -// Traps: Have IEW send a signal to commit saying that there's a trap to -// be handled. Have commit send the PC back to the fetch stage, along -// with the current commit PC. Fetch will directly access the IPR and save -// off all the proper stuff. Commit can send out a squash, or something -// close to it. -// Do the same for hwrei(). However, requires that commit be specifically -// built to support that kind of stuff. Probably not horrible to have -// commit support having the CPU tell it to squash the other stages and -// restart at a given address. The IPR register does become an issue. -// Probably not a big deal if the IPR stuff isn't cycle accurate. Can just -// have the original function handle writing to the IPR register. - -#ifndef __CPU_O3_CPU_SIMPLE_COMMIT_HH__ -#define __CPU_O3_CPU_SIMPLE_COMMIT_HH__ +#ifndef __CPU_O3_COMMIT_HH__ +#define __CPU_O3_COMMIT_HH__ +#include "arch/faults.hh" +#include "cpu/inst_seq.hh" #include "base/statistics.hh" #include "base/timebuf.hh" +#include "cpu/exetrace.hh" #include "mem/memory_interface.hh" +template +class O3ThreadState; + +/** + * DefaultCommit handles single threaded and SMT commit. Its width is specified + * by the parameters; each cycle it tries to commit that many instructions. The + * SMT policy decides which thread it tries to commit instructions from. Non- + * speculative instructions must reach the head of the ROB before they are + * ready to execute; once they reach the head, commit will broadcast the + * instruction's sequence number to the previous stages so that they can issue/ + * execute the instruction. Only one non-speculative instruction is handled per + * cycle. Commit is responsible for handling all back-end initiated redirects. + * It receives the redirect, and then broadcasts it to all stages, indicating + * the sequence number they should squash until, and any necessary branch mis- + * prediction information as well. It priortizes redirects by instruction's age, + * only broadcasting a redirect if it corresponds to an instruction that should + * currently be in the ROB. This is done by tracking the sequence number of the + * youngest instruction in the ROB, which gets updated to any squashing + * instruction's sequence number, and only broadcasting a redirect if it + * corresponds to an older instruction. Commit also supports multiple cycle + * squashing, to model a ROB that can only remove a certain number of + * instructions per cycle. Eventually traps and interrupts will most likely + * be handled here as well. + */ template -class SimpleCommit +class DefaultCommit { public: // Typedefs from the Impl. @@ -57,62 +70,191 @@ class SimpleCommit typedef typename Impl::Params Params; typedef typename Impl::CPUPol CPUPol; + typedef typename CPUPol::RenameMap RenameMap; typedef typename CPUPol::ROB ROB; typedef typename CPUPol::TimeStruct TimeStruct; + typedef typename CPUPol::FetchStruct FetchStruct; typedef typename CPUPol::IEWStruct IEWStruct; typedef typename CPUPol::RenameStruct RenameStruct; - public: - // I don't believe commit can block, so it will only have two - // statuses for now. - // Actually if there's a cache access that needs to block (ie - // uncachable load or just a mem access in commit) then the stage - // may have to wait. - enum Status { + typedef typename CPUPol::IEW IEW; + + typedef O3ThreadState Thread; + + class TrapEvent : public Event { + private: + DefaultCommit *commit; + unsigned tid; + + public: + TrapEvent(DefaultCommit *_commit, unsigned _tid); + + void process(); + const char *description(); + }; + + /** Overall commit status. Used to determine if the CPU can deschedule + * itself due to a lack of activity. + */ + enum CommitStatus{ + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, ROBSquashing, - DcacheMissStall, - DcacheMissComplete + TrapPending, + FetchTrapPending + }; + + /** Commit policy for SMT mode. */ + enum CommitPolicy { + Aggressive, + RoundRobin, + OldestReady }; private: - Status _status; + /** Overall commit status. */ + CommitStatus _status; + /** Next commit status, to be set at the end of the cycle. */ + CommitStatus _nextStatus; + /** Per-thread status. */ + ThreadStatus commitStatus[Impl::MaxThreads]; + /** Commit policy used in SMT mode. */ + CommitPolicy commitPolicy; public: - SimpleCommit(Params ¶ms); + /** Construct a DefaultCommit with the given parameters. */ + DefaultCommit(Params *params); + /** Returns the name of the DefaultCommit. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); + /** Sets the CPU pointer. */ void setCPU(FullCPU *cpu_ptr); + /** Sets the list of threads. */ + void setThreads(std::vector &threads); + + /** Sets the main time buffer pointer, used for backwards communication. */ void setTimeBuffer(TimeBuffer *tb_ptr); + void setFetchQueue(TimeBuffer *fq_ptr); + + /** Sets the pointer to the queue coming from rename. */ void setRenameQueue(TimeBuffer *rq_ptr); + /** Sets the pointer to the queue coming from IEW. */ void setIEWQueue(TimeBuffer *iq_ptr); + /** Sets the poitner to the IEW stage. */ + void setIEWStage(IEW *iew_stage); + + /** The pointer to the IEW stage. Used solely to ensure that syscalls do + * not execute until all stores have written back. + */ + IEW *iewStage; + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list *at_ptr); + + /** Sets pointer to the commited state rename map. */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); + + /** Sets pointer to the ROB. */ void setROB(ROB *rob_ptr); + /** Initializes stage by sending back the number of free entries. */ + void initStage(); + + /** Ticks the commit stage, which tries to commit instructions. */ void tick(); + /** Handles any squashes that are sent from IEW, and adds instructions + * to the ROB and tries to commit instructions. + */ void commit(); - private: + /** Returns the number of free ROB entries for a specific thread. */ + unsigned numROBFreeEntries(unsigned tid); + void generateXCEvent(unsigned tid); + + private: + /** Updates the overall status of commit with the nextStatus, and + * tell the CPU if commit is active/inactive. */ + void updateStatus(); + + /** Sets the next status based on threads' statuses, which becomes the + * current status at the end of the cycle. + */ + void setNextStatus(); + + /** Checks if the ROB is completed with squashing. This is for the case + * where the ROB can take multiple cycles to complete squashing. + */ + bool robDoneSquashing(); + + /** Returns if any of the threads have the number of ROB entries changed + * on this cycle. Used to determine if the number of free ROB entries needs + * to be sent back to previous stages. + */ + bool changedROBEntries(); + + void squashFromTrap(unsigned tid); + + void squashFromXC(unsigned tid); + + void squashInFlightInsts(unsigned tid); + + private: + /** Commits as many instructions as possible. */ void commitInsts(); + /** Tries to commit the head ROB instruction passed in. + * @param head_inst The instruction to be committed. + */ bool commitHead(DynInstPtr &head_inst, unsigned inst_num); + void generateTrapEvent(unsigned tid); + + /** Gets instructions from rename and inserts them into the ROB. */ void getInsts(); + /** Marks completed instructions using information sent from IEW. */ void markCompletedInsts(); - public: - uint64_t readCommitPC(); + /** Gets the thread to commit, based on the SMT policy. */ + int getCommittingThread(); - void setSquashing() { _status = ROBSquashing; } + /** Returns the thread ID to use based on a round robin policy. */ + int roundRobin(); + + /** Returns the thread ID to use based on an oldest instruction policy. */ + int oldestReady(); + + public: + /** Returns the PC of the head instruction of the ROB. */ + uint64_t readPC(); + + uint64_t readPC(unsigned tid) { return PC[tid]; } + + void setPC(uint64_t val, unsigned tid) { PC[tid] = val; } + + uint64_t readNextPC(unsigned tid) { return nextPC[tid]; } + + void setNextPC(uint64_t val, unsigned tid) { nextPC[tid] = val; } + + /** Sets that the ROB is currently squashing. */ + void setSquashing(unsigned tid); private: /** Time buffer interface. */ @@ -124,6 +266,10 @@ class SimpleCommit /** Wire to read information from IEW (for ROB). */ typename TimeBuffer::wire robInfoFromIEW; + TimeBuffer *fetchQueue; + + typename TimeBuffer::wire fromFetch; + /** IEW instruction queue interface. */ TimeBuffer *iewQueue; @@ -136,22 +282,56 @@ class SimpleCommit /** Wire to read information from rename queue. */ typename TimeBuffer::wire fromRename; + public: /** ROB interface. */ ROB *rob; + private: /** Pointer to FullCPU. */ FullCPU *cpu; /** Memory interface. Used for d-cache accesses. */ MemInterface *dcacheInterface; + std::vector thread; + private: + Fault fetchFault; + InstSeqNum fetchFaultSN; + int fetchTrapWait; + /** Records that commit has written to the time buffer this cycle. Used for + * the CPU to determine if it can deschedule itself if there is no activity. + */ + bool wroteToTimeBuffer; + + /** Records if the number of ROB entries has changed this cycle. If it has, + * then the number of free entries must be re-broadcast. + */ + bool changedROBNumEntries[Impl::MaxThreads]; + + /** A counter of how many threads are currently squashing. */ + int squashCounter; + + /** Records if a thread has to squash this cycle due to a trap. */ + bool trapSquash[Impl::MaxThreads]; + + /** Records if a thread has to squash this cycle due to an XC write. */ + bool xcSquash[Impl::MaxThreads]; + + /** Priority List used for Commit Policy */ + std::list priority_list; + /** IEW to Commit delay, in ticks. */ unsigned iewToCommitDelay; + /** Commit to IEW delay, in ticks. */ + unsigned commitToIEWDelay; + /** Rename to ROB delay, in ticks. */ unsigned renameToROBDelay; + unsigned fetchToCommitDelay; + /** Rename width, in instructions. Used so ROB knows how many * instructions to get from the rename instruction queue. */ @@ -165,16 +345,53 @@ class SimpleCommit /** Commit width, in instructions. */ unsigned commitWidth; - Stats::Scalar<> commitCommittedInsts; - Stats::Scalar<> commitSquashedInsts; - Stats::Scalar<> commitSquashEvents; - Stats::Scalar<> commitNonSpecStalls; - Stats::Scalar<> commitCommittedBranches; - Stats::Scalar<> commitCommittedLoads; - Stats::Scalar<> commitCommittedMemRefs; - Stats::Scalar<> branchMispredicts; + /** Number of Reorder Buffers */ + unsigned numRobs; - Stats::Distribution<> n_committed_dist; + /** Number of Active Threads */ + unsigned numThreads; + + Tick trapLatency; + + Tick fetchTrapLatency; + Tick fetchFaultTick; + + Addr PC[Impl::MaxThreads]; + + Addr nextPC[Impl::MaxThreads]; + + /** The sequence number of the youngest valid instruction in the ROB. */ + InstSeqNum youngestSeqNum[Impl::MaxThreads]; + + /** Pointer to the list of active threads. */ + std::list *activeThreads; + + /** Rename map interface. */ + RenameMap *renameMap[Impl::MaxThreads]; + + /** Stat for the total number of committed instructions. */ + Stats::Scalar<> commitCommittedInsts; + /** Stat for the total number of squashed instructions discarded by commit. + */ + Stats::Scalar<> commitSquashedInsts; + /** Stat for the total number of times commit is told to squash. + * @todo: Actually increment this stat. + */ + Stats::Scalar<> commitSquashEvents; + /** Stat for the total number of times commit has had to stall due to a non- + * speculative instruction reaching the head of the ROB. + */ + Stats::Scalar<> commitNonSpecStalls; + /** Stat for the total number of committed branches. */ + Stats::Scalar<> commitCommittedBranches; + /** Stat for the total number of committed loads. */ + Stats::Scalar<> commitCommittedLoads; + /** Stat for the total number of committed memory references. */ + Stats::Scalar<> commitCommittedMemRefs; + /** Stat for the total number of branch mispredicts that caused a squash. */ + Stats::Scalar<> branchMispredicts; + /** Distribution of the number of committed instructions each cycle. */ + Stats::Distribution<> numCommittedDist; }; -#endif // __CPU_O3_CPU_SIMPLE_COMMIT_HH__ +#endif // __CPU_O3_COMMIT_HH__ diff --git a/cpu/o3/commit_impl.hh b/cpu/o3/commit_impl.hh index e289bc0c0..ef1ba9282 100644 --- a/cpu/o3/commit_impl.hh +++ b/cpu/o3/commit_impl.hh @@ -26,25 +26,112 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include +#include +#include +#include +#include +#include + +#include "base/loader/symtab.hh" #include "base/timebuf.hh" -#include "cpu/o3/commit.hh" #include "cpu/exetrace.hh" +#include "cpu/o3/commit.hh" +#include "cpu/o3/thread_state.hh" + +using namespace std; template -SimpleCommit::SimpleCommit(Params ¶ms) - : dcacheInterface(params.dcacheInterface), - iewToCommitDelay(params.iewToCommitDelay), - renameToROBDelay(params.renameToROBDelay), - renameWidth(params.renameWidth), - iewWidth(params.executeWidth), - commitWidth(params.commitWidth) +DefaultCommit::TrapEvent::TrapEvent(DefaultCommit *_commit, + unsigned _tid) + : Event(&mainEventQueue, CPU_Tick_Pri), commit(_commit), tid(_tid) { - _status = Idle; + this->setFlags(Event::AutoDelete); } template void -SimpleCommit::regStats() +DefaultCommit::TrapEvent::process() +{ + commit->trapSquash[tid] = true; +} + +template +const char * +DefaultCommit::TrapEvent::description() +{ + return "Trap event"; +} + +template +DefaultCommit::DefaultCommit(Params *params) + : dcacheInterface(params->dcacheInterface), + squashCounter(0), + iewToCommitDelay(params->iewToCommitDelay), + commitToIEWDelay(params->commitToIEWDelay), + renameToROBDelay(params->renameToROBDelay), + fetchToCommitDelay(params->commitToFetchDelay), + renameWidth(params->renameWidth), + iewWidth(params->executeWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads) +{ + _status = Active; + _nextStatus = Inactive; + string policy = params->smtCommitPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Assign commit policy + if (policy == "aggressive"){ + commitPolicy = Aggressive; + + DPRINTF(Commit,"Commit Policy set to Aggressive."); + } else if (policy == "roundrobin"){ + commitPolicy = RoundRobin; + + //Set-Up Priority List + for (int tid=0; tid < numThreads; tid++) { + priority_list.push_back(tid); + } + + DPRINTF(Commit,"Commit Policy set to Round Robin."); + } else if (policy == "oldestready"){ + commitPolicy = OldestReady; + + DPRINTF(Commit,"Commit Policy set to Oldest Ready."); + } else { + assert(0 && "Invalid SMT Commit Policy. Options Are: {Aggressive," + "RoundRobin,OldestReady}"); + } + + for (int i=0; i < numThreads; i++) { + commitStatus[i] = Idle; + changedROBNumEntries[i] = false; + trapSquash[i] = false; + xcSquash[i] = false; + } + + // Hardcoded trap latency. + trapLatency = 6; + fetchTrapLatency = 12; + fetchFaultTick = 0; + fetchTrapWait = 0; +} + +template +std::string +DefaultCommit::name() const +{ + return cpu->name() + ".commit"; +} + +template +void +DefaultCommit::regStats() { commitCommittedInsts .name(name() + ".commitCommittedInsts") @@ -79,7 +166,7 @@ SimpleCommit::regStats() .name(name() + ".branchMispredicts") .desc("The number of times a branch was mispredicted") .prereq(branchMispredicts); - n_committed_dist + numCommittedDist .init(0,commitWidth,1) .name(name() + ".COM:committed_per_cycle") .desc("Number of insts commited each cycle") @@ -89,15 +176,26 @@ SimpleCommit::regStats() template void -SimpleCommit::setCPU(FullCPU *cpu_ptr) +DefaultCommit::setCPU(FullCPU *cpu_ptr) { DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); cpu = cpu_ptr; + + // Commit must broadcast the number of free entries it has at the start of + // the simulation, so it starts as active. + cpu->activateStage(FullCPU::CommitIdx); } template void -SimpleCommit::setTimeBuffer(TimeBuffer *tb_ptr) +DefaultCommit::setThreads(vector &threads) +{ + thread = threads; +} + +template +void +DefaultCommit::setTimeBuffer(TimeBuffer *tb_ptr) { DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); timeBuffer = tb_ptr; @@ -111,7 +209,18 @@ SimpleCommit::setTimeBuffer(TimeBuffer *tb_ptr) template void -SimpleCommit::setRenameQueue(TimeBuffer *rq_ptr) +DefaultCommit::setFetchQueue(TimeBuffer *fq_ptr) +{ + DPRINTF(Commit, "Commit: Setting fetch queue pointer.\n"); + fetchQueue = fq_ptr; + + // Setup wire to get instructions from rename (for the ROB). + fromFetch = fetchQueue->getWire(-fetchToCommitDelay); +} + +template +void +DefaultCommit::setRenameQueue(TimeBuffer *rq_ptr) { DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); renameQueue = rq_ptr; @@ -122,7 +231,7 @@ SimpleCommit::setRenameQueue(TimeBuffer *rq_ptr) template void -SimpleCommit::setIEWQueue(TimeBuffer *iq_ptr) +DefaultCommit::setIEWQueue(TimeBuffer *iq_ptr) { DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); iewQueue = iq_ptr; @@ -133,7 +242,33 @@ SimpleCommit::setIEWQueue(TimeBuffer *iq_ptr) template void -SimpleCommit::setROB(ROB *rob_ptr) +DefaultCommit::setIEWStage(IEW *iew_stage) +{ + iewStage = iew_stage; +} + +template +void +DefaultCommit::setActiveThreads(list *at_ptr) +{ + DPRINTF(Commit, "Commit: Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template +void +DefaultCommit::setRenameMap(RenameMap rm_ptr[]) +{ + DPRINTF(Commit, "Setting rename map pointers.\n"); + + for (int i=0; i < numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template +void +DefaultCommit::setROB(ROB *rob_ptr) { DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); rob = rob_ptr; @@ -141,41 +276,317 @@ SimpleCommit::setROB(ROB *rob_ptr) template void -SimpleCommit::tick() +DefaultCommit::initStage() { - // If the ROB is currently in its squash sequence, then continue - // to squash. In this case, commit does not do anything. Otherwise - // run commit. - if (_status == ROBSquashing) { - if (rob->isDoneSquashing()) { - _status = Running; - } else { - rob->doSquash(); + rob->setActiveThreads(activeThreads); + rob->resetEntries(); - // Send back sequence number of tail of ROB, so other stages - // can squash younger instructions. Note that really the only - // stage that this is important for is the IEW stage; other - // stages can just clear all their state as long as selective - // replay isn't used. - toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum(); - toIEW->commitInfo.robSquashing = true; - } - } else { - commit(); + // Broadcast the number of free entries. + for (int i=0; i < numThreads; i++) { + toIEW->commitInfo[i].usedROB = true; + toIEW->commitInfo[i].freeROBEntries = rob->numFreeEntries(i); } - markCompletedInsts(); - - // Writeback number of free ROB entries here. - DPRINTF(Commit, "Commit: ROB has %d free entries.\n", - rob->numFreeEntries()); - toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); + cpu->activityThisCycle(); } template void -SimpleCommit::commit() +DefaultCommit::updateStatus() { + if (commitStatus[0] == TrapPending || + commitStatus[0] == FetchTrapPending) { + _nextStatus = Active; + } + + if (_nextStatus == Inactive && _status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(FullCPU::CommitIdx); + } else if (_nextStatus == Active && _status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(FullCPU::CommitIdx); + } + + _status = _nextStatus; + + // reset ROB changed variable + list::iterator threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + changedROBNumEntries[tid] = false; + } +} + +template +void +DefaultCommit::setNextStatus() +{ + int squashes = 0; + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (commitStatus[tid] == ROBSquashing) { + squashes++; + } + } + + assert(squashes == squashCounter); + + // If commit is currently squashing, then it will have activity for the + // next cycle. Set its next status as active. + if (squashCounter) { + _nextStatus = Active; + } +} + +template +bool +DefaultCommit::changedROBEntries() +{ + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + return true; + } + } + + return false; +} + +template +unsigned +DefaultCommit::numROBFreeEntries(unsigned tid) +{ + return rob->numFreeEntries(tid); +} + +template +void +DefaultCommit::generateTrapEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid); + + TrapEvent *trap = new TrapEvent(this, tid); + + trap->schedule(curTick + trapLatency); + + thread[tid]->trapPending = true; +} + +template +void +DefaultCommit::generateXCEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating XC squash event for [tid:%i]\n", tid); + + xcSquash[tid] = true; +} + +template +void +DefaultCommit::squashFromTrap(unsigned tid) +{ + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + // Hopefully this doesn't mess things up. Basically I want to squash + // all instructions of this thread. + InstSeqNum squashed_inst = rob->isEmpty() ? + 0 : rob->readHeadInst(tid)->seqNum - 1; + + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB (0 in this case. + // Hopefully nothing breaks.) + youngestSeqNum[tid] = 0; + + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; + + // Send back the sequence number of the squashed instruction. + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; + + // Send back the squash signal to tell stages that they should + // squash. + toIEW->commitInfo[tid].squash = true; + + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; + + toIEW->commitInfo[tid].branchMispredict = false; + +// toIEW->commitInfo[tid].branchTaken = fromIEW->branchTaken[tid]; + + toIEW->commitInfo[tid].nextPC = PC[tid]; + + DPRINTF(Commit, "Squashing from trap, restarting at PC %#x\n", PC[tid]); + // Hopefully nobody tries to use the mispredPC becuase I said there + // wasn't a branch mispredict. +// toIEW->commitInfo[tid].mispredPC = fromIEW->mispredPC[tid]; + + thread[tid]->trapPending = false; + thread[tid]->inSyscall = false; + + trapSquash[tid] = false; + + // Not sure what to set this to... + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); + + ++squashCounter; +} + +template +void +DefaultCommit::squashFromXC(unsigned tid) +{ + // For now these are identical. In the future, the squash from trap + // might execute the trap prior to the squash. + + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + // Hopefully this doesn't mess things up. Basically I want to squash + // all instructions of this thread. + InstSeqNum squashed_inst = rob->isEmpty() ? + 0 : rob->readHeadInst(tid)->seqNum - 1;; + + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB (0 in this case. + // Hopefully nothing breaks.) + youngestSeqNum[tid] = 0; + + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; + + // Send back the sequence number of the squashed instruction. + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; + + // Send back the squash signal to tell stages that they should + // squash. + toIEW->commitInfo[tid].squash = true; + + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; + + toIEW->commitInfo[tid].branchMispredict = false; + +// toIEW->commitInfo[tid].branchTaken = fromIEW->branchTaken[tid]; + + toIEW->commitInfo[tid].nextPC = PC[tid]; + + DPRINTF(Commit, "Squashing from XC, restarting at PC %#x\n", PC[tid]); + // Hopefully nobody tries to use the mispredPC becuase I said there + // wasn't a branch mispredict. +// toIEW->commitInfo[tid].mispredPC = fromIEW->mispredPC[tid]; + + thread[tid]->inSyscall = false; + assert(!thread[tid]->trapPending); + // Not sure what to set this to... + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); + + xcSquash[tid] = false; + + ++squashCounter; +} + +template +void +DefaultCommit::squashInFlightInsts(unsigned tid) +{ + // @todo: Fix this hardcoded number. + for (int i = 0; i < -5; ++i) { + for (int j = 0; j < (*iewQueue)[i].size; ++j) { + DynInstPtr inst = (*iewQueue)[i].insts[j]; + if (inst->threadNumber == tid && + !inst->isSquashed()) { + inst->setSquashed(); + } + } + } +} + +template +void +DefaultCommit::tick() +{ + wroteToTimeBuffer = false; + _nextStatus = Inactive; + + // If the ROB is currently in its squash sequence, then continue + // to squash. In this case, commit does not do anything. Otherwise + // run commit. + list::iterator threads = (*activeThreads).begin(); + + // Maybe this should be dependent upon any of the commits actually + // squashing. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (commitStatus[tid] == ROBSquashing) { + + if (rob->isDoneSquashing(tid)) { + commitStatus[tid] = Running; + --squashCounter; + } else { + DPRINTF(Commit,"[tid:%u]: Still Squashing, cannot commit any" + "insts this cycle.\n", tid); + } + } + } + + commit(); + + markCompletedInsts(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isEmpty(tid) && rob->readHeadInst(tid)->readyToCommit()) { + // The ROB has more instructions it can commit. Its next status + // will be active. + _nextStatus = Active; + + DynInstPtr inst = rob->readHeadInst(tid); + + DPRINTF(Commit,"[tid:%i]: Instruction [sn:%lli] PC %#x is head of" + " ROB and ready to commit\n", + tid, inst->seqNum, inst->readPC()); + + } else if (!rob->isEmpty(tid)) { + DynInstPtr inst = rob->readHeadInst(tid); + + DPRINTF(Commit,"[tid:%i]: Can't commit, Instruction [sn:%lli] PC " + "%#x is head of ROB and not ready\n", + tid, inst->seqNum, inst->readPC()); + } + + DPRINTF(Commit, "[tid:%i]: ROB has %d insts & %d free entries.\n", + tid, rob->countInsts(tid), rob->numFreeEntries(tid)); + } + + + if (wroteToTimeBuffer) { + DPRINTF(Activity,"Activity This Cycle.\n"); + cpu->activityThisCycle(); + } + + updateStatus(); +} + +template +void +DefaultCommit::commit() +{ + ////////////////////////////////////// // Check for interrupts ////////////////////////////////////// @@ -187,17 +598,44 @@ SimpleCommit::commit() // hwrei() is what resets the PC to the place where instruction execution // beings again. #if FULL_SYSTEM - if (//checkInterrupts && +//#if 0 + if (cpu->checkInterrupts && cpu->check_interrupts() && - !cpu->inPalMode(readCommitPC())) { - // Will need to squash all instructions currently in flight and have - // the interrupt handler restart at the last non-committed inst. - // Most of that can be handled through the trap() function. The - // processInterrupts() function really just checks for interrupts - // and then calls trap() if there is an interrupt present. + !cpu->inPalMode(readPC()) && + !trapSquash[0] && + !xcSquash[0]) { +// commitStatus[0] = TrapPending; + toIEW->commitInfo[0].interruptPending = true; + if (rob->isEmpty() && !iewStage->hasStoresToWB()) { + // Will need to squash all instructions currently in flight and have + // the interrupt handler restart at the last non-committed inst. + // Most of that can be handled through the trap() function. The + // processInterrupts() function really just checks for interrupts + // and then calls trap() if there is an interrupt present. - // CPU will handle implementation of the interrupt. - cpu->processInterrupts(); + // Not sure which thread should be the one to interrupt. For now + // always do thread 0. + assert(!thread[0]->inSyscall); + thread[0]->inSyscall = true; + + // CPU will handle implementation of the interrupt. + cpu->processInterrupts(); + + // Now squash or record that I need to squash this cycle. + commitStatus[0] = TrapPending; + + // Exit state update mode to avoid accidental updating. + thread[0]->inSyscall = false; + + // Generate trap squash event. + generateTrapEvent(0); + + toIEW->commitInfo[0].clearInterrupt = true; + + DPRINTF(Commit, "Interrupt detected.\n"); + } else { + DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n"); + } } #endif // FULL_SYSTEM @@ -205,43 +643,113 @@ SimpleCommit::commit() // Check for squash signal, handle that first //////////////////////////////////// - // Want to mainly check if the IEW stage is telling the ROB to squash. - // Should I also check if the commit stage is telling the ROB to squah? - // This might be necessary to keep the same timing between the IQ and - // the ROB... - if (fromIEW->squash) { - DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n"); + // Check if the IEW stage is telling the ROB to squash. + list::iterator threads = (*activeThreads).begin(); - _status = ROBSquashing; + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; - InstSeqNum squashed_inst = fromIEW->squashedSeqNum; - - rob->squash(squashed_inst); - - // Send back the sequence number of the squashed instruction. - toIEW->commitInfo.doneSeqNum = squashed_inst; - - // Send back the squash signal to tell stages that they should squash. - toIEW->commitInfo.squash = true; - - // Send back the rob squashing signal so other stages know that the - // ROB is in the process of squashing. - toIEW->commitInfo.robSquashing = true; - - toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict; - - toIEW->commitInfo.branchTaken = fromIEW->branchTaken; - - toIEW->commitInfo.nextPC = fromIEW->nextPC; - - toIEW->commitInfo.mispredPC = fromIEW->mispredPC; - - if (toIEW->commitInfo.branchMispredict) { - ++branchMispredicts; + if (fromFetch->fetchFault) { + // Record the fault. Wait until it's empty in the ROB. Then handle the trap. + fetchFault = fromFetch->fetchFault; + fetchFaultSN = fromFetch->fetchFaultSN; + fetchFaultTick = curTick + fetchTrapLatency; + commitStatus[0] = FetchTrapPending; + DPRINTF(Commit, "Fault from fetch recorded. Will trap if the " + "ROB empties without squashing the fault.\n"); + fetchTrapWait = 0; } + if (fromFetch->clearFetchFault) { + DPRINTF(Commit, "Received clear fetch fault signal\n"); + fetchTrapWait = 0; + if (commitStatus[0] == FetchTrapPending) { + DPRINTF(Commit, "Clearing fault from fetch\n"); + commitStatus[0] = Running; + } + } + + // Not sure which one takes priority. I think if we have + // both, that's a bad sign. + if (trapSquash[tid] == true) { + assert(!xcSquash[tid]); + squashFromTrap(tid); + } else if (xcSquash[tid] == true) { + squashFromXC(tid); + } + + // Squashed sequence number must be older than youngest valid + // instruction in the ROB. This prevents squashes from younger + // instructions overriding squashes from older instructions. + if (fromIEW->squash[tid] && + commitStatus[tid] != TrapPending && + fromIEW->squashedSeqNum[tid] <= youngestSeqNum[tid]) { + + DPRINTF(Commit, "[tid:%u]: Squashing instructions in the " + "ROB.\n", + tid); + + DPRINTF(Commit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n", + tid, + fromIEW->mispredPC[tid], + fromIEW->squashedSeqNum[tid]); + + DPRINTF(Commit, "[tid:%i]: Redirecting to PC %#x\n", + tid, + fromIEW->nextPC[tid]); + + commitStatus[tid] = ROBSquashing; + + ++squashCounter; + + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid]; + + if (fromIEW->includeSquashInst[tid] == true) + squashed_inst--; + + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB. + youngestSeqNum[tid] = squashed_inst; + + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; + + // Send back the sequence number of the squashed instruction. + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; + + // Send back the squash signal to tell stages that they should + // squash. + toIEW->commitInfo[tid].squash = true; + + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; + + toIEW->commitInfo[tid].branchMispredict = + fromIEW->branchMispredict[tid]; + + toIEW->commitInfo[tid].branchTaken = + fromIEW->branchTaken[tid]; + + toIEW->commitInfo[tid].nextPC = fromIEW->nextPC[tid]; + + DPRINTF(Commit, "Squashing from IEW, restarting at PC %#x\n", + fromIEW->nextPC[tid]); + + toIEW->commitInfo[tid].mispredPC = + fromIEW->mispredPC[tid]; + + if (toIEW->commitInfo[tid].branchMispredict) { + ++branchMispredicts; + } + } + } - if (_status != ROBSquashing) { + setNextStatus(); + + if (squashCounter != numThreads) { // If we're not currently squashing, then get instructions. getInsts(); @@ -249,24 +757,29 @@ SimpleCommit::commit() commitInsts(); } - // If the ROB is empty, we can set this stage to idle. Use this - // in the future when the Idle status will actually be utilized. -#if 0 - if (rob->isEmpty()) { - DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n"); - _status = Idle; - // Schedule an event so that commit will actually wake up - // once something gets put in the ROB. + //Check for any activity + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + toIEW->commitInfo[tid].usedROB = true; + toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid); + + if (rob->isEmpty(tid)) { + toIEW->commitInfo[tid].emptyROB = true; + } + + wroteToTimeBuffer = true; + changedROBNumEntries[tid] = false; + } } -#endif } -// Loop that goes through as many instructions in the ROB as possible and -// tries to commit them. The actual work for committing is done by the -// commitHead() function. template void -SimpleCommit::commitInsts() +DefaultCommit::commitInsts() { //////////////////////////////////// // Handle commit @@ -276,94 +789,193 @@ SimpleCommit::commitInsts() // Can't commit and squash things at the same time... //////////////////////////////////// - if (rob->isEmpty()) - return; - - DynInstPtr head_inst = rob->readHeadInst(); + DPRINTF(Commit, "Trying to commit instructions in the ROB.\n"); unsigned num_committed = 0; + DynInstPtr head_inst; +#if FULL_SYSTEM + if (commitStatus[0] == FetchTrapPending) { + DPRINTF(Commit, "Fault from fetch is pending.\n"); + if (rob->isEmpty()) { + fetchTrapWait++; + if (fetchTrapWait > 10000000) { + panic("Fetch trap has been pending for a long time!"); + } + if (fetchFaultTick > curTick) { + DPRINTF(Commit, "Not enough cycles since fault, fault will " + "happen on %lli\n", + fetchFaultTick); + cpu->activityThisCycle(); + return; + } else if (iewStage->hasStoresToWB()) { + DPRINTF(Commit, "IEW still has stores to WB. Waiting until " + "they are completed. fetchTrapWait:%i\n", + fetchTrapWait); + cpu->activityThisCycle(); + return; + } else if (cpu->inPalMode(readPC())) { + DPRINTF(Commit, "In pal mode right now. fetchTrapWait:%i\n", + fetchTrapWait); + return; + } + fetchTrapWait = 0; + DPRINTF(Commit, "ROB is empty, handling fetch trap.\n"); + + assert(!thread[0]->inSyscall); + + thread[0]->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + cpu->trap(fetchFault, 0); + + // Exit state update mode to avoid accidental updating. + thread[0]->inSyscall = false; + + commitStatus[0] = TrapPending; + // Set it up so that we squash next cycle + trapSquash[0] = true; + return; + } + } +#endif // Commit as many instructions as possible until the commit bandwidth // limit is reached, or it becomes impossible to commit any more. - while (!rob->isEmpty() && - head_inst->readyToCommit() && - num_committed < commitWidth) - { - DPRINTF(Commit, "Commit: Trying to commit head instruction.\n"); + while (num_committed < commitWidth) { + int commit_thread = getCommittingThread(); + + if (commit_thread == -1 || !rob->isHeadReady(commit_thread)) + break; + + head_inst = rob->readHeadInst(commit_thread); + + int tid = head_inst->threadNumber; + + assert(tid == commit_thread); + + DPRINTF(Commit, "Trying to commit head instruction, [sn:%i] [tid:%i]\n", + head_inst->seqNum, tid); // If the head instruction is squashed, it is ready to retire at any // time. However, we need to avoid updating any other state // incorrectly if it's already been squashed. if (head_inst->isSquashed()) { - DPRINTF(Commit, "Commit: Retiring squashed instruction from " + DPRINTF(Commit, "Retiring squashed instruction from " "ROB.\n"); // Tell ROB to retire head instruction. This retires the head // inst in the ROB without affecting any other stages. - rob->retireHead(); + rob->retireHead(commit_thread); ++commitSquashedInsts; + // Record that the number of ROB entries has changed. + changedROBNumEntries[tid] = true; } else { + PC[tid] = head_inst->readPC(); + nextPC[tid] = head_inst->readNextPC(); + // Increment the total number of non-speculative instructions // executed. // Hack for now: it really shouldn't happen until after the // commit is deemed to be successful, but this count is needed // for syscalls. - cpu->funcExeInst++; + thread[tid]->funcExeInst++; // Try to commit the head instruction. bool commit_success = commitHead(head_inst, num_committed); - // Update what instruction we are looking at if the commit worked. if (commit_success) { ++num_committed; - // Send back which instruction has been committed. - // @todo: Update this later when a wider pipeline is used. - // Hmm, can't really give a pointer here...perhaps the - // sequence number instead (copy). - toIEW->commitInfo.doneSeqNum = head_inst->seqNum; + // Record that the number of ROB entries has changed. + changedROBNumEntries[tid] = true; + + // Set the doneSeqNum to the youngest committed instruction. + toIEW->commitInfo[tid].doneSeqNum = head_inst->seqNum; ++commitCommittedInsts; - if (!head_inst->isNop()) { - cpu->instDone(); + // To match the old model, don't count nops and instruction + // prefetches towards the total commit count. + if (!head_inst->isNop() && !head_inst->isInstPrefetch()) { + cpu->instDone(tid); } + + PC[tid] = nextPC[tid]; +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread[tid]->inSyscall && !thread[tid]->trapPending); + oldpc = PC[tid]; + cpu->system->pcEventQueue.service( + thread[tid]->getXCProxy()); + count++; + } while (oldpc != PC[tid]); + if (count > 1) { + DPRINTF(Commit, "PC skip function event, stopping commit\n"); + break; + } +#endif } else { + DPRINTF(Commit, "Unable to commit head instruction PC:%#x " + "[tid:%i] [sn:%i].\n", + head_inst->readPC(), tid ,head_inst->seqNum); break; } } - - // Update the pointer to read the next instruction in the ROB. - head_inst = rob->readHeadInst(); } DPRINTF(CommitRate, "%i\n", num_committed); - n_committed_dist.sample(num_committed); + numCommittedDist.sample(num_committed); } template bool -SimpleCommit::commitHead(DynInstPtr &head_inst, unsigned inst_num) +DefaultCommit::commitHead(DynInstPtr &head_inst, unsigned inst_num) { // Make sure instruction is valid assert(head_inst); + int tid = head_inst->threadNumber; + // If the instruction is not executed yet, then it is a non-speculative // or store inst. Signal backwards that it should be executed. if (!head_inst->isExecuted()) { // Keep this number correct. We have not yet actually executed // and committed this instruction. - cpu->funcExeInst--; + thread[tid]->funcExeInst--; - if (head_inst->isNonSpeculative()) { - DPRINTF(Commit, "Commit: Encountered a store or non-speculative " - "instruction at the head of the ROB, PC %#x.\n", - head_inst->readPC()); + head_inst->reachedCommit = true; - toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; + if (head_inst->isNonSpeculative() || + head_inst->isMemBarrier() || + head_inst->isWriteBarrier()) { +#if !FULL_SYSTEM + // Hack to make sure syscalls aren't executed until all stores + // write back their data. This direct communication shouldn't + // be used for anything other than this. + if (inst_num > 0 || iewStage->hasStoresToWB()) +#else + if ((head_inst->isMemBarrier() || head_inst->isWriteBarrier() || + head_inst->isQuiesce()) && + iewStage->hasStoresToWB()) +#endif + { + DPRINTF(Commit, "Waiting for all stores to writeback.\n"); + return false; + } + + DPRINTF(Commit, "Encountered a barrier or non-speculative " + "instruction [sn:%lli] at the head of the ROB, PC %#x.\n", + head_inst->seqNum, head_inst->readPC()); + + // Send back the non-speculative instruction's sequence number. + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; // Change the instruction so it won't try to commit again until // it is executed. @@ -371,25 +983,34 @@ SimpleCommit::commitHead(DynInstPtr &head_inst, unsigned inst_num) ++commitNonSpecStalls; + return false; + } else if (head_inst->isLoad()) { + DPRINTF(Commit, "[sn:%lli]: Uncached load, PC %#x.\n", + head_inst->seqNum, head_inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; + toIEW->commitInfo[tid].uncached = true; + toIEW->commitInfo[tid].uncachedLoad = head_inst; + + head_inst->clearCanCommit(); + return false; } else { - panic("Commit: Trying to commit un-executed instruction " + panic("Trying to commit un-executed instruction " "of unknown type!\n"); } } // Now check if it's one of the special trap or barrier or // serializing instructions. - if (head_inst->isThreadSync() || - head_inst->isSerializing() || - head_inst->isMemBarrier() || - head_inst->isWriteBarrier() ) + if (head_inst->isThreadSync())/* || +// head_inst->isMemBarrier() || +head_inst->isWriteBarrier())*/ { - // Not handled for now. Mem barriers and write barriers are safe - // to simply let commit as memory accesses only happen once they - // reach the head of commit. Not sure about the other two. - panic("Serializing or barrier instructions" - " are not handled yet.\n"); + // Not handled for now. + panic("Barrier instructions are not handled yet.\n"); } // Check if the instruction caused a fault. If so, trap. @@ -398,7 +1019,32 @@ SimpleCommit::commitHead(DynInstPtr &head_inst, unsigned inst_num) if (inst_fault != NoFault) { if (!head_inst->isNop()) { #if FULL_SYSTEM - cpu->trap(inst_fault); + DPRINTF(Commit, "Inst [sn:%lli] PC %#x has a fault\n", + head_inst->seqNum, head_inst->readPC()); + + assert(!thread[tid]->inSyscall); + + thread[tid]->inSyscall = true; + + // Hack for now; DTB will sometimes need the machine instruction + // for when faults happen. So we will set it here, prior to the + // DTB possibly needing it for this translation. + thread[tid]->setInst( + static_cast(head_inst->staticInst->machInst)); + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + cpu->trap(inst_fault, tid); + + // Exit state update mode to avoid accidental updating. + thread[tid]->inSyscall = false; + + commitStatus[tid] = TrapPending; + + // Generate trap squash event. + generateTrapEvent(tid); + + return false; #else // !FULL_SYSTEM panic("fault (%d) detected @ PC %08p", inst_fault, head_inst->PC); @@ -409,37 +1055,32 @@ SimpleCommit::commitHead(DynInstPtr &head_inst, unsigned inst_num) // Check if we're really ready to commit. If not then return false. // I'm pretty sure all instructions should be able to commit if they've // reached this far. For now leave this in as a check. - if (!rob->isHeadReady()) { - panic("Commit: Unable to commit head instruction!\n"); + if (!rob->isHeadReady(tid)) { + panic("Unable to commit head instruction!\n"); return false; } - // If it's a branch, then send back branch prediction update info - // to the fetch stage. - // This should be handled in the iew stage if a mispredict happens... - if (head_inst->isControl()) { - -#if 0 - toIEW->nextPC = head_inst->readPC(); - //Maybe switch over to BTB incorrect. - toIEW->btbMissed = head_inst->btbMiss(); - toIEW->target = head_inst->nextPC; - //Maybe also include global history information. - //This simple version will have no branch prediction however. -#endif - ++commitCommittedBranches; } // Now that the instruction is going to be committed, finalize its // trace data. if (head_inst->traceData) { + head_inst->traceData->setFetchSeq(head_inst->seqNum); + head_inst->traceData->setCPSeq(thread[tid]->numInst); head_inst->traceData->finalize(); + head_inst->traceData = NULL; } - //Finally clear the head ROB entry. - rob->retireHead(); + // Update the commit rename map + for (int i = 0; i < head_inst->numDestRegs(); i++) { + renameMap[tid]->setEntry(head_inst->destRegIdx(i), + head_inst->renamedDestRegIdx(i)); + } + + // Finally clear the head ROB entry. + rob->retireHead(tid); // Return true to indicate that we have committed an instruction. return true; @@ -447,37 +1088,45 @@ SimpleCommit::commitHead(DynInstPtr &head_inst, unsigned inst_num) template void -SimpleCommit::getInsts() +DefaultCommit::getInsts() { ////////////////////////////////////// // Handle ROB functions ////////////////////////////////////// - // Read any issued instructions and place them into the ROB. Do this + // Read any renamed instructions and place them into the ROB. Do this // prior to squashing to avoid having instructions in the ROB that // don't get squashed properly. int insts_to_process = min((int)renameWidth, fromRename->size); - for (int inst_num = 0; - inst_num < insts_to_process; - ++inst_num) + for (int inst_num = 0; inst_num < insts_to_process; ++inst_num) { - if (!fromRename->insts[inst_num]->isSquashed()) { - DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n", - fromRename->insts[inst_num]->readPC()); - rob->insertInst(fromRename->insts[inst_num]); + DynInstPtr inst = fromRename->insts[inst_num]; + int tid = inst->threadNumber; + + if (!inst->isSquashed() && + commitStatus[tid] != ROBSquashing) { + changedROBNumEntries[tid] = true; + + DPRINTF(Commit, "Inserting PC %#x [sn:%i] [tid:%i] into ROB.\n", + inst->readPC(), inst->seqNum, tid); + + rob->insertInst(inst); + + assert(rob->getThreadEntries(tid) <= rob->getMaxEntries(tid)); + + youngestSeqNum[tid] = inst->seqNum; } else { - DPRINTF(Commit, "Commit: Instruction %i PC %#x was " + DPRINTF(Commit, "Instruction PC %#x [sn:%i] [tid:%i] was " "squashed, skipping.\n", - fromRename->insts[inst_num]->seqNum, - fromRename->insts[inst_num]->readPC()); + inst->readPC(), inst->seqNum, tid); } } } template void -SimpleCommit::markCompletedInsts() +DefaultCommit::markCompletedInsts() { // Grab completed insts out of the IEW instruction queue, and mark // instructions completed within the ROB. @@ -485,18 +1134,159 @@ SimpleCommit::markCompletedInsts() inst_num < fromIEW->size && fromIEW->insts[inst_num]; ++inst_num) { - DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n", - fromIEW->insts[inst_num]->readPC(), - fromIEW->insts[inst_num]->seqNum); + if (!fromIEW->insts[inst_num]->isSquashed()) { + DPRINTF(Commit, "[tid:%i]: Marking PC %#x, SN %i ready within ROB.\n", + fromIEW->insts[inst_num]->threadNumber, + fromIEW->insts[inst_num]->readPC(), + fromIEW->insts[inst_num]->seqNum); - // Mark the instruction as ready to commit. - fromIEW->insts[inst_num]->setCanCommit(); + // Mark the instruction as ready to commit. + fromIEW->insts[inst_num]->setCanCommit(); + } } } template uint64_t -SimpleCommit::readCommitPC() +DefaultCommit::readPC() { - return rob->readHeadPC(); + // @todo: Fix this single thread hack. + return PC[0]; +} + +template +void +DefaultCommit::setSquashing(unsigned tid) +{ + if (_status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + _status = Active; + cpu->activateStage(FullCPU::CommitIdx); + } + + if (commitStatus[tid] != ROBSquashing) { + commitStatus[tid] = ROBSquashing; + ++squashCounter; + } +} + +template +bool +DefaultCommit::robDoneSquashing() +{ + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isDoneSquashing(tid)) + return false; + } + + return true; +} + +//////////////////////////////////////// +// // +// SMT COMMIT POLICY MAITAINED HERE // +// // +//////////////////////////////////////// +template +int +DefaultCommit::getCommittingThread() +{ + if (numThreads > 1) { + switch (commitPolicy) { + + case Aggressive: + //If Policy is Aggressive, commit will call + //this function multiple times per + //cycle + return oldestReady(); + + case RoundRobin: + return roundRobin(); + + case OldestReady: + return oldestReady(); + + default: + return -1; + } + } else { + int tid = (*activeThreads).front(); + + if (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending) { + return tid; + } else { + return -1; + } + } +} + +template +int +DefaultCommit::roundRobin() +{ + list::iterator pri_iter = priority_list.begin(); + list::iterator end = priority_list.end(); + + while (pri_iter != end) { + unsigned tid = *pri_iter; + + if (commitStatus[tid] == Running || + commitStatus[tid] == Idle) { + + if (rob->isHeadReady(tid)) { + priority_list.erase(pri_iter); + priority_list.push_back(tid); + + return tid; + } + } + + pri_iter++; + } + + return -1; +} + +template +int +DefaultCommit::oldestReady() +{ + unsigned oldest = 0; + bool first = true; + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isEmpty(tid) && + (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending)) { + + if (rob->isHeadReady(tid)) { + + DynInstPtr head_inst = rob->readHeadInst(tid); + + if (first) { + oldest = tid; + first = false; + } else if (head_inst->seqNum < oldest) { + oldest = tid; + } + } + } + } + + if (!first) { + return oldest; + } else { + return -1; + } } diff --git a/cpu/o3/cpu.cc b/cpu/o3/cpu.cc index 62d68bb33..d322037bc 100644 --- a/cpu/o3/cpu.cc +++ b/cpu/o3/cpu.cc @@ -41,13 +41,21 @@ #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/cpu.hh" +#include "sim/stat_control.hh" + using namespace std; -BaseFullCPU::BaseFullCPU(Params ¶ms) - : BaseCPU(¶ms), cpu_id(0) +BaseFullCPU::BaseFullCPU(Params *params) + : BaseCPU(params), cpu_id(0) { } +void +BaseFullCPU::regStats() +{ + BaseCPU::regStats(); +} + template FullO3CPU::TickEvent::TickEvent(FullO3CPU *c) : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) @@ -70,96 +78,76 @@ FullO3CPU::TickEvent::description() //Call constructor to all the pipeline stages here template -FullO3CPU::FullO3CPU(Params ¶ms) -#if FULL_SYSTEM +FullO3CPU::FullO3CPU(Params *params) : BaseFullCPU(params), -#else - : BaseFullCPU(params), -#endif // FULL_SYSTEM tickEvent(this), + removeInstsThisCycle(false), fetch(params), decode(params), rename(params), iew(params), commit(params), - regFile(params.numPhysIntRegs, params.numPhysFloatRegs), + regFile(params->numPhysIntRegs, params->numPhysFloatRegs), - freeList(TheISA::NumIntRegs, params.numPhysIntRegs, - TheISA::NumFloatRegs, params.numPhysFloatRegs), + freeList(params->numberOfThreads,//number of activeThreads + TheISA::NumIntRegs, params->numPhysIntRegs, + TheISA::NumFloatRegs, params->numPhysFloatRegs), - renameMap(TheISA::NumIntRegs, params.numPhysIntRegs, - TheISA::NumFloatRegs, params.numPhysFloatRegs, - TheISA::NumMiscRegs, - TheISA::ZeroReg, - TheISA::ZeroReg + TheISA::NumIntRegs), + rob(params->numROBEntries, params->squashWidth, + params->smtROBPolicy, params->smtROBThreshold, + params->numberOfThreads), - rob(params.numROBEntries, params.squashWidth), + scoreboard(params->numberOfThreads,//number of activeThreads + TheISA::NumIntRegs, params->numPhysIntRegs, + TheISA::NumFloatRegs, params->numPhysFloatRegs, + TheISA::NumMiscRegs * number_of_threads, + TheISA::ZeroReg), // What to pass to these time buffers? // For now just have these time buffers be pretty big. + // @todo: Make these time buffer sizes parameters. timeBuffer(5, 5), fetchQueue(5, 5), decodeQueue(5, 5), renameQueue(5, 5), iewQueue(5, 5), - - cpuXC(NULL), + activityBuffer(5, 0), + activityCount(0), globalSeqNum(1), #if FULL_SYSTEM - system(params.system), + system(params->system), memCtrl(system->memctrl), physmem(system->physmem), - itb(params.itb), - dtb(params.dtb), - mem(params.mem), + mem(params->mem), #else - // Hardcoded for a single thread!! - mem(params.workload[0]->getMemory()), + pTable(params->pTable), #endif // FULL_SYSTEM - icacheInterface(params.icacheInterface), - dcacheInterface(params.dcacheInterface), - deferRegistration(params.defReg), - numInsts(0), - funcExeInst(0) + icacheInterface(params->icacheInterface), + dcacheInterface(params->dcacheInterface), + deferRegistration(params->deferRegistration) { _status = Idle; #if !FULL_SYSTEM - thread.resize(this->number_of_threads); + thread.resize(number_of_threads); + tids.resize(number_of_threads); #endif - for (int i = 0; i < this->number_of_threads; ++i) { -#if FULL_SYSTEM - assert(i == 0); - thread[i] = new CPUExecContext(this, 0, system, itb, dtb, mem); - system->execContexts[i] = thread[i]->getProxy(); - - execContexts.push_back(system->execContexts[i]); -#else - if (i < params.workload.size()) { - DPRINTF(FullCPU, "FullCPU: Workload[%i]'s starting PC is %#x, " - "process is %#x", - i, params.workload[i]->prog_entry, thread[i]); - thread[i] = new CPUExecContext(this, i, params.workload[i], i); - } - assert(params.workload[i]->getMemory() != NULL); - assert(mem != NULL); - execContexts.push_back(thread[i]->getProxy()); -#endif // !FULL_SYSTEM - } - - // Note that this is a hack so that my code which still uses xc-> will - // still work. I should remove this eventually - cpuXC = thread[0]; - // The stages also need their CPU pointer setup. However this must be // done at the upper level CPU because they have pointers to the upper // level CPU, and not this FullO3CPU. + // Set up Pointers to the activeThreads list for each stage + fetch.setActiveThreads(&activeThreads); + decode.setActiveThreads(&activeThreads); + rename.setActiveThreads(&activeThreads); + iew.setActiveThreads(&activeThreads); + commit.setActiveThreads(&activeThreads); + // Give each of the stages the time buffer they will use. fetch.setTimeBuffer(&timeBuffer); decode.setTimeBuffer(&timeBuffer); @@ -170,6 +158,7 @@ FullO3CPU::FullO3CPU(Params ¶ms) // Also setup each of the stages' queues. fetch.setFetchQueue(&fetchQueue); decode.setFetchQueue(&fetchQueue); + commit.setFetchQueue(&fetchQueue); decode.setDecodeQueue(&decodeQueue); rename.setDecodeQueue(&decodeQueue); rename.setRenameQueue(&renameQueue); @@ -178,16 +167,91 @@ FullO3CPU::FullO3CPU(Params ¶ms) commit.setIEWQueue(&iewQueue); commit.setRenameQueue(&renameQueue); - // Setup the rename map for whichever stages need it. - rename.setRenameMap(&renameMap); - iew.setRenameMap(&renameMap); + commit.setIEWStage(&iew); + rename.setIEWStage(&iew); + rename.setCommitStage(&commit); - // Setup the free list for whichever stages need it. + //Make Sure That this a Valid Architeture + //@todo: move this up in constructor + numThreads = number_of_threads; + +#if !FULL_SYSTEM + int activeThreads = params->workload.size(); +#else + int activeThreads = 1; +#endif + + assert(params->numPhysIntRegs >= numThreads * TheISA::NumIntRegs); + assert(params->numPhysFloatRegs >= numThreads * TheISA::NumFloatRegs); + + rename.setScoreboard(&scoreboard); + iew.setScoreboard(&scoreboard); + + // Setup the rename map for whichever stages need it. + PhysRegIndex lreg_idx = 0; + PhysRegIndex freg_idx = params->numPhysIntRegs; //Index to 1 after int regs + + for (int tid=0; tid < numThreads; tid++) { + bool bindRegs = (tid <= activeThreads - 1); + + commitRenameMap[tid].init(TheISA::NumIntRegs, + params->numPhysIntRegs, + lreg_idx, //Index for Logical. Regs + + TheISA::NumFloatRegs, + params->numPhysFloatRegs, + freg_idx, //Index for Float Regs + + TheISA::NumMiscRegs, + + TheISA::ZeroReg, + TheISA::ZeroReg, + + tid, + false); + + renameMap[tid].init(TheISA::NumIntRegs, + params->numPhysIntRegs, + lreg_idx, //Index for Logical. Regs + + TheISA::NumFloatRegs, + params->numPhysFloatRegs, + freg_idx, //Index for Float Regs + + TheISA::NumMiscRegs, + + TheISA::ZeroReg, + TheISA::ZeroReg, + + tid, + bindRegs); + } + + rename.setRenameMap(renameMap); + commit.setRenameMap(commitRenameMap); + + // Give renameMap & rename stage access to the freeList; + for (int i=0; i < numThreads; i++) { + renameMap[i].setFreeList(&freeList); + } rename.setFreeList(&freeList); - renameMap.setFreeList(&freeList); + + // Setup the page table for whichever stages need it. +#if !FULL_SYSTEM + fetch.setPageTable(pTable); + iew.setPageTable(pTable); +#endif // Setup the ROB for whichever stages need it. commit.setROB(&rob); + + lastRunningCycle = curTick; + + for (int i = 0; i < NumStages; ++i) { + stageActive[i] = false; + } + + contextSwitch = false; } template @@ -199,7 +263,58 @@ template void FullO3CPU::fullCPURegStats() { + BaseFullCPU::regStats(); + // Register any of the FullCPU's stats here. + timesIdled + .name(name() + ".timesIdled") + .desc("Number of times that the entire CPU went into an idle state and" + " unscheduled itself") + .prereq(timesIdled); + + idleCycles + .name(name() + ".idleCycles") + .desc("Total number of cycles that the CPU has spent unscheduled due " + "to idling") + .prereq(idleCycles); + + // Number of Instructions simulated + // -------------------------------- + // Should probably be in Base CPU but need templated + // MaxThreads so put in here instead + committedInsts + .init(numThreads) + .name(name() + ".committedInsts") + .desc("Number of Instructions Simulated"); + + totalCommittedInsts + .name(name() + ".committedInsts_total") + .desc("Number of Instructions Simulated"); + + cpi + .name(name() + ".cpi") + .desc("CPI: Cycles Per Instruction") + .precision(6); + cpi = simTicks / committedInsts; + + totalCpi + .name(name() + ".cpi_total") + .desc("CPI: Total CPI of All Threads") + .precision(6); + totalCpi = simTicks / totalCommittedInsts; + + ipc + .name(name() + ".ipc") + .desc("IPC: Instructions Per Cycle") + .precision(6); + ipc = committedInsts / simTicks; + + totalIpc + .name(name() + ".ipc_total") + .desc("IPC: Total IPC of All Threads") + .precision(6); + totalIpc = totalCommittedInsts / simTicks; + } template @@ -208,9 +323,11 @@ FullO3CPU::tick() { DPRINTF(FullCPU, "\n\nFullCPU: Ticking main, FullO3CPU.\n"); - //Tick each of the stages if they're actually running. - //Will want to figure out a way to unschedule itself if they're all - //going to be idle for a long time. + ++numCycles; + + activity = false; + + //Tick each of the stages fetch.tick(); decode.tick(); @@ -221,7 +338,11 @@ FullO3CPU::tick() commit.tick(); - // Now advance the time buffers, unless the stage is stalled. +#if !FULL_SYSTEM + doContextSwitch(); +#endif + + // Now advance the time buffers timeBuffer.advance(); fetchQueue.advance(); @@ -229,81 +350,310 @@ FullO3CPU::tick() renameQueue.advance(); iewQueue.advance(); - if (_status == Running && !tickEvent.scheduled()) + advanceActivityBuffer(); + + if (removeInstsThisCycle) { + cleanUpRemovedInsts(); + } + + if (activityCount && !tickEvent.scheduled()) { tickEvent.schedule(curTick + 1); + } + +#if !FULL_SYSTEM + updateThreadPriority(); +#endif + } template void FullO3CPU::init() { - if(!deferRegistration) - { - this->registerExecContexts(); + if (deferRegistration) { + return; + } + // Set inSyscall so that the CPU doesn't squash when initially + // setting up registers. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = true; + + registerExecContexts(); + + // Need to do a copy of the xc->regs into the CPU's regfile so + // that it can start properly. + + for (int tid=0; tid < number_of_threads; tid++) { // Need to do a copy of the xc->regs into the CPU's regfile so // that it can start properly. #if FULL_SYSTEM - ExecContext *src_xc = system->execContexts[0]; - TheISA::initCPU(src_xc, src_xc->readCpuId()); + ExecContext *src_xc = system->execContexts[tid]; #else - ExecContext *src_xc = thread[0]->getProxy(); + ExecContext *src_xc = thread[tid]->getXCProxy(); #endif - // First loop through the integer registers. - for (int i = 0; i < TheISA::NumIntRegs; ++i) - { - regFile.intRegFile[i] = src_xc->readIntReg(i); + // Threads start in the Suspended State + if (src_xc->status() != ExecContext::Suspended) { + continue; } - // Then loop through the floating point registers. - for (int i = 0; i < TheISA::NumFloatRegs; ++i) - { - regFile.floatRegFile[i].d = src_xc->readFloatRegDouble(i); - regFile.floatRegFile[i].q = src_xc->readFloatRegInt(i); - } -/* - // Then loop through the misc registers. - regFile.miscRegs.fpcr = src_xc->regs.miscRegs.fpcr; - regFile.miscRegs.uniq = src_xc->regs.miscRegs.uniq; - regFile.miscRegs.lock_flag = src_xc->regs.miscRegs.lock_flag; - regFile.miscRegs.lock_addr = src_xc->regs.miscRegs.lock_addr; -*/ - // Then finally set the PC and the next PC. - regFile.pc = src_xc->readPC(); - regFile.npc = src_xc->readNextPC(); +#if FULL_SYSTEM + TheISA::initCPU(src_xc, src_xc->readCpuId()); +#endif + } + + // Clear inSyscall. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = false; + + // Probably should just make a call to all the stages to init stage, + // regardless of whether or not they need it. Keeps it more independent. + fetch.initStage(); + iew.initStage(); + rename.initStage(); + commit.initStage(); + + commit.setThreads(thread); +} + +template +void +FullO3CPU::insertThread(unsigned tid) +{ + DPRINTF(FullCPU,"[tid:%i] Initializing thread data"); + // Will change now that the PC and thread state is internal to the CPU + // and not in the CPUExecContext. +#if 0 +#if FULL_SYSTEM + ExecContext *src_xc = system->execContexts[tid]; +#else + CPUExecContext *src_xc = thread[tid]; +#endif + + //Bind Int Regs to Rename Map + for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) { + PhysRegIndex phys_reg = freeList.getIntReg(); + + renameMap[tid].setEntry(ireg,phys_reg); + scoreboard.setReg(phys_reg); + } + + //Bind Float Regs to Rename Map + for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) { + PhysRegIndex phys_reg = freeList.getFloatReg(); + + renameMap[tid].setEntry(freg,phys_reg); + scoreboard.setReg(phys_reg); + } + + //Copy Thread Data Into RegFile + this->copyFromXC(tid); + + //Set PC/NPC + regFile.pc[tid] = src_xc->readPC(); + regFile.npc[tid] = src_xc->readNextPC(); + + src_xc->setStatus(ExecContext::Active); + + activateContext(tid,1); + + //Reset ROB/IQ/LSQ Entries + commit.rob->resetEntries(); + iew.resetEntries(); +#endif +} + +template +void +FullO3CPU::removeThread(unsigned tid) +{ + DPRINTF(FullCPU,"[tid:%i] Removing thread data"); +#if 0 + //Unbind Int Regs from Rename Map + for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) { + PhysRegIndex phys_reg = renameMap[tid].lookup(ireg); + + scoreboard.unsetReg(phys_reg); + freeList.addReg(phys_reg); + } + + //Unbind Float Regs from Rename Map + for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) { + PhysRegIndex phys_reg = renameMap[tid].lookup(freg); + + scoreboard.unsetReg(phys_reg); + freeList.addReg(phys_reg); + } + + //Copy Thread Data From RegFile + /* Fix Me: + * Do we really need to do this if we are removing a thread + * in the sense that it's finished (exiting)? If the thread is just + * being suspended we might... + */ +// this->copyToXC(tid); + + //Squash Throughout Pipeline + fetch.squash(0,tid); + decode.squash(tid); + rename.squash(tid); + + assert(iew.ldstQueue.getCount(tid) == 0); + + //Reset ROB/IQ/LSQ Entries + if (activeThreads.size() >= 1) { + commit.rob->resetEntries(); + iew.resetEntries(); + } +#endif +} + + +template +void +FullO3CPU::activateWhenReady(int tid) +{ + DPRINTF(FullCPU,"[tid:%i]: Checking if resources are available for incoming" + "(e.g. PhysRegs/ROB/IQ/LSQ) \n", + tid); + + bool ready = true; + + if (freeList.numFreeIntRegs() >= TheISA::NumIntRegs) { + DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough " + "Phys. Int. Regs.\n", + tid); + ready = false; + } else if (freeList.numFreeFloatRegs() >= TheISA::NumFloatRegs) { + DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough " + "Phys. Float. Regs.\n", + tid); + ready = false; + } else if (commit.rob->numFreeEntries() >= + commit.rob->entryAmount(activeThreads.size() + 1)) { + DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough " + "ROB entries.\n", + tid); + ready = false; + } else if (iew.instQueue.numFreeEntries() >= + iew.instQueue.entryAmount(activeThreads.size() + 1)) { + DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough " + "IQ entries.\n", + tid); + ready = false; + } else if (iew.ldstQueue.numFreeEntries() >= + iew.ldstQueue.entryAmount(activeThreads.size() + 1)) { + DPRINTF(FullCPU,"[tid:%i] Suspending thread due to not enough " + "LSQ entries.\n", + tid); + ready = false; + } + + if (ready) { + insertThread(tid); + + contextSwitch = false; + + cpuWaitList.remove(tid); + } else { + suspendContext(tid); + + //blocks fetch + contextSwitch = true; + + //do waitlist + cpuWaitList.push_back(tid); } } template void -FullO3CPU::activateContext(int thread_num, int delay) +FullO3CPU::activateContext(int tid, int delay) { + // Needs to set each stage to running as well. + list::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive == activeThreads.end()) { + //May Need to Re-code this if the delay variable is the + //delay needed for thread to activate + DPRINTF(FullCPU, "Adding Thread %i to active threads list\n", + tid); + + activeThreads.push_back(tid); + } + + assert(_status == Idle); scheduleTickEvent(delay); + // Be sure to signal that there's some activity so the CPU doesn't + // deschedule itself. + activityThisCycle(); + fetch.wakeFromQuiesce(); + _status = Running; } template void -FullO3CPU::suspendContext(int thread_num) +FullO3CPU::suspendContext(int tid) { - panic("suspendContext unimplemented!"); + DPRINTF(FullCPU,"[tid: %i]: Suspended ...\n", tid); + unscheduleTickEvent(); + _status = Idle; +/* + //Remove From Active List, if Active + list::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive != activeThreads.end()) { + DPRINTF(FullCPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(isActive); + } +*/ } template void -FullO3CPU::deallocateContext(int thread_num) +FullO3CPU::deallocateContext(int tid) { - panic("deallocateContext unimplemented!"); + DPRINTF(FullCPU,"[tid:%i]: Deallocating ...", tid); +/* + //Remove From Active List, if Active + list::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive != activeThreads.end()) { + DPRINTF(FullCPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(isActive); + + removeThread(tid); + } +*/ } template void -FullO3CPU::haltContext(int thread_num) +FullO3CPU::haltContext(int tid) { - panic("haltContext unimplemented!"); + DPRINTF(FullCPU,"[tid:%i]: Halted ...", tid); +/* + //Remove From Active List, if Active + list::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive != activeThreads.end()) { + DPRINTF(FullCPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(isActive); + + removeThread(tid); + } +*/ } template @@ -336,7 +686,6 @@ template InstSeqNum FullO3CPU::getAndIncrementInstSeq() { - // Hopefully this works right. return globalSeqNum++; } @@ -398,124 +747,274 @@ FullO3CPU::setFloatRegInt(int reg_idx, uint64_t val) template uint64_t -FullO3CPU::readPC() +FullO3CPU::readArchIntReg(int reg_idx, unsigned tid) { - return regFile.readPC(); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readIntReg(phys_reg); +} + +template +float +FullO3CPU::readArchFloatRegSingle(int reg_idx, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readFloatRegSingle(phys_reg); +} + +template +double +FullO3CPU::readArchFloatRegDouble(int reg_idx, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readFloatRegDouble(phys_reg); +} + +template +uint64_t +FullO3CPU::readArchFloatRegInt(int reg_idx, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readFloatRegInt(phys_reg); } template void -FullO3CPU::setNextPC(uint64_t val) +FullO3CPU::setArchIntReg(int reg_idx, uint64_t val, unsigned tid) { - regFile.setNextPC(val); + if (reg_idx == TheISA::ZeroReg) { + warn("Setting r31 through ArchIntReg in CPU, cycle %i\n", curTick); + } + + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setIntReg(phys_reg, val); } template void -FullO3CPU::setPC(Addr new_PC) +FullO3CPU::setArchFloatRegSingle(int reg_idx, float val, unsigned tid) { - regFile.setPC(new_PC); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatRegSingle(phys_reg, val); } template void +FullO3CPU::setArchFloatRegDouble(int reg_idx, double val, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatRegDouble(phys_reg, val); +} + +template +void +FullO3CPU::setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatRegInt(phys_reg, val); +} + +template +uint64_t +FullO3CPU::readPC(unsigned tid) +{ + return commit.readPC(tid); +} + +template +void +FullO3CPU::setPC(Addr new_PC,unsigned tid) +{ + commit.setPC(new_PC, tid); +} + +template +uint64_t +FullO3CPU::readNextPC(unsigned tid) +{ + return commit.readNextPC(tid); +} + +template +void +FullO3CPU::setNextPC(uint64_t val,unsigned tid) +{ + commit.setNextPC(val, tid); +} + +template +typename FullO3CPU::ListIt FullO3CPU::addInst(DynInstPtr &inst) { instList.push_back(inst); + + return --(instList.end()); } template void -FullO3CPU::instDone() +FullO3CPU::instDone(unsigned tid) { // Keep an instruction count. - numInsts++; + thread[tid]->numInst++; + thread[tid]->numInsts++; + committedInsts[tid]++; + totalCommittedInsts++; // Check for instruction-count-based events. - comInstEventQueue[0]->serviceEvents(numInsts); + comInstEventQueue[tid]->serviceEvents(thread[tid]->numInst); } template void -FullO3CPU::removeBackInst(DynInstPtr &inst) +FullO3CPU::addToRemoveList(DynInstPtr &inst) { - DynInstPtr inst_to_delete; + removeInstsThisCycle = true; - // Walk through the instruction list, removing any instructions - // that were inserted after the given instruction, inst. - while (instList.back() != inst) - { - assert(!instList.empty()); - - // Obtain the pointer to the instruction. - inst_to_delete = instList.back(); - - DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n", - inst_to_delete->seqNum, inst_to_delete->readPC()); - - // Remove the instruction from the list. - instList.pop_back(); - - // Mark it as squashed. - inst_to_delete->setSquashed(); - } + removeList.push(inst->getInstListIt()); } template void FullO3CPU::removeFrontInst(DynInstPtr &inst) { - DynInstPtr inst_to_remove; + unsigned tid = inst->threadNumber; - // The front instruction should be the same one being asked to be removed. - assert(instList.front() == inst); + DPRINTF(FullCPU, "FullCPU: Removing committed instruction [tid:%i] PC %#x " + "[sn:%lli]\n", + tid, inst->readPC(), inst->seqNum); + + removeInstsThisCycle = true; // Remove the front instruction. - inst_to_remove = inst; - instList.pop_front(); - - DPRINTF(FullCPU, "FullCPU: Removing committed instruction %#x, PC %#x\n", - inst_to_remove, inst_to_remove->readPC()); + removeList.push(inst->getInstListIt()); } template void -FullO3CPU::removeInstsNotInROB() +FullO3CPU::removeInstsNotInROB(unsigned tid) { - DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction " - "list.\n"); + DPRINTF(FullCPU, "FullCPU: Thread %i: Deleting instructions from instruction" + " list.\n", tid); - DynInstPtr rob_tail = rob.readTailInst(); + ListIt end_it; - removeBackInst(rob_tail); -} + bool rob_empty = false; -template -void -FullO3CPU::removeInstsUntil(const InstSeqNum &seq_num) -{ - DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction " - "list.\n"); - - DynInstPtr inst_to_delete; - - while (instList.back()->seqNum > seq_num) { - assert(!instList.empty()); - - // Obtain the pointer to the instruction. - inst_to_delete = instList.back(); - - DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n", - inst_to_delete->seqNum, inst_to_delete->readPC()); - - // Remove the instruction from the list. - instList.back() = NULL; - instList.pop_back(); - - // Mark it as squashed. - inst_to_delete->setSquashed(); + if (instList.empty()) { + return; + } else if (rob.isEmpty(/*tid*/)) { + DPRINTF(FullCPU, "FullCPU: ROB is empty, squashing all insts.\n"); + end_it = instList.begin(); + rob_empty = true; + } else { + end_it = (rob.readTailInst(tid))->getInstListIt(); + DPRINTF(FullCPU, "FullCPU: ROB is not empty, squashing insts not in ROB.\n"); } + removeInstsThisCycle = true; + + ListIt inst_it = instList.end(); + + inst_it--; + + // Walk through the instruction list, removing any instructions + // that were inserted after the given instruction iterator, end_it. + while (inst_it != end_it) { + assert(!instList.empty()); + + bool break_loop = (inst_it == instList.begin()); + + squashInstIt(inst_it, tid); + + inst_it--; + + if (break_loop) + break; + } + + // If the ROB was empty, then we actually need to remove the first + // instruction as well. + if (rob_empty) { + squashInstIt(inst_it, tid); + } +} + +template +void +FullO3CPU::removeInstsUntil(const InstSeqNum &seq_num, + unsigned tid) +{ + assert(!instList.empty()); + + removeInstsThisCycle = true; + + ListIt inst_iter = instList.end(); + + inst_iter--; + + DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction " + "list that are from [tid:%i] and above [sn:%lli] (end=%lli).\n", + tid, seq_num, (*inst_iter)->seqNum); + + while ((*inst_iter)->seqNum > seq_num) { + + bool break_loop = (inst_iter == instList.begin()); + + squashInstIt(inst_iter, tid); + + inst_iter--; + + if (break_loop) + break; + } +} + +template +inline void +FullO3CPU::squashInstIt(const ListIt &instIt, const unsigned &tid) +{ + if ((*instIt)->threadNumber == tid) { + DPRINTF(FullCPU, "FullCPU: Squashing instruction, " + "[tid:%i] [sn:%lli] PC %#x\n", + (*instIt)->threadNumber, + (*instIt)->seqNum, + (*instIt)->readPC()); + + // Mark it as squashed. + (*instIt)->setSquashed(); + + //@todo: Formulate a consistent method for deleting + //instructions from the instruction list + // Remove the instruction from the list. + removeList.push(instIt); + } +} + +template +void +FullO3CPU::cleanUpRemovedInsts() +{ + while (!removeList.empty()) { + DPRINTF(FullCPU, "FullCPU: Removing instruction, " + "[tid:%i] [sn:%lli] PC %#x\n", + (*removeList.front())->threadNumber, + (*removeList.front())->seqNum, + (*removeList.front())->readPC()); + + instList.erase(removeList.front()); + + removeList.pop(); + } + + removeInstsThisCycle = false; } template @@ -530,16 +1029,22 @@ void FullO3CPU::dumpInsts() { int num = 0; - typename list::iterator inst_list_it = instList.begin(); - while (inst_list_it != instList.end()) - { - cprintf("Instruction:%i\nPC:%#x\nSN:%lli\nIssued:%i\nSquashed:%i\n\n", - num, (*inst_list_it)->readPC(), (*inst_list_it)->seqNum, - (*inst_list_it)->isIssued(), (*inst_list_it)->isSquashed()); + ListIt inst_list_it = instList.begin(); + + cprintf("Dumping Instruction List\n"); + + while (inst_list_it != instList.end()) { + cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*inst_list_it)->readPC(), (*inst_list_it)->threadNumber, + (*inst_list_it)->seqNum, (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); inst_list_it++; ++num; } + + } template @@ -549,5 +1054,139 @@ FullO3CPU::wakeDependents(DynInstPtr &inst) iew.wakeDependents(inst); } +template +void +FullO3CPU::wakeCPU() +{ + if (activityCount || tickEvent.scheduled()) { + return; + } + + idleCycles += curTick - lastRunningCycle; + + tickEvent.schedule(curTick); +} + +template +void +FullO3CPU::activityThisCycle() +{ + if (activityBuffer[0]) { + return; + } + + activityBuffer[0] = true; + activity = true; + ++activityCount; + + DPRINTF(Activity, "Activity: %i\n", activityCount); +} + +template +void +FullO3CPU::advanceActivityBuffer() +{ + if (activityBuffer[-5]) { + --activityCount; + + assert(activityCount >= 0); + + DPRINTF(Activity, "Activity: %i\n", activityCount); + + if (activityCount == 0) { + DPRINTF(FullCPU, "No activity left, going to idle!\n"); + lastRunningCycle = curTick; + timesIdled++; + } + } + + activityBuffer.advance(); +} + +template +void +FullO3CPU::activateStage(const StageIdx idx) +{ + if (!stageActive[idx]) { + ++activityCount; + + stageActive[idx] = true; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already active.\n", idx); + } + + // @todo: Number is hardcoded for now. Replace with parameter. + assert(activityCount < 15); +} + +template +void +FullO3CPU::deactivateStage(const StageIdx idx) +{ + if (stageActive[idx]) { + --activityCount; + + stageActive[idx] = false; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already inactive.\n", idx); + } + + assert(activityCount >= 0); +} + +template +int +FullO3CPU::getFreeTid() +{ + for (int i=0; i < numThreads; i++) { + if (!tids[i]) { + tids[i] = true; + return i; + } + } + + return -1; +} + +template +void +FullO3CPU::doContextSwitch() +{ + if (contextSwitch) { + + //ADD CODE TO DEACTIVE THREAD HERE (???) + + for (int tid=0; tid < cpuWaitList.size(); tid++) { + activateWhenReady(tid); + } + + if (cpuWaitList.size() == 0) + contextSwitch = true; + } +} + +template +void +FullO3CPU::updateThreadPriority() +{ + if (activeThreads.size() > 1) + { + //DEFAULT TO ROUND ROBIN SCHEME + //e.g. Move highest priority to end of thread list + list::iterator list_begin = activeThreads.begin(); + list::iterator list_end = activeThreads.end(); + + unsigned high_thread = *list_begin; + + activeThreads.erase(list_begin); + + activeThreads.push_back(high_thread); + } +} + // Forward declaration of FullO3CPU. template class FullO3CPU; diff --git a/cpu/o3/cpu.hh b/cpu/o3/cpu.hh index 6577e46e4..91eaf9d6f 100644 --- a/cpu/o3/cpu.hh +++ b/cpu/o3/cpu.hh @@ -26,18 +26,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -//Todo: Add in a lot of the functions that are ISA specific. Also define -//the functions that currently exist within the base cpu class. Define -//everything for the simobject stuff so it can be serialized and -//instantiated, add in debugging statements everywhere. Have CPU schedule -//itself properly. Threads! -// Avoid running stages and advancing queues if idle/stalled. - -#ifndef __CPU_O3_CPU_FULL_CPU_HH__ -#define __CPU_O3_CPU_FULL_CPU_HH__ +#ifndef __CPU_O3_FULL_CPU_HH__ +#define __CPU_O3_FULL_CPU_HH__ #include #include +#include +#include #include #include "base/statistics.hh" @@ -47,10 +42,12 @@ #include "cpu/cpu_exec_context.hh" #include "cpu/o3/comm.hh" #include "cpu/o3/cpu_policy.hh" +#include "cpu/o3/scoreboard.hh" +#include "cpu/o3/thread_state.hh" #include "sim/process.hh" class ExecContext; -class FunctionalMemory; +class MemInterface; class Process; class BaseFullCPU : public BaseCPU @@ -59,11 +56,9 @@ class BaseFullCPU : public BaseCPU public: typedef BaseCPU::Params Params; -#if FULL_SYSTEM - BaseFullCPU(Params ¶ms); -#else - BaseFullCPU(Params ¶ms); -#endif // FULL_SYSTEM + BaseFullCPU(Params *params); + + void regStats(); protected: int cpu_id; @@ -78,31 +73,42 @@ class FullO3CPU : public BaseFullCPU typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; + typedef O3ThreadState Thread; + + typedef typename std::list::iterator ListIt; + public: enum Status { Running, Idle, Halted, - Blocked // ? + Blocked }; + /** Overall CPU status. */ Status _status; private: class TickEvent : public Event { private: + /** Pointer to the CPU. */ FullO3CPU *cpu; public: + /** Constructs a tick event. */ TickEvent(FullO3CPU *c); + + /** Processes a tick event, calling tick() on the CPU. */ void process(); + /** Returns the description of the tick event. */ const char *description(); }; + /** The tick event used for scheduling CPU ticks. */ TickEvent tickEvent; - /// Schedule tick event, regardless of its current state. + /** Schedule tick event, regardless of its current state. */ void scheduleTickEvent(int delay) { if (tickEvent.squashed()) @@ -111,7 +117,7 @@ class FullO3CPU : public BaseFullCPU tickEvent.schedule(curTick + delay); } - /// Unschedule tick event, regardless of its current state. + /** Unschedule tick event, regardless of its current state. */ void unscheduleTickEvent() { if (tickEvent.scheduled()) @@ -119,21 +125,82 @@ class FullO3CPU : public BaseFullCPU } public: - FullO3CPU(Params ¶ms); + /** Constructs a CPU with the given parameters. */ + FullO3CPU(Params *params); + /** Destructor. */ ~FullO3CPU(); + /** Registers statistics. */ void fullCPURegStats(); + /** Ticks CPU, calling tick() on each stage, and checking the overall + * activity to see if the CPU should deschedule itself. + */ void tick(); + /** Initialize the CPU */ void init(); - void activateContext(int thread_num, int delay); - void suspendContext(int thread_num); - void deallocateContext(int thread_num); - void haltContext(int thread_num); + /** Setup CPU to insert a thread's context */ + void insertThread(unsigned tid); + /** Remove all of a thread's context from CPU */ + void removeThread(unsigned tid); + + /** Count the Total Instructions Committed in the CPU. */ + virtual Counter totalInstructions() const + { + Counter total(0); + + for (int i=0; i < thread.size(); i++) + total += thread[i]->numInst; + + return total; + } + + /** Add Thread to Active Threads List. */ + void activateContext(int tid, int delay); + + /** Remove Thread from Active Threads List */ + void suspendContext(int tid); + + /** Remove Thread from Active Threads List && + * Remove Thread Context from CPU. + */ + void deallocateContext(int tid); + + /** Remove Thread from Active Threads List && + * Remove Thread Context from CPU. + */ + void haltContext(int tid); + + /** Activate a Thread When CPU Resources are Available. */ + void activateWhenReady(int tid); + + /** Add or Remove a Thread Context in the CPU. */ + void doContextSwitch(); + + /** Update The Order In Which We Process Threads. */ + void updateThreadPriority(); + + /** Executes a syscall on this cycle. + * --------------------------------------- + * Note: this is a virtual function. CPU-Specific + * functionality defined in derived classes + */ + virtual void syscall(int tid) {} + + /** Check if there are any system calls pending. */ + void checkSyscalls(); + + /** Switches out this CPU. + * @todo: Implement this. + */ void switchOut(); + + /** Takes over from another CPU. + * @todo: Implement this. + */ void takeOverFrom(BaseCPU *oldCPU); /** Get the current instruction sequence number, and increment it. */ @@ -147,21 +214,28 @@ class FullO3CPU : public BaseFullCPU bool validDataAddr(Addr addr) { return true; } /** Get instruction asid. */ - int getInstAsid() - { return regFile.miscRegs.getInstAsid(); } + int getInstAsid(unsigned tid) + { return regFile.miscRegs[tid].getInstAsid(); } /** Get data asid. */ - int getDataAsid() - { return regFile.miscRegs.getDataAsid(); } + int getDataAsid(unsigned tid) + { return regFile.miscRegs[tid].getDataAsid(); } #else - bool validInstAddr(Addr addr) - { return thread[0]->validInstAddr(addr); } + /** Check if this address is a valid instruction address. */ + bool validInstAddr(Addr addr,unsigned tid) + { return thread[tid]->validInstAddr(addr); } - bool validDataAddr(Addr addr) - { return thread[0]->validDataAddr(addr); } + /** Check if this address is a valid data address. */ + bool validDataAddr(Addr addr,unsigned tid) + { return thread[tid]->validDataAddr(addr); } - int getInstAsid() { return thread[0]->getInstAsid(); } - int getDataAsid() { return thread[0]->getDataAsid(); } + /** Get instruction asid. */ + int getInstAsid(unsigned tid) + { return thread[tid]->asid; } + + /** Get data asid. */ + int getDataAsid(unsigned tid) + { return thread[tid]->asid; } #endif @@ -184,29 +258,40 @@ class FullO3CPU : public BaseFullCPU void setFloatRegInt(int reg_idx, uint64_t val); - uint64_t readPC(); + uint64_t readArchIntReg(int reg_idx, unsigned tid); - void setNextPC(uint64_t val); + float readArchFloatRegSingle(int reg_idx, unsigned tid); - void setPC(Addr new_PC); + double readArchFloatRegDouble(int reg_idx, unsigned tid); + + uint64_t readArchFloatRegInt(int reg_idx, unsigned tid); + + void setArchIntReg(int reg_idx, uint64_t val, unsigned tid); + + void setArchFloatRegSingle(int reg_idx, float val, unsigned tid); + + void setArchFloatRegDouble(int reg_idx, double val, unsigned tid); + + void setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid); + + uint64_t readPC(unsigned tid); + + void setPC(Addr new_PC,unsigned tid); + + uint64_t readNextPC(unsigned tid); + + void setNextPC(uint64_t val,unsigned tid); /** Function to add instruction onto the head of the list of the * instructions. Used when new instructions are fetched. */ - void addInst(DynInstPtr &inst); + ListIt addInst(DynInstPtr &inst); /** Function to tell the CPU that an instruction has completed. */ - void instDone(); + void instDone(unsigned tid); - /** Remove all instructions in back of the given instruction, but leave - * that instruction in the list. This is useful in a squash, when there - * are instructions in this list that don't exist in structures such as - * the ROB. The instruction doesn't have to be the last instruction in - * the list, but will be once this function completes. - * @todo: Remove only up until that inst? Squashed inst is most likely - * valid. - */ - void removeBackInst(DynInstPtr &inst); + /** Add Instructions to the CPU Remove List*/ + void addToRemoveList(DynInstPtr &inst); /** Remove an instruction from the front of the list. It is expected * that there are no instructions in front of it (that is, none are older @@ -218,10 +303,14 @@ class FullO3CPU : public BaseFullCPU void removeFrontInst(DynInstPtr &inst); /** Remove all instructions that are not currently in the ROB. */ - void removeInstsNotInROB(); + void removeInstsNotInROB(unsigned tid); /** Remove all instructions younger than the given sequence number. */ - void removeInstsUntil(const InstSeqNum &seq_num); + void removeInstsUntil(const InstSeqNum &seq_num,unsigned tid); + + inline void squashInstIt(const ListIt &instIt, const unsigned &tid); + + void cleanUpRemovedInsts(); /** Remove all instructions from the list. */ void removeAllInsts(); @@ -236,43 +325,38 @@ class FullO3CPU : public BaseFullCPU public: /** List of all the instructions in flight. */ - list instList; + std::list instList; + + /** List of all the instructions that will be removed at the end of this + * cycle. + */ + std::queue removeList; + +#ifdef DEBUG + std::set snList; +#endif + + /** Records if instructions need to be removed this cycle due to being + * retired or squashed. + */ + bool removeInstsThisCycle; - //not sure these should be private. protected: /** The fetch stage. */ typename CPUPolicy::Fetch fetch; - /** The fetch stage's status. */ - typename CPUPolicy::Fetch::Status fetchStatus; - /** The decode stage. */ typename CPUPolicy::Decode decode; - /** The decode stage's status. */ - typename CPUPolicy::Decode::Status decodeStatus; - /** The dispatch stage. */ typename CPUPolicy::Rename rename; - /** The dispatch stage's status. */ - typename CPUPolicy::Rename::Status renameStatus; - /** The issue/execute/writeback stages. */ typename CPUPolicy::IEW iew; - /** The issue/execute/writeback stage's status. */ - typename CPUPolicy::IEW::Status iewStatus; - /** The commit stage. */ typename CPUPolicy::Commit commit; - /** The fetch stage's status. */ - typename CPUPolicy::Commit::Status commitStatus; - - //Might want to just pass these objects in to the constructors of the - //appropriate stage. regFile is in iew, freeList in dispatch, renameMap - //in dispatch, and the rob in commit. /** The register file. */ typename CPUPolicy::RegFile regFile; @@ -280,12 +364,33 @@ class FullO3CPU : public BaseFullCPU typename CPUPolicy::FreeList freeList; /** The rename map. */ - typename CPUPolicy::RenameMap renameMap; + typename CPUPolicy::RenameMap renameMap[Impl::MaxThreads]; + + /** The commit rename map. */ + typename CPUPolicy::RenameMap commitRenameMap[Impl::MaxThreads]; /** The re-order buffer. */ typename CPUPolicy::ROB rob; + /** Active Threads List */ + std::list activeThreads; + + /** Integer Register Scoreboard */ + Scoreboard scoreboard; + public: + /** Enum to give each stage a specific index, so when calling + * activateStage() or deactivateStage(), they can specify which stage + * is being activated/deactivated. + */ + enum StageIdx { + FetchIdx, + DecodeIdx, + RenameIdx, + IEWIdx, + CommitIdx, + NumStages }; + /** Typedefs from the Impl to get the structs that each of the * time buffers should use. */ @@ -314,46 +419,123 @@ class FullO3CPU : public BaseFullCPU /** The IEW stage's instruction queue. */ TimeBuffer iewQueue; + private: + /** Time buffer that tracks if any cycles has active communication in them. + * It should be as long as the longest communication latency in the system. + * Each time any time buffer is written, the activity buffer should also + * be written to. The activityBuffer is advanced along with all the other + * time buffers, so it should always have a 1 somewhere in it only if there + * is active communication in a time buffer. + */ + TimeBuffer activityBuffer; + + /** Tracks how many stages and cycles of time buffer have activity. Stages + * increment this count when they switch to active, and decrement it when + * they switch to inactive. Whenever a cycle that previously had no + * information is written in the time buffer, this is incremented. When + * a cycle that had information exits the time buffer due to age, this + * count is decremented. When the count is 0, there is no activity in the + * CPU, and it can be descheduled. + */ + int activityCount; + + /** Records if there has been activity this cycle. */ + bool activity; + + /** Records which stages are active/inactive. */ + bool stageActive[NumStages]; + public: - /** The temporary exec context to support older accessors. */ - CPUExecContext *cpuXC; + /** Wakes the CPU, rescheduling the CPU if it's not already active. */ + void wakeCPU(); + /** Records that there is activity this cycle. */ + void activityThisCycle(); + /** Advances the activity buffer, decrementing the activityCount if active + * communication just left the time buffer, and descheduling the CPU if + * there is no activity. + */ + void advanceActivityBuffer(); + /** Marks a stage as active. */ + void activateStage(const StageIdx idx); + /** Deactivates a stage. */ + void deactivateStage(const StageIdx idx); + /** Gets a free thread id. Use if thread ids change across system. */ + int getFreeTid(); + + public: /** Temporary function to get pointer to exec context. */ - ExecContext *xcBase() + ExecContext *xcBase(unsigned tid) { - return thread[0]->getProxy(); - } - - CPUExecContext *cpuXCBase() - { - return thread[0]; + return thread[tid]->getXCProxy(); } + /** The global sequence number counter. */ InstSeqNum globalSeqNum; #if FULL_SYSTEM + /** Pointer to the system. */ System *system; + /** Pointer to the memory controller. */ MemoryController *memCtrl; + /** Pointer to physical memory. */ PhysicalMemory *physmem; - - AlphaITB *itb; - AlphaDTB *dtb; - -// SWContext *swCtx; #endif - std::vector thread; + // List of all ExecContexts. + std::vector thread; + + /** Pointer to memory. */ FunctionalMemory *mem; +#if 0 + /** Page table pointer. */ + PageTable *pTable; +#endif + + /** Pointer to the icache interface. */ MemInterface *icacheInterface; + /** Pointer to the dcache interface. */ MemInterface *dcacheInterface; + /** Whether or not the CPU should defer its registration. */ bool deferRegistration; - Counter numInsts; + /** Is there a context switch pending? */ + bool contextSwitch; - Counter funcExeInst; + /** Threads Scheduled to Enter CPU */ + std::list cpuWaitList; + + /** The cycle that the CPU was last running, used for statistics. */ + Tick lastRunningCycle; + + /** Number of Threads CPU can process */ + unsigned numThreads; + + /** Mapping for system thread id to cpu id */ + std::map threadMap; + + /** Available thread ids in the cpu*/ + std::vector tids; + + /** Stat for total number of times the CPU is descheduled. */ + Stats::Scalar<> timesIdled; + /** Stat for total number of cycles the CPU spends descheduled. */ + Stats::Scalar<> idleCycles; + /** Stat for the number of committed instructions per thread. */ + Stats::Vector<> committedInsts; + /** Stat for the total number of committed instructions. */ + Stats::Scalar<> totalCommittedInsts; + /** Stat for the CPI per thread. */ + Stats::Formula cpi; + /** Stat for the total CPI. */ + Stats::Formula totalCpi; + /** Stat for the IPC per thread. */ + Stats::Formula ipc; + /** Stat for the total IPC. */ + Stats::Formula totalIpc; }; #endif diff --git a/cpu/o3/cpu_policy.hh b/cpu/o3/cpu_policy.hh index 41f06f81b..52227013e 100644 --- a/cpu/o3/cpu_policy.hh +++ b/cpu/o3/cpu_policy.hh @@ -26,13 +26,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_CPU_POLICY_HH__ -#define __CPU_O3_CPU_CPU_POLICY_HH__ +#ifndef __CPU_O3_CPU_POLICY_HH__ +#define __CPU_O3_CPU_POLICY_HH__ #include "cpu/o3/bpred_unit.hh" #include "cpu/o3/free_list.hh" #include "cpu/o3/inst_queue.hh" -#include "cpu/o3/ldstq.hh" +#include "cpu/o3/lsq.hh" +#include "cpu/o3/lsq_unit.hh" #include "cpu/o3/mem_dep_unit.hh" #include "cpu/o3/regfile.hh" #include "cpu/o3/rename_map.hh" @@ -57,32 +58,34 @@ struct SimpleCPUPolicy typedef ROB ROB; typedef InstructionQueue IQ; typedef MemDepUnit MemDepUnit; - typedef LDSTQ LDSTQ; + typedef LSQ LSQ; + typedef LSQUnit LSQUnit; - typedef SimpleFetch Fetch; - typedef SimpleDecode Decode; - typedef SimpleRename Rename; - typedef SimpleIEW IEW; - typedef SimpleCommit Commit; + + typedef DefaultFetch Fetch; + typedef DefaultDecode Decode; + typedef DefaultRename Rename; + typedef DefaultIEW IEW; + typedef DefaultCommit Commit; /** The struct for communication between fetch and decode. */ - typedef SimpleFetchSimpleDecode FetchStruct; + typedef DefaultFetchDefaultDecode FetchStruct; /** The struct for communication between decode and rename. */ - typedef SimpleDecodeSimpleRename DecodeStruct; + typedef DefaultDecodeDefaultRename DecodeStruct; /** The struct for communication between rename and IEW. */ - typedef SimpleRenameSimpleIEW RenameStruct; + typedef DefaultRenameDefaultIEW RenameStruct; /** The struct for communication between IEW and commit. */ - typedef SimpleIEWSimpleCommit IEWStruct; + typedef DefaultIEWDefaultCommit IEWStruct; /** The struct for communication within the IEW stage. */ typedef IssueStruct IssueStruct; /** The struct for all backwards communication. */ - typedef TimeBufStruct TimeStruct; + typedef TimeBufStruct TimeStruct; }; -#endif //__CPU_O3_CPU_CPU_POLICY_HH__ +#endif //__CPU_O3_CPU_POLICY_HH__ diff --git a/cpu/o3/decode.cc b/cpu/o3/decode.cc index 290648318..b14fbb7a3 100644 --- a/cpu/o3/decode.cc +++ b/cpu/o3/decode.cc @@ -30,4 +30,4 @@ #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/decode_impl.hh" -template class SimpleDecode; +template class DefaultDecode; diff --git a/cpu/o3/decode.hh b/cpu/o3/decode.hh index 5b9a0f822..279ff556e 100644 --- a/cpu/o3/decode.hh +++ b/cpu/o3/decode.hh @@ -26,16 +26,23 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_SIMPLE_DECODE_HH__ -#define __CPU_O3_CPU_SIMPLE_DECODE_HH__ +#ifndef __CPU_O3_DECODE_HH__ +#define __CPU_O3_DECODE_HH__ #include #include "base/statistics.hh" #include "base/timebuf.hh" +/** + * DefaultDecode class handles both single threaded and SMT decode. Its width is + * specified by the parameters; each cycles it tries to decode that many + * instructions. Because instructions are actually decoded when the StaticInst + * is created, this stage does not do much other than check any PC-relative + * branches. + */ template -class SimpleDecode +class DefaultDecode { private: // Typedefs from the Impl. @@ -50,49 +57,126 @@ class SimpleDecode typedef typename CPUPol::TimeStruct TimeStruct; public: - // The only time decode will become blocked is if dispatch becomes - // blocked, which means IQ or ROB is probably full. - enum Status { + /** Overall decode stage status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ + enum DecodeStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, + StartSquash, Squashing, Blocked, Unblocking }; private: - // May eventually need statuses on a per thread basis. - Status _status; + /** Decode status. */ + DecodeStatus _status; + + /** Per-thread status. */ + ThreadStatus decodeStatus[Impl::MaxThreads]; public: - SimpleDecode(Params ¶ms); + /** DefaultDecode constructor. */ + DefaultDecode(Params *params); + /** Returns the name of decode. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); + /** Sets CPU pointer. */ void setCPU(FullCPU *cpu_ptr); + /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer *tb_ptr); + /** Sets pointer to time buffer used to communicate to the next stage. */ void setDecodeQueue(TimeBuffer *dq_ptr); + /** Sets pointer to time buffer coming from fetch. */ void setFetchQueue(TimeBuffer *fq_ptr); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list *at_ptr); + + /** Ticks decode, processing all input signals and decoding as many + * instructions as possible. + */ void tick(); - void decode(); + /** Determines what to do based on decode's current status. + * @param status_change decode() sets this variable if there was a status + * change (ie switching from from blocking to unblocking). + * @param tid Thread id to decode instructions from. + */ + void decode(bool &status_change, unsigned tid); + + /** Processes instructions from fetch and passes them on to rename. + * Decoding of instructions actually happens when they are created in + * fetch, so this function mostly checks if PC-relative branches are + * correct. + */ + void decodeInsts(unsigned tid); private: + /** Inserts a thread's instructions into the skid buffer, to be decoded + * once decode unblocks. + */ + void skidInsert(unsigned tid); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall decode status based on all of the threads' statuses. */ + void updateStatus(); + + /** Separates instructions from fetch into individual lists of instructions + * sorted by thread. + */ + void sortInsts(); + + /** Reads all stall signals from the backwards communication timebuffer. */ + void readStallSignals(unsigned tid); + + /** Checks all input signals and updates decode's status appropriately. */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Checks all stall signals, and returns if any are true. */ + bool checkStall(unsigned tid) const; + + /** Returns if there any instructions from fetch on this cycle. */ inline bool fetchInstsValid(); - void block(); + /** Switches decode to blocking, and signals back that decode has + * become blocked. + * @return Returns true if there is a status change. + */ + bool block(unsigned tid); - inline void unblock(); + /** Switches decode to unblocking if the skid buffer is empty, and + * signals back that decode has unblocked. + * @return Returns true if there is a status change. + */ + bool unblock(unsigned tid); - void squash(DynInstPtr &inst); + /** Squashes if there is a PC-relative branch that was predicted + * incorrectly. Sends squash information back to fetch. + */ + void squash(DynInstPtr &inst, unsigned tid); public: - // Might want to make squash a friend function. - void squash(); + /** Squashes due to commit signalling a squash. Changes status to + * squashing and clears block/unblock signals as needed. + */ + unsigned squash(unsigned tid); private: // Interfaces to objects outside of decode. @@ -127,10 +211,27 @@ class SimpleDecode /** Wire to get fetch's output from fetch queue. */ typename TimeBuffer::wire fromFetch; - /** Skid buffer between fetch and decode. */ - std::queue skidBuffer; + /** Queue of all instructions coming from fetch this cycle. */ + std::queue insts[Impl::MaxThreads]; + + /** Skid buffer between fetch and decode. */ + std::queue skidBuffer[Impl::MaxThreads]; + + /** Variable that tracks if decode has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Source of possible stalls. */ + struct Stalls { + bool rename; + bool iew; + bool commit; + }; + + /** Tracks which stages are telling decode to stall. */ + Stalls stalls[Impl::MaxThreads]; - //Consider making these unsigned to avoid any confusion. /** Rename to decode delay, in ticks. */ unsigned renameToDecodeDelay; @@ -146,20 +247,41 @@ class SimpleDecode /** The width of decode, in instructions. */ unsigned decodeWidth; - /** The instruction that decode is currently on. It needs to have - * persistent state so that when a stall occurs in the middle of a - * group of instructions, it can restart at the proper instruction. - */ - unsigned numInst; + /** Index of instructions being sent to rename. */ + unsigned toRenameIndex; + /** number of Active Threads*/ + unsigned numThreads; + + /** List of active thread ids */ + std::list *activeThreads; + + /** Number of branches in flight. */ + unsigned branchCount[Impl::MaxThreads]; + + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** Stat for total number of idle cycles. */ Stats::Scalar<> decodeIdleCycles; + /** Stat for total number of blocked cycles. */ Stats::Scalar<> decodeBlockedCycles; + /** Stat for total number of normal running cycles. */ + Stats::Scalar<> decodeRunCycles; + /** Stat for total number of unblocking cycles. */ Stats::Scalar<> decodeUnblockCycles; + /** Stat for total number of squashing cycles. */ Stats::Scalar<> decodeSquashCycles; + /** Stat for number of times a branch mispredict is detected. */ Stats::Scalar<> decodeBranchMispred; + /** Stat for number of times decode detected a non-control instruction + * incorrectly predicted as a branch. + */ Stats::Scalar<> decodeControlMispred; + /** Stat for total number of decoded instructions. */ Stats::Scalar<> decodeDecodedInsts; + /** Stat for total number of squashed instructions. */ Stats::Scalar<> decodeSquashedInsts; }; -#endif // __CPU_O3_CPU_SIMPLE_DECODE_HH__ +#endif // __CPU_O3_DECODE_HH__ diff --git a/cpu/o3/decode_impl.hh b/cpu/o3/decode_impl.hh index 463f0ddac..f1aea27b4 100644 --- a/cpu/o3/decode_impl.hh +++ b/cpu/o3/decode_impl.hh @@ -28,22 +28,42 @@ #include "cpu/o3/decode.hh" +using namespace std; + template -SimpleDecode::SimpleDecode(Params ¶ms) - : renameToDecodeDelay(params.renameToDecodeDelay), - iewToDecodeDelay(params.iewToDecodeDelay), - commitToDecodeDelay(params.commitToDecodeDelay), - fetchToDecodeDelay(params.fetchToDecodeDelay), - decodeWidth(params.decodeWidth), - numInst(0) +DefaultDecode::DefaultDecode(Params *params) + : renameToDecodeDelay(params->renameToDecodeDelay), + iewToDecodeDelay(params->iewToDecodeDelay), + commitToDecodeDelay(params->commitToDecodeDelay), + fetchToDecodeDelay(params->fetchToDecodeDelay), + decodeWidth(params->decodeWidth), + numThreads(params->numberOfThreads) { - DPRINTF(Decode, "Decode: decodeWidth=%i.\n", decodeWidth); - _status = Idle; + DPRINTF(Decode, "decodeWidth=%i.\n", decodeWidth); + _status = Inactive; + + for (int i = 0; i < numThreads; ++i) { + decodeStatus[i] = Idle; + + stalls[i].rename = false; + stalls[i].iew = false; + stalls[i].commit = false; + } + + // @todo: Make into a parameter + skidBufferMax = (fetchToDecodeDelay * params->fetchWidth) + decodeWidth; +} + +template +std::string +DefaultDecode::name() const +{ + return cpu->name() + ".decode"; } template void -SimpleDecode::regStats() +DefaultDecode::regStats() { decodeIdleCycles .name(name() + ".decodeIdleCycles") @@ -53,6 +73,10 @@ SimpleDecode::regStats() .name(name() + ".decodeBlockedCycles") .desc("Number of cycles decode is blocked") .prereq(decodeBlockedCycles); + decodeRunCycles + .name(name() + ".decodeRunCycles") + .desc("Number of cycles decode is running") + .prereq(decodeRunCycles); decodeUnblockCycles .name(name() + ".decodeUnblockCycles") .desc("Number of cycles decode is unblocking") @@ -82,17 +106,17 @@ SimpleDecode::regStats() template void -SimpleDecode::setCPU(FullCPU *cpu_ptr) +DefaultDecode::setCPU(FullCPU *cpu_ptr) { - DPRINTF(Decode, "Decode: Setting CPU pointer.\n"); + DPRINTF(Decode, "Setting CPU pointer.\n"); cpu = cpu_ptr; } template void -SimpleDecode::setTimeBuffer(TimeBuffer *tb_ptr) +DefaultDecode::setTimeBuffer(TimeBuffer *tb_ptr) { - DPRINTF(Decode, "Decode: Setting time buffer pointer.\n"); + DPRINTF(Decode, "Setting time buffer pointer.\n"); timeBuffer = tb_ptr; // Setup wire to write information back to fetch. @@ -106,9 +130,9 @@ SimpleDecode::setTimeBuffer(TimeBuffer *tb_ptr) template void -SimpleDecode::setDecodeQueue(TimeBuffer *dq_ptr) +DefaultDecode::setDecodeQueue(TimeBuffer *dq_ptr) { - DPRINTF(Decode, "Decode: Setting decode queue pointer.\n"); + DPRINTF(Decode, "Setting decode queue pointer.\n"); decodeQueue = dq_ptr; // Setup wire to write information to proper place in decode queue. @@ -117,260 +141,515 @@ SimpleDecode::setDecodeQueue(TimeBuffer *dq_ptr) template void -SimpleDecode::setFetchQueue(TimeBuffer *fq_ptr) +DefaultDecode::setFetchQueue(TimeBuffer *fq_ptr) { - DPRINTF(Decode, "Decode: Setting fetch queue pointer.\n"); + DPRINTF(Decode, "Setting fetch queue pointer.\n"); fetchQueue = fq_ptr; // Setup wire to read information from fetch queue. fromFetch = fetchQueue->getWire(-fetchToDecodeDelay); } +template +void +DefaultDecode::setActiveThreads(list *at_ptr) +{ + DPRINTF(Decode, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template +bool +DefaultDecode::checkStall(unsigned tid) const +{ + bool ret_val = false; + + if (stalls[tid].rename) { + DPRINTF(Decode,"[tid:%i]: Stall fom Rename stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].iew) { + DPRINTF(Decode,"[tid:%i]: Stall fom IEW stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Decode,"[tid:%i]: Stall fom Commit stage detected.\n", tid); + ret_val = true; + } + + return ret_val; +} + template inline bool -SimpleDecode::fetchInstsValid() +DefaultDecode::fetchInstsValid() { return fromFetch->size > 0; } template -void -SimpleDecode::block() +bool +DefaultDecode::block(unsigned tid) { - DPRINTF(Decode, "Decode: Blocking.\n"); + DPRINTF(Decode, "[tid:%u]: Blocking.\n", tid); - // Set the status to Blocked. - _status = Blocked; + // If the decode status is blocked or unblocking then decode has not yet + // signalled fetch to unblock. In that case, there is no need to tell + // fetch to block. + if (decodeStatus[tid] != Blocked && + decodeStatus[tid] != Unblocking) { + toFetch->decodeBlock[tid] = true; + wroteToTimeBuffer = true; + } // Add the current inputs to the skid buffer so they can be // reprocessed when this stage unblocks. - skidBuffer.push(*fromFetch); + skidInsert(tid); - // Note that this stage only signals previous stages to stall when - // it is the cause of the stall originates at this stage. Otherwise - // the previous stages are expected to check all possible stall signals. + if (decodeStatus[tid] != Blocked) { + // Set the status to Blocked. + decodeStatus[tid] = Blocked; + return true; + } + + return false; } template -inline void -SimpleDecode::unblock() +bool +DefaultDecode::unblock(unsigned tid) { - DPRINTF(Decode, "Decode: Unblocking, going to remove " - "instructions from skid buffer.\n"); - // Remove the now processed instructions from the skid buffer. - skidBuffer.pop(); + DPRINTF(Decode, "[tid:%u]: Trying to unblock.\n", tid); - // If there's still information in the skid buffer, then - // continue to tell previous stages to stall. They will be - // able to restart once the skid buffer is empty. - if (!skidBuffer.empty()) { - toFetch->decodeInfo.stall = true; - } else { - DPRINTF(Decode, "Decode: Finished unblocking.\n"); - _status = Running; + // Decode is done unblocking only if the skid buffer is empty. + if (skidBuffer[tid].empty()) { + DPRINTF(Decode, "[tid:%u]: Done unblocking.\n", tid); + toFetch->decodeUnblock[tid] = true; + wroteToTimeBuffer = true; + + decodeStatus[tid] = Running; + return true; } + + return false; } -// This squash is specifically for when Decode detects a PC-relative branch -// was predicted incorrectly. template void -SimpleDecode::squash(DynInstPtr &inst) +DefaultDecode::squash(DynInstPtr &inst, unsigned tid) { - DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction " - "detected at decode.\n"); - Addr new_PC = inst->readNextPC(); + DPRINTF(Decode, "[tid:%i]: Squashing due to incorrect branch prediction " + "detected at decode.\n", tid); - toFetch->decodeInfo.branchMispredict = true; - toFetch->decodeInfo.doneSeqNum = inst->seqNum; - toFetch->decodeInfo.predIncorrect = true; - toFetch->decodeInfo.squash = true; - toFetch->decodeInfo.nextPC = new_PC; - toFetch->decodeInfo.branchTaken = true; + toFetch->decodeInfo[tid].branchMispredict = true; + toFetch->decodeInfo[tid].doneSeqNum = inst->seqNum; + toFetch->decodeInfo[tid].predIncorrect = true; + toFetch->decodeInfo[tid].squash = true; + toFetch->decodeInfo[tid].nextPC = inst->readNextPC(); + toFetch->decodeInfo[tid].branchTaken = true; + + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { + toFetch->decodeUnblock[tid] = 1; + } // Set status to squashing. - _status = Squashing; + decodeStatus[tid] = Squashing; + + for (int i=0; isize; i++) { + if (fromFetch->insts[i]->threadNumber == tid && + fromFetch->insts[i]->seqNum > inst->seqNum) { + fromFetch->insts[i]->squashed = true; + } + } + + while (!insts[tid].empty()) { + insts[tid].pop(); + } // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) { - skidBuffer.pop(); + while (!skidBuffer[tid].empty()) { + skidBuffer[tid].pop(); } // Squash instructions up until this one - // Slightly unrealistic! - cpu->removeInstsUntil(inst->seqNum); + cpu->removeInstsUntil(inst->seqNum, tid); +} + +template +unsigned +DefaultDecode::squash(unsigned tid) +{ + DPRINTF(Decode, "[tid:%i]: Squashing.\n",tid); + + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { +#if !FULL_SYSTEM + // In syscall emulation, we can have both a block and a squash due + // to a syscall in the same cycle. This would cause both signals to + // be high. This shouldn't happen in full system. + if (toFetch->decodeBlock[tid]) { + toFetch->decodeBlock[tid] = 0; + } else { + toFetch->decodeUnblock[tid] = 1; + } +#else + toFetch->decodeUnblock[tid] = 1; +#endif + } + + // Set status to squashing. + decodeStatus[tid] = Squashing; + + // Go through incoming instructions from fetch and squash them. + unsigned squash_count = 0; + + for (int i=0; isize; i++) { + if (fromFetch->insts[i]->threadNumber == tid) { + fromFetch->insts[i]->squashed = true; + squash_count++; + } + } + + while (!insts[tid].empty()) { + insts[tid].pop(); + } + + // Clear the skid buffer in case it has any data in it. + while (!skidBuffer[tid].empty()) { + skidBuffer[tid].pop(); + } + + return squash_count; } template void -SimpleDecode::squash() +DefaultDecode::skidInsert(unsigned tid) { - DPRINTF(Decode, "Decode: Squashing.\n"); - // Set status to squashing. - _status = Squashing; + DynInstPtr inst = NULL; - // Maybe advance the time buffer? Not sure what to do in the normal - // case. + while (!insts[tid].empty()) { + inst = insts[tid].front(); - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) - { - skidBuffer.pop(); + insts[tid].pop(); + + assert(tid == inst->threadNumber); + + DPRINTF(Decode,"Inserting [sn:%lli] PC:%#x into decode skidBuffer %i\n", + inst->seqNum, inst->readPC(), inst->threadNumber); + + skidBuffer[tid].push(inst); + } + + // Eventually need to enforce this by not letting a thread + // fetch past its skidbuffer + assert(skidBuffer[tid].size() <= skidBufferMax); +} + +template +bool +DefaultDecode::skidsEmpty() +{ + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template +void +DefaultDecode::updateStatus() +{ + bool any_unblocking = false; + + list::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (decodeStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } + } + + // Decode will have activity if it's unblocking. + if (any_unblocking) { + if (_status == Inactive) { + _status = Active; + + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(FullCPU::DecodeIdx); + } + } else { + // If it's not unblocking, then decode will not have any internal + // activity. Switch it to inactive. + if (_status == Active) { + _status = Inactive; + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(FullCPU::DecodeIdx); + } + } +} + +template +void +DefaultDecode::sortInsts() +{ + int insts_from_fetch = fromFetch->size; + + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); + + for (int i = 0; i < insts_from_fetch; ++i) { + insts[fromFetch->insts[i]->threadNumber].push(fromFetch->insts[i]); } } template void -SimpleDecode::tick() +DefaultDecode::readStallSignals(unsigned tid) { - // Decode should try to execute as many instructions as its bandwidth + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; + } + + if (fromRename->renameUnblock[tid]) { + assert(stalls[tid].rename); + stalls[tid].rename = false; + } + + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template +bool +DefaultDecode::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is. + // Check stall signals, block if necessary. + // If status was blocked + // Check if stall conditions have passed + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + + // Update the per thread stall statuses. + readStallSignals(tid); + + // Check squash signals from commit. + if (fromCommit->commitInfo[tid].squash) { + + DPRINTF(Decode, "[tid:%u]: Squashing instructions due to squash " + "from commit.\n", tid); + + squash(tid); + + return true; + } + + // Check ROB squash signals from commit. + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Decode, "[tid:%]: ROB is still squashing.\n",tid); + + // Continue to squash. + decodeStatus[tid] = Squashing; + + return true; + } + + if (checkStall(tid)) { + return block(tid); + } + + if (decodeStatus[tid] == Blocked) { + DPRINTF(Decode, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); + + decodeStatus[tid] = Unblocking; + + unblock(tid); + + return true; + } + + if (decodeStatus[tid] == Squashing) { + // Switch status to running if decode isn't being told to block or + // squash this cycle. + DPRINTF(Decode, "[tid:%u]: Done squashing, switching to running.\n", + tid); + + decodeStatus[tid] = Running; + + return false; + } + + // If we've reached this point, we have not gotten any signals that + // cause decode to change its status. Decode remains the same as before. + return false; +} + +template +void +DefaultDecode::tick() +{ + wroteToTimeBuffer = false; + + bool status_change = false; + + toRenameIndex = 0; + + list::iterator threads = (*activeThreads).begin(); + + sortInsts(); + + //Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(Decode,"Processing [tid:%i]\n",tid); + status_change = checkSignalsAndUpdate(tid) || status_change; + + decode(status_change, tid); + } + + if (status_change) { + updateStatus(); + } + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + + cpu->activityThisCycle(); + } +} + +template +void +DefaultDecode::decode(bool &status_change, unsigned tid) +{ + // If status is Running or idle, + // call decodeInsts() + // If status is Unblocking, + // buffer any instructions coming from fetch + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (decodeStatus[tid] == Blocked) { + ++decodeBlockedCycles; + } else if (decodeStatus[tid] == Squashing) { + ++decodeSquashCycles; + } + + // Decode should try to decode as many instructions as its bandwidth // will allow, as long as it is not currently blocked. - if (_status != Blocked && _status != Squashing) { - DPRINTF(Decode, "Decode: Not blocked, so attempting to run " - "stage.\n"); + if (decodeStatus[tid] == Running || + decodeStatus[tid] == Idle) { + DPRINTF(Decode, "[tid:%u] Not blocked, so attempting to run " + "stage.\n",tid); + + decodeInsts(tid); + } else if (decodeStatus[tid] == Unblocking) { // Make sure that the skid buffer has something in it if the // status is unblocking. - assert(_status == Unblocking ? !skidBuffer.empty() : 1); - - decode(); + assert(!skidsEmpty()); // If the status was unblocking, then instructions from the skid // buffer were used. Remove those instructions and handle // the rest of unblocking. - if (_status == Unblocking) { - ++decodeUnblockCycles; - - if (fetchInstsValid()) { - // Add the current inputs to the skid buffer so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromFetch); - } - - unblock(); - } - } else if (_status == Blocked) { - ++decodeBlockedCycles; + decodeInsts(tid); if (fetchInstsValid()) { - block(); + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); } - if (!fromRename->renameInfo.stall && - !fromIEW->iewInfo.stall && - !fromCommit->commitInfo.stall) { - DPRINTF(Decode, "Decode: Stall signals cleared, going to " - "unblock.\n"); - _status = Unblocking; - - // Continue to tell previous stage to block until this - // stage is done unblocking. - toFetch->decodeInfo.stall = true; - } else { - DPRINTF(Decode, "Decode: Still blocked.\n"); - toFetch->decodeInfo.stall = true; - } - - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - } - } else if (_status == Squashing) { - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - _status = Running; - } else if (fromCommit->commitInfo.squash) { - ++decodeSquashCycles; - - squash(); - } + status_change = unblock(tid) || status_change; } } -template +template void -SimpleDecode::decode() +DefaultDecode::decodeInsts(unsigned tid) { - // Check time buffer if being told to squash. - if (fromCommit->commitInfo.squash) { - squash(); - return; - } + // Instructions can come either from the skid buffer or the list of + // instructions coming from fetch, depending on decode's status. + int insts_available = decodeStatus[tid] == Unblocking ? + skidBuffer[tid].size() : insts[tid].size(); - // Check time buffer if being told to stall. - if (fromRename->renameInfo.stall || - fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) { - block(); - return; - } - - // Check fetch queue to see if instructions are available. - // If no available instructions, do nothing, unless this stage is - // currently unblocking. - if (!fetchInstsValid() && _status != Unblocking) { - DPRINTF(Decode, "Decode: Nothing to do, breaking out early.\n"); + if (insts_available == 0) { + DPRINTF(Decode, "[tid:%u] Nothing to do, breaking out" + " early.\n",tid); // Should I change the status to idle? ++decodeIdleCycles; return; + } else if (decodeStatus[tid] == Unblocking) { + DPRINTF(Decode, "[tid:%u] Unblocking, removing insts from skid " + "buffer.\n",tid); + ++decodeUnblockCycles; + } else if (decodeStatus[tid] == Running) { + ++decodeRunCycles; } - // Might be better to use a base DynInst * instead? DynInstPtr inst; - unsigned to_rename_index = 0; + std::queue + &insts_to_decode = decodeStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; - int insts_available = _status == Unblocking ? - skidBuffer.front().size - numInst : - fromFetch->size; + DPRINTF(Decode, "[tid:%u]: Sending instruction to rename.\n",tid); - // Debug block... -#if 0 - if (insts_available) { - DPRINTF(Decode, "Decode: Instructions available.\n"); - } else { - if (_status == Unblocking && skidBuffer.empty()) { - DPRINTF(Decode, "Decode: No instructions available, skid buffer " - "empty.\n"); - } else if (_status != Unblocking && - !fromFetch->insts[0]) { - DPRINTF(Decode, "Decode: No instructions available, fetch queue " - "empty.\n"); - } else { - panic("Decode: No instructions available, unexpected condition!" - "\n"); - } - } -#endif + while (insts_available > 0 && toRenameIndex < decodeWidth) { + assert(!insts_to_decode.empty()); - while (insts_available > 0) - { - DPRINTF(Decode, "Decode: Sending instruction to rename.\n"); + inst = insts_to_decode.front(); - inst = _status == Unblocking ? skidBuffer.front().insts[numInst] : - fromFetch->insts[numInst]; + insts_to_decode.pop(); - DPRINTF(Decode, "Decode: Processing instruction %i with PC %#x\n", - inst->seqNum, inst->readPC()); + DPRINTF(Decode, "[tid:%u]: Processing instruction [sn:%lli] with " + "PC %#x\n", + tid, inst->seqNum, inst->readPC()); if (inst->isSquashed()) { - DPRINTF(Decode, "Decode: Instruction %i with PC %#x is " + DPRINTF(Decode, "[tid:%u]: Instruction %i with PC %#x is " "squashed, skipping.\n", - inst->seqNum, inst->readPC()); + tid, inst->seqNum, inst->readPC()); ++decodeSquashedInsts; - ++numInst; --insts_available; continue; } - // Also check if instructions have no source registers. Mark // them as ready to issue at any time. Not sure if this check // should exist here or at a later stage; however it doesn't matter // too much for function correctness. - // Isn't this handled by the inst queue? if (inst->numSrcRegs() == 0) { inst->setCanIssue(); } @@ -378,9 +657,12 @@ SimpleDecode::decode() // This current instruction is valid, so add it into the decode // queue. The next instruction may not be valid, so check to // see if branches were predicted correctly. - toRename->insts[to_rename_index] = inst; + toRename->insts[toRenameIndex] = inst; ++(toRename->size); + ++toRenameIndex; + ++decodeDecodedInsts; + --insts_available; // Ensure that if it was predicted as a branch, it really is a // branch. @@ -388,38 +670,39 @@ SimpleDecode::decode() panic("Instruction predicted as a branch!"); ++decodeControlMispred; + // Might want to set some sort of boolean and just do // a check at the end - squash(inst); + squash(inst, inst->threadNumber); + break; } // Go ahead and compute any PC-relative branches. - if (inst->isDirectCtrl() && inst->isUncondCtrl()) { - inst->setNextPC(inst->branchTarget()); if (inst->mispredicted()) { ++decodeBranchMispred; + // Might want to set some sort of boolean and just do // a check at the end - squash(inst); + squash(inst, inst->threadNumber); + break; } } - - // Normally can check if a direct branch has the right target - // addr (either the immediate, or the branch PC + 4) and redirect - // fetch if it's incorrect. - - // Increment which instruction we're looking at. - ++numInst; - ++to_rename_index; - ++decodeDecodedInsts; - - --insts_available; } - numInst = 0; + // If we didn't process all instructions, then we will need to block + // and put all those instructions into the skid buffer. + if (!insts_to_decode.empty()) { + block(tid); + } + + // Record that decode has written to the time buffer for activity + // tracking. + if (toRenameIndex) { + wroteToTimeBuffer = true; + } } diff --git a/cpu/o3/fetch.cc b/cpu/o3/fetch.cc index 8ad5e6565..7959416be 100644 --- a/cpu/o3/fetch.cc +++ b/cpu/o3/fetch.cc @@ -30,4 +30,4 @@ #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/fetch_impl.hh" -template class SimpleFetch; +template class DefaultFetch; diff --git a/cpu/o3/fetch.hh b/cpu/o3/fetch.hh index cc64800d9..f0f3f2745 100644 --- a/cpu/o3/fetch.hh +++ b/cpu/o3/fetch.hh @@ -26,11 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Todo: SMT fetch, -// Add a way to get a stage's current status. - -#ifndef __CPU_O3_CPU_SIMPLE_FETCH_HH__ -#define __CPU_O3_CPU_SIMPLE_FETCH_HH__ +#ifndef __CPU_O3_FETCH_HH__ +#define __CPU_O3_FETCH_HH__ #include "base/statistics.hh" #include "base/timebuf.hh" @@ -39,13 +36,15 @@ #include "sim/eventq.hh" /** - * SimpleFetch class to fetch a single instruction each cycle. SimpleFetch - * will stall if there's an Icache miss, but otherwise assumes a one cycle - * Icache hit. + * DefaultFetch class handles both single threaded and SMT fetch. Its width is + * specified by the parameters; each cycle it tries to fetch that many + * instructions. It supports using a branch predictor to predict direction and + * targets. + * It supports the idling functionalitiy of the CPU by indicating to the CPU + * when it is active and inactive. */ - template -class SimpleFetch +class DefaultFetch { public: /** Typedefs from Impl. */ @@ -55,56 +54,125 @@ class SimpleFetch typedef typename Impl::FullCPU FullCPU; typedef typename Impl::Params Params; + /** Typedefs from the CPU policy. */ typedef typename CPUPol::BPredUnit BPredUnit; typedef typename CPUPol::FetchStruct FetchStruct; typedef typename CPUPol::TimeStruct TimeStruct; /** Typedefs from ISA. */ typedef TheISA::MachInst MachInst; + typedef TheISA::ExtMachInst ExtMachInst; public: - enum Status { + /** Overall fetch status. Used to determine if the CPU can deschedule itsef + * due to a lack of activity. + */ + enum FetchStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, Squashing, Blocked, + Fetching, + TrapPending, + QuiescePending, IcacheMissStall, IcacheMissComplete }; - // May eventually need statuses on a per thread basis. - Status _status; + /** Fetching Policy, Add new policies here.*/ + enum FetchPriority { + SingleThread, + RoundRobin, + Branch, + IQ, + LSQ + }; - bool stalled; + private: + /** Fetch status. */ + FetchStatus _status; + + /** Per-thread status. */ + ThreadStatus fetchStatus[Impl::MaxThreads]; + + /** Fetch policy. */ + FetchPriority fetchPolicy; + + /** List that has the threads organized by priority. */ + std::list priorityList; public: class CacheCompletionEvent : public Event { private: - SimpleFetch *fetch; + MemReqPtr req; + /** Pointer to fetch. */ + DefaultFetch *fetch; + /** Thread id. */ +// unsigned threadId; public: - CacheCompletionEvent(SimpleFetch *_fetch); + /** Constructs a cache completion event, which tells fetch when the + * cache miss is complete. + */ + CacheCompletionEvent(MemReqPtr &_req, DefaultFetch *_fetch); + /** Processes cache completion event. */ virtual void process(); + /** Returns the description of the cache completion event. */ virtual const char *description(); }; public: - /** SimpleFetch constructor. */ - SimpleFetch(Params ¶ms); + /** DefaultFetch constructor. */ + DefaultFetch(Params *params); + /** Returns the name of fetch. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); + /** Sets CPU pointer. */ void setCPU(FullCPU *cpu_ptr); + /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer *time_buffer); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list *at_ptr); + + /** Sets pointer to time buffer used to communicate to the next stage. */ void setFetchQueue(TimeBuffer *fq_ptr); - void processCacheCompletion(); + /** Sets pointer to page table. */ +// void setPageTable(PageTable *pt_ptr); + + /** Initialize stage. */ + void initStage(); + + /** Processes cache completion event. */ + void processCacheCompletion(MemReqPtr &req); + + void wakeFromQuiesce(); private: + /** Changes the status of this stage to active, and indicates this to the + * CPU. + */ + inline void switchToActive(); + + /** Changes the status of this stage to inactive, and indicates this to the + * CPU. + */ + inline void switchToInactive(); + /** * Looks up in the branch predictor to see if the next PC should be * either next PC+=MachInst or a branch target. @@ -120,30 +188,76 @@ class SimpleFetch * fault that happened. Puts the data into the class variable * cacheData. * @param fetch_PC The PC address that is being fetched from. + * @param ret_fault The fault reference that will be set to the result of + * the icache access. + * @param tid Thread id. * @return Any fault that occured. */ - Fault fetchCacheLine(Addr fetch_PC); + bool fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid); - inline void doSquash(const Addr &new_PC); + /** Squashes a specific thread and resets the PC. */ + inline void doSquash(const Addr &new_PC, unsigned tid); - void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num); + /** Squashes a specific thread and resets the PC. Also tells the CPU to + * remove any instructions between fetch and decode that should be sqaushed. + */ + void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num, + unsigned tid); + + /** Checks if a thread is stalled. */ + bool checkStall(unsigned tid) const; + + /** Updates overall fetch stage status; to be called at the end of each + * cycle. */ + FetchStatus updateFetchStatus(); public: - // Figure out PC vs next PC and how it should be updated - void squash(const Addr &new_PC); + /** Squashes a specific thread and resets the PC. Also tells the CPU to + * remove any instructions that are not in the ROB. The source of this + * squash should be the commit stage. + */ + void squash(const Addr &new_PC, unsigned tid); + /** Ticks the fetch stage, processing all inputs signals and fetching + * as many instructions as possible. + */ void tick(); - void fetch(); + /** Checks all input signals and updates the status as necessary. + * @return: Returns if the status has changed due to input signals. + */ + bool checkSignalsAndUpdate(unsigned tid); - // Align an address (typically a PC) to the start of an I-cache block. - // We fold in the PISA 64- to 32-bit conversion here as well. + /** Does the actual fetching of instructions and passing them on to the + * next stage. + * @param status_change fetch() sets this variable if there was a status + * change (ie switching to IcacheMissStall). + */ + void fetch(bool &status_change); + + /** Align a PC to the start of an I-cache block. */ Addr icacheBlockAlignPC(Addr addr) { addr = TheISA::realPCToFetchPC(addr); return (addr & ~(cacheBlkMask)); } + private: + /** Returns the appropriate thread to fetch, given the fetch policy. */ + int getFetchingThread(FetchPriority &fetch_priority); + + /** Returns the appropriate thread to fetch using a round robin policy. */ + int roundRobin(); + + /** Returns the appropriate thread to fetch using the IQ count policy. */ + int iqCount(); + + /** Returns the appropriate thread to fetch using the LSQ count policy. */ + int lsqCount(); + + /** Returns the appropriate thread to fetch using the branch count policy. */ + int branchCount(); + private: /** Pointer to the FullCPU. */ FullCPU *cpu; @@ -176,8 +290,31 @@ class SimpleFetch /** BPredUnit. */ BPredUnit branchPred; + Addr PC[Impl::MaxThreads]; + + Addr nextPC[Impl::MaxThreads]; + /** Memory request used to access cache. */ - MemReqPtr memReq; + MemReqPtr memReq[Impl::MaxThreads]; + + /** Variable that tracks if fetch has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Tracks how many instructions has been fetched this cycle. */ + int numInst; + + /** Source of possible stalls. */ + struct Stalls { + bool decode; + bool rename; + bool iew; + bool commit; + }; + + /** Tracks which stages are telling fetch to stall. */ + Stalls stalls[Impl::MaxThreads]; /** Decode to fetch delay, in ticks. */ unsigned decodeToFetchDelay; @@ -201,23 +338,56 @@ class SimpleFetch Addr cacheBlkMask; /** The cache line being fetched. */ - uint8_t *cacheData; + uint8_t *cacheData[Impl::MaxThreads]; /** Size of instructions. */ int instSize; /** Icache stall statistics. */ - Counter lastIcacheStall; + Counter lastIcacheStall[Impl::MaxThreads]; + /** List of Active Threads */ + std::list *activeThreads; + + /** Number of threads. */ + unsigned numThreads; + + /** Number of threads that are actively fetching. */ + unsigned numFetchingThreads; + + /** Thread ID being fetched. */ + int threadFetched; + + bool interruptPending; + +#if !FULL_SYSTEM + /** Page table pointer. */ +// PageTable *pTable; +#endif + + // @todo: Consider making these vectors and tracking on a per thread basis. + /** Stat for total number of cycles stalled due to an icache miss. */ Stats::Scalar<> icacheStallCycles; + /** Stat for total number of fetched instructions. */ Stats::Scalar<> fetchedInsts; + /** Stat for total number of predicted branches. */ Stats::Scalar<> predictedBranches; + /** Stat for total number of cycles spent fetching. */ Stats::Scalar<> fetchCycles; + /** Stat for total number of cycles spent squashing. */ Stats::Scalar<> fetchSquashCycles; + /** Stat for total number of cycles spent blocked due to other stages in + * the pipeline. + */ + Stats::Scalar<> fetchIdleCycles; Stats::Scalar<> fetchBlockedCycles; + /** Stat for total number of fetched cache lines. */ Stats::Scalar<> fetchedCacheLines; - - Stats::Distribution<> fetch_nisn_dist; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; + Stats::Formula idleRate; + Stats::Formula branchRate; + Stats::Formula fetchRate; }; -#endif //__CPU_O3_CPU_SIMPLE_FETCH_HH__ +#endif //__CPU_O3_FETCH_HH__ diff --git a/cpu/o3/fetch_impl.hh b/cpu/o3/fetch_impl.hh index 8029fc732..7abc5733f 100644 --- a/cpu/o3/fetch_impl.hh +++ b/cpu/o3/fetch_impl.hh @@ -26,66 +26,101 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Remove this later; used only for debugging. -#define OPCODE(X) (X >> 26) & 0x3f - #include "arch/isa_traits.hh" #include "sim/byteswap.hh" #include "cpu/exetrace.hh" +#include "cpu/o3/fetch.hh" #include "mem/base_mem.hh" #include "mem/mem_interface.hh" #include "mem/mem_req.hh" -#include "cpu/o3/fetch.hh" #include "sim/root.hh" +#if FULL_SYSTEM +#include "base/remote_gdb.hh" +#include "mem/functional/memory_control.hh" +#include "mem/functional/physical.hh" +#include "sim/system.hh" +#include "arch/tlb.hh" +#include "arch/vtophys.hh" +#else // !FULL_SYSTEM +#include "mem/functional/functional.hh" +#endif // FULL_SYSTEM + +#include + +using namespace std; + template -SimpleFetch::CacheCompletionEvent -::CacheCompletionEvent(SimpleFetch *_fetch) - : Event(&mainEventQueue), +DefaultFetch::CacheCompletionEvent::CacheCompletionEvent(MemReqPtr &_req, + DefaultFetch *_fetch) + : Event(&mainEventQueue, Delayed_Writeback_Pri), + req(_req), fetch(_fetch) { + this->setFlags(Event::AutoDelete); } template void -SimpleFetch::CacheCompletionEvent::process() +DefaultFetch::CacheCompletionEvent::process() { - fetch->processCacheCompletion(); + fetch->processCacheCompletion(req); } template const char * -SimpleFetch::CacheCompletionEvent::description() +DefaultFetch::CacheCompletionEvent::description() { - return "SimpleFetch cache completion event"; + return "DefaultFetch cache completion event"; } template -SimpleFetch::SimpleFetch(Params ¶ms) - : icacheInterface(params.icacheInterface), +DefaultFetch::DefaultFetch(Params *params) + : icacheInterface(params->icacheInterface), branchPred(params), - decodeToFetchDelay(params.decodeToFetchDelay), - renameToFetchDelay(params.renameToFetchDelay), - iewToFetchDelay(params.iewToFetchDelay), - commitToFetchDelay(params.commitToFetchDelay), - fetchWidth(params.fetchWidth) + decodeToFetchDelay(params->decodeToFetchDelay), + renameToFetchDelay(params->renameToFetchDelay), + iewToFetchDelay(params->iewToFetchDelay), + commitToFetchDelay(params->commitToFetchDelay), + fetchWidth(params->fetchWidth), + numThreads(params->numberOfThreads), + numFetchingThreads(params->smtNumFetchingThreads), + interruptPending(false) { - DPRINTF(Fetch, "Fetch: Fetch constructor called\n"); + if (numThreads > Impl::MaxThreads) + fatal("numThreads is not a valid value\n"); - // Set status to idle. - _status = Idle; + DPRINTF(Fetch, "Fetch constructor called\n"); - // Create a new memory request. - memReq = new MemReq(); - // Not sure of this parameter. I think it should be based on the - // thread number. -#if !FULL_SYSTEM - memReq->asid = 0; -#else - memReq->asid = 0; -#endif // FULL_SYSTEM - memReq->data = new uint8_t[64]; + // Set fetch stage's status to inactive. + _status = Inactive; + + string policy = params->smtFetchPolicy; + + // Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + // Figure out fetch policy + if (policy == "singlethread") { + fetchPolicy = SingleThread; + } else if (policy == "roundrobin") { + fetchPolicy = RoundRobin; + DPRINTF(Fetch, "Fetch policy set to Round Robin\n"); + } else if (policy == "branch") { + fetchPolicy = Branch; + DPRINTF(Fetch, "Fetch policy set to Branch Count\n"); + } else if (policy == "iqcount") { + fetchPolicy = IQ; + DPRINTF(Fetch, "Fetch policy set to IQ count\n"); + } else if (policy == "lsqcount") { + fetchPolicy = LSQ; + DPRINTF(Fetch, "Fetch policy set to LSQ count\n"); + } else { + fatal("Invalid Fetch Policy. Options Are: {SingleThread," + " RoundRobin,LSQcount,IQcount}\n"); + } // Size of cache block. cacheBlkSize = icacheInterface ? icacheInterface->getBlockSize() : 64; @@ -93,16 +128,45 @@ SimpleFetch::SimpleFetch(Params ¶ms) // Create mask to get rid of offset bits. cacheBlkMask = (cacheBlkSize - 1); + for (int tid=0; tid < numThreads; tid++) { + + fetchStatus[tid] = Running; + + priorityList.push_back(tid); + + // Create a new memory request. + memReq[tid] = NULL; +// memReq[tid] = new MemReq(); +/* + // Need a way of setting this correctly for parallel programs + // @todo: Figure out how to properly set asid vs thread_num. + memReq[tid]->asid = tid; + memReq[tid]->thread_num = tid; + memReq[tid]->data = new uint8_t[64]; +*/ + // Create space to store a cache line. + cacheData[tid] = new uint8_t[cacheBlkSize]; + + stalls[tid].decode = 0; + stalls[tid].rename = 0; + stalls[tid].iew = 0; + stalls[tid].commit = 0; + } + // Get the size of an instruction. instSize = sizeof(MachInst); +} - // Create space to store a cache line. - cacheData = new uint8_t[cacheBlkSize]; +template +std::string +DefaultFetch::name() const +{ + return cpu->name() + ".fetch"; } template void -SimpleFetch::regStats() +DefaultFetch::regStats() { icacheStallCycles .name(name() + ".icacheStallCycles") @@ -113,55 +177,88 @@ SimpleFetch::regStats() .name(name() + ".fetchedInsts") .desc("Number of instructions fetch has processed") .prereq(fetchedInsts); + predictedBranches .name(name() + ".predictedBranches") .desc("Number of branches that fetch has predicted taken") .prereq(predictedBranches); + fetchCycles .name(name() + ".fetchCycles") .desc("Number of cycles fetch has run and was not squashing or" " blocked") .prereq(fetchCycles); + fetchSquashCycles .name(name() + ".fetchSquashCycles") .desc("Number of cycles fetch has spent squashing") .prereq(fetchSquashCycles); + + fetchIdleCycles + .name(name() + ".fetchIdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + fetchBlockedCycles .name(name() + ".fetchBlockedCycles") .desc("Number of cycles fetch has spent blocked") .prereq(fetchBlockedCycles); + fetchedCacheLines .name(name() + ".fetchedCacheLines") .desc("Number of cache lines fetched") .prereq(fetchedCacheLines); - fetch_nisn_dist + fetchNisnDist .init(/* base value */ 0, /* last value */ fetchWidth, /* bucket size */ 1) - .name(name() + ".FETCH:rate_dist") + .name(name() + ".rateDist") .desc("Number of instructions fetched each cycle (Total)") - .flags(Stats::pdf) - ; + .flags(Stats::pdf); + + idleRate + .name(name() + ".idleRate") + .desc("Percent of cycles fetch was idle") + .prereq(idleRate); + idleRate = fetchIdleCycles * 100 / cpu->numCycles; + + branchRate + .name(name() + ".branchRate") + .desc("Number of branch fetches per cycle") + .flags(Stats::total); + branchRate = predictedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; branchPred.regStats(); } template void -SimpleFetch::setCPU(FullCPU *cpu_ptr) +DefaultFetch::setCPU(FullCPU *cpu_ptr) { - DPRINTF(Fetch, "Fetch: Setting the CPU pointer.\n"); + DPRINTF(Fetch, "Setting the CPU pointer.\n"); cpu = cpu_ptr; - // This line will be removed eventually. - memReq->xc = cpu->xcBase(); + + // Set ExecContexts for Memory Requests +// for (int tid=0; tid < numThreads; tid++) +// memReq[tid]->xc = cpu->xcBase(tid); + + // Fetch needs to start fetching instructions at the very beginning, + // so it must start up in active state. + switchToActive(); } template void -SimpleFetch::setTimeBuffer(TimeBuffer *time_buffer) +DefaultFetch::setTimeBuffer(TimeBuffer *time_buffer) { - DPRINTF(Fetch, "Fetch: Setting the time buffer pointer.\n"); + DPRINTF(Fetch, "Setting the time buffer pointer.\n"); timeBuffer = time_buffer; // Create wires to get information from proper places in time buffer. @@ -173,32 +270,122 @@ SimpleFetch::setTimeBuffer(TimeBuffer *time_buffer) template void -SimpleFetch::setFetchQueue(TimeBuffer *fq_ptr) +DefaultFetch::setActiveThreads(list *at_ptr) { - DPRINTF(Fetch, "Fetch: Setting the fetch queue pointer.\n"); + DPRINTF(Fetch, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template +void +DefaultFetch::setFetchQueue(TimeBuffer *fq_ptr) +{ + DPRINTF(Fetch, "Setting the fetch queue pointer.\n"); fetchQueue = fq_ptr; // Create wire to write information to proper place in fetch queue. toDecode = fetchQueue->getWire(0); } +#if 0 template void -SimpleFetch::processCacheCompletion() +DefaultFetch::setPageTable(PageTable *pt_ptr) { - DPRINTF(Fetch, "Fetch: Waking up from cache miss.\n"); + DPRINTF(Fetch, "Setting the page table pointer.\n"); +#if !FULL_SYSTEM + pTable = pt_ptr; +#endif +} +#endif + +template +void +DefaultFetch::initStage() +{ + for (int tid = 0; tid < numThreads; tid++) { + PC[tid] = cpu->readPC(tid); + nextPC[tid] = cpu->readNextPC(tid); + } +} + +template +void +DefaultFetch::processCacheCompletion(MemReqPtr &req) +{ + unsigned tid = req->thread_num; + + DPRINTF(Fetch, "[tid:%u] Waking up from cache miss.\n",tid); // Only change the status if it's still waiting on the icache access // to return. // Can keep track of how many cache accesses go unused due to // misspeculation here. - if (_status == IcacheMissStall) - _status = IcacheMissComplete; + if (fetchStatus[tid] != IcacheMissStall || + req != memReq[tid]) + return; + + // Wake up the CPU (if it went to sleep and was waiting on this completion + // event). + cpu->wakeCPU(); + + DPRINTF(Activity, "[tid:%u] Activating fetch due to cache completion\n", + tid); + + switchToActive(); + + // Only switch to IcacheMissComplete if we're not stalled as well. + if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheMissComplete; + } + +// memcpy(cacheData[tid], memReq[tid]->data, memReq[tid]->size); + + // Reset the completion event to NULL. + memReq[tid] = NULL; +// memReq[tid]->completionEvent = NULL; +} + +template +void +DefaultFetch::wakeFromQuiesce() +{ + DPRINTF(Fetch, "Waking up from quiesce\n"); + // Hopefully this is safe + fetchStatus[0] = Running; +} + +template +inline void +DefaultFetch::switchToActive() +{ + if (_status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(FullCPU::FetchIdx); + + _status = Active; + } +} + +template +inline void +DefaultFetch::switchToInactive() +{ + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(FullCPU::FetchIdx); + + _status = Inactive; + } } template bool -SimpleFetch::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) +DefaultFetch::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) { // Do branch prediction check here. // A bit of a misnomer...next_PC is actually the current PC until @@ -211,7 +398,7 @@ SimpleFetch::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) return false; } - predict_taken = branchPred.predict(inst, next_PC); + predict_taken = branchPred.predict(inst, next_PC, inst->threadNumber); if (predict_taken) { ++predictedBranches; @@ -221,37 +408,48 @@ SimpleFetch::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) } template -Fault -SimpleFetch::fetchCacheLine(Addr fetch_PC) +bool +DefaultFetch::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid) { // Check if the instruction exists within the cache. // If it does, then proceed on to read the instruction and the rest // of the instructions in the cache line until either the end of the // cache line or a predicted taken branch is encountered. + Fault fault = NoFault; #if FULL_SYSTEM // Flag to say whether or not address is physical addr. - unsigned flags = cpu->inPalMode() ? PHYSICAL : 0; + unsigned flags = cpu->inPalMode(fetch_PC) ? PHYSICAL : 0; #else unsigned flags = 0; #endif // FULL_SYSTEM - Fault fault = NoFault; + if (interruptPending && flags == 0) { + // Hold off fetch from getting new instructions while an interrupt + // is pending. + return false; + } // Align the fetch PC so it's at the start of a cache block. fetch_PC = icacheBlockAlignPC(fetch_PC); - // Setup the memReq to do a read of the first isntruction's address. + // Setup the memReq to do a read of the first instruction's address. // Set the appropriate read size and flags as well. - memReq->cmd = Read; - memReq->reset(fetch_PC, cacheBlkSize, flags); + memReq[tid] = new MemReq(); + + memReq[tid]->asid = tid; + memReq[tid]->thread_num = tid; + memReq[tid]->data = new uint8_t[64]; + memReq[tid]->xc = cpu->xcBase(tid); + memReq[tid]->cmd = Read; + memReq[tid]->reset(fetch_PC, cacheBlkSize, flags); // Translate the instruction request. - // Should this function be - // in the CPU class ? Probably...ITB/DTB should exist within the - // CPU. - - fault = cpu->translateInstReq(memReq); +//#if FULL_SYSTEM + fault = cpu->translateInstReq(memReq[tid]); +//#else +// fault = pTable->translate(memReq[tid]); +//#endif // In the case of faults, the fetch stage may need to stall and wait // on what caused the fetch (ITB or Icache miss). @@ -259,213 +457,416 @@ SimpleFetch::fetchCacheLine(Addr fetch_PC) // If translation was successful, attempt to read the first // instruction. if (fault == NoFault) { + if (cpu->system->memctrl->badaddr(memReq[tid]->paddr)) { + DPRINTF(Fetch, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path!", + memReq[tid]->paddr); + ret_fault = TheISA::genMachineCheckFault(); + return false; + } + DPRINTF(Fetch, "Fetch: Doing instruction read.\n"); - fault = cpu->mem->read(memReq, cacheData); + fault = cpu->mem->read(memReq[tid], cacheData[tid]); // This read may change when the mem interface changes. - fetchedCacheLines++; - } + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (icacheInterface && !icacheInterface->isBlocked()) { + DPRINTF(Fetch, "Doing cache access.\n"); - // Now do the timing access to see whether or not the instruction - // exists within the cache. - if (icacheInterface && fault == NoFault) { - DPRINTF(Fetch, "Fetch: Doing timing memory access.\n"); - memReq->completionEvent = NULL; + memReq[tid]->completionEvent = NULL; - memReq->time = curTick; + memReq[tid]->time = curTick; - MemAccessResult result = icacheInterface->access(memReq); + MemAccessResult result = icacheInterface->access(memReq[tid]); - // If the cache missed (in this model functional and timing - // memories are different), then schedule an event to wake - // up this stage once the cache miss completes. - if (result != MA_HIT && icacheInterface->doEvents()) { - memReq->completionEvent = new CacheCompletionEvent(this); + // If the cache missed, then schedule an event to wake + // up this stage once the cache miss completes. + // @todo: Possibly allow for longer than 1 cycle cache hits. + if (result != MA_HIT && icacheInterface->doEvents()) { - // How does current model work as far as individual - // stages scheduling/unscheduling? - // Perhaps have only the main CPU scheduled/unscheduled, - // and have it choose what stages to run appropriately. + memReq[tid]->completionEvent = + new CacheCompletionEvent(memReq[tid], this); - DPRINTF(Fetch, "Fetch: Stalling due to icache miss.\n"); - _status = IcacheMissStall; + lastIcacheStall[tid] = curTick; + + DPRINTF(Activity, "[tid:%i]: Activity: Stalling due to I-cache " + "miss.\n", tid); + + fetchStatus[tid] = IcacheMissStall; + } else { + DPRINTF(Fetch, "[tid:%i]: I-Cache hit. Doing Instruction " + "read.\n", tid); + +// memcpy(cacheData[tid], memReq[tid]->data, memReq[tid]->size); + + fetchedCacheLines++; + } + } else { + DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid); + ret_fault = NoFault; + return false; } } - return fault; + ret_fault = fault; + return true; } template inline void -SimpleFetch::doSquash(const Addr &new_PC) +DefaultFetch::doSquash(const Addr &new_PC, unsigned tid) { - DPRINTF(Fetch, "Fetch: Squashing, setting PC to: %#x.\n", new_PC); + DPRINTF(Fetch, "[tid:%i]: Squashing, setting PC to: %#x.\n", + tid, new_PC); - cpu->setNextPC(new_PC + instSize); - cpu->setPC(new_PC); + PC[tid] = new_PC; + nextPC[tid] = new_PC + instSize; // Clear the icache miss if it's outstanding. - if (_status == IcacheMissStall && icacheInterface) { - DPRINTF(Fetch, "Fetch: Squashing outstanding Icache miss.\n"); - // @todo: Use an actual thread number here. - icacheInterface->squash(0); + if (fetchStatus[tid] == IcacheMissStall && icacheInterface) { + DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n", + tid); +// icacheInterface->squash(tid); +/* + if (memReq[tid]->completionEvent) { + if (memReq[tid]->completionEvent->scheduled()) { + memReq[tid]->completionEvent->squash(); + } else { + delete memReq[tid]->completionEvent; + memReq[tid]->completionEvent = NULL; + } + } +*/ + memReq[tid] = NULL; } - _status = Squashing; + if (fetchStatus[tid] == TrapPending) { + // @todo: Hardcoded number here + + // This is only effective if communication to and from commit + // is identical. If it's faster to commit than it is from + // commit to here, then it causes problems. + + bool found_fault = false; + for (int i = 0; i > -5; --i) { + if (fetchQueue->access(i)->fetchFault) { + DPRINTF(Fetch, "[tid:%i]: Fetch used to be in a trap, " + "clearing it.\n", + tid); + fetchQueue->access(i)->fetchFault = NoFault; + found_fault = true; + } + } + if (!found_fault) { + warn("%lli Fault from fetch not found in time buffer!", + curTick); + } + toDecode->clearFetchFault = true; + } + + fetchStatus[tid] = Squashing; ++fetchSquashCycles; } template void -SimpleFetch::squashFromDecode(const Addr &new_PC, - const InstSeqNum &seq_num) +DefaultFetch::squashFromDecode(const Addr &new_PC, + const InstSeqNum &seq_num, + unsigned tid) { - DPRINTF(Fetch, "Fetch: Squashing from decode.\n"); + DPRINTF(Fetch, "[tid:%i]: Squashing from decode.\n",tid); - doSquash(new_PC); + doSquash(new_PC, tid); // Tell the CPU to remove any instructions that are in flight between // fetch and decode. - cpu->removeInstsUntil(seq_num); + cpu->removeInstsUntil(seq_num, tid); +} + +template +bool +DefaultFetch::checkStall(unsigned tid) const +{ + bool ret_val = false; + + if (cpu->contextSwitch) { + DPRINTF(Fetch,"[tid:%i]: Stalling for a context switch.\n",tid); + ret_val = true; + } else if (stalls[tid].decode) { + DPRINTF(Fetch,"[tid:%i]: Stall from Decode stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].rename) { + DPRINTF(Fetch,"[tid:%i]: Stall from Rename stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].iew) { + DPRINTF(Fetch,"[tid:%i]: Stall from IEW stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Fetch,"[tid:%i]: Stall from Commit stage detected.\n",tid); + ret_val = true; + } + + return ret_val; +} + +template +typename DefaultFetch::FetchStatus +DefaultFetch::updateFetchStatus() +{ + //Check Running + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + + unsigned tid = *threads++; + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == Squashing || + fetchStatus[tid] == IcacheMissComplete) { + + if (_status == Inactive) { + DPRINTF(Activity, "[tid:%i]: Activating stage.\n",tid); + + if (fetchStatus[tid] == IcacheMissComplete) { + DPRINTF(Activity, "[tid:%i]: Activating fetch due to cache" + "completion\n",tid); + } + + cpu->activateStage(FullCPU::FetchIdx); + } + + return Active; + } + } + + // Stage is switching from active to inactive, notify CPU of it. + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(FullCPU::FetchIdx); + } + + return Inactive; } template void -SimpleFetch::squash(const Addr &new_PC) +DefaultFetch::squash(const Addr &new_PC, unsigned tid) { - DPRINTF(Fetch, "Fetch: Squash from commit.\n"); + DPRINTF(Fetch, "[tid:%u]: Squash from commit.\n",tid); - doSquash(new_PC); + doSquash(new_PC, tid); // Tell the CPU to remove any instructions that are not in the ROB. - cpu->removeInstsNotInROB(); + cpu->removeInstsNotInROB(tid); } -template +template void -SimpleFetch::tick() +DefaultFetch::tick() { + list::iterator threads = (*activeThreads).begin(); + bool status_change = false; + + wroteToTimeBuffer = false; + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // Check the signals for each thread to determine the proper status + // for each thread. + bool updated_status = checkSignalsAndUpdate(tid); + status_change = status_change || updated_status; + } + + DPRINTF(Fetch, "Running stage.\n"); + + // Reset the number of the instruction we're fetching. + numInst = 0; + + if (fromCommit->commitInfo[0].interruptPending) { + interruptPending = true; + } + if (fromCommit->commitInfo[0].clearInterrupt) { + interruptPending = false; + } + + for (threadFetched = 0; threadFetched < numFetchingThreads; + threadFetched++) { + // Fetch each of the actively fetching threads. + fetch(status_change); + } + + // Record number of instructions fetched this cycle for distribution. + fetchNisnDist.sample(numInst); + + if (status_change) { + // Change the fetch stage status if there was a status change. + _status = updateFetchStatus(); + } + + // If there was activity this cycle, inform the CPU of it. + if (wroteToTimeBuffer || cpu->contextSwitch) { + DPRINTF(Activity, "Activity this cycle.\n"); + + cpu->activityThisCycle(); + } +} + +template +bool +DefaultFetch::checkSignalsAndUpdate(unsigned tid) +{ + // Update the per thread stall statuses. + if (fromDecode->decodeBlock[tid]) { + stalls[tid].decode = true; + } + + if (fromDecode->decodeUnblock[tid]) { + assert(stalls[tid].decode); + assert(!fromDecode->decodeBlock[tid]); + stalls[tid].decode = false; + } + + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; + } + + if (fromRename->renameUnblock[tid]) { + assert(stalls[tid].rename); + assert(!fromRename->renameBlock[tid]); + stalls[tid].rename = false; + } + + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + assert(!fromIEW->iewBlock[tid]); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + assert(!fromCommit->commitBlock[tid]); + stalls[tid].commit = false; + } + // Check squash signals from commit. - if (fromCommit->commitInfo.squash) { - DPRINTF(Fetch, "Fetch: Squashing instructions due to squash " - "from commit.\n"); + if (fromCommit->commitInfo[tid].squash) { + + DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " + "from commit.\n",tid); // In any case, squash. - squash(fromCommit->commitInfo.nextPC); + squash(fromCommit->commitInfo[tid].nextPC,tid); // Also check if there's a mispredict that happened. - if (fromCommit->commitInfo.branchMispredict) { - branchPred.squash(fromCommit->commitInfo.doneSeqNum, - fromCommit->commitInfo.nextPC, - fromCommit->commitInfo.branchTaken); + if (fromCommit->commitInfo[tid].branchMispredict) { + branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum, + fromCommit->commitInfo[tid].nextPC, + fromCommit->commitInfo[tid].branchTaken, + tid); } else { - branchPred.squash(fromCommit->commitInfo.doneSeqNum); + branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum, + tid); } - return; - } else if (fromCommit->commitInfo.doneSeqNum) { + return true; + } else if (fromCommit->commitInfo[tid].doneSeqNum) { // Update the branch predictor if it wasn't a squashed instruction - // that was braodcasted. - branchPred.update(fromCommit->commitInfo.doneSeqNum); + // that was broadcasted. + branchPred.update(fromCommit->commitInfo[tid].doneSeqNum, tid); } // Check ROB squash signals from commit. - if (fromCommit->commitInfo.robSquashing) { - DPRINTF(Fetch, "Fetch: ROB is still squashing.\n"); + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Fetch, "[tid:%u]: ROB is still squashing Thread %u.\n", tid); // Continue to squash. - _status = Squashing; + fetchStatus[tid] = Squashing; - ++fetchSquashCycles; - return; + return true; } // Check squash signals from decode. - if (fromDecode->decodeInfo.squash) { - DPRINTF(Fetch, "Fetch: Squashing instructions due to squash " - "from decode.\n"); + if (fromDecode->decodeInfo[tid].squash) { + DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " + "from decode.\n",tid); // Update the branch predictor. - if (fromDecode->decodeInfo.branchMispredict) { - branchPred.squash(fromDecode->decodeInfo.doneSeqNum, - fromDecode->decodeInfo.nextPC, - fromDecode->decodeInfo.branchTaken); + if (fromDecode->decodeInfo[tid].branchMispredict) { + branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum, + fromDecode->decodeInfo[tid].nextPC, + fromDecode->decodeInfo[tid].branchTaken, + tid); } else { - branchPred.squash(fromDecode->decodeInfo.doneSeqNum); + branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum, + tid); } - if (_status != Squashing) { - // Squash unless we're already squashing? - squashFromDecode(fromDecode->decodeInfo.nextPC, - fromDecode->decodeInfo.doneSeqNum); - return; + if (fetchStatus[tid] != Squashing) { + // Squash unless we're already squashing + squashFromDecode(fromDecode->decodeInfo[tid].nextPC, + fromDecode->decodeInfo[tid].doneSeqNum, + tid); + + return true; } } - // Check if any of the stall signals are high. - if (fromDecode->decodeInfo.stall || - fromRename->renameInfo.stall || - fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) - { - // Block stage, regardless of current status. + if (checkStall(tid) && fetchStatus[tid] != IcacheMissStall) { + DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid); - DPRINTF(Fetch, "Fetch: Stalling stage.\n"); - DPRINTF(Fetch, "Fetch: Statuses: Decode: %i Rename: %i IEW: %i " - "Commit: %i\n", - fromDecode->decodeInfo.stall, - fromRename->renameInfo.stall, - fromIEW->iewInfo.stall, - fromCommit->commitInfo.stall); + fetchStatus[tid] = Blocked; - _status = Blocked; - - ++fetchBlockedCycles; - return; - } else if (_status == Blocked) { - // Unblock stage if status is currently blocked and none of the - // stall signals are being held high. - _status = Running; - - ++fetchBlockedCycles; - return; + return true; } - // If fetch has reached this point, then there are no squash signals - // still being held high. Check if fetch is in the squashing state; - // if so, fetch can switch to running. - // Similarly, there are no blocked signals still being held high. - // Check if fetch is in the blocked state; if so, fetch can switch to - // running. - if (_status == Squashing) { - DPRINTF(Fetch, "Fetch: Done squashing, switching to running.\n"); + if (fetchStatus[tid] == Blocked || + fetchStatus[tid] == Squashing) { + // Switch status to running if fetch isn't being told to block or + // squash this cycle. + DPRINTF(Fetch, "[tid:%i]: Done squashing, switching to running.\n", + tid); - // Switch status to running - _status = Running; + fetchStatus[tid] = Running; - ++fetchCycles; - - fetch(); - } else if (_status != IcacheMissStall) { - DPRINTF(Fetch, "Fetch: Running stage.\n"); - - ++fetchCycles; - - fetch(); + return true; } + + // If we've reached this point, we have not gotten any signals that + // cause fetch to change its status. Fetch remains the same as before. + return false; } template void -SimpleFetch::fetch() +DefaultFetch::fetch(bool &status_change) { ////////////////////////////////////////// // Start actual fetch ////////////////////////////////////////// + int tid = getFetchingThread(fetchPolicy); + + if (tid == -1) { + DPRINTF(Fetch,"There are no more threads available to fetch from.\n"); + + // Breaks looping condition in tick() + threadFetched = numFetchingThreads; + return; + } // The current PC. - Addr fetch_PC = cpu->readPC(); + Addr &fetch_PC = PC[tid]; // Fault code for memory access. Fault fault = NoFault; @@ -473,45 +874,54 @@ SimpleFetch::fetch() // If returning from the delay of a cache miss, then update the status // to running, otherwise do the cache access. Possibly move this up // to tick() function. - if (_status == IcacheMissComplete) { - DPRINTF(Fetch, "Fetch: Icache miss is complete.\n"); + if (fetchStatus[tid] == IcacheMissComplete) { + DPRINTF(Fetch, "[tid:%i]: Icache miss is complete.\n", + tid); - // Reset the completion event to NULL. - memReq->completionEvent = NULL; + fetchStatus[tid] = Running; + status_change = true; + } else if (fetchStatus[tid] == Running) { + DPRINTF(Fetch, "[tid:%i]: Attempting to translate and read " + "instruction, starting at PC %08p.\n", + tid, fetch_PC); - _status = Running; + bool fetch_success = fetchCacheLine(fetch_PC, fault, tid); + if (!fetch_success) + return; } else { - DPRINTF(Fetch, "Fetch: Attempting to translate and read " - "instruction, starting at PC %08p.\n", - fetch_PC); + if (fetchStatus[tid] == Blocked) { + ++fetchBlockedCycles; + } else if (fetchStatus[tid] == Squashing) { + ++fetchSquashCycles; + } - fault = fetchCacheLine(fetch_PC); - } - - // If we had a stall due to an icache miss, then return. It'd - // be nicer if this were handled through the kind of fault that - // is returned by the function. - if (_status == IcacheMissStall) { + // Status is Idle, Squashing, Blocked, or IcacheMissStall, so + // fetch should do nothing. return; } - // As far as timing goes, the CPU will need to send an event through - // the MemReq in order to be woken up once the memory access completes. - // Probably have a status on a per thread basis so each thread can - // block independently and be woken up independently. + ++fetchCycles; + + // If we had a stall due to an icache miss, then return. + if (fetchStatus[tid] == IcacheMissStall) { + status_change = true; + return; + } Addr next_PC = fetch_PC; InstSeqNum inst_seq; MachInst inst; - unsigned offset = fetch_PC & cacheBlkMask; - unsigned fetched; + ExtMachInst ext_inst; + // @todo: Fix this hack. + unsigned offset = (fetch_PC & cacheBlkMask) & ~3; if (fault == NoFault) { // If the read of the first instruction was successful, then grab the // instructions from the rest of the cache line and put them into the // queue heading to decode. - DPRINTF(Fetch, "Fetch: Adding instructions to queue to decode.\n"); + DPRINTF(Fetch, "[tid:%i]: Adding instructions to queue to " + "decode.\n",tid); ////////////////////////// // Fetch first instruction @@ -521,12 +931,11 @@ SimpleFetch::fetch() // ended this fetch block. bool predicted_branch = false; - for (fetched = 0; + for (; offset < cacheBlkSize && - fetched < fetchWidth && + numInst < fetchWidth && !predicted_branch; - ++fetched) - { + ++numInst) { // Get a sequence number. inst_seq = cpu->getAndIncrementInstSeq(); @@ -536,31 +945,40 @@ SimpleFetch::fetch() // Get the instruction from the array of the cache line. inst = gtoh(*reinterpret_cast - (&cacheData[offset])); + (&cacheData[tid][offset])); + + ext_inst = TheISA::makeExtMI(inst, fetch_PC); // Create a new DynInst from the instruction fetched. - DynInstPtr instruction = new DynInst(inst, fetch_PC, next_PC, + DynInstPtr instruction = new DynInst(ext_inst, fetch_PC, + next_PC, inst_seq, cpu); + instruction->setThread(tid); - DPRINTF(Fetch, "Fetch: Instruction %i created, with PC %#x\n", - inst_seq, instruction->readPC()); + instruction->setASID(tid); - DPRINTF(Fetch, "Fetch: Instruction opcode is: %03p\n", - OPCODE(inst)); + instruction->setState(cpu->thread[tid]); + + DPRINTF(Fetch, "[tid:%i]: Instruction PC %#x created " + "[sn:%lli]\n", + tid, instruction->readPC(), inst_seq); + + DPRINTF(Fetch, "[tid:%i]: Instruction is: %s\n", + tid, instruction->staticInst->disassemble(fetch_PC)); instruction->traceData = - Trace::getInstRecord(curTick, cpu->xcBase(), cpu, + Trace::getInstRecord(curTick, cpu->xcBase(tid), cpu, instruction->staticInst, - instruction->readPC(), 0); + instruction->readPC(),tid); predicted_branch = lookupAndUpdateNextPC(instruction, next_PC); // Add instruction to the CPU's list of instructions. - cpu->addInst(instruction); + instruction->setInstListIt(cpu->addInst(instruction)); // Write the instruction to the first slot in the queue // that heads to decode. - toDecode->insts[fetched] = instruction; + toDecode->insts[numInst] = instruction; toDecode->size++; @@ -570,27 +988,36 @@ SimpleFetch::fetch() // Move to the next instruction, unless we have a branch. fetch_PC = next_PC; + if (instruction->isQuiesce()) { + warn("%lli: Quiesce instruction encountered, halting fetch!", curTick); + fetchStatus[tid] = QuiescePending; + ++numInst; + status_change = true; + break; + } + offset+= instSize; } + } - fetch_nisn_dist.sample(fetched); + if (numInst > 0) { + wroteToTimeBuffer = true; } // Now that fetching is completed, update the PC to signify what the next - // cycle will be. Might want to move this to the beginning of this - // function so that the PC updates at the beginning of everything. - // Or might want to leave setting the PC to the main CPU, with fetch - // only changing the nextPC (will require correct determination of - // next PC). + // cycle will be. if (fault == NoFault) { - DPRINTF(Fetch, "Fetch: Setting PC to %08p.\n", next_PC); - cpu->setPC(next_PC); - cpu->setNextPC(next_PC + instSize); + + DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC); + + + PC[tid] = next_PC; + nextPC[tid] = next_PC + instSize; } else { // If the issue was an icache miss, then we can just return and // wait until it is handled. - if (_status == IcacheMissStall) { - return; + if (fetchStatus[tid] == IcacheMissStall) { + panic("Fetch should have exited prior to this!"); } // Handle the fault. @@ -601,17 +1028,169 @@ SimpleFetch::fetch() // have it handled by the upper level CPU class which peeks into the // time buffer and sees if a squash comes along, in which case it // changes the status. - - DPRINTF(Fetch, "Fetch: Blocked, need to handle the trap.\n"); - - _status = Blocked; #if FULL_SYSTEM + // Tell the commit stage the fault we had. + toDecode->fetchFault = fault; + toDecode->fetchFaultSN = cpu->globalSeqNum; + + DPRINTF(Fetch, "[tid:%i]: Blocked, need to handle the trap.\n",tid); + + fetchStatus[tid] = TrapPending; + status_change = true; + + warn("%lli fault (%d) detected @ PC %08p", curTick, fault, PC[tid]); // cpu->trap(fault); // Send a signal to the ROB indicating that there's a trap from the // fetch stage that needs to be handled. Need to indicate that // there's a fault, and the fault type. #else // !FULL_SYSTEM - fatal("fault (%d) detected @ PC %08p", fault, cpu->readPC()); + fatal("fault (%d) detected @ PC %08p", fault, PC[tid]); #endif // FULL_SYSTEM } } + + +/////////////////////////////////////// +// // +// SMT FETCH POLICY MAINTAINED HERE // +// // +/////////////////////////////////////// +template +int +DefaultFetch::getFetchingThread(FetchPriority &fetch_priority) +{ + if (numThreads > 1) { + switch (fetch_priority) { + + case SingleThread: + return 0; + + case RoundRobin: + return roundRobin(); + + case IQ: + return iqCount(); + + case LSQ: + return lsqCount(); + + case Branch: + return branchCount(); + + default: + return -1; + } + } else { + int tid = *((*activeThreads).begin()); + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == IcacheMissComplete || + fetchStatus[tid] == Idle) { + return tid; + } else { + return -1; + } + } + +} + + +template +int +DefaultFetch::roundRobin() +{ + list::iterator pri_iter = priorityList.begin(); + list::iterator end = priorityList.end(); + + int high_pri; + + while (pri_iter != end) { + high_pri = *pri_iter; + + assert(high_pri <= numThreads); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheMissComplete || + fetchStatus[high_pri] == Idle) { + + priorityList.erase(pri_iter); + priorityList.push_back(high_pri); + + return high_pri; + } + + pri_iter++; + } + + return -1; +} + +template +int +DefaultFetch::iqCount() +{ + priority_queue PQ; + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + PQ.push(fromIEW->iewInfo[tid].iqCount); + } + + while (!PQ.empty()) { + + unsigned high_pri = PQ.top(); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheMissComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template +int +DefaultFetch::lsqCount() +{ + priority_queue PQ; + + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + PQ.push(fromIEW->iewInfo[tid].ldstqCount); + } + + while (!PQ.empty()) { + + unsigned high_pri = PQ.top(); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheMissComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template +int +DefaultFetch::branchCount() +{ + list::iterator threads = (*activeThreads).begin(); + + return *threads; +} diff --git a/cpu/o3/free_list.cc b/cpu/o3/free_list.cc index 6f0b4be1e..bd0f4f034 100644 --- a/cpu/o3/free_list.cc +++ b/cpu/o3/free_list.cc @@ -30,7 +30,8 @@ #include "cpu/o3/free_list.hh" -SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs, +SimpleFreeList::SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, unsigned _numPhysicalIntRegs, unsigned _numLogicalFloatRegs, unsigned _numPhysicalFloatRegs) @@ -40,43 +41,30 @@ SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs, numPhysicalFloatRegs(_numPhysicalFloatRegs), numPhysicalRegs(numPhysicalIntRegs + numPhysicalFloatRegs) { - DPRINTF(FreeList, "FreeList: Creating new free list object.\n"); - - // DEBUG stuff. - freeIntRegsScoreboard.resize(numPhysicalIntRegs); - - freeFloatRegsScoreboard.resize(numPhysicalRegs); - - for (PhysRegIndex i = 0; i < numLogicalIntRegs; ++i) { - freeIntRegsScoreboard[i] = 0; - } + DPRINTF(FreeList, "Creating new free list object.\n"); // Put all of the extra physical registers onto the free list. This // means excluding all of the base logical registers. - for (PhysRegIndex i = numLogicalIntRegs; + for (PhysRegIndex i = numLogicalIntRegs * activeThreads; i < numPhysicalIntRegs; ++i) { freeIntRegs.push(i); - - freeIntRegsScoreboard[i] = 1; - } - - for (PhysRegIndex i = 0; i < numPhysicalIntRegs + numLogicalFloatRegs; - ++i) - { - freeFloatRegsScoreboard[i] = 0; } // Put all of the extra physical registers onto the free list. This // means excluding all of the base logical registers. Because the // float registers' indices start where the physical registers end, // some math must be done to determine where the free registers start. - for (PhysRegIndex i = numPhysicalIntRegs + numLogicalFloatRegs; - i < numPhysicalRegs; ++i) + PhysRegIndex i = numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads); + + for ( ; i < numPhysicalRegs; ++i) { freeFloatRegs.push(i); - - freeFloatRegsScoreboard[i] = 1; } } +std::string +SimpleFreeList::name() const +{ + return "cpu.freelist"; +} diff --git a/cpu/o3/free_list.hh b/cpu/o3/free_list.hh index 0b85dba1e..29e84cd44 100644 --- a/cpu/o3/free_list.hh +++ b/cpu/o3/free_list.hh @@ -26,8 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_FREE_LIST_HH__ -#define __CPU_O3_CPU_FREE_LIST_HH__ +#ifndef __CPU_O3_FREE_LIST_HH__ +#define __CPU_O3_FREE_LIST_HH__ #include #include @@ -45,10 +45,9 @@ * other classes, it assumes that the indices for the floating point * registers starts after the integer registers end. Hence the variable * numPhysicalIntRegs is logically equivalent to the baseFP dependency. - * Note that - * while this most likely should be called FreeList, the name "FreeList" - * is used in a typedef within the CPU Policy, and therefore no class - * can be named simply "FreeList". + * Note that while this most likely should be called FreeList, the name + * "FreeList" is used in a typedef within the CPU Policy, and therefore no + * class can be named simply "FreeList". * @todo: Give a better name to the base FP dependency. */ class SimpleFreeList @@ -75,36 +74,51 @@ class SimpleFreeList /** Total number of physical registers. */ int numPhysicalRegs; - /** DEBUG stuff below. */ - std::vector freeIntRegsScoreboard; - - std::vector freeFloatRegsScoreboard; - public: - SimpleFreeList(unsigned _numLogicalIntRegs, + /** Constructs a free list. + * @param activeThreads Number of active threads. + * @param _numLogicalIntRegs Number of logical integer registers. + * @param _numPhysicalIntRegs Number of physical integer registers. + * @param _numLogicalFloatRegs Number of logical fp registers. + * @param _numPhysicalFloatRegs Number of physical fp registers. + */ + SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, unsigned _numPhysicalIntRegs, unsigned _numLogicalFloatRegs, unsigned _numPhysicalFloatRegs); + /** Gives the name of the freelist. */ + std::string name() const; + + /** Gets a free integer register. */ inline PhysRegIndex getIntReg(); + /** Gets a free fp register. */ inline PhysRegIndex getFloatReg(); + /** Adds a register back to the free list. */ inline void addReg(PhysRegIndex freed_reg); + /** Adds an integer register back to the free list. */ inline void addIntReg(PhysRegIndex freed_reg); + /** Adds a fp register back to the free list. */ inline void addFloatReg(PhysRegIndex freed_reg); + /** Checks if there are any free integer registers. */ bool hasFreeIntRegs() { return !freeIntRegs.empty(); } + /** Checks if there are any free fp registers. */ bool hasFreeFloatRegs() { return !freeFloatRegs.empty(); } + /** Returns the number of free integer registers. */ int numFreeIntRegs() { return freeIntRegs.size(); } + /** Returns the number of free fp registers. */ int numFreeFloatRegs() { return freeFloatRegs.size(); } }; @@ -112,7 +126,8 @@ class SimpleFreeList inline PhysRegIndex SimpleFreeList::getIntReg() { - DPRINTF(Rename, "FreeList: Trying to get free integer register.\n"); + DPRINTF(FreeList, "Trying to get free integer register.\n"); + if (freeIntRegs.empty()) { panic("No free integer registers!"); } @@ -121,17 +136,14 @@ SimpleFreeList::getIntReg() freeIntRegs.pop(); - // DEBUG - assert(freeIntRegsScoreboard[free_reg]); - freeIntRegsScoreboard[free_reg] = 0; - return(free_reg); } inline PhysRegIndex SimpleFreeList::getFloatReg() { - DPRINTF(Rename, "FreeList: Trying to get free float register.\n"); + DPRINTF(FreeList, "Trying to get free float register.\n"); + if (freeFloatRegs.empty()) { panic("No free integer registers!"); } @@ -140,42 +152,28 @@ SimpleFreeList::getFloatReg() freeFloatRegs.pop(); - // DEBUG - assert(freeFloatRegsScoreboard[free_reg]); - freeFloatRegsScoreboard[free_reg] = 0; - return(free_reg); } inline void SimpleFreeList::addReg(PhysRegIndex freed_reg) { - DPRINTF(Rename, "Freelist: Freeing register %i.\n", freed_reg); + DPRINTF(FreeList,"Freeing register %i.\n", freed_reg); //Might want to add in a check for whether or not this register is //already in there. A bit vector or something similar would be useful. if (freed_reg < numPhysicalIntRegs) { - freeIntRegs.push(freed_reg); - - // DEBUG - assert(freeIntRegsScoreboard[freed_reg] == false); - freeIntRegsScoreboard[freed_reg] = 1; + if (freed_reg != TheISA::ZeroReg) + freeIntRegs.push(freed_reg); } else if (freed_reg < numPhysicalRegs) { - freeFloatRegs.push(freed_reg); - - // DEBUG - assert(freeFloatRegsScoreboard[freed_reg] == false); - freeFloatRegsScoreboard[freed_reg] = 1; + if (freed_reg != (TheISA::ZeroReg + numPhysicalIntRegs)) + freeFloatRegs.push(freed_reg); } } inline void SimpleFreeList::addIntReg(PhysRegIndex freed_reg) { - DPRINTF(Rename, "Freelist: Freeing int register %i.\n", freed_reg); - - // DEBUG - assert(!freeIntRegsScoreboard[freed_reg]); - freeIntRegsScoreboard[freed_reg] = 1; + DPRINTF(FreeList,"Freeing int register %i.\n", freed_reg); freeIntRegs.push(freed_reg); } @@ -183,13 +181,9 @@ SimpleFreeList::addIntReg(PhysRegIndex freed_reg) inline void SimpleFreeList::addFloatReg(PhysRegIndex freed_reg) { - DPRINTF(Rename, "Freelist: Freeing float register %i.\n", freed_reg); - - // DEBUG - assert(!freeFloatRegsScoreboard[freed_reg]); - freeFloatRegsScoreboard[freed_reg] = 1; + DPRINTF(FreeList,"Freeing float register %i.\n", freed_reg); freeFloatRegs.push(freed_reg); } -#endif // __CPU_O3_CPU_FREE_LIST_HH__ +#endif // __CPU_O3_FREE_LIST_HH__ diff --git a/cpu/o3/fu_pool.cc b/cpu/o3/fu_pool.cc new file mode 100644 index 000000000..9b6ac15d9 --- /dev/null +++ b/cpu/o3/fu_pool.cc @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#include + +#include "cpu/o3/fu_pool.hh" +#include "encumbered/cpu/full/fu_pool.hh" +#include "sim/builder.hh" + +using namespace std; + +//////////////////////////////////////////////////////////////////////////// +// +// A pool of function units +// + +inline void +FUPool::FUIdxQueue::addFU(int fu_idx) +{ + funcUnitsIdx.push_back(fu_idx); + ++size; +} + +inline int +FUPool::FUIdxQueue::getFU() +{ + int retval = funcUnitsIdx[idx++]; + + if (idx == size) + idx = 0; + + return retval; +} + +FUPool::~FUPool() +{ + fuListIterator i = funcUnits.begin(); + fuListIterator end = funcUnits.end(); + for (; i != end; ++i) + delete *i; +} + + +// Constructor +FUPool::FUPool(string name, vector paramList) + : SimObject(name) +{ + numFU = 0; + + funcUnits.clear(); + + for (int i = 0; i < Num_OpClasses; ++i) { + maxOpLatencies[i] = 0; + maxIssueLatencies[i] = 0; + } + + // + // Iterate through the list of FUDescData structures + // + for (FUDDiterator i = paramList.begin(); i != paramList.end(); ++i) { + + // + // Don't bother with this if we're not going to create any FU's + // + if ((*i)->number) { + // + // Create the FuncUnit object from this structure + // - add the capabilities listed in the FU's operation + // description + // + // We create the first unit, then duplicate it as needed + // + FuncUnit *fu = new FuncUnit; + + OPDDiterator j = (*i)->opDescList.begin(); + OPDDiterator end = (*i)->opDescList.end(); + for (; j != end; ++j) { + // indicate that this pool has this capability + capabilityList.set((*j)->opClass); + + // Add each of the FU's that will have this capability to the + // appropriate queue. + for (int k = 0; k < (*i)->number; ++k) + fuPerCapList[(*j)->opClass].addFU(numFU + k); + + // indicate that this FU has the capability + fu->addCapability((*j)->opClass, (*j)->opLat, (*j)->issueLat); + + if ((*j)->opLat > maxOpLatencies[(*j)->opClass]) + maxOpLatencies[(*j)->opClass] = (*j)->opLat; + + if ((*j)->issueLat > maxIssueLatencies[(*j)->opClass]) + maxIssueLatencies[(*j)->opClass] = (*j)->issueLat; + } + + numFU++; + + // Add the appropriate number of copies of this FU to the list + ostringstream s; + + s << (*i)->name() << "(0)"; + fu->name = s.str(); + funcUnits.push_back(fu); + + for (int c = 1; c < (*i)->number; ++c) { + ostringstream s; + numFU++; + FuncUnit *fu2 = new FuncUnit(*fu); + + s << (*i)->name() << "(" << c << ")"; + fu2->name = s.str(); + funcUnits.push_back(fu2); + } + } + } + + unitBusy.resize(numFU); + + for (int i = 0; i < numFU; i++) { + unitBusy[i] = false; + } +} + +void +FUPool::annotateMemoryUnits(unsigned hit_latency) +{ + maxOpLatencies[MemReadOp] = hit_latency; + + fuListIterator i = funcUnits.begin(); + fuListIterator iend = funcUnits.end(); + for (; i != iend; ++i) { + if ((*i)->provides(MemReadOp)) + (*i)->opLatency(MemReadOp) = hit_latency; + + if ((*i)->provides(MemWriteOp)) + (*i)->opLatency(MemWriteOp) = hit_latency; + } +} + +int +FUPool::getUnit(OpClass capability) +{ + // If this pool doesn't have the specified capability, + // return this information to the caller + if (!capabilityList[capability]) + return -2; + + int fu_idx = fuPerCapList[capability].getFU(); + int start_idx = fu_idx; + + // Iterate through the circular queue if needed, stopping if we've reached + // the first element again. + while (unitBusy[fu_idx]) { + fu_idx = fuPerCapList[capability].getFU(); + if (fu_idx == start_idx) { + // No FU available + return -1; + } + } + + unitBusy[fu_idx] = true; + + return fu_idx; +} + +void +FUPool::freeUnit(int fu_idx) +{ + assert(unitBusy[fu_idx]); + unitsToBeFreed.push_back(fu_idx); +} + +void +FUPool::processFreeUnits() +{ + while (!unitsToBeFreed.empty()) { + int fu_idx = unitsToBeFreed.back(); + unitsToBeFreed.pop_back(); + + assert(unitBusy[fu_idx]); + + unitBusy[fu_idx] = false; + } +} + +void +FUPool::dump() +{ + cout << "Function Unit Pool (" << name() << ")\n"; + cout << "======================================\n"; + cout << "Free List:\n"; + + for (int i = 0; i < numFU; ++i) { + if (unitBusy[i]) { + continue; + } + + cout << " [" << i << "] : "; + + cout << funcUnits[i]->name << " "; + + cout << "\n"; + } + + cout << "======================================\n"; + cout << "Busy List:\n"; + for (int i = 0; i < numFU; ++i) { + if (!unitBusy[i]) { + continue; + } + + cout << " [" << i << "] : "; + + cout << funcUnits[i]->name << " "; + + cout << "\n"; + } +} + +// + +//////////////////////////////////////////////////////////////////////////// +// +// The SimObjects we use to get the FU information into the simulator +// +//////////////////////////////////////////////////////////////////////////// + +// +// FUPool - Contails a list of FUDesc objects to make available +// + +// +// The FuPool object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(FUPool) + + SimObjectVectorParam FUList; + +END_DECLARE_SIM_OBJECT_PARAMS(FUPool) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(FUPool) + + INIT_PARAM(FUList, "list of FU's for this pool") + +END_INIT_SIM_OBJECT_PARAMS(FUPool) + + +CREATE_SIM_OBJECT(FUPool) +{ + return new FUPool(getInstanceName(), FUList); +} + +REGISTER_SIM_OBJECT("FUPool", FUPool) + diff --git a/cpu/o3/fu_pool.hh b/cpu/o3/fu_pool.hh new file mode 100644 index 000000000..d7b7acadb --- /dev/null +++ b/cpu/o3/fu_pool.hh @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#ifndef __CPU_O3_FU_POOL_HH__ +#define __CPU_O3_FU_POOL_HH__ + +#include +#include +#include +#include + +#include "base/sched_list.hh" +#include "encumbered/cpu/full/op_class.hh" +#include "sim/sim_object.hh" + +class FUDesc; +class FuncUnit; + +/** + * Pool of FU's, specific to the new CPU model. The old FU pool had lists of + * free units and busy units, and whenever a FU was needed it would iterate + * through the free units to find a FU that provided the capability. This pool + * has lists of units specific to each of the capabilities, and whenever a FU + * is needed, it iterates through that list to find a free unit. The previous + * FU pool would have to be ticked each cycle to update which units became + * free. This FU pool lets the IEW stage handle freeing units, which frees + * them as their scheduled execution events complete. This limits units in this + * model to either have identical issue and op latencies, or 1 cycle issue + * latencies. + */ +class FUPool : public SimObject +{ + private: + /** Maximum op execution latencies, per op class. */ + unsigned maxOpLatencies[Num_OpClasses]; + /** Maximum issue latencies, per op class. */ + unsigned maxIssueLatencies[Num_OpClasses]; + + /** Bitvector listing capabilities of this FU pool. */ + std::bitset capabilityList; + + /** Bitvector listing which FUs are busy. */ + std::vector unitBusy; + + /** List of units to be freed at the end of this cycle. */ + std::vector unitsToBeFreed; + + /** + * Class that implements a circular queue to hold FU indices. The hope is + * that FUs that have been just used will be moved to the end of the queue + * by iterating through it, thus leaving free units at the head of the + * queue. + */ + class FUIdxQueue { + public: + /** Constructs a circular queue of FU indices. */ + FUIdxQueue() + : idx(0), size(0) + { } + + /** Adds a FU to the queue. */ + inline void addFU(int fu_idx); + + /** Returns the index of the FU at the head of the queue, and changes + * the index to the next element. + */ + inline int getFU(); + + private: + /** Circular queue index. */ + int idx; + + /** Size of the queue. */ + int size; + + /** Queue of FU indices. */ + std::vector funcUnitsIdx; + }; + + /** Per op class queues of FUs that provide that capability. */ + FUIdxQueue fuPerCapList[Num_OpClasses]; + + /** Number of FUs. */ + int numFU; + + /** Functional units. */ + std::vector funcUnits; + + typedef std::vector::iterator fuListIterator; + + public: + + /** Constructs a FU pool. */ + FUPool(std::string name, std::vector l); + ~FUPool(); + + /** Annotates units that provide memory operations. Included only because + * old FU pool provided this function. + */ + void annotateMemoryUnits(unsigned hit_latency); + + /** + * Gets a FU providing the requested capability. Will mark the unit as busy, + * but leaves the freeing of the unit up to the IEW stage. + * @param capability The capability requested. + * @return Returns -2 if the FU pool does not have the capability, -1 if + * there is no free FU, and the FU's index otherwise. + */ + int getUnit(OpClass capability); + + /** Frees a FU at the end of this cycle. */ + void freeUnit(int fu_idx); + + /** Frees all FUs on the list. */ + void processFreeUnits(); + + /** Returns the total number of FUs. */ + int size() { return numFU; } + + /** Debugging function used to dump FU information. */ + void dump(); + + /** Returns the operation execution latency of the given capability. */ + unsigned getOpLatency(OpClass capability) { + return maxOpLatencies[capability]; + } + + /** Returns the issue latency of the given capability. */ + unsigned getIssueLatency(OpClass capability) { + return maxIssueLatencies[capability]; + } +}; + +#endif // __CPU_O3_FU_POOL_HH__ diff --git a/cpu/o3/iew.cc b/cpu/o3/iew.cc index 45b5610e7..90d035f71 100644 --- a/cpu/o3/iew.cc +++ b/cpu/o3/iew.cc @@ -31,4 +31,4 @@ #include "cpu/o3/iew_impl.hh" #include "cpu/o3/inst_queue.hh" -template class SimpleIEW; +template class DefaultIEW; diff --git a/cpu/o3/iew.hh b/cpu/o3/iew.hh index 1e370d4e6..e55837812 100644 --- a/cpu/o3/iew.hh +++ b/cpu/o3/iew.hh @@ -26,22 +26,38 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -//Todo: Update with statuses. -//Need to handle delaying writes to the writeback bus if it's full at the -//given time. - -#ifndef __CPU_O3_CPU_SIMPLE_IEW_HH__ -#define __CPU_O3_CPU_SIMPLE_IEW_HH__ +#ifndef __CPU_O3_IEW_HH__ +#define __CPU_O3_IEW_HH__ #include -#include "config/full_system.hh" #include "base/statistics.hh" #include "base/timebuf.hh" +#include "config/full_system.hh" #include "cpu/o3/comm.hh" +#include "cpu/o3/scoreboard.hh" +#include "cpu/o3/lsq.hh" +class FUPool; + +/** + * DefaultIEW handles both single threaded and SMT IEW(issue/execute/writeback). + * It handles the dispatching of instructions to the LSQ/IQ as part of the issue + * stage, and has the IQ try to issue instructions each cycle. The execute + * latency is actually tied into the issue latency to allow the IQ to be able to + * do back-to-back scheduling without having to speculatively schedule + * instructions. This happens by having the IQ have access to the functional + * units, and the IQ gets the execution latencies from the FUs when it issues + * instructions. Instructions reach the execute stage on the last cycle of + * their execution, which is when the IQ knows to wake up any dependent + * instructions, allowing back to back scheduling. The execute portion of IEW + * separates memory instructions from non-memory instructions, either telling + * the LSQ to execute the instruction, or executing the instruction directly. + * The writeback portion of IEW completes the instructions by waking up any + * dependents, and marking the register ready on the scoreboard. + */ template -class SimpleIEW +class DefaultIEW { private: //Typedefs from Impl @@ -52,7 +68,7 @@ class SimpleIEW typedef typename CPUPol::IQ IQ; typedef typename CPUPol::RenameMap RenameMap; - typedef typename CPUPol::LDSTQ LDSTQ; + typedef typename CPUPol::LSQ LSQ; typedef typename CPUPol::TimeStruct TimeStruct; typedef typename CPUPol::IEWStruct IEWStruct; @@ -60,77 +76,214 @@ class SimpleIEW typedef typename CPUPol::IssueStruct IssueStruct; friend class Impl::FullCPU; + friend class CPUPol::IQ; + public: + /** Overall IEW stage status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ enum Status { + Active, + Inactive + }; + + /** Status for Issue, Execute, and Writeback stages. */ + enum StageStatus { Running, Blocked, Idle, + StartSquash, Squashing, Unblocking }; private: + /** Overall stage status. */ Status _status; - Status _issueStatus; - Status _exeStatus; - Status _wbStatus; + /** Dispatch status. */ + StageStatus dispatchStatus[Impl::MaxThreads]; + /** Execute status. */ + StageStatus exeStatus; + /** Writeback status. */ + StageStatus wbStatus; public: - class WritebackEvent : public Event { + /** LdWriteback event for a load completion. */ + class LdWritebackEvent : public Event { private: + /** Instruction that is writing back data to the register file. */ DynInstPtr inst; - SimpleIEW *iewStage; + /** Pointer to IEW stage. */ + DefaultIEW *iewStage; public: - WritebackEvent(DynInstPtr &_inst, SimpleIEW *_iew); + /** Constructs a load writeback event. */ + LdWritebackEvent(DynInstPtr &_inst, DefaultIEW *_iew); + /** Processes writeback event. */ virtual void process(); + /** Returns the description of the writeback event. */ virtual const char *description(); }; public: - SimpleIEW(Params ¶ms); + /** Constructs a DefaultIEW with the given parameters. */ + DefaultIEW(Params *params); + /** Returns the name of the DefaultIEW stage. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); + /** Initializes stage; sends back the number of free IQ and LSQ entries. */ + void initStage(); + + /** Sets CPU pointer for IEW, IQ, and LSQ. */ void setCPU(FullCPU *cpu_ptr); + /** Sets main time buffer used for backwards communication. */ void setTimeBuffer(TimeBuffer *tb_ptr); + /** Sets time buffer for getting instructions coming from rename. */ void setRenameQueue(TimeBuffer *rq_ptr); + /** Sets time buffer to pass on instructions to commit. */ void setIEWQueue(TimeBuffer *iq_ptr); - void setRenameMap(RenameMap *rm_ptr); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list *at_ptr); - void squash(); + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *sb_ptr); - void squashDueToBranch(DynInstPtr &inst); + /** Sets page table pointer within LSQ. */ +// void setPageTable(PageTable *pt_ptr); - void squashDueToMem(DynInstPtr &inst); - - void block(); - - inline void unblock(); + /** Squashes instructions in IEW for a specific thread. */ + void squash(unsigned tid); + /** Wakes all dependents of a completed instruction. */ void 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); + + /** Sends an instruction to commit through the time buffer. */ void instToCommit(DynInstPtr &inst); - private: - void dispatchInsts(); + /** Inserts unused instructions of a thread into the skid buffer. */ + void skidInsert(unsigned tid); + /** Returns the max of the number of entries in all of the skid buffers. */ + int skidCount(); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall IEW status based on all of the stages' statuses. */ + void updateStatus(); + + /** Resets entries of the IQ and the LSQ. */ + void resetEntries(); + + /** Tells the CPU to wakeup if it has descheduled itself due to no + * activity. Used mainly by the LdWritebackEvent. + */ + void wakeCPU(); + + /** Reports to the CPU that there is activity this cycle. */ + void activityThisCycle(); + + /** Tells CPU that the IEW stage is active and running. */ + inline void activateStage(); + + /** Tells CPU that the IEW stage is inactive and idle. */ + inline void deactivateStage(); + +//#if !FULL_SYSTEM + /** Returns if the LSQ has any stores to writeback. */ + bool hasStoresToWB() { return ldstQueue.hasStoresToWB(); } +//#endif + + private: + /** Sends commit proper information for a squash due to a branch + * mispredict. + */ + void squashDueToBranch(DynInstPtr &inst, unsigned thread_id); + + /** Sends commit proper information for a squash due to a memory order + * violation. + */ + void squashDueToMemOrder(DynInstPtr &inst, unsigned thread_id); + + /** Sends commit proper information for a squash due to memory becoming + * blocked (younger issued instructions must be retried). + */ + void squashDueToMemBlocked(DynInstPtr &inst, unsigned thread_id); + + /** Sets Dispatch to blocked, and signals back to other stages to block. */ + void block(unsigned thread_id); + + /** Unblocks Dispatch if the skid buffer is empty, and signals back to + * other stages to unblock. + */ + void unblock(unsigned thread_id); + + /** Determines proper actions to take given Dispatch's status. */ + void dispatch(unsigned tid); + + /** Dispatches instructions to IQ and LSQ. */ + void dispatchInsts(unsigned tid); + + /** Executes instructions. In the case of memory operations, it informs the + * LSQ to execute the instructions. Also handles any redirects that occur + * due to the executed instructions. + */ void executeInsts(); + /** Writebacks instructions. In our model, the instruction's execute() + * function atomically reads registers, executes, and writes registers. + * Thus this writeback only wakes up dependent instructions, and informs + * the scoreboard of registers becoming ready. + */ + void writebackInsts(); + + /** Returns the number of valid, non-squashed instructions coming from + * rename to dispatch. + */ + unsigned validInstsFromRename(); + + /** Reads the stall signals. */ + void readStallSignals(unsigned tid); + + /** Checks if any of the stall conditions are currently true. */ + bool checkStall(unsigned tid); + + /** Processes inputs and changes state accordingly. */ + void checkSignalsAndUpdate(unsigned tid); + + /** Sorts instructions coming from rename into lists separated by thread. */ + void sortInsts(); + public: + /** Ticks IEW stage, causing Dispatch, the IQ, the LSQ, Execute, and + * Writeback to run for one cycle. + */ void tick(); - void iew(); - - //Interfaces to objects inside and outside of IEW. - /** Time buffer interface. */ + private: + /** Pointer to main time buffer used for backwards communication. */ TimeBuffer *timeBuffer; + /** Wire to write information heading to previous stages. */ + typename TimeBuffer::wire toFetch; + /** Wire to get commit's output from backwards time buffer. */ typename TimeBuffer::wire fromCommit; @@ -158,32 +311,67 @@ class SimpleIEW /** Wire to write infromation heading to commit. */ typename TimeBuffer::wire toCommit; - //Will need internal queue to hold onto instructions coming from - //the rename stage in case of a stall. - /** Skid buffer between rename and IEW. */ - std::queue skidBuffer; + /** Queue of all instructions coming from rename this cycle. */ + std::queue insts[Impl::MaxThreads]; - protected: + /** Skid buffer between rename and IEW. */ + std::queue skidBuffer[Impl::MaxThreads]; + + /** Scoreboard pointer. */ + Scoreboard* scoreboard; + + public: /** Instruction queue. */ IQ instQueue; - LDSTQ ldstQueue; + /** Load / store queue. */ + LSQ ldstQueue; -#if !FULL_SYSTEM - public: - void lsqWriteback(); -#endif + /** Pointer to the functional unit pool. */ + FUPool *fuPool; private: - /** Pointer to rename map. Might not want this stage to directly - * access this though... - */ - RenameMap *renameMap; - - /** CPU interface. */ + /** CPU pointer. */ FullCPU *cpu; + /** Records if IEW has written to the time buffer this cycle, so that the + * CPU can deschedule itself if there is no activity. + */ + bool wroteToTimeBuffer; + + /** Source of possible stalls. */ + struct Stalls { + bool commit; + }; + + /** Stages that are telling IEW to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** Debug function to print instructions that are issued this cycle. */ + void printAvailableInsts(); + + public: + /** Records if the LSQ needs to be updated on the next cycle, so that + * IEW knows if there will be activity on the next cycle. + */ + bool updateLSQNextCycle; + private: + /** Records if there is a fetch redirect on this cycle for each thread. */ + bool fetchRedirect[Impl::MaxThreads]; + + /** Used to track if all instructions have been dispatched this cycle. + * If they have not, then blocking must have occurred, and the instructions + * would already be added to the skid buffer. + * @todo: Fix this hack. + */ + bool dispatchedAllInsts; + + /** Records if the queues have been changed (inserted or issued insts), + * so that IEW knows to broadcast the updated amount of free entries. + */ + bool updatedQueues; + /** Commit to IEW delay, in ticks. */ unsigned commitToIEWDelay; @@ -211,29 +399,63 @@ class SimpleIEW */ unsigned executeWidth; - /** Number of cycles stage has been squashing. Used so that the stage - * knows when it can start unblocking, which is when the previous stage - * has received the stall signal and clears up its outputs. - */ - unsigned cyclesSquashing; + /** 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; + + /** Number of active threads. */ + unsigned numThreads; + + /** Pointer to list of active threads. */ + std::list *activeThreads; + + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** Stat for total number of idle cycles. */ Stats::Scalar<> iewIdleCycles; + /** Stat for total number of squashing cycles. */ Stats::Scalar<> iewSquashCycles; + /** Stat for total number of blocking cycles. */ Stats::Scalar<> iewBlockCycles; + /** Stat for total number of unblocking cycles. */ Stats::Scalar<> iewUnblockCycles; -// Stats::Scalar<> iewWBInsts; + /** Stat for total number of instructions dispatched. */ Stats::Scalar<> iewDispatchedInsts; + /** Stat for total number of squashed instructions dispatch skips. */ Stats::Scalar<> iewDispSquashedInsts; + /** Stat for total number of dispatched load instructions. */ Stats::Scalar<> iewDispLoadInsts; + /** Stat for total number of dispatched store instructions. */ Stats::Scalar<> iewDispStoreInsts; + /** Stat for total number of dispatched non speculative instructions. */ Stats::Scalar<> iewDispNonSpecInsts; + /** Stat for number of times the IQ becomes full. */ Stats::Scalar<> iewIQFullEvents; + /** Stat for number of times the LSQ becomes full. */ + Stats::Scalar<> iewLSQFullEvents; + /** Stat for total number of executed instructions. */ Stats::Scalar<> iewExecutedInsts; + /** Stat for total number of executed load instructions. */ Stats::Scalar<> iewExecLoadInsts; + /** Stat for total number of executed store instructions. */ Stats::Scalar<> iewExecStoreInsts; + /** Stat for total number of squashed instructions skipped at execute. */ Stats::Scalar<> iewExecSquashedInsts; + /** Stat for total number of memory ordering violation events. */ Stats::Scalar<> memOrderViolationEvents; + /** Stat for total number of incorrect predicted taken branches. */ Stats::Scalar<> predictedTakenIncorrect; + /** Stat for total number of incorrect predicted not taken branches. */ + Stats::Scalar<> predictedNotTakenIncorrect; + /** Stat for total number of mispredicted branches detected at execute. */ + Stats::Formula branchMispredicts; }; -#endif // __CPU_O3_CPU_IEW_HH__ +#endif // __CPU_O3_IEW_HH__ diff --git a/cpu/o3/iew_impl.hh b/cpu/o3/iew_impl.hh index 85217dd10..21eb7dcf8 100644 --- a/cpu/o3/iew_impl.hh +++ b/cpu/o3/iew_impl.hh @@ -29,59 +29,84 @@ // @todo: Fix the instantaneous communication among all the stages within // iew. There's a clear delay between issue and execute, yet backwards // communication happens simultaneously. -// Update the statuses for each stage. #include #include "base/timebuf.hh" +#include "cpu/o3/fu_pool.hh" #include "cpu/o3/iew.hh" +using namespace std; + template -SimpleIEW::WritebackEvent::WritebackEvent(DynInstPtr &_inst, - SimpleIEW *_iew) - : Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew) +DefaultIEW::LdWritebackEvent::LdWritebackEvent(DynInstPtr &_inst, + DefaultIEW *_iew) + : Event(&mainEventQueue), inst(_inst), iewStage(_iew) { this->setFlags(Event::AutoDelete); } template void -SimpleIEW::WritebackEvent::process() +DefaultIEW::LdWritebackEvent::process() { - DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n"); + DPRINTF(IEW, "Load writeback event [sn:%lli]\n", inst->seqNum); + DPRINTF(Activity, "Activity: Ld Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + + iewStage->wakeCPU(); + + if (inst->isSquashed()) { + inst = NULL; + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Execute again to copy data to proper place. + if (inst->isStore()) { + inst->completeAcc(); + } + } // Need to insert instruction into queue to commit iewStage->instToCommit(inst); - // Need to execute second half of the instruction, do actual writing to - // registers and such - inst->execute(); + + //wroteToTimeBuffer = true; + iewStage->activityThisCycle(); + + inst = NULL; } template const char * -SimpleIEW::WritebackEvent::description() +DefaultIEW::LdWritebackEvent::description() { - return "LSQ writeback event"; + return "Load writeback event"; } template -SimpleIEW::SimpleIEW(Params ¶ms) +DefaultIEW::DefaultIEW(Params *params) : // Just make this time buffer really big for now + // @todo: Make this into a parameter. issueToExecQueue(5, 5), instQueue(params), ldstQueue(params), - commitToIEWDelay(params.commitToIEWDelay), - renameToIEWDelay(params.renameToIEWDelay), - issueToExecuteDelay(params.issueToExecuteDelay), - issueReadWidth(params.issueWidth), - issueWidth(params.issueWidth), - executeWidth(params.executeWidth) + fuPool(params->fuPool), + commitToIEWDelay(params->commitToIEWDelay), + renameToIEWDelay(params->renameToIEWDelay), + issueToExecuteDelay(params->issueToExecuteDelay), + issueReadWidth(params->issueWidth), + issueWidth(params->issueWidth), + executeWidth(params->executeWidth), + numThreads(params->numberOfThreads) { - DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth); - _status = Idle; - _issueStatus = Idle; - _exeStatus = Idle; - _wbStatus = Idle; + DPRINTF(IEW, "executeIntWidth: %i.\n", params->executeIntWidth); + _status = Active; + exeStatus = Running; + wbStatus = Idle; // Setup wire to read instructions coming from issue. fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay); @@ -89,15 +114,36 @@ SimpleIEW::SimpleIEW(Params ¶ms) // Instruction queue needs the queue between issue and execute. instQueue.setIssueToExecuteQueue(&issueToExecQueue); + instQueue.setIEW(this); ldstQueue.setIEW(this); + + for (int i=0; i < numThreads; i++) { + dispatchStatus[i] = Running; + stalls[i].commit = false; + fetchRedirect[i] = false; + } + + updateLSQNextCycle = false; + + // @todo: Make into a parameter + skidBufferMax = (3 * (renameToIEWDelay * params->renameWidth)) + issueWidth; +} + +template +std::string +DefaultIEW::name() const +{ + return cpu->name() + ".iew"; } template void -SimpleIEW::regStats() +DefaultIEW::regStats() { instQueue.regStats(); + //ldstQueue.regStats(); + iewIdleCycles .name(name() + ".iewIdleCycles") .desc("Number of cycles IEW is idle"); @@ -140,6 +186,10 @@ SimpleIEW::regStats() .name(name() + ".iewIQFullEvents") .desc("Number of times the IQ has become full, causing a stall"); + iewLSQFullEvents + .name(name() + ".iewLSQFullEvents") + .desc("Number of times the LSQ has become full, causing a stall"); + iewExecutedInsts .name(name() + ".iewExecutedInsts") .desc("Number of executed instructions"); @@ -163,24 +213,51 @@ SimpleIEW::regStats() predictedTakenIncorrect .name(name() + ".predictedTakenIncorrect") .desc("Number of branches that were predicted taken incorrectly"); + + predictedNotTakenIncorrect + .name(name() + ".predictedNotTakenIncorrect") + .desc("Number of branches that were predicted not taken incorrectly"); + + branchMispredicts + .name(name() + ".branchMispredicts") + .desc("Number of branch mispredicts detected at execute"); + + branchMispredicts = predictedTakenIncorrect + predictedNotTakenIncorrect; } template void -SimpleIEW::setCPU(FullCPU *cpu_ptr) +DefaultIEW::initStage() { - DPRINTF(IEW, "IEW: Setting CPU pointer.\n"); + for (int tid=0; tid < numThreads; tid++) { + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(tid); + + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLSQEntries = + ldstQueue.numFreeEntries(tid); + } +} + +template +void +DefaultIEW::setCPU(FullCPU *cpu_ptr) +{ + DPRINTF(IEW, "Setting CPU pointer.\n"); cpu = cpu_ptr; instQueue.setCPU(cpu_ptr); ldstQueue.setCPU(cpu_ptr); + + cpu->activateStage(FullCPU::IEWIdx); } template void -SimpleIEW::setTimeBuffer(TimeBuffer *tb_ptr) +DefaultIEW::setTimeBuffer(TimeBuffer *tb_ptr) { - DPRINTF(IEW, "IEW: Setting time buffer pointer.\n"); + DPRINTF(IEW, "Setting time buffer pointer.\n"); timeBuffer = tb_ptr; // Setup wire to read information from time buffer, from commit. @@ -189,15 +266,17 @@ SimpleIEW::setTimeBuffer(TimeBuffer *tb_ptr) // Setup wire to write information back to previous stages. toRename = timeBuffer->getWire(0); + toFetch = timeBuffer->getWire(0); + // Instruction queue also needs main time buffer. instQueue.setTimeBuffer(tb_ptr); } template void -SimpleIEW::setRenameQueue(TimeBuffer *rq_ptr) +DefaultIEW::setRenameQueue(TimeBuffer *rq_ptr) { - DPRINTF(IEW, "IEW: Setting rename queue pointer.\n"); + DPRINTF(IEW, "Setting rename queue pointer.\n"); renameQueue = rq_ptr; // Setup wire to read information from rename queue. @@ -206,9 +285,9 @@ SimpleIEW::setRenameQueue(TimeBuffer *rq_ptr) template void -SimpleIEW::setIEWQueue(TimeBuffer *iq_ptr) +DefaultIEW::setIEWQueue(TimeBuffer *iq_ptr) { - DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n"); + DPRINTF(IEW, "Setting IEW queue pointer.\n"); iewQueue = iq_ptr; // Setup wire to write instructions to commit. @@ -217,355 +296,900 @@ SimpleIEW::setIEWQueue(TimeBuffer *iq_ptr) template void -SimpleIEW::setRenameMap(RenameMap *rm_ptr) +DefaultIEW::setActiveThreads(list *at_ptr) { - DPRINTF(IEW, "IEW: Setting rename map pointer.\n"); - renameMap = rm_ptr; + DPRINTF(IEW, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; + + ldstQueue.setActiveThreads(at_ptr); + instQueue.setActiveThreads(at_ptr); } template void -SimpleIEW::squash() +DefaultIEW::setScoreboard(Scoreboard *sb_ptr) { - DPRINTF(IEW, "IEW: Squashing all instructions.\n"); - _status = Squashing; + DPRINTF(IEW, "Setting scoreboard pointer.\n"); + scoreboard = sb_ptr; +} + +#if 0 +template +void +DefaultIEW::setPageTable(PageTable *pt_ptr) +{ + ldstQueue.setPageTable(pt_ptr); +} +#endif + +template +void +DefaultIEW::squash(unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing all instructions.\n", + tid); // Tell the IQ to start squashing. - instQueue.squash(); + instQueue.squash(tid); // Tell the LDSTQ to start squashing. - ldstQueue.squash(fromCommit->commitInfo.doneSeqNum); -} + ldstQueue.squash(fromCommit->commitInfo[tid].doneSeqNum,tid); -template -void -SimpleIEW::squashDueToBranch(DynInstPtr &inst) -{ - DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n", - inst->PC); - // Perhaps leave the squashing up to the ROB stage to tell it when to - // squash? - _status = Squashing; + updatedQueues = true; - // Tell rename to squash through the time buffer. - toCommit->squash = true; - // Also send PC update information back to prior stages. - toCommit->squashedSeqNum = inst->seqNum; - toCommit->mispredPC = inst->readPC(); - toCommit->nextPC = inst->readNextPC(); - toCommit->branchMispredict = true; - // Prediction was incorrect, so send back inverse. - toCommit->branchTaken = inst->readNextPC() != - (inst->readPC() + sizeof(TheISA::MachInst)); -} + // Clear the skid buffer in case it has any data in it. + while (!skidBuffer[tid].empty()) { -template -void -SimpleIEW::squashDueToMem(DynInstPtr &inst) -{ - DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n", - inst->PC); - // Perhaps leave the squashing up to the ROB stage to tell it when to - // squash? - _status = Squashing; + if (skidBuffer[tid].front()->isLoad() || + skidBuffer[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } - // Tell rename to squash through the time buffer. - toCommit->squash = true; - // Also send PC update information back to prior stages. - toCommit->squashedSeqNum = inst->seqNum; - toCommit->nextPC = inst->readNextPC(); -} + toRename->iewInfo[tid].dispatched++; -template -void -SimpleIEW::block() -{ - DPRINTF(IEW, "IEW: Blocking.\n"); - // Set the status to Blocked. - _status = Blocked; + skidBuffer[tid].pop(); + } - // Add the current inputs to the skid buffer so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromRename); + while (!insts[tid].empty()) { + if (insts[tid].front()->isLoad() || + insts[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } - // Note that this stage only signals previous stages to stall when - // it is the cause of the stall originates at this stage. Otherwise - // the previous stages are expected to check all possible stall signals. -} + toRename->iewInfo[tid].dispatched++; -template -inline void -SimpleIEW::unblock() -{ - // Check if there's information in the skid buffer. If there is, then - // set status to unblocking, otherwise set it directly to running. - DPRINTF(IEW, "IEW: Reading instructions out of the skid " - "buffer.\n"); - // Remove the now processed instructions from the skid buffer. - skidBuffer.pop(); - - // If there's still information in the skid buffer, then - // continue to tell previous stages to stall. They will be - // able to restart once the skid buffer is empty. - if (!skidBuffer.empty()) { - toRename->iewInfo.stall = true; - } else { - DPRINTF(IEW, "IEW: Stage is done unblocking.\n"); - _status = Running; + insts[tid].pop(); } } template void -SimpleIEW::wakeDependents(DynInstPtr &inst) +DefaultIEW::squashDueToBranch(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, PC: %#x " + "[sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + // Tell rename to squash through the time buffer. + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->mispredPC[tid] = inst->readPC(); + toCommit->nextPC[tid] = inst->readNextPC(); + toCommit->branchMispredict[tid] = true; + // Prediction was incorrect, so send back inverse. + toCommit->branchTaken[tid] = inst->readNextPC() != + (inst->readPC() + sizeof(TheISA::MachInst)); + + toCommit->includeSquashInst[tid] = false; + //toCommit->iewSquashNum[tid] = inst->seqNum; + + wroteToTimeBuffer = true; +} + +template +void +DefaultIEW::squashDueToMemOrder(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + // Tell rename to squash through the time buffer. + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readNextPC(); + + toCommit->includeSquashInst[tid] = false; + //toCommit->iewSquashNum[tid] = inst->seqNum; + + wroteToTimeBuffer = true; +} + +template +void +DefaultIEW::squashDueToMemBlocked(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readPC(); + + toCommit->includeSquashInst[tid] = true; + + ldstQueue.setLoadBlockedHandled(tid); + + wroteToTimeBuffer = true; +} + +template +void +DefaultIEW::block(unsigned tid) +{ + DPRINTF(IEW, "[tid:%u]: Blocking.\n", tid); + + if (dispatchStatus[tid] != Blocked && + dispatchStatus[tid] != Unblocking) { + toRename->iewBlock[tid] = true; + wroteToTimeBuffer = true; + } + + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + + // Set the status to Blocked. + dispatchStatus[tid] = Blocked; +} + +template +void +DefaultIEW::unblock(unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Reading instructions out of the skid " + "buffer %u.\n",tid, tid); + + // If the skid bufffer is empty, signal back to previous stages to unblock. + // Also switch status to running. + if (skidBuffer[tid].empty()) { + toRename->iewUnblock[tid] = true; + wroteToTimeBuffer = true; + DPRINTF(IEW, "[tid:%i]: Done unblocking.\n",tid); + dispatchStatus[tid] = Running; + } +} + +template +void +DefaultIEW::wakeDependents(DynInstPtr &inst) { instQueue.wakeDependents(inst); } +template +void +DefaultIEW::rescheduleMemInst(DynInstPtr &inst) +{ + instQueue.rescheduleMemInst(inst); +} template void -SimpleIEW::instToCommit(DynInstPtr &inst) +DefaultIEW::replayMemInst(DynInstPtr &inst) { + instQueue.replayMemInst(inst); +} +template +void +DefaultIEW::instToCommit(DynInstPtr &inst) +{ + // First check the time slot that this instruction will write + // to. If there are free write ports at the time, then go ahead + // and write the instruction to that time. If there are not, + // keep looking back to see where's the first time there's a + // free slot. What happens if you run out of free spaces? + // For now naively assume that all instructions take one cycle. + // Otherwise would have to look into the time buffer based on the + // latency of the instruction. + while ((*iewQueue)[wbCycle].insts[wbNumInst]) { + ++wbNumInst; + if (wbNumInst == issueWidth) { + ++wbCycle; + wbNumInst = 0; + } + + assert(wbCycle < 5); + } + + // Add finished instruction to queue to commit. + (*iewQueue)[wbCycle].insts[wbNumInst] = inst; + (*iewQueue)[wbCycle].size++; +} + +template +unsigned +DefaultIEW::validInstsFromRename() +{ + unsigned inst_count = 0; + + for (int i=0; isize; i++) { + if (!fromRename->insts[i]->squashed) + inst_count++; + } + + return inst_count; +} + +template +void +DefaultIEW::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; + + while (!insts[tid].empty()) { + inst = insts[tid].front(); + + insts[tid].pop(); + + DPRINTF(Decode,"[tid:%i]: Inserting [sn:%lli] PC:%#x into " + "dispatch skidBuffer %i\n",tid, inst->seqNum, + inst->readPC(),tid); + + skidBuffer[tid].push(inst); + } + + assert(skidBuffer[tid].size() <= skidBufferMax && + "Skidbuffer Exceeded Max Size"); +} + +template +int +DefaultIEW::skidCount() +{ + int max=0; + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_count = skidBuffer[*threads++].size(); + if (max < thread_count) + max = thread_count; + } + + return max; +} + +template +bool +DefaultIEW::skidsEmpty() +{ + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; } template void -SimpleIEW::dispatchInsts() +DefaultIEW::updateStatus() { - //////////////////////////////////////// - // DISPATCH/ISSUE stage - //////////////////////////////////////// + bool any_unblocking = false; - //Put into its own function? - //Add instructions to IQ if there are any instructions there + list::iterator threads = (*activeThreads).begin(); - // Check if there are any instructions coming from rename, and we're. - // not squashing. - if (fromRename->size > 0) { - int insts_to_add = fromRename->size; + threads = (*activeThreads).begin(); - // Loop through the instructions, putting them in the instruction - // queue. - for (int inst_num = 0; inst_num < insts_to_add; ++inst_num) - { - DynInstPtr inst = fromRename->insts[inst_num]; + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; - // Make sure there's a valid instruction there. - assert(inst); - - DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n", - inst->readPC()); - - // Be sure to mark these instructions as ready so that the - // commit stage can go ahead and execute them, and mark - // them as issued so the IQ doesn't reprocess them. - if (inst->isSquashed()) { - ++iewDispSquashedInsts; - continue; - } else if (instQueue.isFull()) { - DPRINTF(IEW, "IEW: Issue: IQ has become full.\n"); - // Call function to start blocking. - block(); - // Tell previous stage to stall. - toRename->iewInfo.stall = true; - - ++iewIQFullEvents; - break; - } else if (inst->isLoad()) { - DPRINTF(IEW, "IEW: Issue: Memory instruction " - "encountered, adding to LDSTQ.\n"); - - // Reserve a spot in the load store queue for this - // memory access. - ldstQueue.insertLoad(inst); - - ++iewDispLoadInsts; - } else if (inst->isStore()) { - ldstQueue.insertStore(inst); - - ++iewDispStoreInsts; - } else if (inst->isNonSpeculative()) { - DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction " - "encountered, skipping.\n"); - - // Same hack as with stores. - inst->setCanCommit(); - - // Specificall insert it as nonspeculative. - instQueue.insertNonSpec(inst); - - ++iewDispNonSpecInsts; - - continue; - } else if (inst->isNop()) { - DPRINTF(IEW, "IEW: Issue: Nop instruction encountered " - ", skipping.\n"); - - inst->setIssued(); - inst->setExecuted(); - inst->setCanCommit(); - - instQueue.advanceTail(inst); - - continue; - } else if (inst->isExecuted()) { - assert(0 && "Instruction shouldn't be executed.\n"); - DPRINTF(IEW, "IEW: Issue: Executed branch encountered, " - "skipping.\n"); - - inst->setIssued(); - inst->setCanCommit(); - - instQueue.advanceTail(inst); - - continue; - } - - // If the instruction queue is not full, then add the - // instruction. - instQueue.insert(fromRename->insts[inst_num]); - - ++iewDispatchedInsts; + if (dispatchStatus[tid] == Unblocking) { + any_unblocking = true; + break; } } + + // If there are no ready instructions waiting to be scheduled by the IQ, + // and there's no stores waiting to write back, and dispatch is not + // unblocking, then there is no internal activity for the IEW stage. + if (_status == Active && !instQueue.hasReadyInsts() && + !ldstQueue.willWB() && !any_unblocking) { + DPRINTF(IEW, "IEW switching to idle\n"); + + deactivateStage(); + + _status = Inactive; + } else if (_status == Inactive && (instQueue.hasReadyInsts() || + ldstQueue.willWB() || + any_unblocking)) { + // Otherwise there is internal activity. Set to active. + DPRINTF(IEW, "IEW switching to active\n"); + + activateStage(); + + _status = Active; + } } template void -SimpleIEW::executeInsts() +DefaultIEW::resetEntries() { - //////////////////////////////////////// - //EXECUTE/WRITEBACK stage - //////////////////////////////////////// + instQueue.resetEntries(); + ldstQueue.resetEntries(); +} - //Put into its own function? - //Similarly should probably have separate execution for int vs FP. - // Above comment is handled by the issue queue only issuing a valid - // mix of int/fp instructions. - //Actually okay to just have one execution, buuuuuut will need - //somewhere that defines the execution latency of all instructions. - // @todo: Move to the FU pool used in the current full cpu. +template +void +DefaultIEW::readStallSignals(unsigned tid) +{ + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } - int fu_usage = 0; - bool fetch_redirect = false; - int inst_slot = 0; - int time_slot = 0; + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template +bool +DefaultIEW::checkStall(unsigned tid) +{ + bool ret_val(false); + + if (stalls[tid].commit) { + DPRINTF(IEW,"[tid:%i]: Stall from Commit stage detected.\n",tid); + ret_val = true; + } else if (instQueue.isFull(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: IQ is full.\n",tid); + ret_val = true; + } else if (ldstQueue.isFull(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: LSQ is full\n",tid); + + if (ldstQueue.numLoads(tid) > 0 ) { + + DPRINTF(IEW,"[tid:%i]: LSQ oldest load: [sn:%i] \n", + tid,ldstQueue.getLoadHeadSeqNum(tid)); + } + + if (ldstQueue.numStores(tid) > 0) { + + DPRINTF(IEW,"[tid:%i]: LSQ oldest store: [sn:%i] \n", + tid,ldstQueue.getStoreHeadSeqNum(tid)); + } + + ret_val = true; + } else if (ldstQueue.isStalled(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: LSQ stall detected.\n",tid); + ret_val = true; + } + + return ret_val; +} + +template +void +DefaultIEW::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is + // Check stall signals, block if there is. + // If status was Blocked + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + + readStallSignals(tid); + + if (fromCommit->commitInfo[tid].squash) { + squash(tid); + + if (dispatchStatus[tid] == Blocked || + dispatchStatus[tid] == Unblocking) { + toRename->iewUnblock[tid] = true; + wroteToTimeBuffer = true; + } + + dispatchStatus[tid] = Squashing; + + fetchRedirect[tid] = false; + return; + } + + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(IEW, "[tid:%i]: ROB is still squashing.\n"); + + dispatchStatus[tid] = Squashing; + + return; + } + + if (checkStall(tid)) { + block(tid); + dispatchStatus[tid] = Blocked; + return; + } + + if (dispatchStatus[tid] == Blocked) { + // Status from previous cycle was blocked, but there are no more stall + // conditions. Switch over to unblocking. + DPRINTF(IEW, "[tid:%i]: Done blocking, switching to unblocking.\n", + tid); + + dispatchStatus[tid] = Unblocking; + + unblock(tid); + + return; + } + + if (dispatchStatus[tid] == Squashing) { + // Switch status to running if rename isn't being told to block or + // squash this cycle. + DPRINTF(IEW, "[tid:%i]: Done squashing, switching to running.\n", + tid); + + dispatchStatus[tid] = Running; + + return; + } +} + +template +void +DefaultIEW::sortInsts() +{ + int insts_from_rename = fromRename->size; + + for (int i = 0; i < numThreads; i++) + assert(insts[i].empty()); + + for (int i = 0; i < insts_from_rename; ++i) { + insts[fromRename->insts[i]->threadNumber].push(fromRename->insts[i]); + } +} + +template +void +DefaultIEW::wakeCPU() +{ + cpu->wakeCPU(); +} + +template +void +DefaultIEW::activityThisCycle() +{ + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); +} + +template +inline void +DefaultIEW::activateStage() +{ + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(FullCPU::IEWIdx); +} + +template +inline void +DefaultIEW::deactivateStage() +{ + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(FullCPU::IEWIdx); +} + +template +void +DefaultIEW::dispatch(unsigned tid) +{ + // If status is Running or idle, + // call dispatchInsts() + // If status is Unblocking, + // buffer any instructions coming from rename + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (dispatchStatus[tid] == Blocked) { + ++iewBlockCycles; + + } else if (dispatchStatus[tid] == Squashing) { + ++iewSquashCycles; + } + + // Dispatch should try to dispatch as many instructions as its bandwidth + // will allow, as long as it is not currently blocked. + if (dispatchStatus[tid] == Running || + dispatchStatus[tid] == Idle) { + DPRINTF(IEW, "[tid:%i] Not blocked, so attempting to run " + "dispatch.\n", tid); + + dispatchInsts(tid); + } else if (dispatchStatus[tid] == Unblocking) { + // Make sure that the skid buffer has something in it if the + // status is unblocking. + assert(!skidsEmpty()); + + // If the status was unblocking, then instructions from the skid + // buffer were used. Remove those instructions and handle + // the rest of unblocking. + dispatchInsts(tid); + + ++iewUnblockCycles; + + if (validInstsFromRename() && dispatchedAllInsts) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + unblock(tid); + } +} + +template +void +DefaultIEW::dispatchInsts(unsigned tid) +{ + dispatchedAllInsts = true; + + // Obtain instructions from skid buffer if unblocking, or queue from rename + // otherwise. + std::queue &insts_to_dispatch = + dispatchStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + int insts_to_add = insts_to_dispatch.size(); + + DynInstPtr inst; + bool add_to_iq = false; + int dis_num_inst = 0; + + // Loop through the instructions, putting them in the instruction + // queue. + for ( ; dis_num_inst < insts_to_add && + dis_num_inst < issueReadWidth; + ++dis_num_inst) + { + inst = insts_to_dispatch.front(); + + if (dispatchStatus[tid] == Unblocking) { + DPRINTF(IEW, "[tid:%i]: Issue: Examining instruction from skid " + "buffer\n", tid); + } + + // Make sure there's a valid instruction there. + assert(inst); + + DPRINTF(IEW, "[tid:%i]: Issue: Adding PC %#x [sn:%lli] [tid:%i] to " + "IQ.\n", + tid, inst->readPC(), inst->seqNum, inst->threadNumber); + + // Be sure to mark these instructions as ready so that the + // commit stage can go ahead and execute them, and mark + // them as issued so the IQ doesn't reprocess them. + // ------------- + // @TODO: What happens if the ldstqueue is full? + // Do we process the other instructions? + + // Check for squashed instructions. + if (inst->isSquashed()) { + DPRINTF(IEW, "[tid:%i]: Issue: Squashed instruction encountered, " + "not adding to IQ.\n", tid); + + ++iewDispSquashedInsts; + + insts_to_dispatch.pop(); + + //Tell Rename That An Instruction has been processed + if (inst->isLoad() || inst->isStore()) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + toRename->iewInfo[tid].dispatched++; + + continue; + } + + // Check for full conditions. + if (instQueue.isFull(tid)) { + DPRINTF(IEW, "[tid:%i]: Issue: IQ has become full.\n", tid); + + // Call function to start blocking. + block(tid); + + // Set unblock to false. Special case where we are using + // skidbuffer (unblocking) instructions but then we still + // get full in the IQ. + toRename->iewUnblock[tid] = false; + + dispatchedAllInsts = false; + + ++iewIQFullEvents; + break; + } else if (ldstQueue.isFull(tid)) { + DPRINTF(IEW, "[tid:%i]: Issue: LSQ has become full.\n",tid); + + // Call function to start blocking. + block(tid); + + // Set unblock to false. Special case where we are using + // skidbuffer (unblocking) instructions but then we still + // get full in the IQ. + toRename->iewUnblock[tid] = false; + + dispatchedAllInsts = false; + + ++iewLSQFullEvents; + break; + } + + // Otherwise issue the instruction just fine. + if (inst->isLoad()) { + DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction " + "encountered, adding to LSQ.\n", tid); + + // Reserve a spot in the load store queue for this + // memory access. + ldstQueue.insertLoad(inst); + + ++iewDispLoadInsts; + + add_to_iq = true; + + toRename->iewInfo[tid].dispatchedToLSQ++; + } else if (inst->isStore()) { + DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction " + "encountered, adding to LSQ.\n", tid); + + ldstQueue.insertStore(inst); + + ++iewDispStoreInsts; + + if (inst->isNonSpeculative()) { + inst->setCanCommit(); + instQueue.insertNonSpec(inst); + add_to_iq = false; + + ++iewDispNonSpecInsts; + } else { + add_to_iq = true; + } + + toRename->iewInfo[tid].dispatchedToLSQ++; +#if FULL_SYSTEM + } else if (inst->isMemBarrier() || inst->isWriteBarrier()) { + inst->setCanCommit(); + instQueue.insertBarrier(inst); + add_to_iq = false; +#endif + } else if (inst->isNonSpeculative()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nonspeculative instruction " + "encountered, skipping.\n", tid); + + // Same hack as with stores. + inst->setCanCommit(); + + // Specifically insert it as nonspeculative. + instQueue.insertNonSpec(inst); + + ++iewDispNonSpecInsts; + + add_to_iq = false; + } else if (inst->isNop()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nop instruction encountered, " + "skipping.\n", tid); + + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); + + instQueue.advanceTail(inst); + + add_to_iq = false; + } else if (inst->isExecuted()) { + assert(0 && "Instruction shouldn't be executed.\n"); + DPRINTF(IEW, "Issue: Executed branch encountered, " + "skipping.\n"); + + inst->setIssued(); + inst->setCanCommit(); + + instQueue.advanceTail(inst); + + add_to_iq = false; + } else { + add_to_iq = true; + } + + // If the instruction queue is not full, then add the + // instruction. + if (add_to_iq) { + instQueue.insert(inst); + } + + insts_to_dispatch.pop(); + + toRename->iewInfo[tid].dispatched++; + + ++iewDispatchedInsts; + } + + if (!insts_to_dispatch.empty()) { + DPRINTF(IEW,"[tid:%i]: Issue: Bandwidth Full. Blocking.\n"); + block(tid); + toRename->iewUnblock[tid] = false; + } + + if (dispatchStatus[tid] == Idle && dis_num_inst) { + dispatchStatus[tid] = Running; + + updatedQueues = true; + } + + dis_num_inst = 0; +} + +template +void +DefaultIEW::printAvailableInsts() +{ + int inst = 0; + + cout << "Available Instructions: "; + + while (fromIssue->insts[inst]) { + + if (inst%3==0) cout << "\n\t"; + + cout << "PC: " << fromIssue->insts[inst]->readPC() + << " TN: " << fromIssue->insts[inst]->threadNumber + << " SN: " << fromIssue->insts[inst]->seqNum << " | "; + + inst++; + + } + + cout << "\n"; +} + +template +void +DefaultIEW::executeInsts() +{ + //bool fetch_redirect[(*activeThreads).size()]; + wbNumInst = 0; + wbCycle = 0; + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + fetchRedirect[tid] = false; + } + +#if 0 + printAvailableInsts(); +#endif // Execute/writeback any instructions that are available. - for (int inst_num = 0; - fu_usage < executeWidth && /* Haven't exceeded available FU's. */ - inst_num < issueWidth && - fromIssue->insts[inst_num]; + int inst_num = 0; + for ( ; inst_num < issueWidth && /* Haven't exceeded issue bandwidth */ + fromIssue->insts[inst_num]; ++inst_num) { - DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n"); + DPRINTF(IEW, "Execute: Executing instructions from IQ.\n"); // Get instruction from issue's queue. DynInstPtr inst = fromIssue->insts[inst_num]; - DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC()); + DPRINTF(IEW, "Execute: Processing PC %#x, [tid:%i] [sn:%i].\n", + inst->readPC(), inst->threadNumber,inst->seqNum); // Check if the instruction is squashed; if so then skip it // and don't count it towards the FU usage. if (inst->isSquashed()) { - DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n"); + DPRINTF(IEW, "Execute: Instruction was squashed.\n"); // Consider this instruction executed so that commit can go // ahead and retire the instruction. inst->setExecuted(); - toCommit->insts[inst_num] = inst; + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); ++iewExecSquashedInsts; continue; } - inst->setExecuted(); - - // If an instruction is executed, then count it towards FU usage. - ++fu_usage; + Fault fault = NoFault; // Execute instruction. // Note that if the instruction faults, it will be handled // at the commit stage. - if (inst->isMemRef()) { - DPRINTF(IEW, "IEW: Execute: Calculating address for memory " + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(IEW, "Execute: Calculating address for memory " "reference.\n"); // Tell the LDSTQ to execute this instruction (if it is a load). if (inst->isLoad()) { - ldstQueue.executeLoad(inst); + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = ldstQueue.executeLoad(inst); ++iewExecLoadInsts; } else if (inst->isStore()) { ldstQueue.executeStore(inst); ++iewExecStoreInsts; + + // If the store had a fault then it may not have a mem req + if (inst->req && !(inst->req->flags & LOCKED)) { + inst->setExecuted(); + + instToCommit(inst); + } + // Store conditionals will mark themselves as executed, and + // their writeback event will add the instruction to the queue + // to commit. } else { - panic("IEW: Unexpected memory type!\n"); + panic("Unexpected memory type!\n"); } } else { inst->execute(); ++iewExecutedInsts; + + inst->setExecuted(); + + instToCommit(inst); } - // First check the time slot that this instruction will write - // to. If there are free write ports at the time, then go ahead - // and write the instruction to that time. If there are not, - // keep looking back to see where's the first time there's a - // free slot. What happens if you run out of free spaces? - // For now naively assume that all instructions take one cycle. - // Otherwise would have to look into the time buffer based on the - // latency of the instruction. - (*iewQueue)[time_slot].insts[inst_slot]; - while ((*iewQueue)[time_slot].insts[inst_slot]) { - if (inst_slot < issueWidth) { - ++inst_slot; - } else { - ++time_slot; - inst_slot = 0; - } - - assert(time_slot < 5); - } - - // May actually have to work this out, especially with loads and stores - - // Add finished instruction to queue to commit. - (*iewQueue)[time_slot].insts[inst_slot] = inst; - (*iewQueue)[time_slot].size++; - // Check if branch was correct. This check happens after the // instruction is added to the queue because even if the branch // is mispredicted, the branch instruction itself is still valid. // Only handle this if there hasn't already been something that // redirects fetch in this group of instructions. - if (!fetch_redirect) { - if (inst->mispredicted()) { - fetch_redirect = true; - DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n"); - DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n", + // This probably needs to prioritize the redirects if a different + // scheduler is used. Currently the scheduler schedules the oldest + // instruction first, so the branch resolution order will be correct. + unsigned tid = inst->threadNumber; + + if (!fetchRedirect[tid]) { + + if (inst->mispredicted()) { + fetchRedirect[tid] = true; + + DPRINTF(IEW, "Execute: Branch mispredict detected.\n"); + DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n", inst->nextPC); // If incorrect, then signal the ROB that it must be squashed. - squashDueToBranch(inst); + squashDueToBranch(inst, tid); if (inst->predTaken()) { predictedTakenIncorrect++; + } else { + predictedNotTakenIncorrect++; } - } else if (ldstQueue.violation()) { - fetch_redirect = true; + } else if (ldstQueue.violation(tid)) { + fetchRedirect[tid] = true; - // Get the DynInst that caused the violation. - DynInstPtr violator = ldstQueue.getMemDepViolator(); + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = ldstQueue.getMemDepViolator(tid); - DPRINTF(IEW, "IEW: LDSTQ detected a violation. Violator PC: " + DPRINTF(IEW, "LDSTQ detected a violation. Violator PC: " "%#x, inst PC: %#x. Addr is: %#x.\n", violator->readPC(), inst->readPC(), inst->physEffAddr); @@ -573,129 +1197,42 @@ SimpleIEW::executeInsts() instQueue.violation(inst, violator); // Squash. - squashDueToMem(inst); + squashDueToMemOrder(inst,tid); ++memOrderViolationEvents; + } else if (ldstQueue.loadBlocked(tid) && + !ldstQueue.isLoadBlockedHandled(tid)) { + fetchRedirect[tid] = true; + + DPRINTF(IEW, "Load operation couldn't execute because the " + "memory system is blocked. PC: %#x [sn:%lli]\n", + inst->readPC(), inst->seqNum); + + squashDueToMemBlocked(inst, tid); } } } + + if (inst_num) { + if (exeStatus == Idle) { + exeStatus = Running; + } + + updatedQueues = true; + + cpu->activityThisCycle(); + } + + // Need to reset this in case a writeback event needs to write into the + // iew queue. That way the writeback event will write into the correct + // spot in the queue. + wbNumInst = 0; } -template +template void -SimpleIEW::tick() +DefaultIEW::writebackInsts() { - // Considering putting all the state-determining stuff in this section. - - // Try to fill up issue queue with as many instructions as bandwidth - // allows. - // Decode should try to execute as many instructions as its bandwidth - // will allow, as long as it is not currently blocked. - - // Check if the stage is in a running status. - if (_status != Blocked && _status != Squashing) { - DPRINTF(IEW, "IEW: Status is not blocked, attempting to run " - "stage.\n"); - iew(); - - // If it's currently unblocking, check to see if it should switch - // to running. - if (_status == Unblocking) { - unblock(); - - ++iewUnblockCycles; - } - } else if (_status == Squashing) { - - DPRINTF(IEW, "IEW: Still squashing.\n"); - - // Check if stage should remain squashing. Stop squashing if the - // squash signal clears. - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - DPRINTF(IEW, "IEW: Done squashing, changing status to " - "running.\n"); - - _status = Running; - instQueue.stopSquash(); - } else { - instQueue.doSquash(); - } - - ++iewSquashCycles; - } else if (_status == Blocked) { - // Continue to tell previous stage to stall. - toRename->iewInfo.stall = true; - - // Check if possible stall conditions have cleared. - if (!fromCommit->commitInfo.stall && - !instQueue.isFull()) { - DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n"); - _status = Unblocking; - } - - // If there's still instructions coming from rename, continue to - // put them on the skid buffer. - if (fromRename->size == 0) { - block(); - } - - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - } - - ++iewBlockCycles; - } - - // @todo: Maybe put these at the beginning, so if it's idle it can - // return early. - // Write back number of free IQ entries here. - toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries(); - - ldstQueue.writebackStores(); - - // Check the committed load/store signals to see if there's a load - // or store to commit. Also check if it's being told to execute a - // nonspeculative instruction. - // This is pretty inefficient... - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum); - ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum); - } - - if (fromCommit->commitInfo.nonSpecSeqNum != 0) { - instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum); - } - - DPRINTF(IEW, "IEW: IQ has %i free entries.\n", - instQueue.numFreeEntries()); -} - -template -void -SimpleIEW::iew() -{ - // Might want to put all state checks in the tick() function. - // Check if being told to stall from commit. - if (fromCommit->commitInfo.stall) { - block(); - return; - } else if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - // Also check if commit is telling this stage to squash. - squash(); - return; - } - - dispatchInsts(); - - // Have the instruction queue try to schedule any ready instructions. - instQueue.scheduleReadyInsts(); - - executeInsts(); - // Loop through the head of the time buffer and wake any dependents. // These instructions are about to write back. In the simple model // this loop can really happen within the previous loop, but when @@ -704,33 +1241,152 @@ SimpleIEW::iew() // Either have IEW have direct access to rename map, or have this as // part of backwards communication. for (int inst_num = 0; inst_num < issueWidth && - toCommit->insts[inst_num]; inst_num++) - { + toCommit->insts[inst_num]; inst_num++) { DynInstPtr inst = toCommit->insts[inst_num]; - DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n", + DPRINTF(IEW, "Sending instructions to commit, PC %#x.\n", inst->readPC()); - if(!inst->isSquashed()) { + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed() && inst->isExecuted()) { instQueue.wakeDependents(inst); - for (int i = 0; i < inst->numDestRegs(); i++) - { - renameMap->markAsReady(inst->renamedDestRegIdx(i)); + for (int i = 0; i < inst->numDestRegs(); i++) { + //mark as Ready + DPRINTF(IEW,"Setting Destination Register %i\n", + inst->renamedDestRegIdx(i)); + scoreboard->setReg(inst->renamedDestRegIdx(i)); } } } - - // Also should advance its own time buffers if the stage ran. - // Not the best place for it, but this works (hopefully). - issueToExecQueue.advance(); } -#if !FULL_SYSTEM template void -SimpleIEW::lsqWriteback() +DefaultIEW::tick() { - ldstQueue.writebackAllInsts(); + // Try to fill up issue queue with as many instructions as bandwidth + // allows. + wbNumInst = 0; + wbCycle = 0; + + wroteToTimeBuffer = false; + updatedQueues = false; + + sortInsts(); + + list::iterator threads = (*activeThreads).begin(); + + // Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(IEW,"Issue: Processing [tid:%i]\n",tid); + + checkSignalsAndUpdate(tid); + dispatch(tid); + + } + + if (exeStatus != Squashing) { + executeInsts(); + + writebackInsts(); + + // Have the instruction queue try to schedule any ready instructions. + // (In actuality, this scheduling is for instructions that will + // be executed next cycle.) + instQueue.scheduleReadyInsts(); + + // Also should advance its own time buffers if the stage ran. + // Not the best place for it, but this works (hopefully). + issueToExecQueue.advance(); + } + + bool broadcast_free_entries = false; + + if (updatedQueues || exeStatus == Running || updateLSQNextCycle) { + exeStatus = Idle; + updateLSQNextCycle = false; + + broadcast_free_entries = true; + } + + // Writeback any stores using any leftover bandwidth. + ldstQueue.writebackStores(); + + // Free function units marked as being freed this cycle. + fuPool->processFreeUnits(); + + // Check the committed load/store signals to see if there's a load + // or store to commit. Also check if it's being told to execute a + // nonspeculative instruction. + // This is pretty inefficient... + + threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = (*threads++); + + DPRINTF(IEW,"Processing [tid:%i]\n",tid); + + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + !fromCommit->commitInfo[tid].robSquashing) { + + ldstQueue.commitStores(fromCommit->commitInfo[tid].doneSeqNum,tid); + + ldstQueue.commitLoads(fromCommit->commitInfo[tid].doneSeqNum,tid); + + updateLSQNextCycle = true; + instQueue.commit(fromCommit->commitInfo[tid].doneSeqNum,tid); + } + + if (fromCommit->commitInfo[tid].nonSpecSeqNum != 0) { + + //DPRINTF(IEW,"NonspecInst from thread %i",tid); + if (fromCommit->commitInfo[tid].uncached) { + instQueue.replayMemInst(fromCommit->commitInfo[tid].uncachedLoad); + } else { + instQueue.scheduleNonSpec( + fromCommit->commitInfo[tid].nonSpecSeqNum); + } + } + + if (broadcast_free_entries) { + toFetch->iewInfo[tid].iqCount = + instQueue.getCount(tid); + toFetch->iewInfo[tid].ldstqCount = + ldstQueue.getCount(tid); + + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(); + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLSQEntries = + ldstQueue.numFreeEntries(tid); + + wroteToTimeBuffer = true; + } + + DPRINTF(IEW, "[tid:%i], Dispatch dispatched %i instructions.\n", + tid, toRename->iewInfo[tid].dispatched); + + //thread_queue.pop(); + } + + DPRINTF(IEW, "IQ has %i free entries (Can schedule: %i). " + "LSQ has %i free entries.\n", + instQueue.numFreeEntries(), instQueue.hasReadyInsts(), + ldstQueue.numFreeEntries()); + + updateStatus(); + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); + } } -#endif diff --git a/cpu/o3/inst_queue.hh b/cpu/o3/inst_queue.hh index 43fe96c49..283bbdc22 100644 --- a/cpu/o3/inst_queue.hh +++ b/cpu/o3/inst_queue.hh @@ -26,8 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_INST_QUEUE_HH__ -#define __CPU_O3_CPU_INST_QUEUE_HH__ +#ifndef __CPU_O3_INST_QUEUE_HH__ +#define __CPU_O3_INST_QUEUE_HH__ #include #include @@ -37,8 +37,12 @@ #include "base/statistics.hh" #include "base/timebuf.hh" #include "cpu/inst_seq.hh" +#include "encumbered/cpu/full/op_class.hh" #include "sim/host.hh" +class FUPool; +class MemInterface; + /** * A standard instruction queue class. It holds ready instructions, in * order, in seperate priority queues to facilitate the scheduling of @@ -47,7 +51,14 @@ * floating point registers have their indices start after the integer * registers (ie with 96 int and 96 fp registers, regs 0-95 are integer * and 96-191 are fp). This remains true even for both logical and - * physical register indices. + * physical register indices. The IQ depends on the memory dependence unit to + * track when memory operations are ready in terms of ordering; register + * dependencies are tracked normally. Right now the IQ also handles the + * execution timing; this is mainly to allow back-to-back scheduling without + * requiring IEW to be able to peek into the IQ. At the end of the execution + * latency, the instruction is put into the queue to execute, where it will + * have the execute() function called on it. + * @todo: Make IQ able to handle multiple FU pools. */ template class InstructionQueue @@ -58,87 +69,178 @@ class InstructionQueue typedef typename Impl::DynInstPtr DynInstPtr; typedef typename Impl::Params Params; + typedef typename Impl::CPUPol::IEW IEW; typedef typename Impl::CPUPol::MemDepUnit MemDepUnit; typedef typename Impl::CPUPol::IssueStruct IssueStruct; typedef typename Impl::CPUPol::TimeStruct TimeStruct; - // Typedef of iterator through the list of instructions. Might be - // better to untie this from the FullCPU or pass its information to - // the stages. + // Typedef of iterator through the list of instructions. typedef typename std::list::iterator ListIt; - /** - * Struct for comparing entries to be added to the priority queue. This - * gives reverse ordering to the instructions in terms of sequence - * numbers: the instructions with smaller sequence numbers (and hence - * are older) will be at the top of the priority queue. - */ - struct pqCompare - { - bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const - { - return lhs->seqNum > rhs->seqNum; - } + friend class Impl::FullCPU; + + /** FU completion event class. */ + class FUCompletion : public Event { + private: + /** Executing instruction. */ + DynInstPtr inst; + + /** Index of the FU used for executing. */ + int fuIdx; + + /** Pointer back to the instruction queue. */ + InstructionQueue *iqPtr; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstructionQueue *iq_ptr); + + virtual void process(); + virtual const char *description(); }; - /** - * Struct for comparing entries to be added to the set. This gives - * standard ordering in terms of sequence numbers. - */ - struct setCompare - { - bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const - { - return lhs->seqNum < rhs->seqNum; - } - }; + /** Constructs an IQ. */ + InstructionQueue(Params *params); - typedef std::priority_queue, pqCompare> - ReadyInstQueue; + /** Destructs the IQ. */ + ~InstructionQueue(); - InstructionQueue(Params ¶ms); + /** Returns the name of the IQ. */ + std::string name() const; + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu); + /** Sets CPU pointer. */ + void setCPU(FullCPU *_cpu) { cpu = _cpu; } + /** Sets active threads list. */ + void setActiveThreads(std::list *at_ptr); + + /** Sets the IEW pointer. */ + void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; } + + /** Sets the timer buffer between issue and execute. */ void setIssueToExecuteQueue(TimeBuffer *i2eQueue); + /** Sets the global time buffer. */ void setTimeBuffer(TimeBuffer *tb_ptr); + /** Number of entries needed for given amount of threads. */ + int entryAmount(int num_threads); + + /** Resets max entries for all threads. */ + void resetEntries(); + + /** Returns total number of free entries. */ unsigned numFreeEntries(); + /** Returns number of free entries for a thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns whether or not the IQ is full. */ bool isFull(); + /** Returns whether or not the IQ is full for a specific thread. */ + bool isFull(unsigned tid); + + /** Returns if there are any ready instructions in the IQ. */ + bool hasReadyInsts(); + + /** Inserts a new instruction into the IQ. */ void insert(DynInstPtr &new_inst); + /** Inserts a new, non-speculative instruction into the IQ. */ void insertNonSpec(DynInstPtr &new_inst); + /** Inserts a memory or write barrier into the IQ to make sure + * loads and stores are ordered properly. + */ + void insertBarrier(DynInstPtr &barr_inst); + + /** + * Advances the tail of the IQ, used if an instruction is not added to the + * IQ for scheduling. + * @todo: Rename this function. + */ void advanceTail(DynInstPtr &inst); + /** Process FU completion event. */ + void processFUCompletion(DynInstPtr &inst, int fu_idx); + + /** + * Schedules ready instructions, adding the ready ones (oldest first) to + * the queue to execute. + */ void scheduleReadyInsts(); + /** Schedules a single specific non-speculative instruction. */ void scheduleNonSpec(const InstSeqNum &inst); + /** + * Commits all instructions up to and including the given sequence number, + * for a specific thread. + */ + void commit(const InstSeqNum &inst, unsigned tid = 0); + + /** Wakes all dependents of a completed instruction. */ void wakeDependents(DynInstPtr &completed_inst); + /** Adds a ready memory instruction to the ready list. */ + void addReadyMemInst(DynInstPtr &ready_inst); + + /** + * Reschedules a memory instruction. It will be ready to issue once + * replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &resched_inst); + + /** Replays a memory instruction. It must be rescheduled first. */ + void replayMemInst(DynInstPtr &replay_inst); + + /** Completes a memory operation. */ + void completeMemInst(DynInstPtr &completed_inst); + + /** Indicates an ordering violation between a store and a load. */ void violation(DynInstPtr &store, DynInstPtr &faulting_load); - // Change this to take in the sequence number - void squash(); + /** + * Squashes instructions for a thread. Squashing information is obtained + * from the time buffer. + */ + void squash(unsigned tid); - void doSquash(); + /** Returns the number of used entries for a thread. */ + unsigned getCount(unsigned tid) { return count[tid]; }; - void stopSquash(); + /** Updates the number of free entries. */ + void updateFreeEntries(int num) { freeEntries += num; } + + /** Debug function to print all instructions. */ + void printInsts(); private: + /** Does the actual squashing. */ + void doSquash(unsigned tid); + + ///////////////////////// + // Various pointers + ///////////////////////// + /** Pointer to the CPU. */ FullCPU *cpu; + /** Cache interface. */ + MemInterface *dcacheInterface; + + /** Pointer to IEW stage. */ + IEW *iewStage; + /** The memory dependence unit, which tracks/predicts memory dependences * between instructions. */ - MemDepUnit memDepUnit; + MemDepUnit memDepUnit[Impl::MaxThreads]; /** The queue to the execute stage. Issued instructions will be written * into it. @@ -151,36 +253,45 @@ class InstructionQueue /** Wire to read information from timebuffer. */ typename TimeBuffer::wire fromCommit; - enum InstList { - Int, - Float, - Branch, - Memory, - Misc, - Squashed, - None + /** Function unit pool. */ + FUPool *fuPool; + + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list instList[Impl::MaxThreads]; + + /** + * Struct for comparing entries to be added to the priority queue. This + * gives reverse ordering to the instructions in terms of sequence + * numbers: the instructions with smaller sequence numbers (and hence + * are older) will be at the top of the priority queue. + */ + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } }; - /** List of ready int instructions. Used to keep track of the order in - * which instructions should issue. + /** + * Struct for an IQ entry. It includes the instruction and an iterator + * to the instruction's spot in the IQ. */ - ReadyInstQueue readyIntInsts; + struct IQEntry { + DynInstPtr inst; + ListIt iqIt; + }; - /** List of ready floating point instructions. */ - ReadyInstQueue readyFloatInsts; + typedef std::priority_queue, pqCompare> + ReadyInstQueue; - /** List of ready branch instructions. */ - ReadyInstQueue readyBranchInsts; - - /** List of ready miscellaneous instructions. */ - ReadyInstQueue readyMiscInsts; - - /** List of squashed instructions (which are still valid and in IQ). - * Implemented using a priority queue; the entries must contain both - * the IQ index and sequence number of each instruction so that - * ordering based on sequence numbers can be used. + /** List of ready instructions, per op class. They are separated by op + * class to allow for easy mapping to FUs. */ - ReadyInstQueue squashedInsts; + ReadyInstQueue readyInsts[Num_OpClasses]; /** List of non-speculative instructions that will be scheduled * once the IQ gets a signal from commit. While it's redundant to @@ -188,10 +299,68 @@ class InstructionQueue * inside of DynInst), when these instructions are woken up only * the sequence number will be available. Thus it is most efficient to be * able to search by the sequence number alone. + * @todo: Maybe change this to a priority queue per thread. */ std::map nonSpecInsts; - typedef typename std::map::iterator non_spec_it_t; + typedef typename std::map::iterator NonSpecMapIt; + + /** Entry for the list age ordering by op class. */ + struct ListOrderEntry { + OpClass queueType; + InstSeqNum oldestInst; + }; + + /** List that contains the age order of the oldest instruction of each + * ready queue. Used to select the oldest instruction available + * among op classes. + */ + std::list listOrder; + + typedef typename std::list::iterator ListOrderIt; + + /** Tracks if each ready queue is on the age order list. */ + bool queueOnList[Num_OpClasses]; + + /** Iterators of each ready queue. Points to their spot in the age order + * list. + */ + ListOrderIt readyIt[Num_OpClasses]; + + /** Add an op class to the age order list. */ + void addToOrderList(OpClass op_class); + + /** + * Called when the oldest instruction has been removed from a ready queue; + * this places that ready queue into the proper spot in the age order list. + */ + void moveToYoungerInst(ListOrderIt age_order_it); + + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// + + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; + + /** Number of Total Threads*/ + unsigned numThreads; + + /** Pointer to list of active threads. */ + std::list *activeThreads; + + /** Per Thread IQ count */ + unsigned count[Impl::MaxThreads]; + + /** Max IQ Entries Per Thread */ + unsigned maxEntries[Impl::MaxThreads]; /** Number of free IQ entries left. */ unsigned freeEntries; @@ -199,26 +368,10 @@ class InstructionQueue /** The number of entries in the instruction queue. */ unsigned numEntries; - /** The number of integer instructions that can be issued in one - * cycle. - */ - unsigned intWidth; - - /** The number of floating point instructions that can be issued - * in one cycle. - */ - unsigned floatWidth; - - /** The number of branches that can be issued in one cycle. */ - unsigned branchWidth; - - /** The number of memory instructions that can be issued in one cycle. */ - unsigned memoryWidth; - /** The total number of instructions that can be issued in one cycle. */ unsigned totalWidth; - //The number of physical registers in the CPU. + /** The number of physical registers in the CPU. */ unsigned numPhysRegs; /** The number of physical integer registers in the CPU. */ @@ -237,15 +390,12 @@ class InstructionQueue ////////////////////////////////// /** The sequence number of the squashed instruction. */ - InstSeqNum squashedSeqNum; - - /** Iterator that points to the youngest instruction in the IQ. */ - ListIt tail; + InstSeqNum squashedSeqNum[Impl::MaxThreads]; /** Iterator that points to the last instruction that has been squashed. * This will not be valid unless the IQ is in the process of squashing. */ - ListIt squashIt; + ListIt squashIt[Impl::MaxThreads]; /////////////////////////////////// // Dependency graph stuff @@ -254,6 +404,10 @@ class InstructionQueue class DependencyEntry { public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + DynInstPtr inst; //Might want to include data about what arch. register the //dependence is waiting on. @@ -288,15 +442,17 @@ class InstructionQueue * is basically a secondary scoreboard, and should pretty much mirror * the scoreboard that exists in the rename map. */ - vector regScoreboard; + std::vector regScoreboard; + /** Adds an instruction to the dependency graph, as a producer. */ bool addToDependents(DynInstPtr &new_inst); - void insertDependency(DynInstPtr &new_inst); + + /** Adds an instruction to the dependency graph, as a consumer. */ void createDependency(DynInstPtr &new_inst); + /** Moves an instruction to the ready queue if it is ready. */ void addIfReady(DynInstPtr &inst); - private: /** Debugging function to count how many entries are in the IQ. It does * a linear walk through the instructions, so do not call this function * during normal execution. @@ -313,24 +469,42 @@ class InstructionQueue */ void dumpLists(); + /** Debugging function to dump out all instructions that are in the + * IQ. + */ + void dumpInsts(); + + /** Stat for number of instructions added. */ Stats::Scalar<> iqInstsAdded; + /** Stat for number of non-speculative instructions added. */ Stats::Scalar<> iqNonSpecInstsAdded; // Stats::Scalar<> iqIntInstsAdded; + /** Stat for number of integer instructions issued. */ Stats::Scalar<> iqIntInstsIssued; // Stats::Scalar<> iqFloatInstsAdded; + /** Stat for number of floating point instructions issued. */ Stats::Scalar<> iqFloatInstsIssued; // Stats::Scalar<> iqBranchInstsAdded; + /** Stat for number of branch instructions issued. */ Stats::Scalar<> iqBranchInstsIssued; // Stats::Scalar<> iqMemInstsAdded; + /** Stat for number of memory instructions issued. */ Stats::Scalar<> iqMemInstsIssued; // Stats::Scalar<> iqMiscInstsAdded; + /** Stat for number of miscellaneous instructions issued. */ Stats::Scalar<> iqMiscInstsIssued; + /** Stat for number of squashed instructions that were ready to issue. */ Stats::Scalar<> iqSquashedInstsIssued; - Stats::Scalar<> iqLoopSquashStalls; + /** Stat for number of squashed instructions examined when squashing. */ Stats::Scalar<> iqSquashedInstsExamined; + /** Stat for number of squashed instruction operands examined when + * squashing. + */ Stats::Scalar<> iqSquashedOperandsExamined; + /** Stat for number of non-speculative instructions removed due to a squash. + */ Stats::Scalar<> iqSquashedNonSpecRemoved; }; -#endif //__CPU_O3_CPU_INST_QUEUE_HH__ +#endif //__CPU_O3_INST_QUEUE_HH__ diff --git a/cpu/o3/inst_queue_impl.hh b/cpu/o3/inst_queue_impl.hh index 048dc7c00..cfdd25cd5 100644 --- a/cpu/o3/inst_queue_impl.hh +++ b/cpu/o3/inst_queue_impl.hh @@ -39,32 +39,63 @@ #include "sim/root.hh" +#include "cpu/o3/fu_pool.hh" #include "cpu/o3/inst_queue.hh" -// Either compile error or max int due to sign extension. -// Hack to avoid compile warnings. -const InstSeqNum MaxInstSeqNum = std::numeric_limits::max(); +using namespace std; template -InstructionQueue::InstructionQueue(Params ¶ms) - : memDepUnit(params), - numEntries(params.numIQEntries), - intWidth(params.executeIntWidth), - floatWidth(params.executeFloatWidth), - branchWidth(params.executeBranchWidth), - memoryWidth(params.executeMemoryWidth), - totalWidth(params.issueWidth), - numPhysIntRegs(params.numPhysIntRegs), - numPhysFloatRegs(params.numPhysFloatRegs), - commitToIEWDelay(params.commitToIEWDelay) +InstructionQueue::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstructionQueue *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr) { + this->setFlags(Event::AutoDelete); +} + +template +void +InstructionQueue::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, fuIdx); + inst = NULL; +} + + +template +const char * +InstructionQueue::FUCompletion::description() +{ + return "Functional unit completion event"; +} + +template +InstructionQueue::InstructionQueue(Params *params) + : dcacheInterface(params->dcacheInterface), + fuPool(params->fuPool), + numEntries(params->numIQEntries), + totalWidth(params->issueWidth), + numPhysIntRegs(params->numPhysIntRegs), + numPhysFloatRegs(params->numPhysFloatRegs), + commitToIEWDelay(params->commitToIEWDelay) +{ + assert(fuPool); + + numThreads = params->numberOfThreads; + + //Initialize thread IQ counts + for (int i = 0; i ::InstructionQueue(Params ¶ms) // Resize the register scoreboard. regScoreboard.resize(numPhysRegs); + //Initialize Mem Dependence Units + for (int i = 0; i < numThreads; i++) { + memDepUnit[i].init(params,i); + memDepUnit[i].setIQ(this); + } + // Initialize all the head pointers to point to NULL, and all the // entries as unready. // Note that in actuality, the registers corresponding to the logical @@ -80,13 +117,107 @@ InstructionQueue::InstructionQueue(Params ¶ms) // IQ as the instruction should have been correctly told if those // registers are ready in rename. Thus it can all be initialized as // unready. - for (int i = 0; i < numPhysRegs; ++i) - { + for (int i = 0; i < numPhysRegs; ++i) { dependGraph[i].next = NULL; dependGraph[i].inst = NULL; regScoreboard[i] = false; } + for (int i = 0; i < numThreads; ++i) { + squashedSeqNum[i] = 0; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + queueOnList[i] = false; + readyIt[i] = listOrder.end(); + } + + string policy = params->smtIQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out resource sharing policy + if (policy == "dynamic") { + iqPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = numEntries; + } + + } else if (policy == "partitioned") { + iqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = part_amt; + } + + DPRINTF(Fetch, "IQ sharing policy set to Partitioned:" + "%i entries per thread.\n",part_amt); + + } else if (policy == "threshold") { + iqPolicy = Threshold; + + double threshold = (double)params->smtIQThreshold / 100; + + int thresholdIQ = (int)((double)threshold * numEntries); + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = thresholdIQ; + } + + DPRINTF(Fetch, "IQ sharing policy set to Threshold:" + "%i entries per thread.\n",thresholdIQ); + } else { + assert(0 && "Invalid IQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +} + +template +InstructionQueue::~InstructionQueue() +{ + // Clear the dependency graph + DependencyEntry *curr; + DependencyEntry *prev; + + for (int i = 0; i < numPhysRegs; ++i) { + curr = dependGraph[i].next; + + while (curr) { + DependencyEntry::mem_alloc_counter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + if (dependGraph[i].inst) { + dependGraph[i].inst = NULL; + } + + dependGraph[i].next = NULL; + } + + assert(DependencyEntry::mem_alloc_counter == 0); + + delete [] dependGraph; +} + +template +std::string +InstructionQueue::name() const +{ + return cpu->name() + ".iq"; } template @@ -143,12 +274,6 @@ InstructionQueue::regStats() .desc("Number of squashed instructions issued") .prereq(iqSquashedInstsIssued); - iqLoopSquashStalls - .name(name() + ".iqLoopSquashStalls") - .desc("Number of times issue loop had to restart due to squashed " - "inst; mainly for profiling") - .prereq(iqLoopSquashStalls); - iqSquashedInstsExamined .name(name() + ".iqSquashedInstsExamined") .desc("Number of squashed instructions iterated over during squash;" @@ -166,25 +291,25 @@ InstructionQueue::regStats() .desc("Number of squashed non-spec instructions that were removed") .prereq(iqSquashedNonSpecRemoved); - // Tell mem dependence unit to reg stats as well. - memDepUnit.regStats(); + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } } template void -InstructionQueue::setCPU(FullCPU *cpu_ptr) +InstructionQueue::setActiveThreads(list *at_ptr) { - cpu = cpu_ptr; - - tail = cpu->instList.begin(); + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; } template void -InstructionQueue::setIssueToExecuteQueue( - TimeBuffer *i2e_ptr) +InstructionQueue::setIssueToExecuteQueue(TimeBuffer *i2e_ptr) { - DPRINTF(IQ, "IQ: Set the issue to execute queue.\n"); + DPRINTF(IQ, "Set the issue to execute queue.\n"); issueToExecuteQueue = i2e_ptr; } @@ -192,12 +317,44 @@ template void InstructionQueue::setTimeBuffer(TimeBuffer *tb_ptr) { - DPRINTF(IQ, "IQ: Set the time buffer.\n"); + DPRINTF(IQ, "Set the time buffer.\n"); timeBuffer = tb_ptr; fromCommit = timeBuffer->getWire(-commitToIEWDelay); } +template +int +InstructionQueue::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template +void +InstructionQueue::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list::iterator threads = (*activeThreads).begin(); + list::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (iqPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if(iqPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} + template unsigned InstructionQueue::numFreeEntries() @@ -205,6 +362,13 @@ InstructionQueue::numFreeEntries() return freeEntries; } +template +unsigned +InstructionQueue::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - count[tid]; +} + // Might want to do something more complex if it knows how many instructions // will be issued this cycle. template @@ -218,6 +382,34 @@ InstructionQueue::isFull() } } +template +bool +InstructionQueue::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template +bool +InstructionQueue::hasReadyInsts() +{ + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +} + template void InstructionQueue::insert(DynInstPtr &new_inst) @@ -225,7 +417,7 @@ InstructionQueue::insert(DynInstPtr &new_inst) // Make sure the instruction is valid assert(new_inst); - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", + DPRINTF(IQ, "Adding instruction PC %#x to the IQ.\n", new_inst->readPC()); // Check if there are any free entries. Panic if there are none. @@ -233,26 +425,14 @@ InstructionQueue::insert(DynInstPtr &new_inst) // panicing. assert(freeEntries != 0); - // If the IQ currently has nothing in it, then there's a possibility - // that the tail iterator is invalid (might have been pointing at an - // instruction that was retired). Reset the tail iterator. - if (freeEntries == numEntries) { - tail = cpu->instList.begin(); - } - - // Move the tail iterator. Instructions may not have been issued - // to the IQ, so we may have to increment the iterator more than once. - while ((*tail) != new_inst) { - tail++; - - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } - + instList[new_inst->threadNumber].push_back(new_inst); // Decrease the number of free entries. --freeEntries; + //Mark Instruction as in IQ + new_inst->setInIQ(); + // Look through its source registers (physical regs), and mark any // dependencies. addToDependents(new_inst); @@ -264,9 +444,7 @@ InstructionQueue::insert(DynInstPtr &new_inst) // If it's a memory instruction, add it to the memory dependency // unit. if (new_inst->isMemRef()) { - memDepUnit.insert(new_inst); - // Uh..forgot to look it up and put it on the proper dependency list - // if the instruction should not go yet. + memDepUnit[new_inst->threadNumber].insert(new_inst); } else { // If the instruction is ready then add it to the ready list. addIfReady(new_inst); @@ -274,105 +452,145 @@ InstructionQueue::insert(DynInstPtr &new_inst) ++iqInstsAdded; + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + assert(freeEntries == (numEntries - countInsts())); } template void -InstructionQueue::insertNonSpec(DynInstPtr &inst) +InstructionQueue::insertNonSpec(DynInstPtr &new_inst) { - nonSpecInsts[inst->seqNum] = inst; - // @todo: Clean up this code; can do it by setting inst as unable // to issue, then calling normal insert on the inst. // Make sure the instruction is valid - assert(inst); + assert(new_inst); - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - inst->readPC()); + nonSpecInsts[new_inst->seqNum] = new_inst; + + DPRINTF(IQ, "Adding instruction PC %#x to the IQ.\n", + new_inst->readPC()); // Check if there are any free entries. Panic if there are none. // Might want to have this return a fault in the future instead of // panicing. assert(freeEntries != 0); - // If the IQ currently has nothing in it, then there's a possibility - // that the tail iterator is invalid (might have been pointing at an - // instruction that was retired). Reset the tail iterator. - if (freeEntries == numEntries) { - tail = cpu->instList.begin(); - } - - // Move the tail iterator. Instructions may not have been issued - // to the IQ, so we may have to increment the iterator more than once. - while ((*tail) != inst) { - tail++; - - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } + instList[new_inst->threadNumber].push_back(new_inst); // Decrease the number of free entries. --freeEntries; + //Mark Instruction as in IQ + new_inst->setInIQ(); + // Have this instruction set itself as the producer of its destination // register(s). - createDependency(inst); + createDependency(new_inst); // If it's a memory instruction, add it to the memory dependency // unit. - if (inst->isMemRef()) { - memDepUnit.insertNonSpec(inst); + if (new_inst->isMemRef()) { + memDepUnit[new_inst->threadNumber].insertNonSpec(new_inst); } ++iqNonSpecInstsAdded; + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} + +template +void +InstructionQueue::insertBarrier(DynInstPtr &barr_inst) +{ + memDepUnit[barr_inst->threadNumber].insertBarrier(barr_inst); + + insertNonSpec(barr_inst); } -// Slightly hack function to advance the tail iterator in the case that -// the IEW stage issues an instruction that is not added to the IQ. This -// is needed in case a long chain of such instructions occurs. -// I don't think this is used anymore. template void InstructionQueue::advanceTail(DynInstPtr &inst) { - // Make sure the instruction is valid - assert(inst); - - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - inst->readPC()); - - // Check if there are any free entries. Panic if there are none. - // Might want to have this return a fault in the future instead of - // panicing. - assert(freeEntries != 0); - - // If the IQ currently has nothing in it, then there's a possibility - // that the tail iterator is invalid (might have been pointing at an - // instruction that was retired). Reset the tail iterator. - if (freeEntries == numEntries) { - tail = cpu->instList.begin(); - } - - // Move the tail iterator. Instructions may not have been issued - // to the IQ, so we may have to increment the iterator more than once. - while ((*tail) != inst) { - tail++; - - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } - - assert(freeEntries <= numEntries); - // Have this instruction set itself as the producer of its destination // register(s). createDependency(inst); } -// Need to make sure the number of float and integer instructions -// issued does not exceed the total issue bandwidth. +template +void +InstructionQueue::addToOrderList(OpClass op_class) +{ + assert(!readyInsts[op_class].empty()); + + ListOrderEntry queue_entry; + + queue_entry.queueType = op_class; + + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + ListOrderIt list_it = listOrder.begin(); + ListOrderIt list_end_it = listOrder.end(); + + while (list_it != list_end_it) { + if ((*list_it).oldestInst > queue_entry.oldestInst) { + break; + } + + list_it++; + } + + readyIt[op_class] = listOrder.insert(list_it, queue_entry); + queueOnList[op_class] = true; +} + +template +void +InstructionQueue::moveToYoungerInst(ListOrderIt list_order_it) +{ + // Get iterator of next item on the list + // Delete the original iterator + // Determine if the next item is either the end of the list or younger + // than the new instruction. If so, then add in a new iterator right here. + // If not, then move along. + ListOrderEntry queue_entry; + OpClass op_class = (*list_order_it).queueType; + ListOrderIt next_it = list_order_it; + + ++next_it; + + queue_entry.queueType = op_class; + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + while (next_it != listOrder.end() && + (*next_it).oldestInst < queue_entry.oldestInst) { + ++next_it; + } + + readyIt[op_class] = listOrder.insert(next_it, queue_entry); +} + +template +void +InstructionQueue::processFUCompletion(DynInstPtr &inst, int fu_idx) +{ + // The CPU could have been sleeping until this op completed (*extremely* + // long latency op). Wake it if it was. This may be overkill. + iewStage->wakeCPU(); + + fuPool->freeUnit(fu_idx); + + int &size = issueToExecuteQueue->access(0)->size; + + issueToExecuteQueue->access(0)->insts[size++] = inst; +} + // @todo: Figure out a better way to remove the squashed items from the // lists. Checking the top item of each list to see if it's squashed // wastes time and forces jumps. @@ -380,258 +598,181 @@ template void InstructionQueue::scheduleReadyInsts() { - DPRINTF(IQ, "IQ: Attempting to schedule ready instructions from " - "the IQ.\n"); - - int int_issued = 0; - int float_issued = 0; - int branch_issued = 0; - int memory_issued = 0; - int squashed_issued = 0; - int total_issued = 0; + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); IssueStruct *i2e_info = issueToExecuteQueue->access(0); - bool insts_available = !readyBranchInsts.empty() || - !readyIntInsts.empty() || - !readyFloatInsts.empty() || - !memDepUnit.empty() || - !readyMiscInsts.empty() || - !squashedInsts.empty(); - - // Note: Requires a globally defined constant. - InstSeqNum oldest_inst = MaxInstSeqNum; - InstList list_with_oldest = None; - - // Temporary values. - DynInstPtr int_head_inst; - DynInstPtr float_head_inst; - DynInstPtr branch_head_inst; - DynInstPtr mem_head_inst; - DynInstPtr misc_head_inst; - DynInstPtr squashed_head_inst; - - // Somewhat nasty code to look at all of the lists where issuable - // instructions are located, and choose the oldest instruction among - // those lists. Consider a rewrite in the future. - while (insts_available && total_issued < totalWidth) - { - // Set this to false. Each if-block is required to set it to true - // if there were instructions available this check. This will cause - // this loop to run once more than necessary, but avoids extra calls. - insts_available = false; - - oldest_inst = MaxInstSeqNum; - - list_with_oldest = None; - - if (!readyIntInsts.empty() && - int_issued < intWidth) { - - insts_available = true; - - int_head_inst = readyIntInsts.top(); - - if (int_head_inst->isSquashed()) { - readyIntInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } - - oldest_inst = int_head_inst->seqNum; - - list_with_oldest = Int; - } - - if (!readyFloatInsts.empty() && - float_issued < floatWidth) { - - insts_available = true; - - float_head_inst = readyFloatInsts.top(); - - if (float_head_inst->isSquashed()) { - readyFloatInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } else if (float_head_inst->seqNum < oldest_inst) { - oldest_inst = float_head_inst->seqNum; - - list_with_oldest = Float; + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + if (!queueOnList[i]) { + addToOrderList(OpClass(i)); + } else if (readyInsts[i].top()->seqNum < + (*readyIt[i]).oldestInst) { + listOrder.erase(readyIt[i]); + addToOrderList(OpClass(i)); } } - - if (!readyBranchInsts.empty() && - branch_issued < branchWidth) { - - insts_available = true; - - branch_head_inst = readyBranchInsts.top(); - - if (branch_head_inst->isSquashed()) { - readyBranchInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } else if (branch_head_inst->seqNum < oldest_inst) { - oldest_inst = branch_head_inst->seqNum; - - list_with_oldest = Branch; - } - - } - - if (!memDepUnit.empty() && - memory_issued < memoryWidth) { - - insts_available = true; - - mem_head_inst = memDepUnit.top(); - - if (mem_head_inst->isSquashed()) { - memDepUnit.pop(); - - ++iqLoopSquashStalls; - - continue; - } else if (mem_head_inst->seqNum < oldest_inst) { - oldest_inst = mem_head_inst->seqNum; - - list_with_oldest = Memory; - } - } - - if (!readyMiscInsts.empty()) { - - insts_available = true; - - misc_head_inst = readyMiscInsts.top(); - - if (misc_head_inst->isSquashed()) { - readyMiscInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } else if (misc_head_inst->seqNum < oldest_inst) { - oldest_inst = misc_head_inst->seqNum; - - list_with_oldest = Misc; - } - } - - if (!squashedInsts.empty()) { - - insts_available = true; - - squashed_head_inst = squashedInsts.top(); - - if (squashed_head_inst->seqNum < oldest_inst) { - list_with_oldest = Squashed; - } - - } - - DynInstPtr issuing_inst = NULL; - - switch (list_with_oldest) { - case None: - DPRINTF(IQ, "IQ: Not able to schedule any instructions. Issuing " - "inst is %#x.\n", issuing_inst); - break; - - case Int: - issuing_inst = int_head_inst; - readyIntInsts.pop(); - ++int_issued; - DPRINTF(IQ, "IQ: Issuing integer instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Float: - issuing_inst = float_head_inst; - readyFloatInsts.pop(); - ++float_issued; - DPRINTF(IQ, "IQ: Issuing float instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Branch: - issuing_inst = branch_head_inst; - readyBranchInsts.pop(); - ++branch_issued; - DPRINTF(IQ, "IQ: Issuing branch instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Memory: - issuing_inst = mem_head_inst; - - memDepUnit.pop(); - ++memory_issued; - DPRINTF(IQ, "IQ: Issuing memory instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Misc: - issuing_inst = misc_head_inst; - readyMiscInsts.pop(); - - ++iqMiscInstsIssued; - - DPRINTF(IQ, "IQ: Issuing a miscellaneous instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Squashed: - assert(0 && "Squashed insts should not issue any more!"); - squashedInsts.pop(); - // Set the squashed instruction as able to commit so that commit - // can just drop it from the ROB. This is a bit faked. - ++squashed_issued; - ++freeEntries; - - DPRINTF(IQ, "IQ: Issuing squashed instruction PC %#x.\n", - squashed_head_inst->readPC()); - break; - } - - if (list_with_oldest != None && list_with_oldest != Squashed) { - i2e_info->insts[total_issued] = issuing_inst; - i2e_info->size++; - - issuing_inst->setIssued(); - - ++freeEntries; - ++total_issued; - } - - assert(freeEntries == (numEntries - countInsts())); } - iqIntInstsIssued += int_issued; - iqFloatInstsIssued += float_issued; - iqBranchInstsIssued += branch_issued; - iqMemInstsIssued += memory_issued; - iqSquashedInstsIssued += squashed_issued; + // Have iterator to head of the list + // While I haven't exceeded bandwidth or reached the end of the list, + // Try to get a FU that can do what this op needs. + // If successful, change the oldestInst to the new top of the list, put + // the queue in the proper place in the list. + // Increment the iterator. + // This will avoid trying to schedule a certain op class if there are no + // FUs that handle it. + ListOrderIt order_it = listOrder.begin(); + ListOrderIt order_end_it = listOrder.end(); + int total_issued = 0; + int exec_queue_slot = i2e_info->size; + + while (exec_queue_slot < totalWidth && order_it != order_end_it) { + OpClass op_class = (*order_it).queueType; + + assert(!readyInsts[op_class].empty()); + + DynInstPtr issuing_inst = readyInsts[op_class].top(); + + assert(issuing_inst->seqNum == (*order_it).oldestInst); + + if (issuing_inst->isSquashed()) { + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + listOrder.erase(order_it++); + + ++iqSquashedInstsIssued; + + continue; + } + + int idx = fuPool->getUnit(op_class); + + if (idx == -2) { + assert(op_class == No_OpClass); + + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + DPRINTF(IQ, "Thread %i: Issuing instruction PC that needs no FU" + " %#x [sn:%lli]\n", + issuing_inst->threadNumber, issuing_inst->readPC(), + issuing_inst->seqNum); + + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + issuing_inst->setIssued(); + ++total_issued; + + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[issuing_inst->threadNumber]--; + issuing_inst->removeInIQ(); + } else { + memDepUnit[issuing_inst->threadNumber].issue(issuing_inst); + } + + listOrder.erase(order_it++); + + } else if (idx != -1) { + int op_latency = fuPool->getOpLatency(op_class); + + if (op_latency == 1) { + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnit(idx); + } else { + int issue_latency = fuPool->getIssueLatency(op_class); + + if (issue_latency > 1) { + // Generate completion event for the FU + FUCompletion *execution = new FUCompletion(issuing_inst, + idx, this); + + execution->schedule(curTick + issue_latency - 1); + } else { + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnit(idx); + } + } + + DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x " + "[sn:%lli]\n", + issuing_inst->threadNumber, issuing_inst->readPC(), + issuing_inst->seqNum); + + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + issuing_inst->setIssued(); + ++total_issued; + + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[issuing_inst->threadNumber]--; + issuing_inst->removeInIQ(); + } else { + memDepUnit[issuing_inst->threadNumber].issue(issuing_inst); + } + + listOrder.erase(order_it++); + } else { + ++order_it; + } + } + + if (total_issued) { + cpu->activityThisCycle(); + } else { + DPRINTF(IQ, "Not able to schedule any instructions.\n"); + } } template void InstructionQueue::scheduleNonSpec(const InstSeqNum &inst) { - DPRINTF(IQ, "IQ: Marking nonspeculative instruction with sequence " - "number %i as ready to execute.\n", inst); + DPRINTF(IQ, "Marking nonspeculative instruction [sn:%lli] as ready " + "to execute.\n", inst); - non_spec_it_t inst_it = nonSpecInsts.find(inst); + NonSpecMapIt inst_it = nonSpecInsts.find(inst); assert(inst_it != nonSpecInsts.end()); + unsigned tid = (*inst_it).second->threadNumber; + // Mark this instruction as ready to issue. (*inst_it).second->setCanIssue(); @@ -639,27 +780,58 @@ InstructionQueue::scheduleNonSpec(const InstSeqNum &inst) if (!(*inst_it).second->isMemRef()) { addIfReady((*inst_it).second); } else { - memDepUnit.nonSpecInstReady((*inst_it).second); + memDepUnit[tid].nonSpecInstReady((*inst_it).second); } + (*inst_it).second = NULL; + nonSpecInsts.erase(inst_it); } +template +void +InstructionQueue::commit(const InstSeqNum &inst, unsigned tid) +{ + /*Need to go through each thread??*/ + DPRINTF(IQ, "[tid:%i]: Committing instructions older than [sn:%i]\n", + tid,inst); + + ListIt iq_it = instList[tid].begin(); + + while (iq_it != instList[tid].end() && + (*iq_it)->seqNum <= inst) { + ++iq_it; + instList[tid].pop_front(); + } + + assert(freeEntries == (numEntries - countInsts())); +} + template void InstructionQueue::wakeDependents(DynInstPtr &completed_inst) { - DPRINTF(IQ, "IQ: Waking dependents of completed instruction.\n"); - //Look at the physical destination register of the DynInst - //and look it up on the dependency graph. Then mark as ready - //any instructions within the instruction queue. + DPRINTF(IQ, "Waking dependents of completed instruction.\n"); + + assert(!completed_inst->isSquashed()); + // Look at the physical destination register of the DynInst + // and look it up on the dependency graph. Then mark as ready + // any instructions within the instruction queue. DependencyEntry *curr; + DependencyEntry *prev; // Tell the memory dependence unit to wake any dependents on this - // instruction if it is a memory instruction. - + // instruction if it is a memory instruction. Also complete the memory + // instruction at this point since we know it executed fine. + // @todo: Might want to rename "completeMemInst" to + // something that indicates that it won't need to be replayed, and call + // this earlier. Might not be a big deal. if (completed_inst->isMemRef()) { - memDepUnit.wakeDependents(completed_inst); + memDepUnit[completed_inst->threadNumber].wakeDependents(completed_inst); + completeMemInst(completed_inst); + } else if (completed_inst->isMemBarrier() || + completed_inst->isWriteBarrier()) { + memDepUnit[completed_inst->threadNumber].completeBarrier(completed_inst); } for (int dest_reg_idx = 0; @@ -676,17 +848,17 @@ InstructionQueue::wakeDependents(DynInstPtr &completed_inst) continue; } - DPRINTF(IQ, "IQ: Waking any dependents on register %i.\n", + DPRINTF(IQ, "Waking any dependents on register %i.\n", (int) dest_reg); //Maybe abstract this part into a function. //Go through the dependency chain, marking the registers as ready //within the waiting instructions. - while (dependGraph[dest_reg].next) { - curr = dependGraph[dest_reg].next; + curr = dependGraph[dest_reg].next; - DPRINTF(IQ, "IQ: Waking up a dependent instruction, PC%#x.\n", + while (curr) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", curr->inst->readPC()); // Might want to give more information to the instruction @@ -697,13 +869,13 @@ InstructionQueue::wakeDependents(DynInstPtr &completed_inst) addIfReady(curr->inst); - dependGraph[dest_reg].next = curr->next; - DependencyEntry::mem_alloc_counter--; - curr->inst = NULL; + prev = curr; + curr = prev->next; + prev->inst = NULL; - delete curr; + delete prev; } // Reset the head node now that all of its dependents have been woken @@ -716,63 +888,116 @@ InstructionQueue::wakeDependents(DynInstPtr &completed_inst) } } +template +void +InstructionQueue::addReadyMemInst(DynInstPtr &ready_inst) +{ + OpClass op_class = ready_inst->opClass(); + + readyInsts[op_class].push(ready_inst); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + ready_inst->readPC(), op_class, ready_inst->seqNum); +} + +template +void +InstructionQueue::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template +void +InstructionQueue::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} + +template +void +InstructionQueue::completeMemInst(DynInstPtr &completed_inst) +{ + int tid = completed_inst->threadNumber; + + DPRINTF(IQ, "Completing mem instruction PC:%#x [sn:%lli]\n", + completed_inst->readPC(), completed_inst->seqNum); + + ++freeEntries; + + completed_inst->memOpDone = true; + + memDepUnit[tid].completed(completed_inst); + + count[tid]--; +} + template void InstructionQueue::violation(DynInstPtr &store, DynInstPtr &faulting_load) { - memDepUnit.violation(store, faulting_load); + memDepUnit[store->threadNumber].violation(store, faulting_load); } template void -InstructionQueue::squash() +InstructionQueue::squash(unsigned tid) { - DPRINTF(IQ, "IQ: Starting to squash instructions in the IQ.\n"); + DPRINTF(IQ, "[tid:%i]: Starting to squash instructions in " + "the IQ.\n", tid); // Read instruction sequence number of last instruction out of the // time buffer. - squashedSeqNum = fromCommit->commitInfo.doneSeqNum; + squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; // Setup the squash iterator to point to the tail. - squashIt = tail; + squashIt[tid] = instList[tid].end(); + --squashIt[tid]; // Call doSquash if there are insts in the IQ - if (freeEntries != numEntries) { - doSquash(); + if (count[tid] > 0) { + doSquash(tid); } // Also tell the memory dependence unit to squash. - memDepUnit.squash(squashedSeqNum); + memDepUnit[tid].squash(squashedSeqNum[tid], tid); } template void -InstructionQueue::doSquash() +InstructionQueue::doSquash(unsigned tid) { - // Make sure the squash iterator isn't pointing to nothing. - assert(squashIt != cpu->instList.end()); // Make sure the squashed sequence number is valid. - assert(squashedSeqNum != 0); +// assert(squashedSeqNum[tid] != 0); - DPRINTF(IQ, "IQ: Squashing instructions in the IQ.\n"); + DPRINTF(IQ, "[tid:%i]: Squashing until sequence number %i!\n", + tid, squashedSeqNum[tid]); // Squash any instructions younger than the squashed sequence number // given. - while ((*squashIt)->seqNum > squashedSeqNum) { - DynInstPtr squashed_inst = (*squashIt); + while (squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->seqNum > squashedSeqNum[tid]) { + + DynInstPtr squashed_inst = (*squashIt[tid]); // Only handle the instruction if it actually is in the IQ and // hasn't already been squashed in the IQ. - if (!squashed_inst->isIssued() && - !squashed_inst->isSquashedInIQ()) { + if (squashed_inst->threadNumber != tid || + squashed_inst->isSquashedInIQ()) { + --squashIt[tid]; + continue; + } + + if (!squashed_inst->isIssued() || + (squashed_inst->isMemRef() && + !squashed_inst->memOpDone)) { // Remove the instruction from the dependency list. - // Hack for now: These below don't add themselves to the - // dependency list, so don't try to remove them. - if (!squashed_inst->isNonSpeculative()/* && - !squashed_inst->isStore()*/ - ) { + if (!squashed_inst->isNonSpeculative() && + !squashed_inst->isMemBarrier() && + !squashed_inst->isWriteBarrier()) { for (int src_reg_idx = 0; src_reg_idx < squashed_inst->numSrcRegs(); @@ -787,19 +1012,29 @@ InstructionQueue::doSquash() // dependency chain aren't informed that a specific src // register has become ready. This may not always be true // in the future. + // Instead of doing a linked list traversal, we can just + // remove these squashed instructions either at issue time, + // or when the register is overwritten. The only downside + // to this is it leaves more room for error. + if (!squashed_inst->isReadySrcRegIdx(src_reg_idx) && src_reg < numPhysRegs) { dependGraph[src_reg].remove(squashed_inst); } + ++iqSquashedOperandsExamined; } // Might want to remove producers as well. } else { - nonSpecInsts[squashed_inst->seqNum] = NULL; + NonSpecMapIt ns_inst_it = + nonSpecInsts.find(squashed_inst->seqNum); + assert(ns_inst_it != nonSpecInsts.end()); - nonSpecInsts.erase(squashed_inst->seqNum); + (*ns_inst_it).second = NULL; + + nonSpecInsts.erase(ns_inst_it); ++iqSquashedNonSpecRemoved; } @@ -809,37 +1044,30 @@ InstructionQueue::doSquash() // Mark it as squashed within the IQ. squashed_inst->setSquashedInIQ(); -// squashedInsts.push(squashed_inst); + // @todo: Remove this hack where several statuses are set so the + // inst will flow through the rest of the pipeline. squashed_inst->setIssued(); squashed_inst->setCanCommit(); + squashed_inst->removeInIQ(); + + //Update Thread IQ Count + count[squashed_inst->threadNumber]--; ++freeEntries; - DPRINTF(IQ, "IQ: Instruction PC %#x squashed.\n", - squashed_inst->readPC()); + if (numThreads > 1) { + DPRINTF(IQ, "[tid:%i]: Instruction [sn:%lli] PC %#x " + "squashed.\n", + tid, squashed_inst->seqNum, squashed_inst->readPC()); + } else { + DPRINTF(IQ, "Instruction [sn:%lli] PC %#x squashed.\n", + squashed_inst->seqNum, squashed_inst->readPC()); + } } - --squashIt; + instList[tid].erase(squashIt[tid]--); ++iqSquashedInstsExamined; } - - assert(freeEntries <= numEntries); - - if (freeEntries == numEntries) { - tail = cpu->instList.end(); - } - -} - -template -void -InstructionQueue::stopSquash() -{ - // Clear up the squash variables to ensure that squashing doesn't - // get called improperly. - squashedSeqNum = 0; - - squashIt = cpu->instList.end(); } template @@ -877,8 +1105,7 @@ InstructionQueue::DependencyEntry::remove(DynInstPtr &inst_to_remove) } // Find the instruction to remove within the dependency linked list. - while(curr->inst != inst_to_remove) - { + while (curr->inst != inst_to_remove) { prev = curr; curr = curr->next; @@ -920,7 +1147,7 @@ InstructionQueue::addToDependents(DynInstPtr &new_inst) if (src_reg >= numPhysRegs) { continue; } else if (regScoreboard[src_reg] == false) { - DPRINTF(IQ, "IQ: Instruction PC %#x has src reg %i that " + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " "is being added to the dependency chain.\n", new_inst->readPC(), src_reg); @@ -930,7 +1157,7 @@ InstructionQueue::addToDependents(DynInstPtr &new_inst) // was added to the dependency graph. return_val = true; } else { - DPRINTF(IQ, "IQ: Instruction PC %#x has src reg %i that " + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " "became ready before it reached the IQ.\n", new_inst->readPC(), src_reg); // Mark a register ready within the instruction. @@ -966,13 +1193,13 @@ InstructionQueue::createDependency(DynInstPtr &new_inst) continue; } - dependGraph[dest_reg].inst = new_inst; - if (dependGraph[dest_reg].next) { dumpDependGraph(); - panic("IQ: Dependency graph not empty!"); + panic("Dependency graph %i not empty!", dest_reg); } + dependGraph[dest_reg].inst = new_inst; + // Mark the scoreboard to say it's not yet ready. regScoreboard[dest_reg] = false; } @@ -987,96 +1214,62 @@ InstructionQueue::addIfReady(DynInstPtr &inst) if (inst->readyToIssue()) { //Add the instruction to the proper ready list. - if (inst->isControl()) { + if (inst->isMemRef()) { - DPRINTF(IQ, "IQ: Branch instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyBranchInsts.push(inst); - - } else if (inst->isMemRef()) { - - DPRINTF(IQ, "IQ: Checking if memory instruction can issue.\n"); + DPRINTF(IQ, "Checking if memory instruction can issue.\n"); // Message to the mem dependence unit that this instruction has // its registers ready. - memDepUnit.regsReady(inst); + memDepUnit[inst->threadNumber].regsReady(inst); -#if 0 - if (memDepUnit.readyToIssue(inst)) { - DPRINTF(IQ, "IQ: Memory instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyMemInsts.push(inst); - } else { - // Make dependent on the store. - // Will need some way to get the store instruction it should - // be dependent upon; then when the store issues it can - // put the instruction on the ready list. - // Yet another tree? - assert(0 && "Instruction has no way to actually issue"); - } -#endif - - } else if (inst->isInteger()) { - - DPRINTF(IQ, "IQ: Integer instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyIntInsts.push(inst); - - } else if (inst->isFloating()) { - - DPRINTF(IQ, "IQ: Floating instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyFloatInsts.push(inst); - - } else { - DPRINTF(IQ, "IQ: Miscellaneous instruction is ready to issue, " - "putting it onto the ready list, PC %#x..\n", - inst->readPC()); - - readyMiscInsts.push(inst); + return; } + + OpClass op_class = inst->opClass(); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + inst->readPC(), op_class, inst->seqNum); + + readyInsts[op_class].push(inst); } } -/* - * Caution, this function must not be called prior to tail being updated at - * least once, otherwise it will fail the assertion. This is because - * instList.begin() actually changes upon the insertion of an element into the - * list when the list is empty. - */ template int InstructionQueue::countInsts() { - ListIt count_it = cpu->instList.begin(); + //ksewell:This works but definitely could use a cleaner write + //with a more intuitive way of counting. Right now it's + //just brute force .... + +#if 0 int total_insts = 0; - if (tail == cpu->instList.end()) - return 0; + for (int i = 0; i < numThreads; ++i) { + ListIt count_it = instList[i].begin(); - while (count_it != tail) { - if (!(*count_it)->isIssued()) { - ++total_insts; + while (count_it != instList[i].end()) { + if (!(*count_it)->isSquashed() && !(*count_it)->isSquashedInIQ()) { + if (!(*count_it)->isIssued()) { + ++total_insts; + } else if ((*count_it)->isMemRef() && + !(*count_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++total_insts; + } + } + + ++count_it; } - - ++count_it; - - assert(count_it != cpu->instList.end()); - } - - // Need to count the tail iterator as well. - if (count_it != cpu->instList.end() && - (*count_it) && - !(*count_it)->isIssued()) { - ++total_insts; } return total_insts; +#else + return numEntries - freeEntries; +#endif } template @@ -1090,8 +1283,8 @@ InstructionQueue::dumpDependGraph() curr = &dependGraph[i]; if (curr->inst) { - cprintf("dependGraph[%i]: producer: %#x consumer: ", i, - curr->inst->readPC()); + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); } else { cprintf("dependGraph[%i]: No producer. consumer: ", i); } @@ -1099,7 +1292,8 @@ InstructionQueue::dumpDependGraph() while (curr->next != NULL) { curr = curr->next; - cprintf("%#x ", curr->inst->readPC()); + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); } cprintf("\n"); @@ -1110,27 +1304,87 @@ template void InstructionQueue::dumpLists() { - cprintf("Ready integer list size: %i\n", readyIntInsts.size()); + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts[i].size()); - cprintf("Ready float list size: %i\n", readyFloatInsts.size()); - - cprintf("Ready branch list size: %i\n", readyBranchInsts.size()); - - cprintf("Ready misc list size: %i\n", readyMiscInsts.size()); - - cprintf("Squashed list size: %i\n", squashedInsts.size()); + cprintf("\n"); + } cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); - non_spec_it_t non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); cprintf("Non speculative list: "); - while (non_spec_it != nonSpecInsts.end()) { - cprintf("%#x ", (*non_spec_it).second->readPC()); + while (non_spec_it != non_spec_end_it) { + cprintf("%#x [sn:%lli]", (*non_spec_it).second->readPC(), + (*non_spec_it).second->seqNum); ++non_spec_it; } cprintf("\n"); + ListOrderIt list_order_it = listOrder.begin(); + ListOrderIt list_order_end_it = listOrder.end(); + int i = 1; + + cprintf("List order: "); + + while (list_order_it != list_order_end_it) { + cprintf("%i OpClass:%i [sn:%lli] ", i, (*list_order_it).queueType, + (*list_order_it).oldestInst); + + ++list_order_it; + ++i; + } + + cprintf("\n"); +} + + +template +void +InstructionQueue::dumpInsts() +{ + for (int i = 0; i < numThreads; ++i) { + int num = 0; + int valid_num = 0; + ListIt inst_list_it = instList[i].begin(); + + while (inst_list_it != instList[i].end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } + } } diff --git a/cpu/o3/lsq.cc b/cpu/o3/lsq.cc new file mode 100644 index 000000000..8991ab8f8 --- /dev/null +++ b/cpu/o3/lsq.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha_cpu.hh" +#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/lsq_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class LSQ; + diff --git a/cpu/o3/lsq.hh b/cpu/o3/lsq.hh new file mode 100644 index 000000000..c59b5f13b --- /dev/null +++ b/cpu/o3/lsq.hh @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#ifndef __CPU_O3_LSQ_HH__ +#define __CPU_O3_LSQ_HH__ + +#include +#include + +#include "base/hashmap.hh" +#include "config/full_system.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/cpu_policy.hh" +#include "cpu/o3/lsq_unit.hh" +#include "mem/mem_interface.hh" +//#include "mem/page_table.hh" +#include "sim/sim_object.hh" + +template +class LSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::LSQUnit LSQUnit; + + enum LSQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** Constructs an LSQ with the given parameters. */ + LSQ(Params *params); + + /** Returns the name of the LSQ. */ + std::string name() const; + + /** Sets the pointer to the list of active threads. */ + void setActiveThreads(std::list *at_ptr); + /** Sets the CPU pointer. */ + void setCPU(FullCPU *cpu_ptr); + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr); + /** Sets the page table pointer. */ +// void setPageTable(PageTable *pt_ptr); + + /** Number of entries needed for the given amount of threads.*/ + int entryAmount(int num_threads); + void removeEntries(unsigned tid); + /** Reset the max entries for each thread. */ + void resetEntries(); + /** Resize the max entries for a thread. */ + void resizeEntries(unsigned size, unsigned tid); + + /** Ticks the LSQ. */ + void tick(); + /** Ticks a specific LSQ Unit. */ + void tick(unsigned tid); + + /** Inserts a load into the LSQ. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store into the LSQ. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load. */ + Fault executeLoad(DynInstPtr &inst); + + Fault executeLoad(int lq_idx, unsigned tid); + /** Executes a store. */ + Fault executeStore(DynInstPtr &inst); + + /** + * Commits loads up until the given sequence number for a specific thread. + */ + void commitLoads(InstSeqNum &youngest_inst, unsigned tid); + /** + * Commits stores up until the given sequence number for a specific thread. + */ + void commitStores(InstSeqNum &youngest_inst, unsigned tid); + + /** + * Attempts to write back stores until all cache ports are used or the + * interface becomes blocked. + */ + void writebackStores(); + /** Same as above, but only for one thread. */ + void writebackStores(unsigned tid); + + /** + * Squash instructions from a thread until the specified sequence number. + */ + void squash(const InstSeqNum &squashed_num, unsigned tid); + + /** Returns whether or not there was a memory ordering violation. */ + bool violation(); + /** + * Returns whether or not there was a memory ordering violation for a + * specific thread. + */ + bool violation(unsigned tid); + + /** Returns if a load is blocked due to the memory system for a specific + * thread. + */ + bool loadBlocked(unsigned tid); + + bool isLoadBlockedHandled(unsigned tid) + { return thread[tid].isLoadBlockedHandled(); } + + void setLoadBlockedHandled(unsigned tid) + { thread[tid].setLoadBlockedHandled(); } + + /** Gets the instruction that caused the memory ordering violation. */ + DynInstPtr getMemDepViolator(unsigned tid); + + /** Returns the head index of the load queue for a specific thread. */ + int getLoadHead(unsigned tid); + /** Returns the sequence number of the head of the load queue. */ + InstSeqNum getLoadHeadSeqNum(unsigned tid) + { + return thread[tid].getLoadHeadSeqNum(); + } + + /** Returns the head index of the store queue. */ + int getStoreHead(unsigned tid); + /** Returns the sequence number of the head of the store queue. */ + InstSeqNum getStoreHeadSeqNum(unsigned tid) + { + return thread[tid].getStoreHeadSeqNum(); + } + + /** Returns the number of instructions in all of the queues. */ + int getCount(); + /** Returns the number of instructions in the queues of one thread. */ + int getCount(unsigned tid); + + /** Returns the total number of loads in the load queue. */ + int numLoads(); + /** Returns the total number of loads for a single thread. */ + int numLoads(unsigned tid); + + /** Returns the total number of stores in the store queue. */ + int numStores(); + /** Returns the total number of stores for a single thread. */ + int numStores(unsigned tid); + + /** Returns the total number of loads that are ready. */ + int numLoadsReady(); + /** Returns the number of loads that are ready for a single thread. */ + int numLoadsReady(unsigned tid); + + /** Returns the number of free entries. */ + unsigned numFreeEntries(); + /** Returns the number of free entries for a specific thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns if the LSQ is full (either LQ or SQ is full). */ + bool isFull(); + /** + * Returns if the LSQ is full for a specific thread (either LQ or SQ is + * full). + */ + bool isFull(unsigned tid); + + /** Returns if any of the LQs are full. */ + bool lqFull(); + /** Returns if the LQ of a given thread is full. */ + bool lqFull(unsigned tid); + + /** Returns if any of the SQs are full. */ + bool sqFull(); + /** Returns if the SQ of a given thread is full. */ + bool sqFull(unsigned tid); + + /** + * Returns if the LSQ is stalled due to a memory operation that must be + * replayed. + */ + bool isStalled(); + /** + * Returns if the LSQ of a specific thread is stalled due to a memory + * operation that must be replayed. + */ + bool isStalled(unsigned tid); + + /** Returns whether or not there are any stores to write back to memory. */ + bool hasStoresToWB(); + /** Returns whether or not a specific thread has any stores to write back + * to memory. + */ + bool hasStoresToWB(unsigned tid); + /** Returns the number of stores a specific thread has to write back. */ + int numStoresToWB(unsigned tid); + + /** Returns if the LSQ will write back to memory this cycle. */ + bool willWB(); + /** Returns if the LSQ of a specific thread will write back to memory this + * cycle. + */ + bool willWB(unsigned tid); + + /** Debugging function to print out all instructions. */ + void dumpInsts(); + /** Debugging function to print out instructions from a specific thread. */ + void dumpInsts(unsigned tid); + + /** Executes a read operation, using the load specified at the load index. */ + template + Fault read(MemReqPtr &req, T &data, int load_idx); + + /** Executes a store operation, using the store specified at the store + * index. + */ + template + Fault write(MemReqPtr &req, T &data, int store_idx); + + private: + /** The LSQ policy for SMT mode. */ + LSQPolicy lsqPolicy; + + /** The LSQ units for individual threads. */ + LSQUnit thread[Impl::MaxThreads]; + + /** The CPU pointer. */ + FullCPU *cpu; + + /** The IEW stage pointer. */ + IEW *iewStage; + + /** The pointer to the page table. */ +// PageTable *pTable; + + /** List of Active Threads in System. */ + std::list *activeThreads; + + /** Total Size of LQ Entries. */ + unsigned LQEntries; + /** Total Size of SQ Entries. */ + unsigned SQEntries; + + /** Max LQ Size - Used to Enforce Sharing Policies. */ + unsigned maxLQEntries; + + /** Max SQ Size - Used to Enforce Sharing Policies. */ + unsigned maxSQEntries; + + /** Global Load Count. */ + int loads; + + /** Global Store Count */ + int stores; + + /** Global Store To WB Count */ + int storesToWB; + + /** Number of Threads. */ + unsigned numThreads; +}; + +template +template +Fault +LSQ::read(MemReqPtr &req, T &data, int load_idx) +{ + unsigned tid = req->thread_num; + + return thread[tid].read(req, data, load_idx); +} + +template +template +Fault +LSQ::write(MemReqPtr &req, T &data, int store_idx) +{ + unsigned tid = req->thread_num; + + return thread[tid].write(req, data, store_idx); +} + +#endif // __CPU_O3_LSQ_HH__ diff --git a/cpu/o3/lsq_impl.hh b/cpu/o3/lsq_impl.hh new file mode 100644 index 000000000..523517869 --- /dev/null +++ b/cpu/o3/lsq_impl.hh @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#include "cpu/o3/lsq.hh" + +using namespace std; + +template +LSQ::LSQ(Params *params) + : LQEntries(params->LQEntries), SQEntries(params->SQEntries), + loads(0), stores(0), storesToWB(0), + numThreads(params->numberOfThreads) +{ + DPRINTF(LSQ, "Creating LSQ object.\n"); + + //**********************************************/ + //************ Handle SMT Parameters ***********/ + //**********************************************/ + string policy = params->smtLSQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out fetch policy + if (policy == "dynamic") { + lsqPolicy = Dynamic; + + maxLQEntries = LQEntries; + maxSQEntries = SQEntries; + + DPRINTF(LSQ, "LSQ sharing policy set to Dynamic\n"); + + } else if (policy == "partitioned") { + lsqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + maxLQEntries = LQEntries / numThreads; + maxSQEntries = SQEntries / numThreads; + + DPRINTF(Fetch, "LSQ sharing policy set to Partitioned: " + "%i entries per LQ | %i entries per SQ", + maxLQEntries,maxSQEntries); + + } else if (policy == "threshold") { + lsqPolicy = Threshold; + + assert(params->smtLSQThreshold > LQEntries); + assert(params->smtLSQThreshold > SQEntries); + + //Divide up by threshold amount + //@todo: Should threads check the max and the total + //amount of the LSQ + maxLQEntries = params->smtLSQThreshold; + maxSQEntries = params->smtLSQThreshold; + + DPRINTF(LSQ, "LSQ sharing policy set to Threshold: " + "%i entries per LQ | %i entries per SQ", + maxLQEntries,maxSQEntries); + + } else { + assert(0 && "Invalid LSQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } + + //Initialize LSQs + for (int tid=0; tid < numThreads; tid++) { + thread[tid].init(params, maxLQEntries+1, maxSQEntries+1, tid); + } +} + + +template +std::string +LSQ::name() const +{ + return iewStage->name() + ".lsq"; +} + +template +void +LSQ::setActiveThreads(list *at_ptr) +{ + activeThreads = at_ptr; + assert(activeThreads != 0); +} + +template +void +LSQ::setCPU(FullCPU *cpu_ptr) +{ + cpu = cpu_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setCPU(cpu_ptr); + } +} + +template +void +LSQ::setIEW(IEW *iew_ptr) +{ + iewStage = iew_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setIEW(iew_ptr); + } +} + +#if 0 +template +void +LSQ::setPageTable(PageTable *pt_ptr) +{ + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setPageTable(pt_ptr); + } +} +#endif + +template +int +LSQ::entryAmount(int num_threads) +{ + if (lsqPolicy == Partitioned) { + return LQEntries / num_threads; + } else { + return 0; + } +} + +template +void +LSQ::resetEntries() +{ + if (lsqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list::iterator threads = (*activeThreads).begin(); + list::iterator list_end = (*activeThreads).end(); + + int maxEntries; + + if (lsqPolicy == Partitioned) { + maxEntries = LQEntries / active_threads; + } else if (lsqPolicy == Threshold && active_threads == 1) { + maxEntries = LQEntries; + } else { + maxEntries = LQEntries; + } + + while (threads != list_end) { + resizeEntries(maxEntries,*threads++); + } + } +} + +template +void +LSQ::removeEntries(unsigned tid) +{ + thread[tid].clearLQ(); + thread[tid].clearSQ(); +} + +template +void +LSQ::resizeEntries(unsigned size,unsigned tid) +{ + thread[tid].resizeLQ(size); + thread[tid].resizeSQ(size); +} + +template +void +LSQ::tick() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + thread[tid].tick(); + } +} + +template +void +LSQ::tick(unsigned tid) +{ + thread[tid].tick(); +} + +template +void +LSQ::insertLoad(DynInstPtr &load_inst) +{ + unsigned tid = load_inst->threadNumber; + + thread[tid].insertLoad(load_inst); +} + +template +void +LSQ::insertStore(DynInstPtr &store_inst) +{ + unsigned tid = store_inst->threadNumber; + + thread[tid].insertStore(store_inst); +} + +template +Fault +LSQ::executeLoad(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeLoad(inst); +} + +template +Fault +LSQ::executeLoad(int lq_idx, unsigned tid) +{ + return thread[tid].executeLoad(lq_idx); +} + +template +Fault +LSQ::executeStore(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeStore(inst); +} + +template +void +LSQ::commitLoads(InstSeqNum &youngest_inst,unsigned tid) +{ + thread[tid].commitLoads(youngest_inst); +} + +template +void +LSQ::commitStores(InstSeqNum &youngest_inst,unsigned tid) +{ + thread[tid].commitStores(youngest_inst); +} + +template +void +LSQ::writebackStores() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + if (numStoresToWB(tid) > 0) { + DPRINTF(Writeback,"[tid:%i] Writing back stores. %i stores available" + " for Writeback.\n", tid, numStoresToWB(tid)); + } + + thread[tid].writebackStores(); + } +} + +template +int +LSQ::numStoresToWB(unsigned tid) +{ + return thread[tid].numStoresToWB(); +} + +template +void +LSQ::squash(const InstSeqNum &squashed_num, unsigned tid) +{ + thread[tid].squash(squashed_num); +} + +template +bool +LSQ::violation() +{ + /* Answers: Does Anybody Have a Violation?*/ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (thread[tid].violation()) + return true; + } + + return false; +} + +template +bool +LSQ::violation(unsigned tid) +{ + return thread[tid].violation(); +} + +template +bool +LSQ::loadBlocked(unsigned tid) +{ + return thread[tid].loadBlocked(); +} + +template +typename Impl::DynInstPtr +LSQ::getMemDepViolator(unsigned tid) +{ + return thread[tid].getMemDepViolator(); +} + +template +int +LSQ::getLoadHead(unsigned tid) +{ + return thread[tid].getLoadHead(); +} + +template +int +LSQ::getStoreHead(unsigned tid) +{ + return thread[tid].getStoreHead(); +} + +template +int +LSQ::getCount() +{ + unsigned total = 0; + + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += getCount(tid); + } + + return total; +} + +template +int +LSQ::getCount(unsigned tid) +{ + return thread[tid].getCount(); +} + +template +int +LSQ::numLoads() +{ + unsigned total = 0; + + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += numLoads(tid); + } + + return total; +} + +template +int +LSQ::numLoads(unsigned tid) +{ + return thread[tid].numLoads(); +} + +template +int +LSQ::numStores() +{ + unsigned total = 0; + + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numStores(); + } + + return total; +} + +template +int +LSQ::numStores(unsigned tid) +{ + return thread[tid].numStores(); +} + +template +int +LSQ::numLoadsReady() +{ + unsigned total = 0; + + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numLoadsReady(); + } + + return total; +} + +template +int +LSQ::numLoadsReady(unsigned tid) +{ + return thread[tid].numLoadsReady(); +} + +template +unsigned +LSQ::numFreeEntries() +{ + unsigned total = 0; + + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numFreeEntries(); + } + + return total; +} + +template +unsigned +LSQ::numFreeEntries(unsigned tid) +{ + //if( lsqPolicy == Dynamic ) + //return numFreeEntries(); + //else + return thread[tid].numFreeEntries(); +} + +template +bool +LSQ::isFull() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (! (thread[tid].lqFull() || thread[tid].sqFull()) ) + return false; + } + + return true; +} + +template +bool +LSQ::isFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return isFull(); + else + return thread[tid].lqFull() || thread[tid].sqFull(); +} + +template +bool +LSQ::lqFull() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].lqFull()) + return false; + } + + return true; +} + +template +bool +LSQ::lqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return lqFull(); + else + return thread[tid].lqFull(); +} + +template +bool +LSQ::sqFull() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!sqFull(tid)) + return false; + } + + return true; +} + +template +bool +LSQ::sqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return sqFull(); + else + return thread[tid].sqFull(); +} + +template +bool +LSQ::isStalled() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].isStalled()) + return false; + } + + return true; +} + +template +bool +LSQ::isStalled(unsigned tid) +{ + if( lsqPolicy == Dynamic ) + return isStalled(); + else + return thread[tid].isStalled(); +} + +template +bool +LSQ::hasStoresToWB() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!hasStoresToWB(tid)) + return false; + } + + return true; +} + + +template +bool +LSQ::hasStoresToWB(unsigned tid) +{ + return thread[tid].hasStoresToWB(); +} + +template +bool +LSQ::willWB() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!willWB(tid)) + return false; + } + + return true; +} + +template +bool +LSQ::willWB(unsigned tid) +{ + return thread[tid].willWB(); +} + +template +void +LSQ::dumpInsts() +{ + list::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + thread[tid].dumpInsts(); + } +} + +template +void +LSQ::dumpInsts(unsigned tid) +{ + thread[tid].dumpInsts(); +} diff --git a/cpu/o3/lsq_unit.cc b/cpu/o3/lsq_unit.cc new file mode 100644 index 000000000..dd29007bc --- /dev/null +++ b/cpu/o3/lsq_unit.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha_cpu.hh" +#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/lsq_unit_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class LSQUnit; + diff --git a/cpu/o3/lsq_unit.hh b/cpu/o3/lsq_unit.hh new file mode 100644 index 000000000..73c485ce9 --- /dev/null +++ b/cpu/o3/lsq_unit.hh @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#ifndef __CPU_O3_LSQ_UNIT_HH__ +#define __CPU_O3_LSQ_UNIT_HH__ + +#include +#include +#include + +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/mem_interface.hh" +//#include "mem/page_table.hh" +#include "sim/sim_object.hh" +#include "arch/faults.hh" + +/** + * Class that implements the actual LQ and SQ for each specific thread. + * Both are circular queues; load entries are freed upon committing, while + * store entries are freed once they writeback. The LSQUnit tracks if there + * are memory ordering violations, and also detects partial load to store + * forwarding cases (a store only has part of a load's data) that requires + * the load to wait until the store writes back. In the former case it + * holds onto the instruction until the dependence unit looks at it, and + * in the latter it stalls the LSQ until the store writes back. At that + * point the load is replayed. + */ +template +class LSQUnit { + protected: + typedef TheISA::IntReg IntReg; + public: + typedef typename Impl::Params Params; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::IssueStruct IssueStruct; + + private: + class StoreCompletionEvent : public Event { + public: + /** Constructs a store completion event. */ + StoreCompletionEvent(int store_idx, Event *wb_event, LSQUnit *lsq_ptr); + + /** Processes the store completion event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** The store index of the store being written back. */ + int storeIdx; + /** The writeback event for the store. Needed for store + * conditionals. + */ + Event *wbEvent; + /** The pointer to the LSQ unit that issued the store. */ + LSQUnit *lsqPtr; + }; + + friend class StoreCompletionEvent; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + LSQUnit(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + /** Sets the CPU pointer. */ + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr) + { iewStage = iew_ptr; } + + /** Sets the page table pointer. */ +// void setPageTable(PageTable *pt_ptr); + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + Fault executeLoad(int lq_idx); + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits a specific load, given by the sequence number. */ + void commitLoad(InstSeqNum &inst); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + // @todo: Include stats in the LSQ unit. + //void regStats(); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. It clears + * the bool's value upon this being called. + */ + bool loadBlocked() + { return isLoadBlocked; } + + void clearLoadBlocked() + { isLoadBlocked = false; } + + bool isLoadBlockedHandled() + { return loadBlockedHandled; } + + void setLoadBlockedHandled() + { loadBlockedHandled = true; } + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return stores >= (SQEntries - 1); } + + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue[storeWBIdx].canWB && + !storeQueue[storeWBIdx].completed && + !dcacheInterface->isBlocked(); } + + private: + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Increments the given store index (circular queue). */ + inline void incrStIdx(int &store_idx); + /** Decrements the given store index (circular queue). */ + inline void decrStIdx(int &store_idx); + /** Increments the given load index (circular queue). */ + inline void incrLdIdx(int &load_idx); + /** Decrements the given load index (circular queue). */ + inline void decrLdIdx(int &load_idx); + + private: + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Pointer to the IEW stage. */ + IEW *iewStage; + + /** Pointer to the D-cache. */ + MemInterface *dcacheInterface; + + /** Pointer to the page table. */ +// PageTable *pTable; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The memory request for the store. */ + MemReqPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + }; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissSwitch + }; + + private: + /** The LSQUnit thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::vector storeQueue; + + /** The load queue. */ + std::vector loadQueue; + + // Consider making these 16 bits + /** The number of LQ entries. */ + unsigned LQEntries; + /** The number of SQ entries. */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ (excludes those waiting to + * writeback). + */ + int stores; + /** The number of store instructions in the SQ waiting to writeback. */ + int storesToWB; + + /** The index of the head instruction in the LQ. */ + int loadHead; + /** The index of the tail instruction in the LQ. */ + int loadTail; + + /** The index of the head instruction in the SQ. */ + int storeHead; + /** The index of the first instruction that is ready to be written back, + * and has not yet been written back. + */ + int storeWBIdx; + /** The index of the tail instruction in the SQ. */ + int storeTail; + + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + //list mshrSeqNums; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer::wire fromIssue; + + // Make these per thread? + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + int stallingLoadIdx; + + /** Whether or not a load is blocked due to the memory system. It is + * cleared when this value is checked via loadBlocked(). + */ + bool isLoadBlocked; + + bool loadBlockedHandled; + + InstSeqNum blockedLoadSeqNum; + + /** The oldest faulting load instruction. */ + DynInstPtr loadFaultInst; + /** The oldest faulting store instruction. */ + DynInstPtr storeFaultInst; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + + public: + /** Executes the load at the given index. */ + template + Fault read(MemReqPtr &req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template + Fault write(MemReqPtr &req, T &data, int store_idx); + + /** Returns the index of the head load instruction. */ + int getLoadHead() { return loadHead; } + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (loadQueue[loadHead]) { + return loadQueue[loadHead]->seqNum; + } else { + return 0; + } + + } + + /** Returns the index of the head store instruction. */ + int getStoreHead() { return storeHead; } + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (storeQueue[storeHead].inst) { + return storeQueue[storeHead].inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template +template +Fault +LSQUnit::read(MemReqPtr &req, T &data, int load_idx) +{ + //Depending on issue2execute delay a squashed load could + //execute if it is found to be squashed in the same + //cycle it is scheduled to execute + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->isExecuted()) { + panic("Should not reach this point with split ops!"); + memcpy(&data,req->data,req->size); + + return NoFault; + } + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + // @todo: Fix uncached accesses. + if (req->flags & UNCACHEABLE && + (load_idx != loadHead || !loadQueue[load_idx]->reachedCommit)) { + iewStage->rescheduleMemInst(loadQueue[load_idx]); + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + int store_idx = loadQueue[load_idx]->sqIdx; + + int store_size = 0; + + DPRINTF(LSQUnit, "Read called, load idx: %i, store idx: %i, " + "storeHead: %i addr: %#x\n", + load_idx, store_idx, storeHead, req->paddr); + +#ifdef FULL_SYSTEM + if (req->flags & LOCKED) { + cpu->lockAddr = req->paddr; + cpu->lockFlag = true; + } +#endif + + while (store_idx != -1) { + // End once we've reached the top of the LSQ + if (store_idx == storeWBIdx) { + break; + } + + // Move the index to one younger + if (--store_idx < 0) + store_idx += SQEntries; + + assert(storeQueue[store_idx].inst); + + store_size = storeQueue[store_idx].size; + + if (store_size == 0) + continue; + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->vaddr >= storeQueue[store_idx].inst->effAddr; + bool store_has_upper_limit = + (req->vaddr + req->size) <= (storeQueue[store_idx].inst->effAddr + + store_size); + bool lower_load_has_store_part = + req->vaddr < (storeQueue[store_idx].inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->vaddr + req->size) > storeQueue[store_idx].inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + + int shift_amt = req->vaddr & (store_size - 1); + // Assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = storeQueue[store_idx].data >> shift_amt; + + req->cmd = Read; + assert(!req->completionEvent); + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + + memcpy(req->data, &data, req->size); + + DPRINTF(LSQUnit, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->vaddr, *(req->data)); + + typename IEW::LdWritebackEvent *wb = + new typename IEW::LdWritebackEvent(loadQueue[load_idx], + iewStage); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // @todo: Need to make this a parameter. + wb->schedule(curTick); + + // Should keep track of stat for forwarded data + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if (storeQueue[store_idx].completed) { + continue; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + loadQueue[load_idx]->seqNum < + loadQueue[stallingLoadIdx]->seqNum)) { + stalled = true; + stallingStoreIsn = storeQueue[store_idx].inst->seqNum; + stallingLoadIdx = load_idx; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + iewStage->rescheduleMemInst(loadQueue[load_idx]); + + // Do not generate a writeback event as this instruction is not + // complete. + + DPRINTF(LSQUnit, "Load-store forwarding mis-match. " + "Store idx %i to load addr %#x\n", + store_idx, req->vaddr); + + return NoFault; + } + } + + + // If there's no forwarding case, then go access memory + DynInstPtr inst = loadQueue[load_idx]; + + DPRINTF(LSQUnit, "Doing functional access for inst PC %#x\n", + loadQueue[load_idx]->readPC()); + assert(!req->data); + req->data = new uint8_t[64]; + Fault fault = cpu->read(req, data); + memcpy(req->data, &data, sizeof(T)); + + ++usedPorts; + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + if (dcacheInterface->isBlocked()) { + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < inst->seqNum) + return NoFault; + + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + DPRINTF(LSQUnit, "Doing timing access for inst PC %#x\n", + loadQueue[load_idx]->readPC()); + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + + assert(!req->completionEvent); + req->completionEvent = + new typename IEW::LdWritebackEvent(loadQueue[load_idx], iewStage); + MemAccessResult result = dcacheInterface->access(req); + + assert(dcacheInterface->doEvents()); + + // 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) { + DPRINTF(LSQUnit, "LSQUnit: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + } else { + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + inst->seqNum); + + DPRINTF(LSQUnit, "LSQUnit: D-cache hit!\n"); + } + } +#if 0 + // if we have a cache, do cache access too + if (dcacheInterface) { + if (dcacheInterface->isBlocked()) { + isLoadBlocked = true; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + DPRINTF(LSQUnit, "LSQUnit: D-cache: PC:%#x reading from paddr:%#x " + "vaddr:%#x flags:%i\n", + inst->readPC(), req->paddr, req->vaddr, req->flags); + + // Setup MemReq pointer + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + + assert(!req->completionEvent); + req->completionEvent = + new typename IEW::LdWritebackEvent(loadQueue[load_idx], iewStage); + + // Do Cache Access + MemAccessResult result = dcacheInterface->access(req); + + // 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. + // @todo: Probably should support having no events + if (result != MA_HIT) { + DPRINTF(LSQUnit, "LSQUnit: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + } else { + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + inst->seqNum); + + DPRINTF(LSQUnit, "LSQUnit: D-cache hit!\n"); + } + } else { + fatal("Must use D-cache with new memory system"); + } +#endif + + return fault; +} + +template +template +Fault +LSQUnit::write(MemReqPtr &req, T &data, int store_idx) +{ + assert(storeQueue[store_idx].inst); + + DPRINTF(LSQUnit, "Doing write to store idx %i, addr %#x data %#x" + " | storeHead:%i [sn:%i]\n", + store_idx, req->paddr, data, storeHead, + storeQueue[store_idx].inst->seqNum); +/* + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + req->result = 2; + } else { + req->result = 1; + } + } +*/ + storeQueue[store_idx].req = req; + storeQueue[store_idx].size = sizeof(T); + storeQueue[store_idx].data = data; + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +#endif // __CPU_O3_LSQ_UNIT_HH__ diff --git a/cpu/o3/lsq_unit_impl.hh b/cpu/o3/lsq_unit_impl.hh new file mode 100644 index 000000000..d9a118b0e --- /dev/null +++ b/cpu/o3/lsq_unit_impl.hh @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#include "cpu/o3/lsq_unit.hh" +#include "base/str.hh" + +template +LSQUnit::StoreCompletionEvent::StoreCompletionEvent(int store_idx, + Event *wb_event, + LSQUnit *lsq_ptr) + : Event(&mainEventQueue), + storeIdx(store_idx), + wbEvent(wb_event), + lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template +void +LSQUnit::StoreCompletionEvent::process() +{ + DPRINTF(LSQ, "Cache miss complete for store idx:%i\n", storeIdx); + DPRINTF(Activity, "Activity: st writeback event idx:%i\n", storeIdx); + + //lsqPtr->removeMSHR(lsqPtr->storeQueue[storeIdx].inst->seqNum); + + lsqPtr->cpu->wakeCPU(); + if (wbEvent) + wbEvent->process(); + lsqPtr->completeStore(storeIdx); +} + +template +const char * +LSQUnit::StoreCompletionEvent::description() +{ + return "LSQ store completion event"; +} + +template +LSQUnit::LSQUnit() + : loads(0), stores(0), storesToWB(0), stalled(false), isLoadBlocked(false), + loadBlockedHandled(false) +{ +} + +template +void +LSQUnit::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) + +{ + DPRINTF(LSQUnit, "Creating LSQUnit%i object.\n",id); + + lsqID = id; + + LQEntries = maxLQEntries; + SQEntries = maxSQEntries; + + loadQueue.resize(LQEntries); + storeQueue.resize(SQEntries); + + + // May want to initialize these entries to NULL + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + cachePorts = params->cachePorts; + + dcacheInterface = params->dcacheInterface; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template +std::string +LSQUnit::name() const +{ + if (Impl::MaxThreads == 1) { + return iewStage->name() + ".lsq"; + } else { + return iewStage->name() + ".lsq.thread." + to_string(lsqID); + } +} + +template +void +LSQUnit::clearLQ() +{ + loadQueue.clear(); +} + +template +void +LSQUnit::clearSQ() +{ + storeQueue.clear(); +} + +#if 0 +template +void +LSQUnit::setPageTable(PageTable *pt_ptr) +{ + DPRINTF(LSQUnit, "Setting the page table pointer.\n"); + pTable = pt_ptr; +} +#endif + +template +void +LSQUnit::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template +void +LSQUnit::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template +void +LSQUnit::insert(DynInstPtr &inst) +{ + // Make sure we really have a memory reference. + assert(inst->isMemRef()); + + // Make sure it's one of the two classes of memory references. + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } + + inst->setInLSQ(); +} + +template +void +LSQUnit::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead && loads < LQEntries); + + DPRINTF(LSQUnit, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), loadTail, load_inst->seqNum); + + load_inst->lqIdx = loadTail; + + if (stores == 0) { + load_inst->sqIdx = -1; + } else { + load_inst->sqIdx = storeTail; + } + + loadQueue[loadTail] = load_inst; + + incrLdIdx(loadTail); + + ++loads; +} + +template +void +LSQUnit::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert((storeTail + 1) % SQEntries != storeHead); + assert(stores < SQEntries); + + DPRINTF(LSQUnit, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), storeTail, store_inst->seqNum); + + store_inst->sqIdx = storeTail; + store_inst->lqIdx = loadTail; + + storeQueue[storeTail] = SQEntry(store_inst); + + incrStIdx(storeTail); + + ++stores; + +} + +template +typename Impl::DynInstPtr +LSQUnit::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template +unsigned +LSQUnit::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - stores; + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template +int +LSQUnit::numLoadsReady() +{ + int load_idx = loadHead; + int retval = 0; + + while (load_idx != loadTail) { + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +#if 0 +template +Fault +LSQUnit::executeLoad() +{ + Fault load_fault = NoFault; + DynInstPtr load_inst; + + assert(readyLoads.size() != 0); + + // Execute a ready load. + LdMapIt ready_it = readyLoads.begin(); + + load_inst = (*ready_it).second; + + // Execute the instruction, which is held in the data portion of the + // iterator. + load_fault = load_inst->execute(); + + // If it executed successfully, then switch it over to the executed + // loads list. + if (load_fault == NoFault) { + executedLoads[load_inst->seqNum] = load_inst; + + readyLoads.erase(ready_it); + } else { + loadFaultInst = load_inst; + } + + return load_fault; +} +#endif + +template +Fault +LSQUnit::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(LSQUnit, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + // Make sure it's really in the list. + // Normally it should always be in the list. However, + /* due to a syscall it may not be the list. +#ifdef DEBUG + int i = loadHead; + while (1) { + if (i == loadTail && !find(inst)) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i] == inst) { + break; + } + + i = i + 1; + if (i >= LQEntries) { + i = 0; + } + } +#endif // DEBUG*/ + +// load_fault = inst->initiateAcc(); + load_fault = inst->execute(); + + // If the instruction faulted, then we need to send it along to commit + // without the instruction completing. + if (load_fault != NoFault) { + // Maybe just set it as can commit here, although that might cause + // some other problems with sending traps to the ROB too quickly. + iewStage->instToCommit(inst); + iewStage->activityThisCycle(); + } + + return load_fault; +} + +template +Fault +LSQUnit::executeLoad(int lq_idx) +{ + // Very hackish. Not sure the best way to check that this + // instruction is at the head of the ROB. I should have some sort + // of extra information here so that I'm not overloading the + // canCommit signal for 15 different things. + loadQueue[lq_idx]->setCanCommit(); + Fault ret_fault = executeLoad(loadQueue[lq_idx]); + loadQueue[lq_idx]->clearCanCommit(); + return ret_fault; +} + +template +Fault +LSQUnit::executeStore(DynInstPtr &store_inst) +{ + using namespace TheISA; + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + + DPRINTF(LSQUnit, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + // Check the recently completed loads to see if any match this store's + // address. If so, then we have a memory ordering violation. + int load_idx = store_inst->lqIdx; + + Fault store_fault = store_inst->initiateAcc(); +// Fault store_fault = store_inst->execute(); + + // Store size should now be available. Use it to get proper offset for + // addr comparisons. + int size = storeQueue[store_idx].size; + + if (size == 0) { + DPRINTF(LSQUnit,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (!storeFaultInst) { + if (store_fault != NoFault) { + panic("Fault in a store instruction!"); + storeFaultInst = store_inst; + } else if (store_inst->isNonSpeculative()) { + // Nonspeculative accesses (namely store conditionals) + // need to set themselves as able to writeback if we + // haven't had a fault by here. + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + } + + if (!memDepViolator) { + while (load_idx != loadTail) { + // Actually should only check loads that have actually executed + // Might be safe because effAddr is set to InvalAddr when the + // dyn inst is created. + + // Must actually check all addrs in the proper size range + // Which is more correct than needs to be. What if for now we just + // assume all loads are quad-word loads, and do the addr based + // on that. + // @todo: Fix this, magic number being used here + if ((loadQueue[load_idx]->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = loadQueue[load_idx]; + + return genMachineCheckFault(); + } + + incrLdIdx(load_idx); + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template +void +LSQUnit::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(LSQUnit, "Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->readPC()); + + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template +void +LSQUnit::commitLoad(InstSeqNum &inst) +{ + // Hopefully I don't use this function too much + panic("Don't use this function!"); + + int i = loadHead; + while (1) { + if (i == loadTail) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i]->seqNum == inst) { + break; + } + + ++i; + if (i >= LQEntries) { + i = 0; + } + } + + loadQueue[i]->removeInLSQ(); + loadQueue[i] = NULL; + --loads; +} + +template +void +LSQUnit::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template +void +LSQUnit::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || storeQueue[storeHead].inst); + + int store_idx = storeHead; + + while (store_idx != storeTail) { + assert(storeQueue[store_idx].inst); + if (!storeQueue[store_idx].canWB) { + if (storeQueue[store_idx].inst->seqNum > youngest_inst) { + break; + } + DPRINTF(LSQUnit, "Marking store as able to write back, PC " + "%#x [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].canWB = true; + +// --stores; + ++storesToWB; + } + + incrStIdx(store_idx); + } +} + +template +void +LSQUnit::writebackStores() +{ + while (storesToWB > 0 && + storeWBIdx != storeTail && + storeQueue[storeWBIdx].inst && + storeQueue[storeWBIdx].canWB && + usedPorts < cachePorts) { + + if (storeQueue[storeWBIdx].size == 0) { + completeStore(storeWBIdx); + + incrStIdx(storeWBIdx); + + continue; + } + + if (dcacheInterface && dcacheInterface->isBlocked()) { + DPRINTF(LSQUnit, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + ++usedPorts; + + if (storeQueue[storeWBIdx].inst->isDataPrefetch()) { + incrStIdx(storeWBIdx); + + continue; + } + + assert(storeQueue[storeWBIdx].req); + assert(!storeQueue[storeWBIdx].committed); + + MemReqPtr req = storeQueue[storeWBIdx].req; + storeQueue[storeWBIdx].committed = true; + +// Fault fault = cpu->translateDataWriteReq(req); + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&storeQueue[storeWBIdx].data, req->size); + + DPRINTF(LSQUnit, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + storeWBIdx,storeQueue[storeWBIdx].inst->readPC(), + req->paddr, *(req->data), + storeQueue[storeWBIdx].inst->seqNum); + +// if (fault != NoFault) { + //What should we do if there is a fault??? + //for now panic +// panic("Page Table Fault!!!!!\n"); +// } + switch(storeQueue[storeWBIdx].size) { + case 1: + cpu->write(req, (uint8_t &)storeQueue[storeWBIdx].data); + break; + case 2: + cpu->write(req, (uint16_t &)storeQueue[storeWBIdx].data); + break; + case 4: + cpu->write(req, (uint32_t &)storeQueue[storeWBIdx].data); + break; + case 8: + cpu->write(req, (uint64_t &)storeQueue[storeWBIdx].data); + break; + default: + panic("Unexpected store size!\n"); + } + + if (dcacheInterface) { + MemAccessResult result = dcacheInterface->access(req); + + if (isStalled() && + storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { + DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + iewStage->replayMemInst(loadQueue[stallingLoadIdx]); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + typename IEW::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. +/* + if (cpu->lockFlag && cpu->lockAddr == req->paddr) { + req->result=1; + } else { + req->result = 0; + } +*/ + wb = new typename IEW::LdWritebackEvent(storeQueue[storeWBIdx].inst, + iewStage); + } + + DPRINTF(LSQUnit,"D-Cache Write Miss!\n"); + + DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + + // Will stores need their own kind of writeback events? + // Do stores even need writeback events? + assert(!req->completionEvent); + req->completionEvent = new + StoreCompletionEvent(storeWBIdx, wb, this); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(LSQUnit, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // Increment stat here or something + } else { + DPRINTF(LSQUnit,"D-Cache: Write Hit on idx:%i !\n", + storeWBIdx); + + DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + + + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. +/* + if (req->flags & UNCACHEABLE) { + req->result = 2; + } else { + if (cpu->lockFlag && cpu->lockAddr == req->paddr) { + req->result=1; + } else { + req->result = 0; + } + } +*/ + typename IEW::LdWritebackEvent *wb = + new typename IEW::LdWritebackEvent(storeQueue[storeWBIdx].inst, + iewStage); + wb->schedule(curTick); + } + + completeStore(storeWBIdx); + } + + incrStIdx(storeWBIdx); + } else { + panic("Must HAVE DCACHE!!!!!\n"); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +/*template +void +LSQUnit::removeMSHR(InstSeqNum seqNum) +{ + list::iterator mshr_it = find(mshrSeqNums.begin(), + mshrSeqNums.end(), + seqNum); + + if (mshr_it != mshrSeqNums.end()) { + mshrSeqNums.erase(mshr_it); + DPRINTF(LSQUnit, "Removing MSHR. count = %i\n",mshrSeqNums.size()); + } +}*/ + +template +void +LSQUnit::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(LSQUnit, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n",squashed_num,loads,stores); + + int load_idx = loadTail; + decrLdIdx(load_idx); + + while (loads != 0 && loadQueue[load_idx]->seqNum > squashed_num) { + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(LSQUnit,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + loadQueue[load_idx]->readPC(), + loadQueue[load_idx]->seqNum); + + if (isStalled() && load_idx == stallingLoadIdx) { + stalled = false; + stallingStoreIsn = 0; + stallingLoadIdx = 0; + } + + loadQueue[load_idx]->squashed = true; + loadQueue[load_idx] = NULL; + --loads; + + // Inefficient! + loadTail = load_idx; + + decrLdIdx(load_idx); + } + + if (isLoadBlocked) { + if (squashed_num < blockedLoadSeqNum) { + isLoadBlocked = false; + loadBlockedHandled = false; + blockedLoadSeqNum = 0; + } + } + + int store_idx = storeTail; + decrStIdx(store_idx); + + while (stores != 0 && + storeQueue[store_idx].inst->seqNum > squashed_num) { + + if (storeQueue[store_idx].canWB) { + break; + } + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(LSQUnit,"Store Instruction PC %#x squashed, " + "idx:%i [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + store_idx, storeQueue[store_idx].inst->seqNum); + + // I don't think this can happen. It should have been cleared by the + // stalling load. + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + + storeQueue[store_idx].inst->squashed = true; + storeQueue[store_idx].inst = NULL; + storeQueue[store_idx].canWB = 0; + + if (storeQueue[store_idx].req) { + assert(!storeQueue[store_idx].req->completionEvent); + } + storeQueue[store_idx].req = NULL; + --stores; + + // Inefficient! + storeTail = store_idx; + + decrStIdx(store_idx); + } +} + +template +void +LSQUnit::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + int load_idx = loadHead; + + while (load_idx != loadTail && loadQueue[load_idx]) { + cprintf("%#x ", loadQueue[load_idx]->readPC()); + + incrLdIdx(load_idx); + } + + cprintf("Store queue size: %i\n", stores); + cprintf("Store queue: "); + + int store_idx = storeHead; + + while (store_idx != storeTail && storeQueue[store_idx].inst) { + cprintf("%#x ", storeQueue[store_idx].inst->readPC()); + + incrStIdx(store_idx); + } + + cprintf("\n"); +} + +template +void +LSQUnit::completeStore(int store_idx) +{ + assert(storeQueue[store_idx].inst); + storeQueue[store_idx].completed = true; + --storesToWB; + // A bit conservative because a store completion may not free up entries, + // but hopefully avoids two store completions in one cycle from making + // the CPU tick twice. + cpu->activityThisCycle(); + + if (store_idx == storeHead) { + do { + incrStIdx(storeHead); + + --stores; + } while (storeQueue[storeHead].completed && + storeHead != storeTail); + + iewStage->updateLSQNextCycle = true; + } + + DPRINTF(LSQUnit, "Store head idx:%i\n", storeHead); + + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + iewStage->replayMemInst(loadQueue[stallingLoadIdx]); + } +} + +template +inline void +LSQUnit::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template +inline void +LSQUnit::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template +inline void +LSQUnit::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template +inline void +LSQUnit::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} diff --git a/cpu/o3/mem_dep_unit.cc b/cpu/o3/mem_dep_unit.cc index 9c1e7f9d8..ccdd1a515 100644 --- a/cpu/o3/mem_dep_unit.cc +++ b/cpu/o3/mem_dep_unit.cc @@ -34,3 +34,13 @@ // Force instantation of memory dependency unit using store sets and // AlphaSimpleImpl. template class MemDepUnit; + +template <> +int +MemDepUnit::MemDepEntry::memdep_count = 0; +template <> +int +MemDepUnit::MemDepEntry::memdep_insert = 0; +template <> +int +MemDepUnit::MemDepEntry::memdep_erase = 0; diff --git a/cpu/o3/mem_dep_unit.hh b/cpu/o3/mem_dep_unit.hh index ca63577a1..32ce9f768 100644 --- a/cpu/o3/mem_dep_unit.hh +++ b/cpu/o3/mem_dep_unit.hh @@ -26,15 +26,29 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_MEM_DEP_UNIT_HH__ -#define __CPU_O3_CPU_MEM_DEP_UNIT_HH__ +#ifndef __CPU_O3_MEM_DEP_UNIT_HH__ +#define __CPU_O3_MEM_DEP_UNIT_HH__ -#include +#include #include +#include "base/hashmap.hh" +#include "base/refcnt.hh" #include "base/statistics.hh" #include "cpu/inst_seq.hh" +struct SNHash { + size_t operator() (const InstSeqNum &seq_num) const { + unsigned a = (unsigned)seq_num; + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; + } +}; + +template +class InstructionQueue; + /** * Memory dependency unit class. This holds the memory dependence predictor. * As memory operations are issued to the IQ, they are also issued to this @@ -52,101 +66,162 @@ class MemDepUnit { typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; - public: - MemDepUnit(Params ¶ms); + /** Empty constructor. Must call init() prior to using in this case. */ + MemDepUnit() {} + /** Constructs a MemDepUnit with given parameters. */ + MemDepUnit(Params *params); + + /** Frees up any memory allocated. */ + ~MemDepUnit(); + + /** Returns the name of the memory dependence unit. */ + std::string name() const; + + /** Initializes the unit with parameters and a thread id. */ + void init(Params *params, int tid); + + /** Registers statistics. */ void regStats(); + /** Sets the pointer to the IQ. */ + void setIQ(InstructionQueue *iq_ptr); + + /** Inserts a memory instruction. */ void insert(DynInstPtr &inst); + /** Inserts a non-speculative memory instruction. */ void insertNonSpec(DynInstPtr &inst); - // Will want to make this operation relatively fast. Right now it - // is somewhat slow. - DynInstPtr &top(); - - void pop(); + /** Inserts a barrier instruction. */ + void insertBarrier(DynInstPtr &barr_inst); + /** Indicate that an instruction has its registers ready. */ void regsReady(DynInstPtr &inst); + /** Indicate that a non-speculative instruction is ready. */ void nonSpecInstReady(DynInstPtr &inst); - void issue(DynInstPtr &inst); + /** Reschedules an instruction to be re-executed. */ + void reschedule(DynInstPtr &inst); + /** Replays all instructions that have been rescheduled by moving them to + * the ready list. + */ + void replay(DynInstPtr &inst); + + /** Completes a memory instruction. */ + void completed(DynInstPtr &inst); + + /** Completes a barrier instruction. */ + void completeBarrier(DynInstPtr &inst); + + /** Wakes any dependents of a memory instruction. */ void wakeDependents(DynInstPtr &inst); - void squash(const InstSeqNum &squashed_num); + /** Squashes all instructions up until a given sequence number for a + * specific thread. + */ + void squash(const InstSeqNum &squashed_num, unsigned tid); + /** Indicates an ordering violation between a store and a younger load. */ void violation(DynInstPtr &store_inst, DynInstPtr &violating_load); - inline bool empty() - { return readyInsts.empty(); } + /** Issues the given instruction */ + void issue(DynInstPtr &inst); + + /** Debugging function to dump the lists of instructions. */ + void dumpLists(); private: - typedef typename std::set::iterator sn_it_t; - typedef typename std::map::iterator dyn_it_t; + typedef typename std::list::iterator ListIt; - // Forward declarations so that the following two typedefs work. - class Dependency; - class ltDependency; + class MemDepEntry; - typedef typename std::set::iterator dep_it_t; - typedef typename std::map >::iterator - sd_it_t; + typedef RefCountingPtr MemDepEntryPtr; - struct Dependency { - Dependency(const InstSeqNum &_seqNum) - : seqNum(_seqNum), regsReady(0), memDepReady(0) - { } + /** Memory dependence entries that track memory operations, marking + * when the instruction is ready to execute and what instructions depend + * upon it. + */ + class MemDepEntry : public RefCounted { + public: + /** Constructs a memory dependence entry. */ + MemDepEntry(DynInstPtr &new_inst) + : inst(new_inst), regsReady(false), memDepReady(false), + completed(false), squashed(false) + { + ++memdep_count; - Dependency(const InstSeqNum &_seqNum, bool _regsReady, - bool _memDepReady) - : seqNum(_seqNum), regsReady(_regsReady), - memDepReady(_memDepReady) - { } + DPRINTF(MemDepUnit, "Memory dependency entry created. " + "memdep_count=%i\n", memdep_count); + } - InstSeqNum seqNum; - mutable bool regsReady; - mutable bool memDepReady; - mutable sd_it_t storeDep; + /** Frees any pointers. */ + ~MemDepEntry() + { + for (int i = 0; i < dependInsts.size(); ++i) { + dependInsts[i] = NULL; + } + + --memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry deleted. " + "memdep_count=%i\n", memdep_count); + } + + /** Returns the name of the memory dependence entry. */ + std::string name() const { return "memdepentry"; } + + /** The instruction being tracked. */ + DynInstPtr inst; + + /** The iterator to the instruction's location inside the list. */ + ListIt listIt; + + /** A vector of any dependent instructions. */ + std::vector dependInsts; + + /** If the registers are ready or not. */ + bool regsReady; + /** If all memory dependencies have been satisfied. */ + bool memDepReady; + /** If the instruction is completed. */ + bool completed; + /** If the instruction is squashed. */ + bool squashed; + + /** For debugging. */ + static int memdep_count; + static int memdep_insert; + static int memdep_erase; }; - struct ltDependency { - bool operator() (const Dependency &lhs, const Dependency &rhs) + struct ltMemDepEntry { + bool operator() (const MemDepEntryPtr &lhs, const MemDepEntryPtr &rhs) { - return lhs.seqNum < rhs.seqNum; + return lhs->inst->seqNum < rhs->inst->seqNum; } }; - inline void moveToReady(dep_it_t &woken_inst); + /** Finds the memory dependence entry in the hash map. */ + inline MemDepEntryPtr &findInHash(const DynInstPtr &inst); - /** List of instructions that have passed through rename, yet are still - * waiting on either a memory dependence to resolve or source registers to - * become available before they can issue. - */ - std::set waitingInsts; + /** Moves an entry to the ready list. */ + inline void moveToReady(MemDepEntryPtr &ready_inst_entry); - /** List of instructions that have all their predicted memory dependences - * resolved and their source registers ready. - */ - std::set readyInsts; + typedef m5::hash_map MemDepHash; - // Change this to hold a vector of iterators, which will point to the - // entry of the waiting instructions. - /** List of stores' sequence numbers, each of which has a vector of - * iterators. The iterators point to the appropriate node within - * waitingInsts that has the depenendent instruction. - */ - std::map > storeDependents; + typedef typename MemDepHash::iterator MemDepHashIt; - // For now will implement this as a map...hash table might not be too - // bad, or could move to something that mimics the current dependency - // graph. - std::map memInsts; + /** A hash map of all memory dependence entries. */ + MemDepHash memDepHash; - // Iterator pointer to the top instruction which has is ready. - // Is set by the top() call. - dyn_it_t topInst; + /** A list of all instructions in the memory dependence unit. */ + std::list instList[Impl::MaxThreads]; + + /** A list of all instructions that are going to be replayed. */ + std::list instsToReplay; /** The memory dependence predictor. It is accessed upon new * instructions being added to the IQ, and responds by telling @@ -155,10 +230,25 @@ class MemDepUnit { */ MemDepPred depPred; + bool loadBarrier; + InstSeqNum loadBarrierSN; + bool storeBarrier; + InstSeqNum storeBarrierSN; + + /** Pointer to the IQ. */ + InstructionQueue *iqPtr; + + /** The thread id of this memory dependence unit. */ + int id; + + /** Stat for number of inserted loads. */ Stats::Scalar<> insertedLoads; + /** Stat for number of inserted stores. */ Stats::Scalar<> insertedStores; + /** Stat for number of conflicting loads that had to wait for a store. */ Stats::Scalar<> conflictingLoads; + /** Stat for number of conflicting stores that had to wait for a store. */ Stats::Scalar<> conflictingStores; }; -#endif // __CPU_O3_CPU_MEM_DEP_UNIT_HH__ +#endif // __CPU_O3_MEM_DEP_UNIT_HH__ diff --git a/cpu/o3/mem_dep_unit_impl.hh b/cpu/o3/mem_dep_unit_impl.hh index 296db4c4e..771a0505e 100644 --- a/cpu/o3/mem_dep_unit_impl.hh +++ b/cpu/o3/mem_dep_unit_impl.hh @@ -28,13 +28,56 @@ #include +#include "cpu/o3/inst_queue.hh" #include "cpu/o3/mem_dep_unit.hh" template -MemDepUnit::MemDepUnit(Params ¶ms) - : depPred(params.SSITSize, params.LFSTSize) +MemDepUnit::MemDepUnit(Params *params) + : depPred(params->SSITSize, params->LFSTSize), loadBarrier(false), + loadBarrierSN(0), storeBarrier(false), storeBarrierSN(0), iqPtr(NULL) { - DPRINTF(MemDepUnit, "MemDepUnit: Creating MemDepUnit object.\n"); + DPRINTF(MemDepUnit, "Creating MemDepUnit object.\n"); +} + +template +MemDepUnit::~MemDepUnit() +{ + for (int tid=0; tid < Impl::MaxThreads; tid++) { + + ListIt inst_list_it = instList[tid].begin(); + + MemDepHashIt hash_it; + + while (!instList[tid].empty()) { + hash_it = memDepHash.find((*inst_list_it)->seqNum); + + assert(hash_it != memDepHash.end()); + + memDepHash.erase(hash_it); + + instList[tid].erase(inst_list_it++); + } + } + + assert(MemDepEntry::memdep_count == 0); +} + +template +std::string +MemDepUnit::name() const +{ + return "memdepunit"; +} + +template +void +MemDepUnit::init(Params *params, int tid) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid); + + id = tid; + + depPred.init(params->SSITSize, params->LFSTSize); } template @@ -58,58 +101,79 @@ MemDepUnit::regStats() .desc("Number of conflicting stores."); } +template +void +MemDepUnit::setIQ(InstructionQueue *iq_ptr) +{ + iqPtr = iq_ptr; +} + template void MemDepUnit::insert(DynInstPtr &inst) { - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; - Dependency unresolved_dependencies(inst_seq_num); + MemDepEntryPtr inst_entry = new MemDepEntry(inst); - InstSeqNum producing_store = depPred.checkInst(inst->readPC()); + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair(inst->seqNum, inst_entry)); + MemDepEntry::memdep_insert++; - if (producing_store == 0 || - storeDependents.find(producing_store) == storeDependents.end()) { + // Add the instruction to the instruction list. + instList[tid].push_back(inst); - DPRINTF(MemDepUnit, "MemDepUnit: No dependency for inst PC " - "%#x.\n", inst->readPC()); + inst_entry->listIt = --(instList[tid].end()); - unresolved_dependencies.storeDep = storeDependents.end(); + // Check the dependence predictor for any producing stores. + InstSeqNum producing_store; + if (inst->isLoad() && loadBarrier) { + producing_store = loadBarrierSN; + } else if (inst->isStore() && storeBarrier) { + producing_store = storeBarrierSN; + } else { + producing_store = depPred.checkInst(inst->readPC()); + } + + MemDepEntryPtr store_entry = NULL; + + // If there is a producing store, try to find the entry. + if (producing_store != 0) { + MemDepHashIt hash_it = memDepHash.find(producing_store); + + if (hash_it != memDepHash.end()) { + store_entry = (*hash_it).second; + } + } + + // If no store entry, then instruction can issue as soon as the registers + // are ready. + if (!store_entry) { + DPRINTF(MemDepUnit, "No dependency for inst PC " + "%#x [sn:%lli].\n", inst->readPC(), inst->seqNum); + + inst_entry->memDepReady = true; if (inst->readyToIssue()) { - readyInsts.insert(inst_seq_num); - } else { - unresolved_dependencies.memDepReady = true; + inst_entry->regsReady = true; - waitingInsts.insert(unresolved_dependencies); + moveToReady(inst_entry); } } else { - DPRINTF(MemDepUnit, "MemDepUnit: Adding to dependency list; " - "inst PC %#x is dependent on seq num %i.\n", + // Otherwise make the instruction dependent on the store. + DPRINTF(MemDepUnit, "Adding to dependency list; " + "inst PC %#x is dependent on [sn:%lli].\n", inst->readPC(), producing_store); if (inst->readyToIssue()) { - unresolved_dependencies.regsReady = true; + inst_entry->regsReady = true; } - // Find the store that this instruction is dependent on. - sd_it_t store_loc = storeDependents.find(producing_store); - - assert(store_loc != storeDependents.end()); - - // Record the location of the store that this instruction is - // dependent on. - unresolved_dependencies.storeDep = store_loc; - - // If it's not already ready, then add it to the renamed - // list and the dependencies. - dep_it_t inst_loc = - (waitingInsts.insert(unresolved_dependencies)).first; - // Add this instruction to the list of dependents. - (*store_loc).second.push_back(inst_loc); + store_entry->dependInsts.push_back(inst_entry); - assert(!(*store_loc).second.empty()); +// inst_entry->producingStore = store_entry; if (inst->isLoad()) { ++conflictingLoads; @@ -119,127 +183,105 @@ MemDepUnit::insert(DynInstPtr &inst) } if (inst->isStore()) { - DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - depPred.insertStore(inst->readPC(), inst_seq_num); - - // Make sure this store isn't already in this list. - assert(storeDependents.find(inst_seq_num) == storeDependents.end()); - - // Put a dependency entry in at the store's sequence number. - // Uh, not sure how this works...I want to create an entry but - // I don't have anything to put into the value yet. - storeDependents[inst_seq_num]; - - assert(storeDependents.size() != 0); + depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber); ++insertedStores; - } else if (inst->isLoad()) { ++insertedLoads; } else { - panic("MemDepUnit: Unknown type! (most likely a barrier)."); + panic("Unknown type! (most likely a barrier)."); } - - memInsts[inst_seq_num] = inst; } template void MemDepUnit::insertNonSpec(DynInstPtr &inst) { - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; - Dependency non_spec_inst(inst_seq_num); + MemDepEntryPtr inst_entry = new MemDepEntry(inst); - non_spec_inst.storeDep = storeDependents.end(); + // Insert the MemDepEntry into the hash. + memDepHash.insert( + std::pair(inst->seqNum, inst_entry)); + MemDepEntry::memdep_insert++; - waitingInsts.insert(non_spec_inst); + // Add the instruction to the list. + instList[tid].push_back(inst); + + inst_entry->listIt = --(instList[tid].end()); // Might want to turn this part into an inline function or something. // It's shared between both insert functions. if (inst->isStore()) { - DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - depPred.insertStore(inst->readPC(), inst_seq_num); - - // Make sure this store isn't already in this list. - assert(storeDependents.find(inst_seq_num) == storeDependents.end()); - - // Put a dependency entry in at the store's sequence number. - // Uh, not sure how this works...I want to create an entry but - // I don't have anything to put into the value yet. - storeDependents[inst_seq_num]; - - assert(storeDependents.size() != 0); + depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber); ++insertedStores; - } else if (inst->isLoad()) { ++insertedLoads; } else { - panic("MemDepUnit: Unknown type! (most likely a barrier)."); + panic("Unknown type! (most likely a barrier)."); } - - memInsts[inst_seq_num] = inst; -} - -template -typename Impl::DynInstPtr & -MemDepUnit::top() -{ - topInst = memInsts.find( (*readyInsts.begin()) ); - - DPRINTF(MemDepUnit, "MemDepUnit: Top instruction is PC %#x.\n", - (*topInst).second->readPC()); - - return (*topInst).second; } template void -MemDepUnit::pop() +MemDepUnit::insertBarrier(DynInstPtr &barr_inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Removing instruction PC %#x.\n", - (*topInst).second->readPC()); + InstSeqNum barr_sn = barr_inst->seqNum; + if (barr_inst->isMemBarrier()) { + loadBarrier = true; + loadBarrierSN = barr_sn; + storeBarrier = true; + storeBarrierSN = barr_sn; + DPRINTF(MemDepUnit, "Inserted a memory barrier\n"); + } else if (barr_inst->isWriteBarrier()) { + storeBarrier = true; + storeBarrierSN = barr_sn; + DPRINTF(MemDepUnit, "Inserted a write barrier\n"); + } - wakeDependents((*topInst).second); + unsigned tid = barr_inst->threadNumber; - issue((*topInst).second); + MemDepEntryPtr inst_entry = new MemDepEntry(barr_inst); - memInsts.erase(topInst); + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair(barr_sn, inst_entry)); + MemDepEntry::memdep_insert++; - topInst = memInsts.end(); + // Add the instruction to the instruction list. + instList[tid].push_back(barr_inst); + + inst_entry->listIt = --(instList[tid].end()); } template void MemDepUnit::regsReady(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Marking registers as ready for " - "instruction PC %#x.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Marking registers as ready for " + "instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - InstSeqNum inst_seq_num = inst->seqNum; + MemDepEntryPtr inst_entry = findInHash(inst); - Dependency inst_to_find(inst_seq_num); + inst_entry->regsReady = true; - dep_it_t waiting_inst = waitingInsts.find(inst_to_find); - - assert(waiting_inst != waitingInsts.end()); - - if ((*waiting_inst).memDepReady) { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction has its memory " + if (inst_entry->memDepReady) { + DPRINTF(MemDepUnit, "Instruction has its memory " "dependencies resolved, adding it to the ready list.\n"); - moveToReady(waiting_inst); + moveToReady(inst_entry); } else { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction still waiting on " + DPRINTF(MemDepUnit, "Instruction still waiting on " "memory dependency.\n"); - - (*waiting_inst).regsReady = true; } } @@ -247,149 +289,182 @@ template void MemDepUnit::nonSpecInstReady(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Marking non speculative " - "instruction PC %#x as ready.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Marking non speculative " + "instruction PC %#x as ready [sn:%lli].\n", + inst->readPC(), inst->seqNum); - InstSeqNum inst_seq_num = inst->seqNum; + MemDepEntryPtr inst_entry = findInHash(inst); - Dependency inst_to_find(inst_seq_num); - - dep_it_t waiting_inst = waitingInsts.find(inst_to_find); - - assert(waiting_inst != waitingInsts.end()); - - moveToReady(waiting_inst); + moveToReady(inst_entry); } template void -MemDepUnit::issue(DynInstPtr &inst) +MemDepUnit::reschedule(DynInstPtr &inst) { - assert(readyInsts.find(inst->seqNum) != readyInsts.end()); + instsToReplay.push_back(inst); +} - DPRINTF(MemDepUnit, "MemDepUnit: Issuing instruction PC %#x.\n", - inst->readPC()); +template +void +MemDepUnit::replay(DynInstPtr &inst) +{ + DynInstPtr temp_inst; + bool found_inst = false; - // Remove the instruction from the ready list. - readyInsts.erase(inst->seqNum); + while (!instsToReplay.empty()) { + temp_inst = instsToReplay.front(); - depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); + MemDepEntryPtr inst_entry = findInHash(temp_inst); + + DPRINTF(MemDepUnit, "Replaying mem instruction PC %#x " + "[sn:%lli].\n", + temp_inst->readPC(), temp_inst->seqNum); + + moveToReady(inst_entry); + + if (temp_inst == inst) { + found_inst = true; + } + + instsToReplay.pop_front(); + } + + assert(found_inst); +} + +template +void +MemDepUnit::completed(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Completed mem instruction PC %#x " + "[sn:%lli].\n", + inst->readPC(), inst->seqNum); + + unsigned tid = inst->threadNumber; + + // Remove the instruction from the hash and the list. + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); + + assert(hash_it != memDepHash.end()); + + instList[tid].erase((*hash_it).second->listIt); + +// (*hash_it).second->inst = NULL; + + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); + MemDepEntry::memdep_erase++; +} + +template +void +MemDepUnit::completeBarrier(DynInstPtr &inst) +{ + wakeDependents(inst); + completed(inst); + + InstSeqNum barr_sn = inst->seqNum; + + if (inst->isMemBarrier()) { + assert(loadBarrier && storeBarrier); + if (loadBarrierSN == barr_sn) + loadBarrier = false; + if (storeBarrierSN == barr_sn) + storeBarrier = false; + } else if (inst->isWriteBarrier()) { + assert(storeBarrier); + if (storeBarrierSN == barr_sn) + storeBarrier = false; + } } template void MemDepUnit::wakeDependents(DynInstPtr &inst) { - // Only stores have dependents. - if (!inst->isStore()) { + // Only stores and barriers have dependents. + if (!inst->isStore() && !inst->isMemBarrier() && !inst->isWriteBarrier()) { return; } - // Wake any dependencies. - sd_it_t sd_it = storeDependents.find(inst->seqNum); + MemDepEntryPtr inst_entry = findInHash(inst); - // If there's no entry, then return. Really there should only be - // no entry if the instruction is a load. - if (sd_it == storeDependents.end()) { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction PC %#x, sequence " - "number %i has no dependents.\n", - inst->readPC(), inst->seqNum); + for (int i = 0; i < inst_entry->dependInsts.size(); ++i ) { + MemDepEntryPtr woken_inst = inst_entry->dependInsts[i]; - return; - } - - for (int i = 0; i < (*sd_it).second.size(); ++i ) { - dep_it_t woken_inst = (*sd_it).second[i]; - - DPRINTF(MemDepUnit, "MemDepUnit: Waking up a dependent inst, " - "sequence number %i.\n", - (*woken_inst).seqNum); -#if 0 - // Should we have reached instructions that are actually squashed, - // there will be no more useful instructions in this dependency - // list. Break out early. - if (waitingInsts.find(woken_inst) == waitingInsts.end()) { - DPRINTF(MemDepUnit, "MemDepUnit: Dependents on inst PC %#x " - "are squashed, starting at SN %i. Breaking early.\n", - inst->readPC(), woken_inst); - break; + if (!woken_inst->inst) { + // Potentially removed mem dep entries could be on this list +// inst_entry->dependInsts[i] = NULL; + continue; } -#endif - if ((*woken_inst).regsReady) { + DPRINTF(MemDepUnit, "Waking up a dependent inst, " + "[sn:%lli].\n", + woken_inst->inst->seqNum); + + if (woken_inst->regsReady && !woken_inst->squashed) { moveToReady(woken_inst); } else { - (*woken_inst).memDepReady = true; + woken_inst->memDepReady = true; } +// inst_entry->dependInsts[i] = NULL; } - storeDependents.erase(sd_it); + inst_entry->dependInsts.clear(); } template void -MemDepUnit::squash(const InstSeqNum &squashed_num) +MemDepUnit::squash(const InstSeqNum &squashed_num, + unsigned tid) { - - if (!waitingInsts.empty()) { - dep_it_t waiting_it = waitingInsts.end(); - - --waiting_it; - - // Remove entries from the renamed list as long as we haven't reached - // the end and the entries continue to be younger than the squashed. - while (!waitingInsts.empty() && - (*waiting_it).seqNum > squashed_num) - { - if (!(*waiting_it).memDepReady && - (*waiting_it).storeDep != storeDependents.end()) { - sd_it_t sd_it = (*waiting_it).storeDep; - - // Make sure the iterator that the store has pointing - // back is actually to this instruction. - assert((*sd_it).second.back() == waiting_it); - - // Now remove this from the store's list of dependent - // instructions. - (*sd_it).second.pop_back(); + if (!instsToReplay.empty()) { + ListIt replay_it = instsToReplay.begin(); + while (replay_it != instsToReplay.end()) { + if ((*replay_it)->threadNumber == tid && + (*replay_it)->seqNum > squashed_num) { + instsToReplay.erase(replay_it++); + } else { + ++replay_it; } - - waitingInsts.erase(waiting_it--); } } - if (!readyInsts.empty()) { - sn_it_t ready_it = readyInsts.end(); + ListIt squash_it = instList[tid].end(); + --squash_it; - --ready_it; + MemDepHashIt hash_it; - // Same for the ready list. - while (!readyInsts.empty() && - (*ready_it) > squashed_num) - { - readyInsts.erase(ready_it--); + while (!instList[tid].empty() && + (*squash_it)->seqNum > squashed_num) { + + DPRINTF(MemDepUnit, "Squashing inst [sn:%lli]\n", + (*squash_it)->seqNum); + + hash_it = memDepHash.find((*squash_it)->seqNum); + + assert(hash_it != memDepHash.end()); + + (*hash_it).second->squashed = true; +/* + for (int i = 0; i < (*hash_it).second->dependInsts.size(); ++i) { + (*hash_it).second->dependInsts[i] = NULL; } - } - if (!storeDependents.empty()) { - sd_it_t dep_it = storeDependents.end(); + (*hash_it).second->inst = NULL; +*/ + (*hash_it).second = NULL; - --dep_it; + memDepHash.erase(hash_it); + MemDepEntry::memdep_erase++; - // Same for the dependencies list. - while (!storeDependents.empty() && - (*dep_it).first > squashed_num) - { - // This store's list of dependent instructions should be empty. - assert((*dep_it).second.empty()); - - storeDependents.erase(dep_it--); - } + instList[tid].erase(squash_it--); } // Tell the dependency predictor to squash as well. - depPred.squash(squashed_num); + depPred.squash(squashed_num, tid); } template @@ -397,7 +472,7 @@ void MemDepUnit::violation(DynInstPtr &store_inst, DynInstPtr &violating_load) { - DPRINTF(MemDepUnit, "MemDepUnit: Passing violating PCs to store sets," + DPRINTF(MemDepUnit, "Passing violating PCs to store sets," " load: %#x, store: %#x\n", violating_load->readPC(), store_inst->readPC()); // Tell the memory dependence unit of the violation. @@ -405,15 +480,64 @@ MemDepUnit::violation(DynInstPtr &store_inst, } template -inline void -MemDepUnit::moveToReady(dep_it_t &woken_inst) +void +MemDepUnit::issue(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Adding instruction sequence number %i " - "to the ready list.\n", (*woken_inst).seqNum); + DPRINTF(MemDepUnit, "Issuing instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - // Add it to the ready list. - readyInsts.insert((*woken_inst).seqNum); - - // Remove it from the waiting instructions. - waitingInsts.erase(woken_inst); + depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); +} + +template +inline typename MemDepUnit::MemDepEntryPtr & +MemDepUnit::findInHash(const DynInstPtr &inst) +{ + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); + + assert(hash_it != memDepHash.end()); + + return (*hash_it).second; +} + +template +inline void +MemDepUnit::moveToReady(MemDepEntryPtr &woken_inst_entry) +{ + DPRINTF(MemDepUnit, "Adding instruction [sn:%lli] " + "to the ready list.\n", woken_inst_entry->inst->seqNum); + + assert(!woken_inst_entry->squashed); + + iqPtr->addReadyMemInst(woken_inst_entry->inst); +} + + +template +void +MemDepUnit::dumpLists() +{ + for (unsigned tid=0; tid < Impl::MaxThreads; tid++) { + cprintf("Instruction list %i size: %i\n", + tid, instList[tid].size()); + + ListIt inst_list_it = instList[tid].begin(); + int num = 0; + + while (inst_list_it != instList[tid].end()) { + cprintf("Instruction:%i\nPC:%#x\n[sn:%i]\n[tid:%i]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + inst_list_it++; + ++num; + } + } + + cprintf("Memory dependence hash size: %i\n", memDepHash.size()); + + cprintf("Memory dependence entries: %i\n", MemDepEntry::memdep_count); } diff --git a/cpu/o3/ras.cc b/cpu/o3/ras.cc index 0a7d6ca63..5e7ef38ae 100644 --- a/cpu/o3/ras.cc +++ b/cpu/o3/ras.cc @@ -28,14 +28,17 @@ #include "cpu/o3/ras.hh" -ReturnAddrStack::ReturnAddrStack(unsigned _numEntries) - : numEntries(_numEntries), usedEntries(0), - tos(0) +void +ReturnAddrStack::init(unsigned _numEntries) { - addrStack = new Addr[numEntries]; + numEntries = _numEntries; + usedEntries = 0; + tos = 0; - for (int i = 0; i < numEntries; ++i) - addrStack[i] = 0; + addrStack.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) + addrStack[i] = 0; } void @@ -53,9 +56,6 @@ ReturnAddrStack::push(const Addr &return_addr) void ReturnAddrStack::pop() { - // Not sure it's possible to really track usedEntries properly. -// assert(usedEntries > 0); - if (usedEntries > 0) { --usedEntries; } diff --git a/cpu/o3/ras.hh b/cpu/o3/ras.hh index 46d98181e..5aa4fc05f 100644 --- a/cpu/o3/ras.hh +++ b/cpu/o3/ras.hh @@ -26,43 +26,68 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_RAS_HH__ -#define __CPU_O3_CPU_RAS_HH__ +#ifndef __CPU_O3_RAS_HH__ +#define __CPU_O3_RAS_HH__ // For Addr type. #include "arch/isa_traits.hh" +#include +/** Return address stack class, implements a simple RAS. */ class ReturnAddrStack { public: - ReturnAddrStack(unsigned numEntries); + /** Creates a return address stack, but init() must be called prior to + * use. + */ + ReturnAddrStack() {} + /** Initializes RAS with a specified number of entries. + * @param numEntries Number of entries in the RAS. + */ + void init(unsigned numEntries); + + /** Returns the top address on the RAS. */ Addr top() { return addrStack[tos]; } + /** Returns the index of the top of the RAS. */ unsigned topIdx() { return tos; } + /** Pushes an address onto the RAS. */ void push(const Addr &return_addr); + /** Pops the top address from the RAS. */ void pop(); + /** Changes index to the top of the RAS, and replaces the top address with + * a new target. + * @param top_entry_idx The index of the RAS that will now be the top. + * @param restored_target The new target address of the new top of the RAS. + */ void restore(unsigned top_entry_idx, const Addr &restored_target); private: + /** Increments the top of stack index. */ inline void incrTos() { if (++tos == numEntries) tos = 0; } + /** Decrements the top of stack index. */ inline void decrTos() { tos = (tos == 0 ? numEntries - 1 : tos - 1); } - Addr *addrStack; + /** The RAS itself. */ + std::vector addrStack; + /** The number of entries in the RAS. */ unsigned numEntries; + /** The number of used entries in the RAS. */ unsigned usedEntries; + /** The top of stack index. */ unsigned tos; }; -#endif // __CPU_O3_CPU_RAS_HH__ +#endif // __CPU_O3_RAS_HH__ diff --git a/cpu/o3/regfile.hh b/cpu/o3/regfile.hh index 1e6e10f29..78674c32c 100644 --- a/cpu/o3/regfile.hh +++ b/cpu/o3/regfile.hh @@ -26,10 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_REGFILE_HH__ -#define __CPU_O3_CPU_REGFILE_HH__ - -// @todo: Destructor +#ifndef __CPU_O3_REGFILE_HH__ +#define __CPU_O3_REGFILE_HH__ #include "arch/isa_traits.hh" #include "arch/faults.hh" @@ -42,11 +40,14 @@ #endif -// This really only depends on the ISA, and not the Impl. It might be nicer -// to see if I can make it depend on nothing... -// Things that are in the ifdef FULL_SYSTEM are pretty dependent on the ISA, -// and should go in the AlphaFullCPU. +#include +/** + * Simple physical register file class. + * This really only depends on the ISA, and not the Impl. Things that are + * in the ifdef FULL_SYSTEM are pretty dependent on the ISA, and probably + * should go in the AlphaFullCPU. + */ template class PhysRegFile { @@ -55,19 +56,18 @@ class PhysRegFile typedef TheISA::FloatReg FloatReg; typedef TheISA::MiscRegFile MiscRegFile; typedef TheISA::MiscReg MiscReg; + // Note that most of the definitions of the IntReg, FloatReg, etc. exist + // within the Impl/ISA class and not within this PhysRegFile class. - //Note that most of the definitions of the IntReg, FloatReg, etc. exist - //within the Impl/ISA class and not within this PhysRegFile class. - - //Will need some way to allow stuff like swap_palshadow to access the - //correct registers. Might require code changes to swap_palshadow and - //other execution contexts. - - //Will make these registers public for now, but they probably should - //be private eventually with some accessor functions. + // Will make these registers public for now, but they probably should + // be private eventually with some accessor functions. public: typedef typename Impl::FullCPU FullCPU; + /** + * Constructs a physical register file with the specified amount of + * integer and floating point registers. + */ PhysRegFile(unsigned _numPhysicalIntRegs, unsigned _numPhysicalFloatRegs); @@ -80,6 +80,7 @@ class PhysRegFile // void serialize(std::ostream &os); // void unserialize(Checkpoint *cp, const std::string §ion); + /** Reads an integer register. */ uint64_t readIntReg(PhysRegIndex reg_idx) { assert(reg_idx < numPhysicalIntRegs); @@ -89,6 +90,7 @@ class PhysRegFile return intRegFile[reg_idx]; } + /** Reads a floating point register (single precision). */ float readFloatRegSingle(PhysRegIndex reg_idx) { // Remove the base Float reg dependency. @@ -102,6 +104,7 @@ class PhysRegFile return (float)floatRegFile[reg_idx].d; } + /** Reads a floating point register (double precision). */ double readFloatRegDouble(PhysRegIndex reg_idx) { // Remove the base Float reg dependency. @@ -115,6 +118,7 @@ class PhysRegFile return floatRegFile[reg_idx].d; } + /** Reads a floating point register as an integer. */ uint64_t readFloatRegInt(PhysRegIndex reg_idx) { // Remove the base Float reg dependency. @@ -128,6 +132,7 @@ class PhysRegFile return floatRegFile[reg_idx].q; } + /** Sets an integer register to the given value. */ void setIntReg(PhysRegIndex reg_idx, uint64_t val) { assert(reg_idx < numPhysicalIntRegs); @@ -135,9 +140,11 @@ class PhysRegFile DPRINTF(IEW, "RegFile: Setting int register %i to %lli\n", int(reg_idx), val); - intRegFile[reg_idx] = val; + if (reg_idx != TheISA::ZeroReg) + intRegFile[reg_idx] = val; } + /** Sets a single precision floating point register to the given value. */ void setFloatRegSingle(PhysRegIndex reg_idx, float val) { // Remove the base Float reg dependency. @@ -148,9 +155,11 @@ class PhysRegFile DPRINTF(IEW, "RegFile: Setting float register %i to %8.8f\n", int(reg_idx), val); - floatRegFile[reg_idx].d = (double)val; + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = (double)val; } + /** Sets a double precision floating point register to the given value. */ void setFloatRegDouble(PhysRegIndex reg_idx, double val) { // Remove the base Float reg dependency. @@ -161,9 +170,11 @@ class PhysRegFile DPRINTF(IEW, "RegFile: Setting float register %i to %8.8f\n", int(reg_idx), val); - floatRegFile[reg_idx].d = val; + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; } + /** Sets a floating point register to the given integer value. */ void setFloatRegInt(PhysRegIndex reg_idx, uint64_t val) { // Remove the base Float reg dependency. @@ -174,78 +185,68 @@ class PhysRegFile DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n", int(reg_idx), val); - floatRegFile[reg_idx].q = val; - } - - uint64_t readPC() - { - return pc; - } - - void setPC(uint64_t val) - { - pc = val; - } - - void setNextPC(uint64_t val) - { - npc = val; + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].q = val; } //Consider leaving this stuff and below in some implementation specific //file as opposed to the general register file. Or have a derived class. - MiscReg readMiscReg(int misc_reg) + MiscReg readMiscReg(int misc_reg, unsigned thread_id) { - // Dummy function for now. - // @todo: Fix this once proxy XC is used. - return 0; + return miscRegs[thread_id].readReg(misc_reg); } - Fault setMiscReg(int misc_reg, const MiscReg &val) + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned thread_id) { - // Dummy function for now. - // @todo: Fix this once proxy XC is used. - return NoFault; + return miscRegs[thread_id].readRegWithEffect(misc_reg, fault, + cpu->xcProxies[thread_id]); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned thread_id) + { + return miscRegs[thread_id].setReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned thread_id) + { + return miscRegs[thread_id].setRegWithEffect(misc_reg, val, + cpu->xcProxies[thread_id]); } #if FULL_SYSTEM int readIntrFlag() { return intrflag; } + /** Sets an interrupt flag. */ void setIntrFlag(int val) { intrflag = val; } #endif - // These should be private eventually, but will be public for now - // so that I can hack around the initregs issue. public: /** (signed) integer register file. */ - IntReg *intRegFile; + std::vector intRegFile; /** Floating point register file. */ - FloatReg *floatRegFile; + std::vector floatRegFile; /** Miscellaneous register file. */ - MiscRegFile miscRegs; - - /** Program counter. */ - Addr pc; - - /** Next-cycle program counter. */ - Addr npc; + MiscRegFile miscRegs[Impl::MaxThreads]; #if FULL_SYSTEM private: - // This is ISA specifc stuff; remove it eventually once ISAImpl is used -// IntReg palregs[NumIntRegs]; // PAL shadow registers int intrflag; // interrupt flag - bool pal_shadow; // using pal_shadow registers #endif private: + /** CPU pointer. */ FullCPU *cpu; public: + /** Sets the CPU pointer. */ void setCPU(FullCPU *cpu_ptr) { cpu = cpu_ptr; } + /** Number of physical integer registers. */ unsigned numPhysicalIntRegs; + /** Number of physical floating point registers. */ unsigned numPhysicalFloatRegs; }; @@ -255,11 +256,11 @@ PhysRegFile::PhysRegFile(unsigned _numPhysicalIntRegs, : numPhysicalIntRegs(_numPhysicalIntRegs), numPhysicalFloatRegs(_numPhysicalFloatRegs) { - intRegFile = new IntReg[numPhysicalIntRegs]; - floatRegFile = new FloatReg[numPhysicalFloatRegs]; + intRegFile.resize(numPhysicalIntRegs); + floatRegFile.resize(numPhysicalFloatRegs); - memset(intRegFile, 0, sizeof(*intRegFile)); - memset(floatRegFile, 0, sizeof(*floatRegFile)); + //memset(intRegFile, 0, sizeof(*intRegFile)); + //memset(floatRegFile, 0, sizeof(*floatRegFile)); } -#endif // __CPU_O3_CPU_REGFILE_HH__ +#endif diff --git a/cpu/o3/rename.cc b/cpu/o3/rename.cc index 6e9ee23da..4dc3bf6b2 100644 --- a/cpu/o3/rename.cc +++ b/cpu/o3/rename.cc @@ -30,4 +30,4 @@ #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/rename_impl.hh" -template class SimpleRename; +template class DefaultRename; diff --git a/cpu/o3/rename.hh b/cpu/o3/rename.hh index 07b442964..d5beccde9 100644 --- a/cpu/o3/rename.hh +++ b/cpu/o3/rename.hh @@ -26,23 +26,27 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Todo: -// Fix up trap and barrier handling. -// May want to have different statuses to differentiate the different stall -// conditions. - -#ifndef __CPU_O3_CPU_SIMPLE_RENAME_HH__ -#define __CPU_O3_CPU_SIMPLE_RENAME_HH__ +#ifndef __CPU_O3_RENAME_HH__ +#define __CPU_O3_RENAME_HH__ #include #include "base/statistics.hh" #include "base/timebuf.hh" -// Will need rename maps for both the int reg file and fp reg file. -// Or change rename map class to handle both. (RegFile handles both.) +/** + * DefaultRename handles both single threaded and SMT rename. Its width is + * specified by the parameters; each cycle it tries to rename that many + * instructions. It holds onto the rename history of all instructions with + * destination registers, storing the arch. register, the new physical + * register, and the old physical register, to allow for undoing of mappings + * if squashing happens, or freeing up registers upon commit. Rename handles + * blocking if the ROB, IQ, or LSQ is going to be full. Rename also handles + * barriers, and does so by stalling on the instruction until the ROB is + * empty and there are no instructions in flight to the ROB. + */ template -class SimpleRename +class DefaultRename { public: // Typedefs from the Impl. @@ -51,25 +55,38 @@ class SimpleRename typedef typename Impl::FullCPU FullCPU; typedef typename Impl::Params Params; - typedef typename CPUPol::FetchStruct FetchStruct; + // Typedefs from the CPUPol typedef typename CPUPol::DecodeStruct DecodeStruct; typedef typename CPUPol::RenameStruct RenameStruct; typedef typename CPUPol::TimeStruct TimeStruct; - - // Typedefs from the CPUPol typedef typename CPUPol::FreeList FreeList; typedef typename CPUPol::RenameMap RenameMap; + // These are used only for initialization. + typedef typename CPUPol::IEW IEW; + typedef typename CPUPol::Commit Commit; // Typedefs from the ISA. typedef TheISA::RegIndex RegIndex; + // A deque is used to queue the instructions. Barrier insts must be + // added to the front of the deque, which is the only reason for using + // a deque instead of a queue. (Most other stages use a queue) + typedef std::list InstQueue; + public: - // Rename will block if ROB becomes full or issue queue becomes full, - // or there are no free registers to rename to. - // Only case where rename squashes is if IEW squashes. - enum Status { + /** Overall rename status. Used to determine if the CPU can deschedule + * itself due to a lack of activity. + */ + enum RenameStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, + StartSquash, Squashing, Blocked, Unblocking, @@ -77,86 +94,191 @@ class SimpleRename }; private: - Status _status; + /** Rename status. */ + RenameStatus _status; + + /** Per-thread status. */ + ThreadStatus renameStatus[Impl::MaxThreads]; public: - SimpleRename(Params ¶ms); + /** DefaultRename constructor. */ + DefaultRename(Params *params); + /** Returns the name of rename. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); + /** Sets CPU pointer. */ void setCPU(FullCPU *cpu_ptr); + /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer *tb_ptr); + /** Sets pointer to time buffer used to communicate to the next stage. */ void setRenameQueue(TimeBuffer *rq_ptr); + /** Sets pointer to time buffer coming from decode. */ void setDecodeQueue(TimeBuffer *dq_ptr); - void setRenameMap(RenameMap *rm_ptr); + /** Sets pointer to IEW stage. Used only for initialization. */ + void setIEWStage(IEW *iew_stage) + { iew_ptr = iew_stage; } - void setFreeList(FreeList *fl_ptr); - - void dumpHistory(); - - void tick(); - - void rename(); - - void squash(); + /** Sets pointer to commit stage. Used only for initialization. */ + void setCommitStage(Commit *commit_stage) + { commit_ptr = commit_stage; } private: - void block(); + /** Pointer to IEW stage. Used only for initialization. */ + IEW *iew_ptr; - inline void unblock(); + /** Pointer to commit stage. Used only for initialization. */ + Commit *commit_ptr; - void doSquash(); + public: + /** Initializes variables for the stage. */ + void initStage(); - void removeFromHistory(InstSeqNum inst_seq_num); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list *at_ptr); - inline void renameSrcRegs(DynInstPtr &inst); + /** Sets pointer to rename maps (per-thread structures). */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); - inline void renameDestRegs(DynInstPtr &inst); + /** Sets pointer to the free list. */ + void setFreeList(FreeList *fl_ptr); - inline int calcFreeROBEntries(); + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *_scoreboard); - inline int calcFreeIQEntries(); + /** Squashes all instructions in a thread. */ + void squash(unsigned tid); - /** Holds the previous information for each rename. - * Note that often times the inst may have been deleted, so only access - * the pointer for the address and do not dereference it. + /** Ticks rename, which processes all input signals and attempts to rename + * as many instructions as possible. + */ + void tick(); + + /** Debugging function used to dump history buffer of renamings. */ + void dumpHistory(); + + private: + /** Determines what to do based on rename's current status. + * @param status_change rename() sets this variable if there was a status + * change (ie switching from blocking to unblocking). + * @param tid Thread id to rename instructions from. + */ + void rename(bool &status_change, unsigned tid); + + /** Renames instructions for the given thread. Also handles serializing + * instructions. + */ + void renameInsts(unsigned tid); + + /** Inserts unused instructions from a given thread into the skid buffer, + * to be renamed once rename unblocks. + */ + void skidInsert(unsigned tid); + + /** Separates instructions from decode into individual lists of instructions + * sorted by thread. + */ + void sortInsts(); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall rename status based on all of the threads' statuses. */ + void updateStatus(); + + /** Switches rename to blocking, and signals back that rename has become + * blocked. + * @return Returns true if there is a status change. + */ + bool block(unsigned tid); + + /** Switches rename to unblocking if the skid buffer is empty, and signals + * back that rename has unblocked. + * @return Returns true if there is a status change. + */ + bool unblock(unsigned tid); + + /** Executes actual squash, removing squashed instructions. */ + void doSquash(unsigned tid); + + /** Removes a committed instruction's rename history. */ + void removeFromHistory(InstSeqNum inst_seq_num, unsigned tid); + + /** Renames the source registers of an instruction. */ + inline void renameSrcRegs(DynInstPtr &inst, unsigned tid); + + /** Renames the destination registers of an instruction. */ + inline void renameDestRegs(DynInstPtr &inst, unsigned tid); + + /** Calculates the number of free ROB entries for a specific thread. */ + inline int calcFreeROBEntries(unsigned tid); + + /** Calculates the number of free IQ entries for a specific thread. */ + inline int calcFreeIQEntries(unsigned tid); + + /** Calculates the number of free LSQ entries for a specific thread. */ + inline int calcFreeLSQEntries(unsigned tid); + + /** Returns the number of valid instructions coming from decode. */ + unsigned validInsts(); + + /** Reads signals telling rename to block/unblock. */ + void readStallSignals(unsigned tid); + + /** Checks if any stages are telling rename to block. */ + bool checkStall(unsigned tid); + + void readFreeEntries(unsigned tid); + + bool checkSignalsAndUpdate(unsigned tid); + + /** Either serializes on the next instruction available in the InstQueue, + * or records that it must serialize on the next instruction to enter + * rename. + * @param inst_list The list of younger, unprocessed instructions for the + * thread that has the serializeAfter instruction. + * @param tid The thread id. + */ + void serializeAfter(InstQueue &inst_list, unsigned tid); + + /** Holds the information for each destination register rename. It holds + * the instruction's sequence number, the arch register, the old physical + * register for that arch. register, and the new physical register. */ struct RenameHistory { RenameHistory(InstSeqNum _instSeqNum, RegIndex _archReg, PhysRegIndex _newPhysReg, PhysRegIndex _prevPhysReg) : instSeqNum(_instSeqNum), archReg(_archReg), - newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg), - placeHolder(false) - { - } - - /** Constructor used specifically for cases where a place holder - * rename history entry is being made. - */ - RenameHistory(InstSeqNum _instSeqNum) - : instSeqNum(_instSeqNum), archReg(0), newPhysReg(0), - prevPhysReg(0), placeHolder(true) + newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg) { } + /** The sequence number of the instruction that renamed. */ InstSeqNum instSeqNum; + /** The architectural register index that was renamed. */ RegIndex archReg; + /** The new physical register that the arch. register is renamed to. */ PhysRegIndex newPhysReg; + /** The old physical register that the arch. register was renamed to. */ PhysRegIndex prevPhysReg; - bool placeHolder; }; - std::list historyBuffer; + /** A per-thread list of all destination register renames, used to either + * undo rename mappings or free old physical registers. + */ + std::list historyBuffer[Impl::MaxThreads]; - /** CPU interface. */ + /** Pointer to CPU. */ FullCPU *cpu; - // Interfaces to objects outside of rename. - /** Time buffer interface. */ + /** Pointer to main time buffer used for backwards communication. */ TimeBuffer *timeBuffer; /** Wire to get IEW's output from backwards time buffer. */ @@ -166,7 +288,6 @@ class SimpleRename typename TimeBuffer::wire fromCommit; /** Wire to write infromation heading to previous stages. */ - // Might not be the best name as not only decode will read it. typename TimeBuffer::wire toDecode; /** Rename instruction queue. */ @@ -181,15 +302,71 @@ class SimpleRename /** Wire to get decode's output from decode queue. */ typename TimeBuffer::wire fromDecode; + /** Queue of all instructions coming from decode this cycle. */ + InstQueue insts[Impl::MaxThreads]; + /** Skid buffer between rename and decode. */ - std::queue skidBuffer; + InstQueue skidBuffer[Impl::MaxThreads]; /** Rename map interface. */ - SimpleRenameMap *renameMap; + RenameMap *renameMap[Impl::MaxThreads]; /** Free list interface. */ FreeList *freeList; + /** Pointer to the list of active threads. */ + std::list *activeThreads; + + /** Pointer to the scoreboard. */ + Scoreboard *scoreboard; + + /** Count of instructions in progress that have been sent off to the IQ + * and ROB, but are not yet included in their occupancy counts. + */ + int instsInProgress[Impl::MaxThreads]; + + /** Variable that tracks if decode has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Structures whose free entries impact the amount of instructions that + * can be renamed. + */ + struct FreeEntries { + unsigned iqEntries; + unsigned lsqEntries; + unsigned robEntries; + }; + + /** Per-thread tracking of the number of free entries of back-end + * structures. + */ + FreeEntries freeEntries[Impl::MaxThreads]; + + /** Records if the ROB is empty. In SMT mode the ROB may be dynamically + * partitioned between threads, so the ROB must tell rename when it is + * empty. + */ + bool emptyROB[Impl::MaxThreads]; + + /** Source of possible stalls. */ + struct Stalls { + bool iew; + bool commit; + }; + + /** Tracks which stages are telling decode to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** The barrier instruction that rename has stalled on. */ + DynInstPtr barrierInst[Impl::MaxThreads]; + + /** Records if rename needs to serialize on the next instruction for any + * thread. + */ + bool serializeOnNextInst[Impl::MaxThreads]; + /** Delay between iew and rename, in ticks. */ int iewToRenameDelay; @@ -207,27 +384,68 @@ class SimpleRename */ unsigned commitWidth; - /** The instruction that rename is currently on. It needs to have - * persistent state so that when a stall occurs in the middle of a - * group of instructions, it can restart at the proper instruction. + /** The index of the instruction in the time buffer to IEW that rename is + * currently using. */ - unsigned numInst; + unsigned toIEWIndex; + /** Whether or not rename needs to block this cycle. */ + bool blockThisCycle; + + /** The number of threads active in rename. */ + unsigned numThreads; + + /** The maximum skid buffer size. */ + unsigned skidBufferMax; + + /** Enum to record the source of a structure full stall. Can come from + * either ROB, IQ, LSQ, and it is priortized in that order. + */ + enum FullSource { + ROB, + IQ, + LSQ, + NONE + }; + + /** Function used to increment the stat that corresponds to the source of + * the stall. + */ + inline void incrFullStat(const FullSource &source); + + /** Stat for total number of cycles spent squashing. */ Stats::Scalar<> renameSquashCycles; + /** Stat for total number of cycles spent idle. */ Stats::Scalar<> renameIdleCycles; + /** Stat for total number of cycles spent blocking. */ Stats::Scalar<> renameBlockCycles; + /** Stat for total number of cycles spent stalling for a barrier. */ + Stats::Scalar<> renameBarrierCycles; + /** Stat for total number of cycles spent running normally. */ + Stats::Scalar<> renameRunCycles; + /** Stat for total number of cycles spent unblocking. */ Stats::Scalar<> renameUnblockCycles; + /** Stat for total number of renamed instructions. */ Stats::Scalar<> renameRenamedInsts; + /** Stat for total number of squashed instructions that rename discards. */ Stats::Scalar<> renameSquashedInsts; + /** Stat for total number of times that the ROB starts a stall in rename. */ Stats::Scalar<> renameROBFullEvents; + /** Stat for total number of times that the IQ starts a stall in rename. */ Stats::Scalar<> renameIQFullEvents; + /** Stat for total number of times that the LSQ starts a stall in rename. */ + Stats::Scalar<> renameLSQFullEvents; + /** Stat for total number of times that rename runs out of free registers + * to use to rename. */ Stats::Scalar<> renameFullRegistersEvents; + /** Stat for total number of renamed destination registers. */ Stats::Scalar<> renameRenamedOperands; + /** Stat for total number of source register rename lookups. */ Stats::Scalar<> renameRenameLookups; - Stats::Scalar<> renameHBPlaceHolders; + /** Stat for total number of committed renaming mappings. */ Stats::Scalar<> renameCommittedMaps; + /** Stat for total number of mappings that were undone due to a squash. */ Stats::Scalar<> renameUndoneMaps; - Stats::Scalar<> renameValidUndoneMaps; }; -#endif // __CPU_O3_CPU_SIMPLE_RENAME_HH__ +#endif // __CPU_O3_RENAME_HH__ diff --git a/cpu/o3/rename_impl.hh b/cpu/o3/rename_impl.hh index 2068b36ab..441118ef1 100644 --- a/cpu/o3/rename_impl.hh +++ b/cpu/o3/rename_impl.hh @@ -31,21 +31,51 @@ #include "config/full_system.hh" #include "cpu/o3/rename.hh" +using namespace std; + template -SimpleRename::SimpleRename(Params ¶ms) - : iewToRenameDelay(params.iewToRenameDelay), - decodeToRenameDelay(params.decodeToRenameDelay), - commitToRenameDelay(params.commitToRenameDelay), - renameWidth(params.renameWidth), - commitWidth(params.commitWidth), - numInst(0) +DefaultRename::DefaultRename(Params *params) + : iewToRenameDelay(params->iewToRenameDelay), + decodeToRenameDelay(params->decodeToRenameDelay), + commitToRenameDelay(params->commitToRenameDelay), + renameWidth(params->renameWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads) { - _status = Idle; + _status = Inactive; + + for (int i=0; i< numThreads; i++) { + renameStatus[i] = Idle; + + freeEntries[i].iqEntries = 0; + freeEntries[i].lsqEntries = 0; + freeEntries[i].robEntries = 0; + + stalls[i].iew = false; + stalls[i].commit = false; + barrierInst[i] = NULL; + + instsInProgress[i] = 0; + + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } + + // @todo: Make into a parameter. + skidBufferMax = (2 * (iewToRenameDelay * params->decodeWidth)) + renameWidth; +} + +template +std::string +DefaultRename::name() const +{ + return cpu->name() + ".rename"; } template void -SimpleRename::regStats() +DefaultRename::regStats() { renameSquashCycles .name(name() + ".renameSquashCycles") @@ -59,6 +89,14 @@ SimpleRename::regStats() .name(name() + ".renameBlockCycles") .desc("Number of cycles rename is blocking") .prereq(renameBlockCycles); + renameBarrierCycles + .name(name() + ".renameBarrierCycles") + .desc("Number of cycles rename is blocking due to a barrier stall") + .prereq(renameBarrierCycles); + renameRunCycles + .name(name() + ".renameRunCycles") + .desc("Number of cycles rename is running") + .prereq(renameIdleCycles); renameUnblockCycles .name(name() + ".renameUnblockCycles") .desc("Number of cycles rename is unblocking") @@ -73,12 +111,16 @@ SimpleRename::regStats() .prereq(renameSquashedInsts); renameROBFullEvents .name(name() + ".renameROBFullEvents") - .desc("Number of times rename has considered the ROB 'full'") + .desc("Number of times rename has blocked due to ROB full") .prereq(renameROBFullEvents); renameIQFullEvents .name(name() + ".renameIQFullEvents") - .desc("Number of times rename has considered the IQ 'full'") + .desc("Number of times rename has blocked due to IQ full") .prereq(renameIQFullEvents); + renameLSQFullEvents + .name(name() + ".renameLSQFullEvents") + .desc("Number of times rename has blocked due to LSQ full") + .prereq(renameLSQFullEvents); renameFullRegistersEvents .name(name() + ".renameFullRegisterEvents") .desc("Number of times there has been no free registers") @@ -91,10 +133,6 @@ SimpleRename::regStats() .name(name() + ".renameRenameLookups") .desc("Number of register rename lookups that rename has made") .prereq(renameRenameLookups); - renameHBPlaceHolders - .name(name() + ".renameHBPlaceHolders") - .desc("Number of place holders added to the history buffer") - .prereq(renameHBPlaceHolders); renameCommittedMaps .name(name() + ".renameCommittedMaps") .desc("Number of HB maps that are committed") @@ -103,25 +141,21 @@ SimpleRename::regStats() .name(name() + ".renameUndoneMaps") .desc("Number of HB maps that are undone due to squashing") .prereq(renameUndoneMaps); - renameValidUndoneMaps - .name(name() + ".renameValidUndoneMaps") - .desc("Number of HB maps that are undone, and are not place holders") - .prereq(renameValidUndoneMaps); } template void -SimpleRename::setCPU(FullCPU *cpu_ptr) +DefaultRename::setCPU(FullCPU *cpu_ptr) { - DPRINTF(Rename, "Rename: Setting CPU pointer.\n"); + DPRINTF(Rename, "Setting CPU pointer.\n"); cpu = cpu_ptr; } template void -SimpleRename::setTimeBuffer(TimeBuffer *tb_ptr) +DefaultRename::setTimeBuffer(TimeBuffer *tb_ptr) { - DPRINTF(Rename, "Rename: Setting time buffer pointer.\n"); + DPRINTF(Rename, "Setting time buffer pointer.\n"); timeBuffer = tb_ptr; // Setup wire to read information from time buffer, from IEW stage. @@ -136,9 +170,9 @@ SimpleRename::setTimeBuffer(TimeBuffer *tb_ptr) template void -SimpleRename::setRenameQueue(TimeBuffer *rq_ptr) +DefaultRename::setRenameQueue(TimeBuffer *rq_ptr) { - DPRINTF(Rename, "Rename: Setting rename queue pointer.\n"); + DPRINTF(Rename, "Setting rename queue pointer.\n"); renameQueue = rq_ptr; // Setup wire to write information to future stages. @@ -147,9 +181,9 @@ SimpleRename::setRenameQueue(TimeBuffer *rq_ptr) template void -SimpleRename::setDecodeQueue(TimeBuffer *dq_ptr) +DefaultRename::setDecodeQueue(TimeBuffer *dq_ptr) { - DPRINTF(Rename, "Rename: Setting decode queue pointer.\n"); + DPRINTF(Rename, "Setting decode queue pointer.\n"); decodeQueue = dq_ptr; // Setup wire to get information from decode. @@ -158,214 +192,670 @@ SimpleRename::setDecodeQueue(TimeBuffer *dq_ptr) template void -SimpleRename::setRenameMap(RenameMap *rm_ptr) +DefaultRename::initStage() { - DPRINTF(Rename, "Rename: Setting rename map pointer.\n"); - renameMap = rm_ptr; + for (int tid=0; tid < numThreads; tid++) { + freeEntries[tid].iqEntries = iew_ptr->instQueue.numFreeEntries(tid); + freeEntries[tid].lsqEntries = iew_ptr->ldstQueue.numFreeEntries(tid); + freeEntries[tid].robEntries = commit_ptr->numROBFreeEntries(tid); + emptyROB[tid] = true; + } + + // Clear these pointers so they are not accidentally used in + // non-initialization code. + iew_ptr = NULL; + commit_ptr = NULL; +} + +template +void +DefaultRename::setActiveThreads(list *at_ptr) +{ + DPRINTF(Rename, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + + +template +void +DefaultRename::setRenameMap(RenameMap rm_ptr[]) +{ + DPRINTF(Rename, "Setting rename map pointers.\n"); + + for (int i=0; i void -SimpleRename::setFreeList(FreeList *fl_ptr) +DefaultRename::setFreeList(FreeList *fl_ptr) { - DPRINTF(Rename, "Rename: Setting free list pointer.\n"); + DPRINTF(Rename, "Setting free list pointer.\n"); freeList = fl_ptr; } +template +void +DefaultRename::setScoreboard(Scoreboard *_scoreboard) +{ + DPRINTF(Rename, "Setting scoreboard pointer.\n"); + scoreboard = _scoreboard; +} + template void -SimpleRename::dumpHistory() +DefaultRename::squash(unsigned tid) { - typename list::iterator buf_it = historyBuffer.begin(); + DPRINTF(Rename, "[tid:%u]: Squashing instructions.\n",tid); - while (buf_it != historyBuffer.end()) - { - cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys " - "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg, - (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg); + // Clear the stall signal if rename was blocked or unblocking before. + // If it still needs to block, the blocking should happen the next + // cycle and there should be space to hold everything due to the squash. + if (renameStatus[tid] == Blocked || + renameStatus[tid] == Unblocking || + renameStatus[tid] == BarrierStall) { +#if !FULL_SYSTEM + // In syscall emulation, we can have both a block and a squash due + // to a syscall in the same cycle. This would cause both signals to + // be high. This shouldn't happen in full system. + if (toDecode->renameBlock[tid]) { + toDecode->renameBlock[tid] = 0; + } else { + toDecode->renameUnblock[tid] = 1; + } +#else + toDecode->renameUnblock[tid] = 1; +#endif + barrierInst[tid] = NULL; + } - buf_it++; + // Set the status to Squashing. + renameStatus[tid] = Squashing; + + // Clear the skid buffer in case it has any data in it. + unsigned squashCount = 0; + + for (int i=0; isize; i++) { + if (fromDecode->insts[i]->threadNumber == tid) { + fromDecode->insts[i]->squashed = true; + wroteToTimeBuffer = true; + squashCount++; + } + } + + insts[tid].clear(); + + // Clear the skid buffer in case it has any data in it. + skidBuffer[tid].clear(); + + doSquash(tid); +} + +template +void +DefaultRename::tick() +{ + // Rename will need to try to rename as many instructions as it + // has bandwidth, unless it is blocked. + + wroteToTimeBuffer = false; + + blockThisCycle = false; + + bool status_change = false; + + toIEWIndex = 0; + + sortInsts(); + + list::iterator threads = (*activeThreads).begin(); + + // Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(Rename, "Processing [tid:%i]\n", tid); + + status_change = checkSignalsAndUpdate(tid) || status_change; + + rename(status_change, tid); + } + + if (status_change) { + updateStatus(); + } + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); + } + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // If we committed this cycle then doneSeqNum will be > 0 + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + renameStatus[tid] != Squashing) { + + removeFromHistory(fromCommit->commitInfo[tid].doneSeqNum, + tid); + } + } + + // @todo: make into updateProgress function + for (int tid=0; tid < numThreads; tid++) { + instsInProgress[tid] -= fromIEW->iewInfo[tid].dispatched; + + assert(instsInProgress[tid] >=0); + } + +} + +template +void +DefaultRename::rename(bool &status_change, unsigned tid) +{ + // If status is Running or idle, + // call renameInsts() + // If status is Unblocking, + // buffer any instructions coming from decode + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (renameStatus[tid] == Blocked) { + ++renameBlockCycles; + } else if (renameStatus[tid] == Squashing) { + ++renameSquashCycles; + } else if (renameStatus[tid] == BarrierStall) { + ++renameBarrierCycles; + } + + if (renameStatus[tid] == Running || + renameStatus[tid] == Idle) { + DPRINTF(Rename, "[tid:%u]: Not blocked, so attempting to run " + "stage.\n", tid); + + renameInsts(tid); + } else if (renameStatus[tid] == Unblocking) { + renameInsts(tid); + + ++renameUnblockCycles; + + if (validInsts()) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + // If we switched over to blocking, then there's a potential for + // an overall status change. + status_change = unblock(tid) || status_change || blockThisCycle; } } template void -SimpleRename::block() +DefaultRename::renameInsts(unsigned tid) { - DPRINTF(Rename, "Rename: Blocking.\n"); - // Set status to Blocked. - _status = Blocked; + // Instructions can be either in the skid buffer or the queue of + // instructions coming from decode, depending on the status. + int insts_available = renameStatus[tid] == Unblocking ? + skidBuffer[tid].size() : insts[tid].size(); + + // Check the decode queue to see if instructions are available. + // If there are no available instructions to rename, then do nothing. + if (insts_available == 0) { + DPRINTF(Rename, "[tid:%u]: Nothing to do, breaking out early.\n", + tid); + // Should I change status to idle? + ++renameIdleCycles; + return; + } else if (renameStatus[tid] == Unblocking) { + ++renameUnblockCycles; + } else if (renameStatus[tid] == Running) { + ++renameRunCycles; + } + + DynInstPtr inst; + + // Will have to do a different calculation for the number of free + // entries. + int free_rob_entries = calcFreeROBEntries(tid); + int free_iq_entries = calcFreeIQEntries(tid); + int free_lsq_entries = calcFreeLSQEntries(tid); + int min_free_entries = free_rob_entries; + + FullSource source = ROB; + + if (free_iq_entries < min_free_entries) { + min_free_entries = free_iq_entries; + source = IQ; + } + + if (free_lsq_entries < min_free_entries) { + min_free_entries = free_lsq_entries; + source = LSQ; + } + + // Check if there's any space left. + if (min_free_entries <= 0) { + DPRINTF(Rename, "[tid:%u]: Blocking due to no free ROB/IQ/LSQ " + "entries.\n" + "ROB has %i free entries.\n" + "IQ has %i free entries.\n" + "LSQ has %i free entries.\n", + tid, + free_rob_entries, + free_iq_entries, + free_lsq_entries); + + blockThisCycle = true; + + block(tid); + + incrFullStat(source); + + return; + } else if (min_free_entries < insts_available) { + DPRINTF(Rename, "[tid:%u]: Will have to block this cycle." + "%i insts available, but only %i insts can be " + "renamed due to ROB/IQ/LSQ limits.\n", + tid, insts_available, min_free_entries); + + insts_available = min_free_entries; + + blockThisCycle = true; + + incrFullStat(source); + } + + InstQueue &insts_to_rename = renameStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + DPRINTF(Rename, "[tid:%u]: %i available instructions to " + "send iew.\n", tid, insts_available); + + DPRINTF(Rename, "[tid:%u]: %i insts pipelining from Rename | %i insts " + "dispatched to IQ last cycle.\n", + tid, instsInProgress[tid], fromIEW->iewInfo[tid].dispatched); + + // Handle serializing the next instruction if necessary. + if (serializeOnNextInst[tid]) { + if (emptyROB[tid] && instsInProgress[tid] == 0) { + // ROB already empty; no need to serialize. + serializeOnNextInst[tid] = false; + } else if (!insts_to_rename.empty()) { + insts_to_rename.front()->setSerializeBefore(); + } + } + + int renamed_insts = 0; + + while (insts_available > 0 && toIEWIndex < renameWidth) { + DPRINTF(Rename, "[tid:%u]: Sending instructions to IEW.\n", tid); + + assert(!insts_to_rename.empty()); + + inst = insts_to_rename.front(); + + insts_to_rename.pop_front(); + + //Use skidBuffer with oldest instructions + if (renameStatus[tid] == Unblocking) { + DPRINTF(Rename,"[tid:%u]: Removing [sn:%lli] PC:%#x from rename " + "skidBuffer\n", + tid, inst->seqNum, inst->readPC()); + } + + if (inst->isSquashed()) { + DPRINTF(Rename, "[tid:%u]: instruction %i with PC %#x is " + "squashed, skipping.\n", + tid, inst->seqNum, inst->threadNumber,inst->readPC()); + + ++renameSquashedInsts; + + // Decrement how many instructions are available. + --insts_available; + + continue; + } + + DPRINTF(Rename, "[tid:%u]: Processing instruction [sn:%lli] with " + "PC %#x.\n", + tid, inst->seqNum, inst->readPC()); + + // Handle serializeAfter/serializeBefore instructions. + // serializeAfter marks the next instruction as serializeBefore. + // serializeBefore makes the instruction wait in rename until the ROB + // is empty. + if (inst->isSerializeBefore() && !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) + inst->setSerializeHandled(); + + // Change status over to BarrierStall so that other stages know + // what this is blocked on. + renameStatus[tid] = BarrierStall; + + barrierInst[tid] = inst; + + blockThisCycle = true; + + break; + } else if (inst->isSerializeAfter() && !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize after instruction encountered.\n"); + + inst->setSerializeHandled(); + + serializeAfter(insts_to_rename, tid); + } + + // Check here to make sure there are enough destination registers + // to rename to. Otherwise block. + if (renameMap[tid]->numFreeEntries() < inst->numDestRegs()) { + DPRINTF(Rename, "Blocking due to lack of free " + "physical registers to rename to.\n"); + blockThisCycle = true; + + ++renameFullRegistersEvents; + + break; + } + + renameSrcRegs(inst, inst->threadNumber); + + renameDestRegs(inst, inst->threadNumber); + + ++renamed_insts; + + // Put instruction in rename queue. + toIEW->insts[toIEWIndex] = inst; + ++(toIEW->size); + + // Increment which instruction we're on. + ++toIEWIndex; + + ++renameRenamedInsts; + + // Decrement how many instructions are available. + --insts_available; + } + + instsInProgress[tid] += renamed_insts; + + // If we wrote to the time buffer, record this. + if (toIEWIndex) { + wroteToTimeBuffer = true; + } + + // Check if there's any instructions left that haven't yet been renamed. + // If so then block. + if (insts_available) { + blockThisCycle = true; + } + + if (blockThisCycle) { + block(tid); + toDecode->renameUnblock[tid] = false; + } +} + +template +void +DefaultRename::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; + + while (!insts[tid].empty()) { + inst = insts[tid].front(); + + insts[tid].pop_front(); + + assert(tid == inst->threadNumber); + + DPRINTF(Rename, "[tid:%u]: Inserting [sn:%lli] PC:%#x into Rename " + "skidBuffer\n", tid, inst->seqNum, inst->readPC()); + + skidBuffer[tid].push_back(inst); + } + + if (skidBuffer[tid].size() > skidBufferMax) + panic("Skidbuffer Exceeded Max Size"); +} + +template +void +DefaultRename::sortInsts() +{ + int insts_from_decode = fromDecode->size; + + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); + + for (int i = 0; i < insts_from_decode; ++i) { + DynInstPtr inst = fromDecode->insts[i]; + insts[inst->threadNumber].push_back(inst); + } +} + +template +bool +DefaultRename::skidsEmpty() +{ + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template +void +DefaultRename::updateStatus() +{ + bool any_unblocking = false; + + list::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (renameStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } + } + + // Rename will have activity if it's unblocking. + if (any_unblocking) { + if (_status == Inactive) { + _status = Active; + + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(FullCPU::RenameIdx); + } + } else { + // If it's not unblocking, then rename will not have any internal + // activity. Switch it to inactive. + if (_status == Active) { + _status = Inactive; + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(FullCPU::RenameIdx); + } + } +} + +template +bool +DefaultRename::block(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Blocking.\n", tid); // Add the current inputs onto the skid buffer, so they can be // reprocessed when this stage unblocks. - skidBuffer.push(*fromDecode); + skidInsert(tid); - // Note that this stage only signals previous stages to stall when - // it is the cause of the stall originates at this stage. Otherwise - // the previous stages are expected to check all possible stall signals. + // Only signal backwards to block if the previous stages do not think + // rename is already blocked. + if (renameStatus[tid] != Blocked) { + if (renameStatus[tid] != Unblocking) { + toDecode->renameBlock[tid] = true; + toDecode->renameUnblock[tid] = false; + wroteToTimeBuffer = true; + } + + // Rename can not go from BarrierStall to Blocked, otherwise it would + // not know to complete the barrier stall. + if (renameStatus[tid] != BarrierStall) { + // Set status to Blocked. + renameStatus[tid] = Blocked; + return true; + } + } + + return false; } template -inline void -SimpleRename::unblock() +bool +DefaultRename::unblock(unsigned tid) { - DPRINTF(Rename, "Rename: Read instructions out of skid buffer this " - "cycle.\n"); - // Remove the now processed instructions from the skid buffer. - skidBuffer.pop(); + DPRINTF(Rename, "[tid:%u]: Trying to unblock.\n", tid); - // If there's still information in the skid buffer, then - // continue to tell previous stages to stall. They will be - // able to restart once the skid buffer is empty. - if (!skidBuffer.empty()) { - toDecode->renameInfo.stall = true; - } else { - DPRINTF(Rename, "Rename: Done unblocking.\n"); - _status = Running; + // Rename is done unblocking if the skid buffer is empty. + if (skidBuffer[tid].empty() && renameStatus[tid] != BarrierStall) { + + DPRINTF(Rename, "[tid:%u]: Done unblocking.\n", tid); + + toDecode->renameUnblock[tid] = true; + wroteToTimeBuffer = true; + + renameStatus[tid] = Running; + return true; } + + return false; } template void -SimpleRename::doSquash() +DefaultRename::doSquash(unsigned tid) { - typename list::iterator hb_it = historyBuffer.begin(); + typename list::iterator hb_it = historyBuffer[tid].begin(); - InstSeqNum squashed_seq_num = fromCommit->commitInfo.doneSeqNum; + InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum; -#if FULL_SYSTEM - assert(!historyBuffer.empty()); -#else +//#if FULL_SYSTEM +// assert(!historyBuffer[tid].empty()); +//#else // After a syscall squashes everything, the history buffer may be empty // but the ROB may still be squashing instructions. - if (historyBuffer.empty()) { + if (historyBuffer[tid].empty()) { return; } -#endif // FULL_SYSTEM +//#endif // FULL_SYSTEM // Go through the most recent instructions, undoing the mappings // they did and freeing up the registers. - while ((*hb_it).instSeqNum > squashed_seq_num) - { - assert(hb_it != historyBuffer.end()); + while (!historyBuffer[tid].empty() && + (*hb_it).instSeqNum > squashed_seq_num) { + assert(hb_it != historyBuffer[tid].end()); - DPRINTF(Rename, "Rename: Removing history entry with sequence " - "number %i.\n", (*hb_it).instSeqNum); + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", tid, (*hb_it).instSeqNum); - // If it's not simply a place holder, then add the registers. - if (!(*hb_it).placeHolder) { - // Tell the rename map to set the architected register to the - // previous physical register that it was renamed to. - renameMap->setEntry(hb_it->archReg, hb_it->prevPhysReg); + // Tell the rename map to set the architected register to the + // previous physical register that it was renamed to. + renameMap[tid]->setEntry(hb_it->archReg, hb_it->prevPhysReg); - // Put the renamed physical register back on the free list. - freeList->addReg(hb_it->newPhysReg); + // Put the renamed physical register back on the free list. + freeList->addReg(hb_it->newPhysReg); - ++renameValidUndoneMaps; - } - - historyBuffer.erase(hb_it++); + historyBuffer[tid].erase(hb_it++); ++renameUndoneMaps; } } -template -void -SimpleRename::squash() -{ - DPRINTF(Rename, "Rename: Squashing instructions.\n"); - // Set the status to Squashing. - _status = Squashing; - - numInst = 0; - - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) - { - skidBuffer.pop(); - } - - doSquash(); -} - template void -SimpleRename::removeFromHistory(InstSeqNum inst_seq_num) +DefaultRename::removeFromHistory(InstSeqNum inst_seq_num, unsigned tid) { - DPRINTF(Rename, "Rename: Removing a committed instruction from the " - "history buffer, until sequence number %lli.\n", inst_seq_num); - typename list::iterator hb_it = historyBuffer.end(); + DPRINTF(Rename, "[tid:%u]: Removing a committed instruction from the " + "history buffer %u (size=%i), until [sn:%lli].\n", + tid, tid, historyBuffer[tid].size(), inst_seq_num); + + typename list::iterator hb_it = historyBuffer[tid].end(); --hb_it; - if (hb_it->instSeqNum > inst_seq_num) { - DPRINTF(Rename, "Rename: Old sequence number encountered. Ensure " - "that a syscall happened recently.\n"); + if (historyBuffer[tid].empty()) { + DPRINTF(Rename, "[tid:%u]: History buffer is empty.\n", tid); + return; + } else if (hb_it->instSeqNum > inst_seq_num) { + DPRINTF(Rename, "[tid:%u]: Old sequence number encountered. Ensure " + "that a syscall happened recently.\n", tid); return; } - while ((*hb_it).instSeqNum != inst_seq_num) - { - // Make sure we haven't gone off the end of the list. - assert(hb_it != historyBuffer.end()); + // Commit all the renames up until (and including) the committed sequence + // number. Some or even all of the committed instructions may not have + // rename histories if they did not have destination registers that were + // renamed. + while (!historyBuffer[tid].empty() && + hb_it != historyBuffer[tid].end() && + (*hb_it).instSeqNum <= inst_seq_num) { - // In theory instructions at the end of the history buffer - // should be older than the instruction being removed, which - // means they will have a lower sequence number. Also the - // instruction being removed from the history really should - // be the last instruction in the list, as it is the instruction - // that was just committed that is being removed. - assert(hb_it->instSeqNum < inst_seq_num); - DPRINTF(Rename, "Rename: Freeing up older rename of reg %i, sequence" + DPRINTF(Rename, "[tid:%u]: Freeing up older rename of reg %i, sequence" " number %i.\n", - (*hb_it).prevPhysReg, (*hb_it).instSeqNum); + tid, (*hb_it).prevPhysReg, (*hb_it).instSeqNum); - if (!(*hb_it).placeHolder) { - freeList->addReg((*hb_it).prevPhysReg); - ++renameCommittedMaps; - } - - historyBuffer.erase(hb_it--); - } - - // Finally free up the previous register of the finished instruction - // itself. - if (!(*hb_it).placeHolder) { - freeList->addReg(hb_it->prevPhysReg); + freeList->addReg((*hb_it).prevPhysReg); ++renameCommittedMaps; - } - historyBuffer.erase(hb_it); + historyBuffer[tid].erase(hb_it--); + } } template inline void -SimpleRename::renameSrcRegs(DynInstPtr &inst) +DefaultRename::renameSrcRegs(DynInstPtr &inst,unsigned tid) { + assert(renameMap[tid] != 0); + unsigned num_src_regs = inst->numSrcRegs(); // Get the architectual register numbers from the source and // destination operands, and redirect them to the right register. // Will need to mark dependencies though. - for (int src_idx = 0; src_idx < num_src_regs; src_idx++) - { + for (int src_idx = 0; src_idx < num_src_regs; src_idx++) { RegIndex src_reg = inst->srcRegIdx(src_idx); // Look up the source registers to get the phys. register they've // been renamed to, and set the sources to those registers. - PhysRegIndex renamed_reg = renameMap->lookup(src_reg); + PhysRegIndex renamed_reg = renameMap[tid]->lookup(src_reg); - DPRINTF(Rename, "Rename: Looking up arch reg %i, got " - "physical reg %i.\n", (int)src_reg, (int)renamed_reg); + DPRINTF(Rename, "[tid:%u]: Looking up arch reg %i, got " + "physical reg %i.\n", tid, (int)src_reg, + (int)renamed_reg); inst->renameSrcReg(src_idx, renamed_reg); - // Either incorporate it into the info passed back, - // or make another function call to see if that register is - // ready or not. - if (renameMap->isReady(renamed_reg)) { - DPRINTF(Rename, "Rename: Register is ready.\n"); + // See if the register is ready or not. + if (scoreboard->getReg(renamed_reg) == true) { + DPRINTF(Rename, "[tid:%u]: Register is ready.\n", tid); inst->markSrcRegReady(src_idx); } @@ -376,379 +866,341 @@ SimpleRename::renameSrcRegs(DynInstPtr &inst) template inline void -SimpleRename::renameDestRegs(DynInstPtr &inst) +DefaultRename::renameDestRegs(DynInstPtr &inst,unsigned tid) { - typename SimpleRenameMap::RenameInfo rename_result; + typename RenameMap::RenameInfo rename_result; unsigned num_dest_regs = inst->numDestRegs(); - // If it's an instruction with no destination registers, then put - // a placeholder within the history buffer. It might be better - // to not put it in the history buffer at all (other than branches, - // which always need at least a place holder), and differentiate - // between instructions with and without destination registers - // when getting from commit the instructions that committed. - if (num_dest_regs == 0) { - RenameHistory hb_entry(inst->seqNum); + // Rename the destination registers. + for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++) { + RegIndex dest_reg = inst->destRegIdx(dest_idx); - historyBuffer.push_front(hb_entry); + // Get the physical register that the destination will be + // renamed to. + rename_result = renameMap[tid]->rename(dest_reg); - DPRINTF(Rename, "Rename: Adding placeholder instruction to " - "history buffer, sequence number %lli.\n", - inst->seqNum); + //Mark Scoreboard entry as not ready + scoreboard->unsetReg(rename_result.first); - ++renameHBPlaceHolders; - } else { + DPRINTF(Rename, "[tid:%u]: Renaming arch reg %i to physical " + "reg %i.\n", tid, (int)dest_reg, + (int)rename_result.first); - // Rename the destination registers. - for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++) - { - RegIndex dest_reg = inst->destRegIdx(dest_idx); + // Record the rename information so that a history can be kept. + RenameHistory hb_entry(inst->seqNum, dest_reg, + rename_result.first, + rename_result.second); - // Get the physical register that the destination will be - // renamed to. - rename_result = renameMap->rename(dest_reg); + historyBuffer[tid].push_front(hb_entry); - DPRINTF(Rename, "Rename: Renaming arch reg %i to physical " - "reg %i.\n", (int)dest_reg, - (int)rename_result.first); + DPRINTF(Rename, "[tid:%u]: Adding instruction to history buffer, " + "[sn:%lli].\n",tid, + (*historyBuffer[tid].begin()).instSeqNum); - // Record the rename information so that a history can be kept. - RenameHistory hb_entry(inst->seqNum, dest_reg, - rename_result.first, - rename_result.second); + // Tell the instruction to rename the appropriate destination + // register (dest_idx) to the new physical register + // (rename_result.first), and record the previous physical + // register that the same logical register was renamed to + // (rename_result.second). + inst->renameDestReg(dest_idx, + rename_result.first, + rename_result.second); - historyBuffer.push_front(hb_entry); - - DPRINTF(Rename, "Rename: Adding instruction to history buffer, " - "sequence number %lli.\n", - (*historyBuffer.begin()).instSeqNum); - - // Tell the instruction to rename the appropriate destination - // register (dest_idx) to the new physical register - // (rename_result.first), and record the previous physical - // register that the same logical register was renamed to - // (rename_result.second). - inst->renameDestReg(dest_idx, - rename_result.first, - rename_result.second); - - ++renameRenamedOperands; - } + ++renameRenamedOperands; } } template inline int -SimpleRename::calcFreeROBEntries() +DefaultRename::calcFreeROBEntries(unsigned tid) { - return fromCommit->commitInfo.freeROBEntries - - renameWidth * iewToRenameDelay; + int num_free = freeEntries[tid].robEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); + + //DPRINTF(Rename,"[tid:%i]: %i rob free\n",tid,num_free); + + return num_free; } template inline int -SimpleRename::calcFreeIQEntries() +DefaultRename::calcFreeIQEntries(unsigned tid) { - return fromIEW->iewInfo.freeIQEntries - renameWidth * iewToRenameDelay; + int num_free = freeEntries[tid].iqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); + + //DPRINTF(Rename,"[tid:%i]: %i iq free\n",tid,num_free); + + return num_free; +} + +template +inline int +DefaultRename::calcFreeLSQEntries(unsigned tid) +{ + int num_free = freeEntries[tid].lsqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatchedToLSQ); + + //DPRINTF(Rename,"[tid:%i]: %i lsq free\n",tid,num_free); + + return num_free; +} + +template +unsigned +DefaultRename::validInsts() +{ + unsigned inst_count = 0; + + for (int i=0; isize; i++) { + if (!fromDecode->insts[i]->squashed) + inst_count++; + } + + return inst_count; +} + +template +void +DefaultRename::readStallSignals(unsigned tid) +{ + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template +bool +DefaultRename::checkStall(unsigned tid) +{ + bool ret_val = false; + + if (stalls[tid].iew) { + DPRINTF(Rename,"[tid:%i]: Stall from IEW stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Rename,"[tid:%i]: Stall from Commit stage detected.\n", tid); + ret_val = true; + } else if (calcFreeROBEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: ROB has 0 free entries.\n", tid); + ret_val = true; + } else if (calcFreeIQEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: IQ has 0 free entries.\n", tid); + ret_val = true; + } else if (calcFreeLSQEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: LSQ has 0 free entries.\n", tid); + ret_val = true; + } else if (renameMap[tid]->numFreeEntries() <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: RenameMap has 0 free entries.\n", tid); + ret_val = true; + } else if (renameStatus[tid] == BarrierStall && + (!emptyROB[tid] || instsInProgress[tid])) { + DPRINTF(Rename,"[tid:%i]: Stall: Barrier stall and ROB is not " + "empty.\n", + tid); + ret_val = true; + } + + return ret_val; +} + +template +void +DefaultRename::readFreeEntries(unsigned tid) +{ + bool updated = false; + if (fromIEW->iewInfo[tid].usedIQ) { + freeEntries[tid].iqEntries = + fromIEW->iewInfo[tid].freeIQEntries; + updated = true; + } + + if (fromIEW->iewInfo[tid].usedLSQ) { + freeEntries[tid].lsqEntries = + fromIEW->iewInfo[tid].freeLSQEntries; + updated = true; + } + + if (fromCommit->commitInfo[tid].usedROB) { + freeEntries[tid].robEntries = + fromCommit->commitInfo[tid].freeROBEntries; + emptyROB[tid] = fromCommit->commitInfo[tid].emptyROB; + updated = true; + } + + DPRINTF(Rename, "[tid:%i]: Free IQ: %i, Free ROB: %i, Free LSQ: %i\n", + tid, + freeEntries[tid].iqEntries, + freeEntries[tid].robEntries, + freeEntries[tid].lsqEntries); + + DPRINTF(Rename, "[tid:%i]: %i instructions not yet in ROB\n", + tid, instsInProgress[tid]); +} + +template +bool +DefaultRename::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is + // Check stall signals, block if necessary. + // If status was blocked + // check if stall conditions have passed + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + // If status was barrier stall + // check if ROB is empty and no insts are in flight to the ROB + + readFreeEntries(tid); + readStallSignals(tid); + + if (fromCommit->commitInfo[tid].squash) { + DPRINTF(Rename, "[tid:%u]: Squashing instructions due to squash from " + "commit.\n", tid); + + squash(tid); + + return true; + } + + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Rename, "[tid:%u]: ROB is still squashing.\n", tid); + + renameStatus[tid] = Squashing; + + return true; + } + + if (checkStall(tid)) { + return block(tid); + } + + if (renameStatus[tid] == Blocked) { + DPRINTF(Rename, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); + + renameStatus[tid] = Unblocking; + + unblock(tid); + + return true; + } + + if (renameStatus[tid] == Squashing) { + // Switch status to running if rename isn't being told to block or + // squash this cycle. + DPRINTF(Rename, "[tid:%u]: Done squashing, switching to running.\n", + tid); + + renameStatus[tid] = Running; + + return false; + } + + if (renameStatus[tid] == BarrierStall) { + // Stall ends once the ROB is free. + DPRINTF(Rename, "[tid:%u]: Done with barrier stall, switching to " + "unblocking.\n", tid); + + DynInstPtr barr_inst = barrierInst[tid]; + + renameStatus[tid] = Unblocking; + + unblock(tid); + + DPRINTF(Rename, "[tid:%u]: Processing instruction [%lli] with " + "PC %#x.\n", + tid, barr_inst->seqNum, barr_inst->readPC()); + + // Put instruction into queue here. + barr_inst->clearSerializeBefore(); + + if (!skidBuffer[tid].empty()) { + skidBuffer[tid].push_front(barr_inst); + } else { + insts[tid].push_front(barr_inst); + } + + DPRINTF(Rename, "[tid:%u]: Instruction must be processed by rename." + " Adding to front of list.", tid); + + barrierInst[tid] = NULL; + + return true; + } + + // If we've reached this point, we have not gotten any signals that + // cause rename to change its status. Rename remains the same as before. + return false; } template void -SimpleRename::tick() +DefaultRename::serializeAfter(InstQueue &inst_list, + unsigned tid) { - // Rename will need to try to rename as many instructions as it - // has bandwidth, unless it is blocked. - - // Check if _status is BarrierStall. If so, then check if the number - // of free ROB entries is equal to the number of total ROB entries. - // Once equal then wake this stage up. Set status to unblocking maybe. - - if (_status != Blocked && _status != Squashing) { - DPRINTF(Rename, "Rename: Status is not blocked, will attempt to " - "run stage.\n"); - // Make sure that the skid buffer has something in it if the - // status is unblocking. - assert(_status == Unblocking ? !skidBuffer.empty() : 1); - - rename(); - - // If the status was unblocking, then instructions from the skid - // buffer were used. Remove those instructions and handle - // the rest of unblocking. - if (_status == Unblocking) { - ++renameUnblockCycles; - - if (fromDecode->size > 0) { - // Add the current inputs onto the skid buffer, so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromDecode); - } - - unblock(); - } - } else if (_status == Blocked) { - ++renameBlockCycles; - - // If stage is blocked and still receiving valid instructions, - // make sure to store them in the skid buffer. - if (fromDecode->size > 0) { - - block(); - - // Continue to tell previous stage to stall. - toDecode->renameInfo.stall = true; - } - - if (!fromIEW->iewInfo.stall && - !fromCommit->commitInfo.stall && - calcFreeROBEntries() > 0 && - calcFreeIQEntries() > 0 && - renameMap->numFreeEntries() > 0) { - - // Need to be sure to check all blocking conditions above. - // If they have cleared, then start unblocking. - DPRINTF(Rename, "Rename: Stall signals cleared, going to " - "unblock.\n"); - _status = Unblocking; - - // Continue to tell previous stage to block until this stage - // is done unblocking. - toDecode->renameInfo.stall = true; - } else { - // Otherwise no conditions have changed. Tell previous - // stage to continue blocking. - toDecode->renameInfo.stall = true; - } - - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - return; - } - } else if (_status == Squashing) { - ++renameSquashCycles; - - if (fromCommit->commitInfo.squash) { - squash(); - } else if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - - DPRINTF(Rename, "Rename: Done squashing, going to running.\n"); - _status = Running; - rename(); - } else { - doSquash(); - } - } - - // Ugly code, revamp all of the tick() functions eventually. - if (fromCommit->commitInfo.doneSeqNum != 0 && _status != Squashing) { -#if !FULL_SYSTEM - if (!fromCommit->commitInfo.squash) { - removeFromHistory(fromCommit->commitInfo.doneSeqNum); - } -#else - removeFromHistory(fromCommit->commitInfo.doneSeqNum); -#endif + if (inst_list.empty()) { + // Mark a bit to say that I must serialize on the next instruction. + serializeOnNextInst[tid] = true; + return; } + // Set the next instruction as serializing. + inst_list.front()->setSerializeBefore(); } -template +template +inline void +DefaultRename::incrFullStat(const FullSource &source) +{ + switch (source) { + case ROB: + ++renameROBFullEvents; + break; + case IQ: + ++renameIQFullEvents; + break; + case LSQ: + ++renameLSQFullEvents; + break; + default: + panic("Rename full stall stat should be incremented for a reason!"); + break; + } +} + +template void -SimpleRename::rename() +DefaultRename::dumpHistory() { - // Check if any of the stages ahead of rename are telling rename - // to squash. The squash() function will also take care of fixing up - // the rename map and the free list. - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - DPRINTF(Rename, "Rename: Receiving signal from Commit to squash.\n"); - squash(); - return; - } + typename list::iterator buf_it; - // Check if time buffer is telling this stage to stall. - if (fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) { - DPRINTF(Rename, "Rename: Receiving signal from IEW/Commit to " - "stall.\n"); - block(); - return; - } + for (int i = 0; i < numThreads; i++) { - // Check if the current status is squashing. If so, set its status - // to running and resume execution the next cycle. - if (_status == Squashing) { - DPRINTF(Rename, "Rename: Done squashing.\n"); - _status = Running; - return; - } + buf_it = historyBuffer[i].begin(); - // Check the decode queue to see if instructions are available. - // If there are no available instructions to rename, then do nothing. - // Or, if the stage is currently unblocking, then go ahead and run it. - if (fromDecode->size == 0 && _status != Unblocking) { - DPRINTF(Rename, "Rename: Nothing to do, breaking out early.\n"); - // Should I change status to idle? - return; - } + while (buf_it != historyBuffer[i].end()) { + cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys " + "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg, + (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg); - //////////////////////////////////// - // Actual rename part. - //////////////////////////////////// - - DynInstPtr inst; - - // If we're unblocking, then we may be in the middle of an instruction - // group. Subtract off numInst to get the proper number of instructions - // left. - int insts_available = _status == Unblocking ? - skidBuffer.front().size - numInst : - fromDecode->size; - - bool block_this_cycle = false; - - // Will have to do a different calculation for the number of free - // entries. Number of free entries recorded on this cycle - - // renameWidth * renameToDecodeDelay - int free_rob_entries = calcFreeROBEntries(); - int free_iq_entries = calcFreeIQEntries(); - int min_iq_rob = min(free_rob_entries, free_iq_entries); - - unsigned to_iew_index = 0; - - // Check if there's any space left. - if (min_iq_rob <= 0) { - DPRINTF(Rename, "Rename: Blocking due to no free ROB or IQ " - "entries.\n" - "Rename: ROB has %d free entries.\n" - "Rename: IQ has %d free entries.\n", - free_rob_entries, - free_iq_entries); - block(); - // Tell previous stage to stall. - toDecode->renameInfo.stall = true; - - if (free_rob_entries <= 0) { - ++renameROBFullEvents; - } else { - ++renameIQFullEvents; + buf_it++; } - - return; - } else if (min_iq_rob < insts_available) { - DPRINTF(Rename, "Rename: Will have to block this cycle. Only " - "%i insts can be renamed due to IQ/ROB limits.\n", - min_iq_rob); - - insts_available = min_iq_rob; - - block_this_cycle = true; - - if (free_rob_entries < free_iq_entries) { - ++renameROBFullEvents; - } else { - ++renameIQFullEvents; - } - } - - while (insts_available > 0) { - DPRINTF(Rename, "Rename: Sending instructions to iew.\n"); - - // Get the next instruction either from the skid buffer or the - // decode queue. - inst = _status == Unblocking ? skidBuffer.front().insts[numInst] : - fromDecode->insts[numInst]; - - if (inst->isSquashed()) { - DPRINTF(Rename, "Rename: instruction %i with PC %#x is " - "squashed, skipping.\n", - inst->seqNum, inst->readPC()); - - // Go to the next instruction. - ++numInst; - - ++renameSquashedInsts; - - // Decrement how many instructions are available. - --insts_available; - - continue; - } - - DPRINTF(Rename, "Rename: Processing instruction %i with PC %#x.\n", - inst->seqNum, inst->readPC()); - - // If it's a trap instruction, then it needs to wait here within - // rename until the ROB is empty. Needs a way to detect that the - // ROB is empty. Maybe an event? - // Would be nice if it could be avoided putting this into a - // specific stage and instead just put it into the AlphaFullCPU. - // Might not really be feasible though... - // (EXCB, TRAPB) - if (inst->isSerializing()) { - panic("Rename: Serializing instruction encountered.\n"); - DPRINTF(Rename, "Rename: Serializing instruction " - "encountered.\n"); - - // Change status over to BarrierStall so that other stages know - // what this is blocked on. - _status = BarrierStall; - - block_this_cycle = true; - - break; - } - - // Check here to make sure there are enough destination registers - // to rename to. Otherwise block. - if (renameMap->numFreeEntries() < inst->numDestRegs()) - { - DPRINTF(Rename, "Rename: Blocking due to lack of free " - "physical registers to rename to.\n"); - // Need some sort of event based on a register being freed. - - block_this_cycle = true; - - ++renameFullRegistersEvents; - - break; - } - - renameSrcRegs(inst); - - renameDestRegs(inst); - - // Put instruction in rename queue. - toIEW->insts[to_iew_index] = inst; - ++(toIEW->size); - - // Decrease the number of free ROB and IQ entries. - --free_rob_entries; - --free_iq_entries; - - // Increment which instruction we're on. - ++to_iew_index; - ++numInst; - - ++renameRenamedInsts; - - // Decrement how many instructions are available. - --insts_available; - } - - // Check if there's any instructions left that haven't yet been renamed. - // If so then block. - if (block_this_cycle) { - block(); - - toDecode->renameInfo.stall = true; - } else { - // If we had a successful rename and didn't have to exit early, then - // reset numInst so it will refer to the correct instruction on next - // run. - numInst = 0; } } diff --git a/cpu/o3/rename_map.cc b/cpu/o3/rename_map.cc index 10963f7de..8ba632e65 100644 --- a/cpu/o3/rename_map.cc +++ b/cpu/o3/rename_map.cc @@ -39,98 +39,94 @@ using namespace std; // determine if the register is a logical int, logical fp, physical int, // physical fp, etc. -SimpleRenameMap::SimpleRenameMap(unsigned _numLogicalIntRegs, - unsigned _numPhysicalIntRegs, - unsigned _numLogicalFloatRegs, - unsigned _numPhysicalFloatRegs, - unsigned _numMiscRegs, - RegIndex _intZeroReg, - RegIndex _floatZeroReg) - : numLogicalIntRegs(_numLogicalIntRegs), - numPhysicalIntRegs(_numPhysicalIntRegs), - numLogicalFloatRegs(_numLogicalFloatRegs), - numPhysicalFloatRegs(_numPhysicalFloatRegs), - numMiscRegs(_numMiscRegs), - intZeroReg(_intZeroReg), - floatZeroReg(_floatZeroReg) +SimpleRenameMap::~SimpleRenameMap() { - DPRINTF(Rename, "Rename: Creating rename map. Phys: %i / %i, Float: " - "%i / %i.\n", numLogicalIntRegs, numPhysicalIntRegs, + // Delete the rename maps as they were allocated with new. + //delete [] intRenameMap; + //delete [] floatRenameMap; +} + +void +SimpleRenameMap::init(unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + PhysRegIndex &ireg_idx, + + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + PhysRegIndex &freg_idx, + + unsigned _numMiscRegs, + + RegIndex _intZeroReg, + RegIndex _floatZeroReg, + + int map_id, + bool bindRegs) +{ + id = map_id; + + numLogicalIntRegs = _numLogicalIntRegs; + + numLogicalFloatRegs = _numLogicalFloatRegs; + + numPhysicalIntRegs = _numPhysicalIntRegs; + + numPhysicalFloatRegs = _numPhysicalFloatRegs; + + numMiscRegs = _numMiscRegs; + + intZeroReg = _intZeroReg; + floatZeroReg = _floatZeroReg; + + DPRINTF(Rename, "Creating rename map %i. Phys: %i / %i, Float: " + "%i / %i.\n", id, numLogicalIntRegs, numPhysicalIntRegs, numLogicalFloatRegs, numPhysicalFloatRegs); numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs; numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs; - //Create the rename maps, and their scoreboards. - intRenameMap = new RenameEntry[numLogicalIntRegs]; - floatRenameMap = new RenameEntry[numLogicalRegs]; + //Create the rename maps + intRenameMap.resize(numLogicalIntRegs); + floatRenameMap.resize(numLogicalRegs); - // Should combine this into one scoreboard. - intScoreboard.resize(numPhysicalIntRegs); - floatScoreboard.resize(numPhysicalRegs); - miscScoreboard.resize(numPhysicalRegs + numMiscRegs); + if (bindRegs) { + DPRINTF(Rename, "Binding registers into rename map %i",id); - // Initialize the entries in the integer rename map to point to the - // physical registers of the same index, and consider each register - // ready until the first rename occurs. - for (RegIndex index = 0; index < numLogicalIntRegs; ++index) - { - intRenameMap[index].physical_reg = index; - intScoreboard[index] = 1; + // Initialize the entries in the integer rename map to point to the + // physical registers of the same index + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = ireg_idx++; + } + + // Initialize the entries in the floating point rename map to point to + // the physical registers of the same index + // Although the index refers purely to architected registers, because + // the floating reg indices come after the integer reg indices, they + // may exceed the size of a normal RegIndex (short). + for (PhysRegIndex index = numLogicalIntRegs; index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = freg_idx++; + } + } else { + DPRINTF(Rename, "Binding registers into rename map %i",id); + + PhysRegIndex temp_ireg = ireg_idx; + + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = temp_ireg++; + } + + PhysRegIndex temp_freg = freg_idx; + + for (PhysRegIndex index = numLogicalIntRegs; + index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = temp_freg++; + } } - - // Initialize the rest of the physical registers (the ones that don't - // directly map to a logical register) as unready. - for (PhysRegIndex index = numLogicalIntRegs; - index < numPhysicalIntRegs; - ++index) - { - intScoreboard[index] = 0; - } - - int float_reg_idx = numPhysicalIntRegs; - - // Initialize the entries in the floating point rename map to point to - // the physical registers of the same index, and consider each register - // ready until the first rename occurs. - // Although the index refers purely to architected registers, because - // the floating reg indices come after the integer reg indices, they - // may exceed the size of a normal RegIndex (short). - for (PhysRegIndex index = numLogicalIntRegs; - index < numLogicalRegs; ++index) - { - floatRenameMap[index].physical_reg = float_reg_idx++; - } - - for (PhysRegIndex index = numPhysicalIntRegs; - index < numPhysicalIntRegs + numLogicalFloatRegs; ++index) - { - floatScoreboard[index] = 1; - } - - // Initialize the rest of the physical registers (the ones that don't - // directly map to a logical register) as unready. - for (PhysRegIndex index = numPhysicalIntRegs + numLogicalFloatRegs; - index < numPhysicalRegs; - ++index) - { - floatScoreboard[index] = 0; - } - - // Initialize the entries in the misc register scoreboard to be ready. - for (PhysRegIndex index = numPhysicalRegs; - index < numPhysicalRegs + numMiscRegs; ++index) - { - miscScoreboard[index] = 1; - } -} - -SimpleRenameMap::~SimpleRenameMap() -{ - // Delete the rename maps as they were allocated with new. - delete [] intRenameMap; - delete [] floatRenameMap; } void @@ -167,8 +163,6 @@ SimpleRenameMap::rename(RegIndex arch_reg) assert(renamed_reg >= 0 && renamed_reg < numPhysicalIntRegs); - // Mark register as not ready. - intScoreboard[renamed_reg] = false; } else { // Otherwise return the zero register so nothing bad happens. renamed_reg = intZeroReg; @@ -192,9 +186,6 @@ SimpleRenameMap::rename(RegIndex arch_reg) assert(renamed_reg < numPhysicalRegs && renamed_reg >= numPhysicalIntRegs); - - // Mark register as not ready. - floatScoreboard[renamed_reg] = false; } else { // Otherwise return the zero register so nothing bad happens. renamed_reg = floatZeroReg; @@ -215,8 +206,6 @@ SimpleRenameMap::rename(RegIndex arch_reg) prev_reg = renamed_reg; assert(renamed_reg < numPhysicalRegs + numMiscRegs); - - miscScoreboard[renamed_reg] = false; } return RenameInfo(renamed_reg, prev_reg); @@ -244,25 +233,6 @@ SimpleRenameMap::lookup(RegIndex arch_reg) } } -bool -SimpleRenameMap::isReady(PhysRegIndex phys_reg) -{ - if (phys_reg < numPhysicalIntRegs) { - return intScoreboard[phys_reg]; - } else if (phys_reg < numPhysicalRegs) { - - // Subtract off the base FP offset. -// phys_reg = phys_reg - numPhysicalIntRegs; - - return floatScoreboard[phys_reg]; - } else { - // Subtract off the misc registers offset. -// phys_reg = phys_reg - numPhysicalRegs; - - return miscScoreboard[phys_reg]; - } -} - // In this implementation the miscellaneous registers do not actually rename, // so this function does not allow you to try to change their mappings. void @@ -273,14 +243,16 @@ SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg) (int)arch_reg, renamed_reg); intRenameMap[arch_reg].physical_reg = renamed_reg; - } else { - assert(arch_reg < (numLogicalIntRegs + numLogicalFloatRegs)); + } else if (arch_reg < numLogicalIntRegs + numLogicalFloatRegs) { + DPRINTF(Rename, "Rename Map: Float register %i being set to %i.\n", (int)arch_reg - numLogicalIntRegs, renamed_reg); floatRenameMap[arch_reg].physical_reg = renamed_reg; } + + //assert(arch_reg < (numLogicalIntRegs + numLogicalFloatRegs)); } void @@ -308,30 +280,6 @@ SimpleRenameMap::squash(vector freed_regs, // Take unmap info and roll back the rename map. } -void -SimpleRenameMap::markAsReady(PhysRegIndex ready_reg) -{ - DPRINTF(Rename, "Rename map: Marking register %i as ready.\n", - (int)ready_reg); - - if (ready_reg < numPhysicalIntRegs) { - assert(ready_reg >= 0); - - intScoreboard[ready_reg] = 1; - } else if (ready_reg < numPhysicalRegs) { - - // Subtract off the base FP offset. -// ready_reg = ready_reg - numPhysicalIntRegs; - - floatScoreboard[ready_reg] = 1; - } else { - //Subtract off the misc registers offset. -// ready_reg = ready_reg - numPhysicalRegs; - - miscScoreboard[ready_reg] = 1; - } -} - int SimpleRenameMap::numFreeEntries() { diff --git a/cpu/o3/rename_map.hh b/cpu/o3/rename_map.hh index 57be4a64a..3ecbe45c3 100644 --- a/cpu/o3/rename_map.hh +++ b/cpu/o3/rename_map.hh @@ -30,8 +30,8 @@ // Have it so that there's a more meaningful name given to the variable // that marks the beginning of the FP registers. -#ifndef __CPU_O3_CPU_RENAME_MAP_HH__ -#define __CPU_O3_CPU_RENAME_MAP_HH__ +#ifndef __CPU_O3_RENAME_MAP_HH__ +#define __CPU_O3_RENAME_MAP_HH__ #include #include @@ -63,17 +63,27 @@ class SimpleRenameMap public: //Constructor - SimpleRenameMap(unsigned _numLogicalIntRegs, - unsigned _numPhysicalIntRegs, - unsigned _numLogicalFloatRegs, - unsigned _numPhysicalFloatRegs, - unsigned _numMiscRegs, - RegIndex _intZeroReg, - RegIndex _floatZeroReg); + SimpleRenameMap() {}; /** Destructor. */ ~SimpleRenameMap(); + void init(unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + PhysRegIndex &_int_reg_start, + + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + PhysRegIndex &_float_reg_start, + + unsigned _numMiscRegs, + + RegIndex _intZeroReg, + RegIndex _floatZeroReg, + + int id, + bool bindRegs); + void setFreeList(SimpleFreeList *fl_ptr); //Tell rename map to get a free physical register for a given @@ -84,15 +94,11 @@ class SimpleRenameMap PhysRegIndex lookup(RegIndex phys_reg); - bool isReady(PhysRegIndex arch_reg); - /** * Marks the given register as ready, meaning that its value has been * calculated and written to the register file. * @param ready_reg The index of the physical register that is now ready. */ - void markAsReady(PhysRegIndex ready_reg); - void setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg); void squash(std::vector freed_regs, @@ -101,6 +107,9 @@ class SimpleRenameMap int numFreeEntries(); private: + /** Rename Map ID */ + int id; + /** Number of logical integer registers. */ int numLogicalIntRegs; @@ -143,31 +152,17 @@ class SimpleRenameMap { } }; + //Change this to private + public: /** Integer rename map. */ - RenameEntry *intRenameMap; + std::vector intRenameMap; /** Floating point rename map. */ - RenameEntry *floatRenameMap; + std::vector floatRenameMap; + private: /** Free list interface. */ SimpleFreeList *freeList; - - // Might want to make all these scoreboards into one large scoreboard. - - /** Scoreboard of physical integer registers, saying whether or not they - * are ready. - */ - std::vector intScoreboard; - - /** Scoreboard of physical floating registers, saying whether or not they - * are ready. - */ - std::vector floatScoreboard; - - /** Scoreboard of miscellaneous registers, saying whether or not they - * are ready. - */ - std::vector miscScoreboard; }; -#endif //__CPU_O3_CPU_RENAME_MAP_HH__ +#endif //__CPU_O3_RENAME_MAP_HH__ diff --git a/cpu/o3/rob.hh b/cpu/o3/rob.hh index 1185564ad..48199915f 100644 --- a/cpu/o3/rob.hh +++ b/cpu/o3/rob.hh @@ -26,23 +26,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Todo: Probably add in support for scheduling events (more than one as -// well) on the case of the ROB being empty or full. Considering tracking -// free entries instead of insts in ROB. Differentiate between squashing -// all instructions after the instruction, and all instructions after *and* -// including that instruction. - -#ifndef __CPU_O3_CPU_ROB_HH__ -#define __CPU_O3_CPU_ROB_HH__ +#ifndef __CPU_O3_ROB_HH__ +#define __CPU_O3_ROB_HH__ +#include #include #include /** - * ROB class. Uses the instruction list that exists within the CPU to - * represent the ROB. This class doesn't contain that list, but instead - * a pointer to the CPU to get access to the list. The ROB, in this first - * implementation, is largely what drives squashing. + * ROB class. The ROB is largely what drives squashing. */ template class ROB @@ -54,16 +46,45 @@ class ROB typedef typename Impl::FullCPU FullCPU; typedef typename Impl::DynInstPtr DynInstPtr; - typedef std::pair UnmapInfo_t; - typedef typename list::iterator InstIt_t; + typedef std::pair UnmapInfo; + typedef typename std::list::iterator InstIt; + + /** Possible ROB statuses. */ + enum Status { + Running, + Idle, + ROBSquashing, + DcacheMissStall, + DcacheMissComplete + }; + + /** SMT ROB Sharing Policy */ + enum ROBPolicy{ + Dynamic, + Partitioned, + Threshold + }; + + private: + /** Per-thread ROB status. */ + Status robStatus[Impl::MaxThreads]; + + /** ROB resource sharing policy for SMT mode. */ + ROBPolicy robPolicy; public: /** ROB constructor. - * @param _numEntries Number of entries in ROB. - * @param _squashWidth Number of instructions that can be squashed in a - * single cycle. + * @param _numEntries Number of entries in ROB. + * @param _squashWidth Number of instructions that can be squashed in a + * single cycle. + * @param _smtROBPolicy ROB Partitioning Scheme for SMT. + * @param _smtROBThreshold Max Resources(by %) a thread can have in the ROB. + * @param _numThreads The number of active threads. */ - ROB(unsigned _numEntries, unsigned _squashWidth); + ROB(unsigned _numEntries, unsigned _squashWidth, std::string smtROBPolicy, + unsigned _smtROBThreshold, unsigned _numThreads); + + std::string name() const; /** Function to set the CPU pointer, necessary due to which object the ROB * is created within. @@ -71,12 +92,15 @@ class ROB */ void setCPU(FullCPU *cpu_ptr); - /** Function to insert an instruction into the ROB. The parameter inst is - * not truly required, but is useful for checking correctness. Note - * that whatever calls this function must ensure that there is enough - * space within the ROB for the new instruction. + /** Sets pointer to the list of active threads. + * @param at_ptr Pointer to the list of active threads. + */ + void setActiveThreads(std::list* at_ptr); + + /** Function to insert an instruction into the ROB. Note that whatever + * calls this function must ensure that there is enough space within the + * ROB for the new instruction. * @param inst The instruction being inserted into the ROB. - * @todo Remove the parameter once correctness is ensured. */ void insertInst(DynInstPtr &inst); @@ -84,40 +108,134 @@ class ROB * no guarantee as to the return value if the ROB is empty. * @retval Pointer to the DynInst that is at the head of the ROB. */ - DynInstPtr readHeadInst() { return cpu->instList.front(); } + DynInstPtr readHeadInst(); - DynInstPtr readTailInst() { return (*tail); } + /** Returns a pointer to the head instruction of a specific thread within + * the ROB. + * @return Pointer to the DynInst that is at the head of the ROB. + */ + DynInstPtr readHeadInst(unsigned tid); + /** Returns pointer to the tail instruction within the ROB. There is + * no guarantee as to the return value if the ROB is empty. + * @retval Pointer to the DynInst that is at the tail of the ROB. + */ + DynInstPtr readTailInst(); + + /** Returns a pointer to the tail instruction of a specific thread within + * the ROB. + * @return Pointer to the DynInst that is at the tail of the ROB. + */ + DynInstPtr readTailInst(unsigned tid); + + /** Retires the head instruction, removing it from the ROB. */ void retireHead(); + /** Retires the head instruction of a specific thread, removing it from the + * ROB. + */ + void retireHead(unsigned tid); + + /** Is the oldest instruction across all threads ready. */ bool isHeadReady(); + /** Is the oldest instruction across a particular thread ready. */ + bool isHeadReady(unsigned tid); + + /** Is there any commitable head instruction across all threads ready. */ + bool canCommit(); + + /** Re-adjust ROB partitioning. */ + void resetEntries(); + + /** Number of entries needed For 'num_threads' amount of threads. */ + int entryAmount(int num_threads); + + /** Returns the number of total free entries in the ROB. */ unsigned numFreeEntries(); + /** Returns the number of free entries in a specific ROB paritition. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns the maximum number of entries for a specific thread. */ + unsigned getMaxEntries(unsigned tid) + { return maxEntries[tid]; } + + /** Returns the number of entries being used by a specific thread. */ + unsigned getThreadEntries(unsigned tid) + { return threadEntries[tid]; } + + /** Returns if the ROB is full. */ bool isFull() { return numInstsInROB == numEntries; } + /** Returns if a specific thread's partition is full. */ + bool isFull(unsigned tid) + { return threadEntries[tid] == numEntries; } + + /** Returns if the ROB is empty. */ bool isEmpty() { return numInstsInROB == 0; } - void doSquash(); + /** Returns if a specific thread's partition is empty. */ + bool isEmpty(unsigned tid) + { return threadEntries[tid] == 0; } - void squash(InstSeqNum squash_num); + /** Executes the squash, marking squashed instructions. */ + void doSquash(unsigned tid); + /** Squashes all instructions younger than the given sequence number for + * the specific thread. + */ + void squash(InstSeqNum squash_num, unsigned tid); + + /** Updates the head instruction with the new oldest instruction. */ + void updateHead(); + + /** Updates the tail instruction with the new youngest instruction. */ + void updateTail(); + + /** Reads the PC of the oldest head instruction. */ uint64_t readHeadPC(); + /** Reads the PC of the head instruction of a specific thread. */ + uint64_t readHeadPC(unsigned tid); + + /** Reads the next PC of the oldest head instruction. */ uint64_t readHeadNextPC(); + /** Reads the next PC of the head instruction of a specific thread. */ + uint64_t readHeadNextPC(unsigned tid); + + /** Reads the sequence number of the oldest head instruction. */ InstSeqNum readHeadSeqNum(); + /** Reads the sequence number of the head instruction of a specific thread. + */ + InstSeqNum readHeadSeqNum(unsigned tid); + + /** Reads the PC of the youngest tail instruction. */ uint64_t readTailPC(); + /** Reads the PC of the tail instruction of a specific thread. */ + uint64_t readTailPC(unsigned tid); + + /** Reads the sequence number of the youngest tail instruction. */ InstSeqNum readTailSeqNum(); + /** Reads the sequence number of tail instruction of a specific thread. */ + InstSeqNum readTailSeqNum(unsigned tid); + /** Checks if the ROB is still in the process of squashing instructions. * @retval Whether or not the ROB is done squashing. */ - bool isDoneSquashing() const { return doneSquashing; } + bool isDoneSquashing(unsigned tid) const + { return doneSquashing[tid]; } + + /** Checks if the ROB is still in the process of squashing instructions for + * any thread. + */ + bool isDoneSquashing(); /** This is more of a debugging function than anything. Use * numInstsInROB to get the instructions in the ROB unless you are @@ -125,23 +243,46 @@ class ROB */ int countInsts(); - private: + /** This is more of a debugging function than anything. Use + * threadEntries to get the instructions in the ROB unless you are + * double checking that variable. + */ + int countInsts(unsigned tid); + private: /** Pointer to the CPU. */ FullCPU *cpu; + /** Active Threads in CPU */ + std::list* activeThreads; + /** Number of instructions in the ROB. */ unsigned numEntries; + /** Entries Per Thread */ + unsigned threadEntries[Impl::MaxThreads]; + + /** Max Insts a Thread Can Have in the ROB */ + unsigned maxEntries[Impl::MaxThreads]; + + /** ROB List of Instructions */ + std::list instList[Impl::MaxThreads]; + /** Number of instructions that can be squashed in a single cycle. */ unsigned squashWidth; + public: /** Iterator pointing to the instruction which is the last instruction * in the ROB. This may at times be invalid (ie when the ROB is empty), * however it should never be incorrect. */ - InstIt_t tail; + InstIt tail; + /** Iterator pointing to the instruction which is the first instruction in + * in the ROB*/ + InstIt head; + + private: /** Iterator used for walking through the list of instructions when * squashing. Used so that there is persistent state between cycles; * when squashing, the instructions are marked as squashed but not @@ -149,16 +290,23 @@ class ROB * and after a squash. * This will always be set to cpu->instList.end() if it is invalid. */ - InstIt_t squashIt; + InstIt squashIt[Impl::MaxThreads]; + public: /** Number of instructions in the ROB. */ int numInstsInROB; + DynInstPtr dummyInst; + + private: /** The sequence number of the squashed instruction. */ InstSeqNum squashedSeqNum; /** Is the ROB done squashing. */ - bool doneSquashing; + bool doneSquashing[Impl::MaxThreads]; + + /** Number of active threads. */ + unsigned numThreads; }; -#endif //__CPU_O3_CPU_ROB_HH__ +#endif //__CPU_O3_ROB_HH__ diff --git a/cpu/o3/rob_impl.hh b/cpu/o3/rob_impl.hh index e7a5671d9..96d907cda 100644 --- a/cpu/o3/rob_impl.hh +++ b/cpu/o3/rob_impl.hh @@ -26,20 +26,74 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_ROB_IMPL_HH__ -#define __CPU_O3_CPU_ROB_IMPL_HH__ - #include "config/full_system.hh" #include "cpu/o3/rob.hh" +using namespace std; + template -ROB::ROB(unsigned _numEntries, unsigned _squashWidth) +ROB::ROB(unsigned _numEntries, unsigned _squashWidth, + string _smtROBPolicy, unsigned _smtROBThreshold, + unsigned _numThreads) : numEntries(_numEntries), squashWidth(_squashWidth), numInstsInROB(0), - squashedSeqNum(0) + squashedSeqNum(0), + numThreads(_numThreads) { - doneSquashing = true; + for (int tid=0; tid < numThreads; tid++) { + doneSquashing[tid] = true; + threadEntries[tid] = 0; + } + + string policy = _smtROBPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out rob policy + if (policy == "dynamic") { + robPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=numEntries; + } + + } else if (policy == "partitioned") { + robPolicy = Partitioned; + DPRINTF(Fetch, "ROB sharing policy set to Partitioned\n"); + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=part_amt; + } + + } else if (policy == "threshold") { + robPolicy = Threshold; + DPRINTF(Fetch, "ROB sharing policy set to Threshold\n"); + + int threshold = _smtROBThreshold;; + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=threshold; + } + } else { + assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +} + +template +std::string +ROB::name() const +{ + return cpu->name() + ".rob"; } template @@ -48,49 +102,74 @@ ROB::setCPU(FullCPU *cpu_ptr) { cpu = cpu_ptr; - // Set the tail to the beginning of the CPU instruction list so that - // upon the first instruction being inserted into the ROB, the tail - // iterator can simply be incremented. - tail = cpu->instList.begin(); + // Set the per-thread iterators to the end of the instruction list. + for (int i=0; i < numThreads;i++) { + squashIt[i] = instList[i].end(); + } - // Set the squash iterator to the end of the instruction list. - squashIt = cpu->instList.end(); + // Initialize the "universal" ROB head & tail point to invalid + // pointers + head = instList[0].end(); + tail = instList[0].end(); +} + +template +void +ROB::setActiveThreads(list *at_ptr) +{ + DPRINTF(ROB, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + + +template +void +ROB::resetEntries() +{ + if (robPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list::iterator threads = (*activeThreads).begin(); + list::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (robPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if (robPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} + +template +int +ROB::entryAmount(int num_threads) +{ + if (robPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } } template int ROB::countInsts() { - // Start at 1; if the tail matches cpu->instList.begin(), then there is - // one inst in the ROB. - int return_val = 1; + int total=0; - // There are quite a few special cases. Do not use this function other - // than for debugging purposes. - if (cpu->instList.begin() == cpu->instList.end()) { - // In this case there are no instructions in the list. The ROB - // must be empty. - return 0; - } else if (tail == cpu->instList.end()) { - // In this case, the tail is not yet pointing to anything valid. - // The ROB must be empty. - return 0; - } + for (int i=0;i < numThreads;i++) + total += countInsts(i); - // Iterate through the ROB from the head to the tail, counting the - // entries. - for (InstIt_t i = cpu->instList.begin(); i != tail; ++i) - { - assert(i != cpu->instList.end()); - ++return_val; - } + return total; +} - return return_val; - - // Because the head won't be tracked properly until the ROB gets the - // first instruction, and any time that the ROB is empty and has not - // yet gotten the instruction, this function doesn't work. -// return numInstsInROB; +template +int +ROB::countInsts(unsigned tid) +{ + return instList[tid].size(); } template @@ -98,33 +177,42 @@ void ROB::insertInst(DynInstPtr &inst) { // Make sure we have the right number of instructions. - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); + // Make sure the instruction is valid. assert(inst); - DPRINTF(ROB, "ROB: Adding inst PC %#x to the ROB.\n", inst->readPC()); + DPRINTF(ROB, "Adding inst PC %#x to the ROB.\n", inst->readPC()); // If the ROB is full then exit. assert(numInstsInROB != numEntries); - ++numInstsInROB; + int tid = inst->threadNumber; - // Increment the tail iterator, moving it one instruction back. - // There is a special case if the ROB was empty prior to this insertion, - // in which case the tail will be pointing at instList.end(). If that - // happens, then reset the tail to the beginning of the list. - if (tail != cpu->instList.end()) { - ++tail; - } else { - tail = cpu->instList.begin(); + // Place into ROB + instList[tid].push_back(inst); + + //Set Up head iterator if this is the 1st instruction in the ROB + if (numInstsInROB == 0) { + head = instList[tid].begin(); + assert((*head) == inst); } - // Make sure the tail iterator is actually pointing at the instruction - // added. + //Must Decrement for iterator to actually be valid since __.end() + //actually points to 1 after the last inst + tail = instList[tid].end(); + tail--; + + // Mark as set in ROB + inst->setInROB(); + + // Increment ROB count + ++numInstsInROB; + ++threadEntries[tid]; + assert((*tail) == inst); - DPRINTF(ROB, "ROB: Now has %d instructions.\n", numInstsInROB); - + DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]); } // Whatever calls this function needs to ensure that it properly frees up @@ -133,31 +221,55 @@ template void ROB::retireHead() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); + assert(numInstsInROB > 0); + + // Get the head ROB instruction's TID. + int tid = (*head)->threadNumber; + + retireHead(tid); + + if (numInstsInROB == 0) { + tail = instList[tid].end(); + } +} + +template +void +ROB::retireHead(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); assert(numInstsInROB > 0); // Get the head ROB instruction. - DynInstPtr head_inst = cpu->instList.front(); + InstIt head_it = instList[tid].begin(); + + DynInstPtr head_inst = (*head_it); // Make certain this can retire. assert(head_inst->readyToCommit()); - DPRINTF(ROB, "ROB: Retiring head instruction of the ROB, " - "instruction PC %#x, seq num %i\n", head_inst->readPC(), + DPRINTF(ROB, "[tid:%u]: Retiring head instruction, " + "instruction PC %#x,[sn:%lli]\n", tid, head_inst->readPC(), head_inst->seqNum); // Keep track of how many instructions are in the ROB. --numInstsInROB; + --threadEntries[tid]; + + //Mark DynInstFlags + head_inst->removeInROB(); + head_inst->setCommitted(); + + instList[tid].erase(head_it); + + //Update "Global" Head of ROB + updateHead(); - // Tell CPU to remove the instruction from the list of instructions. // A special case is needed if the instruction being retired is the // only instruction in the ROB; otherwise the tail iterator will become // invalidated. cpu->removeFrontInst(head_inst); - - if (numInstsInROB == 0) { - tail = cpu->instList.end(); - } } template @@ -165,7 +277,36 @@ bool ROB::isHeadReady() { if (numInstsInROB != 0) { - return cpu->instList.front()->readyToCommit(); + return (*head)->readyToCommit(); + } + + return false; +} + +template +bool +ROB::isHeadReady(unsigned tid) +{ + if (threadEntries[tid] != 0) { + return instList[tid].front()->readyToCommit(); + } + + return false; +} + +template +bool +ROB::canCommit() +{ + //@todo: set ActiveThreads through ROB or CPU + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (isHeadReady(tid)) { + return true; + } } return false; @@ -175,130 +316,339 @@ template unsigned ROB::numFreeEntries() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); return numEntries - numInstsInROB; } template -void -ROB::doSquash() +unsigned +ROB::numFreeEntries(unsigned tid) { - DPRINTF(ROB, "ROB: Squashing instructions.\n"); - - assert(squashIt != cpu->instList.end()); - - for (int numSquashed = 0; - numSquashed < squashWidth && (*squashIt)->seqNum != squashedSeqNum; - ++numSquashed) - { - // Ensure that the instruction is younger. - assert((*squashIt)->seqNum > squashedSeqNum); - - DPRINTF(ROB, "ROB: Squashing instruction PC %#x, seq num %i.\n", - (*squashIt)->readPC(), (*squashIt)->seqNum); - - // Mark the instruction as squashed, and ready to commit so that - // it can drain out of the pipeline. - (*squashIt)->setSquashed(); - - (*squashIt)->setCanCommit(); - - // Special case for when squashing due to a syscall. It's possible - // that the squash happened after the head instruction was already - // committed, meaning that (*squashIt)->seqNum != squashedSeqNum - // will never be false. Normally the squash would never be able - // to go past the head of the ROB; in this case it might, so it - // must be handled otherwise it will segfault. -#if !FULL_SYSTEM - if (squashIt == cpu->instList.begin()) { - DPRINTF(ROB, "ROB: Reached head of instruction list while " - "squashing.\n"); - - squashIt = cpu->instList.end(); - - doneSquashing = true; - - return; - } -#endif - - // Move the tail iterator to the next instruction. - squashIt--; - } - - - // Check if ROB is done squashing. - if ((*squashIt)->seqNum == squashedSeqNum) { - DPRINTF(ROB, "ROB: Done squashing instructions.\n"); - - squashIt = cpu->instList.end(); - - doneSquashing = true; - } + return maxEntries[tid] - threadEntries[tid]; } template void -ROB::squash(InstSeqNum squash_num) +ROB::doSquash(unsigned tid) { - DPRINTF(ROB, "ROB: Starting to squash within the ROB.\n"); - doneSquashing = false; + DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n", + tid, squashedSeqNum); + + assert(squashIt[tid] != instList[tid].end()); + + if ((*squashIt[tid])->seqNum < squashedSeqNum) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + return; + } + + bool robTailUpdate = false; + + for (int numSquashed = 0; + numSquashed < squashWidth && + squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->seqNum > squashedSeqNum; + ++numSquashed) + { + DPRINTF(ROB, "[tid:%u]: Squashing instruction PC %#x, seq num %i.\n", + (*squashIt[tid])->threadNumber, + (*squashIt[tid])->readPC(), + (*squashIt[tid])->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*squashIt[tid])->setSquashed(); + + (*squashIt[tid])->setCanCommit(); + + + if (squashIt[tid] == instList[tid].begin()) { + DPRINTF(ROB, "Reached head of instruction list while " + "squashing.\n"); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + + return; + } + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + if ((*squashIt[tid]) == (*tail_thread)) + robTailUpdate = true; + + squashIt[tid]--; + } + + + // Check if ROB is done squashing. + if ((*squashIt[tid])->seqNum <= squashedSeqNum) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + } + + if (robTailUpdate) { + updateTail(); + } +} + + +template +void +ROB::updateHead() +{ + DynInstPtr head_inst; + InstSeqNum lowest_num = 0; + bool first_valid = true; + + // @todo: set ActiveThreads through ROB or CPU + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_num = *threads++; + + if (instList[thread_num].empty()) + continue; + + if (first_valid) { + head = instList[thread_num].begin(); + lowest_num = (*head)->seqNum; + first_valid = false; + continue; + } + + InstIt head_thread = instList[thread_num].begin(); + + DynInstPtr head_inst = (*head_thread); + + assert(head_inst != 0); + + if (head_inst->seqNum < lowest_num) { + head = head_thread; + lowest_num = head_inst->seqNum; + } + } + + if (first_valid) { + head = instList[0].end(); + } + +} + +template +void +ROB::updateTail() +{ + tail = instList[0].end(); + bool first_valid = true; + + list::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (instList[tid].empty()) { + continue; + } + + // If this is the first valid then assign w/out + // comparison + if (first_valid) { + tail = instList[tid].end(); + tail--; + first_valid = false; + continue; + } + + // Assign new tail if this thread's tail is younger + // than our current "tail high" + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + if ((*tail_thread)->seqNum > (*tail)->seqNum) { + tail = tail_thread; + } + } +} + + +template +void +ROB::squash(InstSeqNum squash_num,unsigned tid) +{ + if (isEmpty()) { + DPRINTF(ROB, "Does not need to squash due to being empty " + "[sn:%i]\n", + squash_num); + + return; + } + + DPRINTF(ROB, "Starting to squash within the ROB.\n"); + + robStatus[tid] = ROBSquashing; + + doneSquashing[tid] = false; squashedSeqNum = squash_num; - assert(tail != cpu->instList.end()); + if (!instList[tid].empty()) { + InstIt tail_thread = instList[tid].end(); + tail_thread--; - squashIt = tail; + squashIt[tid] = tail_thread; - doSquash(); + doSquash(tid); + } +} + +template +typename Impl::DynInstPtr +ROB::readHeadInst() +{ + if (numInstsInROB != 0) { + assert((*head)->isInROB()==true); + return *head; + } else { + return dummyInst; + } +} + +template +typename Impl::DynInstPtr +ROB::readHeadInst(unsigned tid) +{ + if (threadEntries[tid] != 0) { + InstIt head_thread = instList[tid].begin(); + + assert((*head_thread)->isInROB()==true); + + return *head_thread; + } else { + return dummyInst; + } } template uint64_t ROB::readHeadPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - DynInstPtr head_inst = cpu->instList.front(); + DynInstPtr head_inst = *head; return head_inst->readPC(); } +template +uint64_t +ROB::readHeadPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readPC(); +} + + template uint64_t ROB::readHeadNextPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - DynInstPtr head_inst = cpu->instList.front(); + DynInstPtr head_inst = *head; return head_inst->readNextPC(); } +template +uint64_t +ROB::readHeadNextPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readNextPC(); +} + + template InstSeqNum ROB::readHeadSeqNum() { - // Return the last sequence number that has not been squashed. Other - // stages can use it to squash any instructions younger than the current - // tail. - DynInstPtr head_inst = cpu->instList.front(); + //assert(numInstsInROB == countInsts()); + DynInstPtr head_inst = *head; return head_inst->seqNum; } +template +InstSeqNum +ROB::readHeadSeqNum(unsigned tid) +{ + InstIt head_thread = instList[tid].begin(); + + return ((*head_thread)->seqNum); +} + +template +typename Impl::DynInstPtr +ROB::readTailInst() +{ + //assert(numInstsInROB == countInsts()); + //assert(tail != instList[0].end()); + + return (*tail); +} + +template +typename Impl::DynInstPtr +ROB::readTailInst(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return *tail_thread; +} + + template uint64_t ROB::readTailPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - assert(tail != cpu->instList.end()); + //assert(tail != instList[0].end()); return (*tail)->readPC(); } +template +uint64_t +ROB::readTailPC(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->readPC(); +} + template InstSeqNum ROB::readTailSeqNum() @@ -309,4 +659,18 @@ ROB::readTailSeqNum() return (*tail)->seqNum; } -#endif // __CPU_O3_CPU_ROB_IMPL_HH__ +template +InstSeqNum +ROB::readTailSeqNum(unsigned tid) +{ + // Return the last sequence number that has not been squashed. Other + // stages can use it to squash any instructions younger than the current + // tail. + // assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->seqNum; +} + diff --git a/cpu/o3/sat_counter.cc b/cpu/o3/sat_counter.cc index d20fff650..a6e131483 100644 --- a/cpu/o3/sat_counter.cc +++ b/cpu/o3/sat_counter.cc @@ -44,7 +44,7 @@ SatCounter::SatCounter(unsigned bits, unsigned initial_val) { // Check to make sure initial value doesn't exceed the max counter value. if (initial_val > maxVal) { - panic("BP: Initial counter value exceeds max size."); + fatal("BP: Initial counter value exceeds max size."); } } @@ -57,7 +57,7 @@ SatCounter::setBits(unsigned bits) void SatCounter::increment() { - if(counter < maxVal) { + if (counter < maxVal) { ++counter; } } @@ -65,7 +65,7 @@ SatCounter::increment() void SatCounter::decrement() { - if(counter > 0) { + if (counter > 0) { --counter; } } diff --git a/cpu/o3/sat_counter.hh b/cpu/o3/sat_counter.hh index b7cfe6423..952f1f86d 100644 --- a/cpu/o3/sat_counter.hh +++ b/cpu/o3/sat_counter.hh @@ -26,8 +26,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_SAT_COUNTER_HH__ -#define __CPU_O3_CPU_SAT_COUNTER_HH__ +#ifndef __CPU_O3_SAT_COUNTER_HH__ +#define __CPU_O3_SAT_COUNTER_HH__ #include "sim/host.hh" @@ -78,13 +78,11 @@ class SatCounter * Read the counter's value. */ const uint8_t read() const - { - return counter; - } + { return counter; } private: uint8_t maxVal; uint8_t counter; }; -#endif // __CPU_O3_CPU_SAT_COUNTER_HH__ +#endif // __CPU_O3_SAT_COUNTER_HH__ diff --git a/cpu/o3/scoreboard.cc b/cpu/o3/scoreboard.cc new file mode 100644 index 000000000..87b0aee94 --- /dev/null +++ b/cpu/o3/scoreboard.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#include "cpu/o3/scoreboard.hh" + +Scoreboard::Scoreboard(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + unsigned _numMiscRegs, + unsigned _zeroRegIdx) + : numLogicalIntRegs(_numLogicalIntRegs), + numPhysicalIntRegs(_numPhysicalIntRegs), + numLogicalFloatRegs(_numLogicalFloatRegs), + numPhysicalFloatRegs(_numPhysicalFloatRegs), + numMiscRegs(_numMiscRegs), + zeroRegIdx(_zeroRegIdx) +{ + //Get Register Sizes + numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs; + numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs; + + //Resize scoreboard appropriately + regScoreBoard.resize(numPhysicalRegs + (numMiscRegs * activeThreads)); + + //Initialize values + for (int i=0; i < numLogicalIntRegs * activeThreads; i++) { + regScoreBoard[i] = 1; + } + + for (int i= numPhysicalIntRegs; + i < numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads); + i++) { + regScoreBoard[i] = 1; + } + + for (int i = numPhysicalRegs; + i < numPhysicalRegs + (numMiscRegs * activeThreads); + i++) { + regScoreBoard[i] = 1; + } +} + +std::string +Scoreboard::name() const +{ + return "cpu.scoreboard"; +} + +bool +Scoreboard::getReg(PhysRegIndex phys_reg) +{ + // Always ready if int or fp zero reg. + if (phys_reg == zeroRegIdx || + phys_reg == (zeroRegIdx + numPhysicalIntRegs)) { + return 1; + } + + return regScoreBoard[phys_reg]; +} + +void +Scoreboard::setReg(PhysRegIndex phys_reg) +{ + DPRINTF(Scoreboard, "Setting reg %i as ready\n", phys_reg); + + regScoreBoard[phys_reg] = 1; +} + +void +Scoreboard::unsetReg(PhysRegIndex ready_reg) +{ + if (ready_reg == zeroRegIdx || + ready_reg == (zeroRegIdx + numPhysicalIntRegs)) { + // Don't do anything if int or fp zero reg. + } + + regScoreBoard[ready_reg] = 0; +} diff --git a/cpu/o3/scoreboard.hh b/cpu/o3/scoreboard.hh new file mode 100644 index 000000000..77f2cf157 --- /dev/null +++ b/cpu/o3/scoreboard.hh @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * 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. + */ + +#ifndef __CPU_O3_SCOREBOARD_HH__ +#define __CPU_O3_SCOREBOARD_HH__ + +#include +#include +#include +#include "arch/alpha/isa_traits.hh" +#include "base/trace.hh" +#include "base/traceflags.hh" +#include "cpu/o3/comm.hh" + +/** + * Implements a simple scoreboard to track which registers are ready. + * This class assumes that the fp registers start, index wise, right after + * the integer registers. The misc. registers start, index wise, right after + * the fp registers. + * @todo: Fix up handling of the zero register in case the decoder does not + * automatically make insts that write the zero register into nops. + */ +class Scoreboard +{ + public: + /** Constructs a scoreboard. + * @param activeThreads The number of active threads. + * @param _numLogicalIntRegs Number of logical integer registers. + * @param _numPhysicalIntRegs Number of physical integer registers. + * @param _numLogicalFloatRegs Number of logical fp registers. + * @param _numPhysicalFloatRegs Number of physical fp registers. + * @param _numMiscRegs Number of miscellaneous registers. + * @param _zeroRegIdx Index of the zero register. + */ + Scoreboard(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + unsigned _numMiscRegs, + unsigned _zeroRegIdx); + + /** Destructor. */ + ~Scoreboard() {} + + /** Returns the name of the scoreboard. */ + std::string name() const; + + /** Checks if the register is ready. */ + bool getReg(PhysRegIndex ready_reg); + + /** Sets the register as ready. */ + void setReg(PhysRegIndex phys_reg); + + /** Sets the register as not ready. */ + void unsetReg(PhysRegIndex ready_reg); + + private: + /** Scoreboard of physical integer registers, saying whether or not they + * are ready. + */ + std::vector regScoreBoard; + + /** Number of logical integer registers. */ + int numLogicalIntRegs; + + /** Number of physical integer registers. */ + int numPhysicalIntRegs; + + /** Number of logical floating point registers. */ + int numLogicalFloatRegs; + + /** Number of physical floating point registers. */ + int numPhysicalFloatRegs; + + /** Number of miscellaneous registers. */ + int numMiscRegs; + + /** Number of logical integer + float registers. */ + int numLogicalRegs; + + /** Number of physical integer + float registers. */ + int numPhysicalRegs; + + /** The logical index of the zero register. */ + int zeroRegIdx; +}; + +#endif diff --git a/cpu/o3/store_set.cc b/cpu/o3/store_set.cc index 11023f4a8..a685646f3 100644 --- a/cpu/o3/store_set.cc +++ b/cpu/o3/store_set.cc @@ -30,43 +30,76 @@ #include "cpu/o3/store_set.hh" StoreSet::StoreSet(int _SSIT_size, int _LFST_size) - : SSIT_size(_SSIT_size), LFST_size(_LFST_size) + : SSITSize(_SSIT_size), LFSTSize(_LFST_size) { DPRINTF(StoreSet, "StoreSet: Creating store set object.\n"); DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n", - SSIT_size, LFST_size); + SSITSize, LFSTSize); - SSIT = new SSID[SSIT_size]; + SSIT.resize(SSITSize); - validSSIT.resize(SSIT_size); + validSSIT.resize(SSITSize); - for (int i = 0; i < SSIT_size; ++i) + for (int i = 0; i < SSITSize; ++i) validSSIT[i] = false; - LFST = new InstSeqNum[LFST_size]; + LFST.resize(LFSTSize); - validLFST.resize(LFST_size); + validLFST.resize(LFSTSize); - SSCounters = new int[LFST_size]; - - for (int i = 0; i < LFST_size; ++i) - { + for (int i = 0; i < LFSTSize; ++i) { validLFST[i] = false; - SSCounters[i] = 0; + LFST[i] = 0; } - index_mask = SSIT_size - 1; + indexMask = SSITSize - 1; - offset_bits = 2; + offsetBits = 2; } +StoreSet::~StoreSet() +{ +} + +void +StoreSet::init(int _SSIT_size, int _LFST_size) +{ + SSITSize = _SSIT_size; + LFSTSize = _LFST_size; + + DPRINTF(StoreSet, "StoreSet: Creating store set object.\n"); + DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n", + SSITSize, LFSTSize); + + SSIT.resize(SSITSize); + + validSSIT.resize(SSITSize); + + for (int i = 0; i < SSITSize; ++i) + validSSIT[i] = false; + + LFST.resize(LFSTSize); + + validLFST.resize(LFSTSize); + + for (int i = 0; i < LFSTSize; ++i) { + validLFST[i] = false; + LFST[i] = 0; + } + + indexMask = SSITSize - 1; + + offsetBits = 2; +} + + void StoreSet::violation(Addr store_PC, Addr load_PC) { int load_index = calcIndex(load_PC); int store_index = calcIndex(store_PC); - assert(load_index < SSIT_size && store_index < SSIT_size); + assert(load_index < SSITSize && store_index < SSITSize); bool valid_load_SSID = validSSIT[load_index]; bool valid_store_SSID = validSSIT[store_index]; @@ -83,10 +116,7 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSIT[store_index] = new_set; - assert(new_set < LFST_size); - - SSCounters[new_set]++; - + assert(new_set < LFSTSize); DPRINTF(StoreSet, "StoreSet: Neither load nor store had a valid " "storeset, creating a new one: %i for load %#x, store %#x\n", @@ -98,9 +128,7 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSIT[store_index] = load_SSID; - assert(load_SSID < LFST_size); - - SSCounters[load_SSID]++; + assert(load_SSID < LFSTSize); DPRINTF(StoreSet, "StoreSet: Load had a valid store set. Adding " "store to that set: %i for load %#x, store %#x\n", @@ -112,9 +140,6 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSIT[load_index] = store_SSID; - // Because we are having a load point to an already existing set, - // the size of the store set is not incremented. - DPRINTF(StoreSet, "StoreSet: Store had a valid store set: %i for " "load %#x, store %#x\n", store_SSID, load_PC, store_PC); @@ -122,29 +147,19 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSID load_SSID = SSIT[load_index]; SSID store_SSID = SSIT[store_index]; - assert(load_SSID < LFST_size && store_SSID < LFST_size); + assert(load_SSID < LFSTSize && store_SSID < LFSTSize); - int load_SS_size = SSCounters[load_SSID]; - int store_SS_size = SSCounters[store_SSID]; - - // If the load has the bigger store set, then assign the store - // to the same store set as the load. Otherwise vice-versa. - if (load_SS_size > store_SS_size) { + // The store set with the lower number wins + if (store_SSID > load_SSID) { SSIT[store_index] = load_SSID; - SSCounters[load_SSID]++; - SSCounters[store_SSID]--; - - DPRINTF(StoreSet, "StoreSet: Load had bigger store set: %i; " + DPRINTF(StoreSet, "StoreSet: Load had smaller store set: %i; " "for load %#x, store %#x\n", load_SSID, load_PC, store_PC); } else { SSIT[load_index] = store_SSID; - SSCounters[store_SSID]++; - SSCounters[load_SSID]--; - - DPRINTF(StoreSet, "StoreSet: Store had bigger store set: %i; " + DPRINTF(StoreSet, "StoreSet: Store had smaller store set: %i; " "for load %#x, store %#x\n", store_SSID, load_PC, store_PC); } @@ -159,13 +174,14 @@ StoreSet::insertLoad(Addr load_PC, InstSeqNum load_seq_num) } void -StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num) +StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid) { int index = calcIndex(store_PC); int store_SSID; - assert(index < SSIT_size); + assert(index < SSITSize); if (!validSSIT[index]) { // Do nothing if there's no valid entry. @@ -173,13 +189,15 @@ StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num) } else { store_SSID = SSIT[index]; - assert(store_SSID < LFST_size); + assert(store_SSID < LFSTSize); // Update the last store that was fetched with the current one. LFST[store_SSID] = store_seq_num; validLFST[store_SSID] = 1; + storeList[store_seq_num] = store_SSID; + DPRINTF(StoreSet, "Store %#x updated the LFST, SSID: %i\n", store_PC, store_SSID); } @@ -192,7 +210,7 @@ StoreSet::checkInst(Addr PC) int inst_SSID; - assert(index < SSIT_size); + assert(index < SSITSize); if (!validSSIT[index]) { DPRINTF(StoreSet, "Inst %#x with index %i had no SSID\n", @@ -203,7 +221,7 @@ StoreSet::checkInst(Addr PC) } else { inst_SSID = SSIT[index]; - assert(inst_SSID < LFST_size); + assert(inst_SSID < LFSTSize); if (!validLFST[inst_SSID]) { @@ -232,7 +250,13 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) int store_SSID; - assert(index < SSIT_size); + assert(index < SSITSize); + + SeqNumMapIt store_list_it = storeList.find(issued_seq_num); + + if (store_list_it != storeList.end()) { + storeList.erase(store_list_it); + } // Make sure the SSIT still has a valid entry for the issued store. if (!validSSIT[index]) { @@ -241,7 +265,7 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) store_SSID = SSIT[index]; - assert(store_SSID < LFST_size); + assert(store_SSID < LFSTSize); // If the last fetched store in the store set refers to the store that // was just issued, then invalidate the entry. @@ -252,18 +276,36 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) } void -StoreSet::squash(InstSeqNum squashed_num) +StoreSet::squash(InstSeqNum squashed_num, unsigned tid) { // Not really sure how to do this well. // Generally this is small enough that it should be okay; short circuit // evaluation should take care of invalid entries. + // Maybe keep a list of valid LFST's? Really ugly either way... DPRINTF(StoreSet, "StoreSet: Squashing until inum %i\n", squashed_num); - for (int i = 0; i < LFST_size; ++i) { - if (validLFST[i] && LFST[i] < squashed_num) { - validLFST[i] = false; + int idx; + SeqNumMapIt store_list_it = storeList.begin(); + + //@todo:Fix to only delete from correct thread + while (!storeList.empty()) { + idx = (*store_list_it).second; + + if ((*store_list_it).first <= squashed_num) { + break; + } + + bool younger = LFST[idx] > squashed_num; + + if (validLFST[idx] && younger) { + DPRINTF(StoreSet, "Squashed [sn:%lli]\n", LFST[idx]); + validLFST[idx] = false; + + storeList.erase(store_list_it++); + } else if (!validLFST[idx] && younger) { + storeList.erase(store_list_it++); } } } @@ -271,12 +313,13 @@ StoreSet::squash(InstSeqNum squashed_num) void StoreSet::clear() { - for (int i = 0; i < SSIT_size; ++i) { + for (int i = 0; i < SSITSize; ++i) { validSSIT[i] = false; } - for (int i = 0; i < LFST_size; ++i) { + for (int i = 0; i < LFSTSize; ++i) { validLFST[i] = false; } -} + storeList.clear(); +} diff --git a/cpu/o3/store_set.hh b/cpu/o3/store_set.hh index 5a885d838..7189db3ab 100644 --- a/cpu/o3/store_set.hh +++ b/cpu/o3/store_set.hh @@ -26,61 +26,80 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_STORE_SET_HH__ -#define __CPU_O3_CPU_STORE_SET_HH__ +#ifndef __CPU_O3_STORE_SET_HH__ +#define __CPU_O3_STORE_SET_HH__ +#include +#include +#include #include #include "arch/isa_traits.hh" #include "cpu/inst_seq.hh" +struct ltseqnum { + bool operator()(const InstSeqNum &lhs, const InstSeqNum &rhs) const + { + return lhs > rhs; + } +}; + class StoreSet { public: typedef unsigned SSID; public: + StoreSet() { }; + StoreSet(int SSIT_size, int LFST_size); + ~StoreSet(); + + void init(int SSIT_size, int LFST_size); + void violation(Addr store_PC, Addr load_PC); void insertLoad(Addr load_PC, InstSeqNum load_seq_num); - void insertStore(Addr store_PC, InstSeqNum store_seq_num); + void insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid); InstSeqNum checkInst(Addr PC); void issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store); - void squash(InstSeqNum squashed_num); + void squash(InstSeqNum squashed_num, unsigned tid); void clear(); private: inline int calcIndex(Addr PC) - { return (PC >> offset_bits) & index_mask; } + { return (PC >> offsetBits) & indexMask; } inline SSID calcSSID(Addr PC) - { return ((PC ^ (PC >> 10)) % LFST_size); } + { return ((PC ^ (PC >> 10)) % LFSTSize); } - SSID *SSIT; + std::vector SSIT; std::vector validSSIT; - InstSeqNum *LFST; + std::vector LFST; std::vector validLFST; - int *SSCounters; + std::map storeList; - int SSIT_size; + typedef std::map::iterator SeqNumMapIt; - int LFST_size; + int SSITSize; - int index_mask; + int LFSTSize; + + int indexMask; // HACK: Hardcoded for now. - int offset_bits; + int offsetBits; }; -#endif // __CPU_O3_CPU_STORE_SET_HH__ +#endif // __CPU_O3_STORE_SET_HH__ diff --git a/cpu/o3/thread_state.hh b/cpu/o3/thread_state.hh new file mode 100644 index 000000000..846f44176 --- /dev/null +++ b/cpu/o3/thread_state.hh @@ -0,0 +1,143 @@ + +#ifndef __CPU_O3_THREAD_STATE_HH__ +#define __CPU_O3_THREAD_STATE_HH__ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/exec_context.hh" +#include "cpu/thread_state.hh" + +class Event; +class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class Process; +class FunctionalMemory; +#endif + +// In the new CPU case this may be quite small...It depends on what I define +// ThreadState to be. Currently it's only the state that exists within +// ExecContext basically. Leaves the interface and manipulation up to the +// CPU. Not sure this is useful/flexible...probably can be if I can avoid +// including state here that parts of the pipeline can't modify directly, +// or at least don't let them. The only problem is for state that's needed +// per thread, per structure. I.e. rename table, memreqs. +// On the other hand, it might be nice to not have to pay the extra pointer +// lookup to get frequently used state such as a memreq (that isn't used much +// elsewhere)... + +// Maybe this ozone thread state should only really have committed state? +// I need to think about why I'm using this and what it's useful for. Clearly +// has benefits for SMT; basically serves same use as CPUExecContext. +// Makes the ExecContext proxy easier. Gives organization/central access point +// to state of a thread that can be accessed normally (i.e. not in-flight +// stuff within a OoO processor). Does this need an XC proxy within it? +template +struct O3ThreadState : public ThreadState { + typedef ExecContext::Status Status; + typedef typename Impl::FullCPU FullCPU; + + Status _status; + + // Current instruction? + TheISA::MachInst inst; + private: + FullCPU *cpu; + public: + + bool inSyscall; + + bool trapPending; + +#if FULL_SYSTEM + O3ThreadState(FullCPU *_cpu, int _thread_num, FunctionalMemory *_mem) + : ThreadState(-1, _thread_num, _mem), + inSyscall(0), trapPending(0) + { } +#else + O3ThreadState(FullCPU *_cpu, int _thread_num, Process *_process, int _asid) + : ThreadState(-1, _thread_num, NULL, _process, _asid), + cpu(_cpu), inSyscall(0), trapPending(0) + { } + + O3ThreadState(FullCPU *_cpu, int _thread_num, FunctionalMemory *_mem, + int _asid) + : ThreadState(-1, _thread_num, _mem, NULL, _asid), + cpu(_cpu), inSyscall(0), trapPending(0) + { } +#endif + + ExecContext *xcProxy; + + ExecContext *getXCProxy() { return xcProxy; } + + Status status() const { return _status; } + + void setStatus(Status new_status) { _status = new_status; } + +#if !FULL_SYSTEM + + Fault dummyTranslation(MemReqPtr &req) + { +#if 0 + assert((req->vaddr >> 48 & 0xffff) == 0); +#endif + + // put the asid in the upper 16 bits of the paddr + req->paddr = req->vaddr & ~((Addr)0xffff << sizeof(Addr) * 8 - 16); + req->paddr = req->paddr | (Addr)req->asid << sizeof(Addr) * 8 - 16; + return NoFault; + } + Fault translateInstReq(MemReqPtr &req) + { + return dummyTranslation(req); + } + Fault translateDataReadReq(MemReqPtr &req) + { + return dummyTranslation(req); + } + Fault translateDataWriteReq(MemReqPtr &req) + { + return dummyTranslation(req); + } + + bool validInstAddr(Addr addr) + { return process->validInstAddr(addr); } + + bool validDataAddr(Addr addr) + { return process->validDataAddr(addr); } +#else + Fault translateInstReq(MemReqPtr &req) + { + return cpu->itb->translate(req); + } + + Fault translateDataReadReq(MemReqPtr &req) + { + return cpu->dtb->translate(req, false); + } + + Fault translateDataWriteReq(MemReqPtr &req) + { + return cpu->dtb->translate(req, true); + } +#endif + + bool misspeculating() { return false; } + + void setInst(TheISA::MachInst _inst) { inst = _inst; } + + Counter readFuncExeInst() { return funcExeInst; } + + void setFuncExeInst(Counter new_val) { funcExeInst = new_val; } + +#if !FULL_SYSTEM + void syscall() { process->syscall(xcProxy); } +#endif +}; + +#endif // __CPU_O3_THREAD_STATE_HH__ diff --git a/cpu/o3/tournament_pred.cc b/cpu/o3/tournament_pred.cc index 3fb580510..89da7b9f5 100644 --- a/cpu/o3/tournament_pred.cc +++ b/cpu/o3/tournament_pred.cc @@ -28,37 +28,37 @@ #include "cpu/o3/tournament_pred.hh" -TournamentBP::TournamentBP(unsigned _local_predictor_size, - unsigned _local_ctr_bits, - unsigned _local_history_table_size, - unsigned _local_history_bits, - unsigned _global_predictor_size, - unsigned _global_ctr_bits, - unsigned _global_history_bits, - unsigned _choice_predictor_size, - unsigned _choice_ctr_bits, +TournamentBP::TournamentBP(unsigned _localPredictorSize, + unsigned _localCtrBits, + unsigned _localHistoryTableSize, + unsigned _localHistoryBits, + unsigned _globalPredictorSize, + unsigned _globalCtrBits, + unsigned _globalHistoryBits, + unsigned _choicePredictorSize, + unsigned _choiceCtrBits, unsigned _instShiftAmt) - : localPredictorSize(_local_predictor_size), - localCtrBits(_local_ctr_bits), - localHistoryTableSize(_local_history_table_size), - localHistoryBits(_local_history_bits), - globalPredictorSize(_global_predictor_size), - globalCtrBits(_global_ctr_bits), - globalHistoryBits(_global_history_bits), - choicePredictorSize(_global_predictor_size), - choiceCtrBits(_choice_ctr_bits), + : localPredictorSize(_localPredictorSize), + localCtrBits(_localCtrBits), + localHistoryTableSize(_localHistoryTableSize), + localHistoryBits(_localHistoryBits), + globalPredictorSize(_globalPredictorSize), + globalCtrBits(_globalCtrBits), + globalHistoryBits(_globalHistoryBits), + choicePredictorSize(_globalPredictorSize), + choiceCtrBits(_choiceCtrBits), instShiftAmt(_instShiftAmt) { //Should do checks here to make sure sizes are correct (powers of 2) //Setup the array of counters for the local predictor - localCtrs = new SatCounter[localPredictorSize]; + localCtrs.resize(localPredictorSize); for (int i = 0; i < localPredictorSize; ++i) localCtrs[i].setBits(localCtrBits); //Setup the history table for the local table - localHistoryTable = new unsigned[localHistoryTableSize]; + localHistoryTable.resize(localHistoryTableSize); for (int i = 0; i < localHistoryTableSize; ++i) localHistoryTable[i] = 0; @@ -67,7 +67,7 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size, localHistoryMask = (1 << localHistoryBits) - 1; //Setup the array of counters for the global predictor - globalCtrs = new SatCounter[globalPredictorSize]; + globalCtrs.resize(globalPredictorSize); for (int i = 0; i < globalPredictorSize; ++i) globalCtrs[i].setBits(globalCtrBits); @@ -78,7 +78,7 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size, globalHistoryMask = (1 << globalHistoryBits) - 1; //Setup the array of counters for the choice predictor - choiceCtrs = new SatCounter[choicePredictorSize]; + choiceCtrs.resize(choicePredictorSize); for (int i = 0; i < choicePredictorSize; ++i) choiceCtrs[i].setBits(choiceCtrBits); @@ -240,8 +240,7 @@ TournamentBP::update(Addr &branch_addr, unsigned correct_gh, bool taken) globalHistory = globalHistory & globalHistoryMask; localHistoryTable[local_history_idx] |= 1; - } - else { + } else { assert(globalHistory < globalPredictorSize && local_predictor_idx < localPredictorSize); diff --git a/cpu/o3/tournament_pred.hh b/cpu/o3/tournament_pred.hh index cb93c2f67..7b600aa53 100644 --- a/cpu/o3/tournament_pred.hh +++ b/cpu/o3/tournament_pred.hh @@ -26,12 +26,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __CPU_O3_CPU_TOURNAMENT_PRED_HH__ -#define __CPU_O3_CPU_TOURNAMENT_PRED_HH__ +#ifndef __CPU_O3_TOURNAMENT_PRED_HH__ +#define __CPU_O3_TOURNAMENT_PRED_HH__ // For Addr type. #include "arch/isa_traits.hh" #include "cpu/o3/sat_counter.hh" +#include class TournamentBP { @@ -39,15 +40,15 @@ class TournamentBP /** * Default branch predictor constructor. */ - TournamentBP(unsigned local_predictor_size, - unsigned local_ctr_bits, - unsigned local_history_table_size, - unsigned local_history_bits, - unsigned global_predictor_size, - unsigned global_history_bits, - unsigned global_ctr_bits, - unsigned choice_predictor_size, - unsigned choice_ctr_bits, + TournamentBP(unsigned localPredictorSize, + unsigned localCtrBits, + unsigned localHistoryTableSize, + unsigned localHistoryBits, + unsigned globalPredictorSize, + unsigned globalHistoryBits, + unsigned globalCtrBits, + unsigned choicePredictorSize, + unsigned choiceCtrBits, unsigned instShiftAmt); /** @@ -78,7 +79,7 @@ class TournamentBP inline void updateHistoriesNotTaken(unsigned local_history_idx); /** Local counters. */ - SatCounter *localCtrs; + std::vector localCtrs; /** Size of the local predictor. */ unsigned localPredictorSize; @@ -87,7 +88,7 @@ class TournamentBP unsigned localCtrBits; /** Array of local history table entries. */ - unsigned *localHistoryTable; + std::vector localHistoryTable; /** Size of the local history table. */ unsigned localHistoryTableSize; @@ -102,7 +103,7 @@ class TournamentBP /** Array of counters that make up the global predictor. */ - SatCounter *globalCtrs; + std::vector globalCtrs; /** Size of the global predictor. */ unsigned globalPredictorSize; @@ -121,7 +122,7 @@ class TournamentBP /** Array of counters that make up the choice predictor. */ - SatCounter *choiceCtrs; + std::vector choiceCtrs; /** Size of the choice predictor (identical to the global predictor). */ unsigned choicePredictorSize; @@ -140,4 +141,4 @@ class TournamentBP unsigned threshold; }; -#endif // __CPU_O3_CPU_TOURNAMENT_PRED_HH__ +#endif // __CPU_O3_TOURNAMENT_PRED_HH__ diff --git a/cpu/thread_state.hh b/cpu/thread_state.hh new file mode 100644 index 000000000..e8381b9d3 --- /dev/null +++ b/cpu/thread_state.hh @@ -0,0 +1,92 @@ + +#ifndef __CPU_THREAD_STATE_HH__ +#define __CPU_THREAD_STATE_HH__ + +#include "cpu/exec_context.hh" + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class Process; +class FunctionalMemory; +#endif + +struct ThreadState { +#if FULL_SYSTEM + ThreadState(int _cpuId, int _tid, FunctionalMemory *_mem) + : cpuId(_cpuId), tid(_tid), mem(_mem), lastActivate(0), lastSuspend(0), + profile(NULL), profileNode(NULL), profilePC(0), quiesceEvent(NULL) +#else + ThreadState(int _cpuId, int _tid, FunctionalMemory *_mem, + Process *_process, short _asid) + : cpuId(_cpuId), tid(_tid), mem(_mem), process(_process), asid(_asid) +#endif + { + funcExeInst = 0; + storeCondFailures = 0; + } + + ExecContext::Status status; + + int cpuId; + + // Index of hardware thread context on the CPU that this represents. + int tid; + + Counter numInst; + Stats::Scalar<> numInsts; + Stats::Scalar<> numMemRefs; + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + FunctionalMemory *mem; // functional storage for process address space + +#if FULL_SYSTEM + Tick lastActivate; + Tick lastSuspend; + + FunctionProfile *profile; + ProfileNode *profileNode; + Addr profilePC; + + EndQuiesceEvent *quiesceEvent; + +#else + Process *process; + + // Address space ID. Note that this is used for TIMING cache + // simulation only; all functional memory accesses should use + // one of the FunctionalMemory pointers above. + short asid; + +#endif + + /** + * Temporary storage to pass the source address from copy_load to + * copy_store. + * @todo Remove this temporary when we have a better way to do it. + */ + Addr copySrcAddr; + /** + * Temp storage for the physical source address of a copy. + * @todo Remove this temporary when we have a better way to do it. + */ + Addr copySrcPhysAddr; + + /* + * number of executed instructions, for matching with syscall trace + * points in EIO files. + */ + Counter funcExeInst; + + // + // Count failed store conditionals so we can warn of apparent + // application deadlock situations. + unsigned storeCondFailures; +}; + +#endif // __CPU_THREAD_STATE_HH__ diff --git a/python/m5/objects/FUPool.py b/python/m5/objects/FUPool.py new file mode 100644 index 000000000..5eecfd12f --- /dev/null +++ b/python/m5/objects/FUPool.py @@ -0,0 +1,8 @@ +from m5 import * +from FullCPU import OpType +from FullCPU import OpDesc +from FullCPU import FUDesc + +class FUPool(SimObject): + type = 'FUPool' + FUList = VectorParam.FUDesc("list of FU's for this pool")