diff options
Diffstat (limited to 'src')
566 files changed, 42216 insertions, 10955 deletions
diff --git a/src/SConscript b/src/SConscript index 558c447e9..a1c18711c 100644 --- a/src/SConscript +++ b/src/SConscript @@ -25,6 +25,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt import os import sys @@ -46,9 +48,7 @@ Import('env') base_sources = Split(''' base/circlebuf.cc - base/copyright.cc base/cprintf.cc - base/embedfile.cc base/fast_alloc.cc base/fifo_buffer.cc base/hostinfo.cc @@ -81,14 +81,20 @@ base_sources = Split(''' base/stats/visit.cc base/stats/text.cc + cpu/activity.cc cpu/base.cc - cpu/cpu_exec_context.cc + cpu/cpuevent.cc cpu/exetrace.cc cpu/op_class.cc cpu/pc_event.cc + cpu/quiesce_event.cc cpu/static_inst.cc cpu/sampler/sampler.cc - + cpu/simple_thread.cc + cpu/thread_state.cc + + encumbered/cpu/full/fu_pool.cc + mem/bridge.cc mem/bus.cc mem/connector.cc @@ -96,10 +102,6 @@ base_sources = Split(''' mem/packet.cc mem/physical.cc mem/port.cc - mem/request.cc - - python/pyconfig.cc - python/embedded_py.cc sim/builder.cc sim/configfile.cc @@ -107,12 +109,12 @@ base_sources = Split(''' sim/eventq.cc sim/faults.cc sim/main.cc + python/swig/main_wrap.cc sim/param.cc sim/profile.cc sim/root.cc sim/serialize.cc sim/sim_events.cc - sim/sim_exit.cc sim/sim_object.cc sim/startup.cc sim/stat_context.cc @@ -217,7 +219,6 @@ full_system_sources = Split(''' dev/uart.cc dev/uart8250.cc - kern/kernel_binning.cc kern/kernel_stats.cc kern/system_events.cc kern/linux/events.cc @@ -284,14 +285,18 @@ memtest_sources = Split(''' cpu/memtest/memtest.cc ''') +# Include file paths are rooted in this directory. SCons will +# automatically expand '.' to refer to both the source directory and +# the corresponding build directory to pick up generated include +# files. +env.Append(CPPPATH=Dir('.')) + # Add a flag defining what THE_ISA should be for all compilation env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())]) -arch_sources = SConscript('arch/SConscript', - exports = 'env', duplicate = False) +arch_sources = SConscript('arch/SConscript', exports = 'env') -cpu_sources = SConscript('cpu/SConscript', - exports = 'env', duplicate = False) +cpu_sources = SConscript('cpu/SConscript', exports = 'env') # This is outside of cpu/SConscript since the source directory isn't # underneath 'cpu'. @@ -326,7 +331,7 @@ env.Command(Split('base/traceflags.hh base/traceflags.cc'), 'base/traceflags.py', 'python $SOURCE $TARGET.base') -SConscript('python/SConscript', exports = ['env'], duplicate=0) +SConscript('python/SConscript', exports = ['env']) # This function adds the specified sources to the given build # environment, and returns a list of all the corresponding SCons @@ -349,49 +354,45 @@ def make_objs(sources, env): # ################################################### -# Include file paths are rooted in this directory. SCons will -# automatically expand '.' to refer to both the source directory and -# the corresponding build directory to pick up generated include -# files. -env.Append(CPPPATH='.') +# List of constructed environments to pass back to SConstruct +envList = [] + +# Function to create a new build environment as clone of current +# environment 'env' with modified object suffix and optional stripped +# binary. Additional keyword arguments are appended to corresponding +# build environment vars. +def makeEnv(label, objsfx, strip = False, **kwargs): + newEnv = env.Copy(OBJSUFFIX=objsfx) + newEnv.Label = label + newEnv.Append(**kwargs) + exe = 'm5.' + label # final executable + bin = exe + '.bin' # executable w/o appended Python zip archive + newEnv.Program(bin, make_objs(sources, newEnv)) + if strip: + stripped_bin = bin + '.stripped' + newEnv.Command(stripped_bin, bin, 'strip $SOURCE -o $TARGET') + bin = stripped_bin + targets = newEnv.Concat(exe, [bin, 'python/m5py.zip']) + newEnv.M5Binary = targets[0] + envList.append(newEnv) # Debug binary -debugEnv = env.Copy(OBJSUFFIX='.do') -debugEnv.Label = 'debug' -debugEnv.Append(CCFLAGS=Split('-g3 -gdwarf-2 -O0')) -debugEnv.Append(CPPDEFINES='DEBUG') -tlist = debugEnv.Program(target = 'm5.debug', - source = make_objs(sources, debugEnv)) -debugEnv.M5Binary = tlist[0] +makeEnv('debug', '.do', + CCFLAGS = Split('-g3 -gdwarf-2 -O0'), + CPPDEFINES = 'DEBUG') # Optimized binary -optEnv = env.Copy() -optEnv.Label = 'opt' -optEnv.Append(CCFLAGS=Split('-g -O3')) -tlist = optEnv.Program(target = 'm5.opt', - source = make_objs(sources, optEnv)) -optEnv.M5Binary = tlist[0] +makeEnv('opt', '.o', + CCFLAGS = Split('-g -O3')) # "Fast" binary -fastEnv = env.Copy(OBJSUFFIX='.fo') -fastEnv.Label = 'fast' -fastEnv.Append(CCFLAGS=Split('-O3')) -fastEnv.Append(CPPDEFINES='NDEBUG') -fastEnv.Program(target = 'm5.fast.unstripped', - source = make_objs(sources, fastEnv)) -tlist = fastEnv.Command(target = 'm5.fast', - source = 'm5.fast.unstripped', - action = 'strip $SOURCE -o $TARGET') -fastEnv.M5Binary = tlist[0] +makeEnv('fast', '.fo', strip = True, + CCFLAGS = Split('-O3'), + CPPDEFINES = 'NDEBUG') # Profiled binary -profEnv = env.Copy(OBJSUFFIX='.po') -profEnv.Label = 'prof' -profEnv.Append(CCFLAGS=Split('-O3 -g -pg'), LINKFLAGS='-pg') -tlist = profEnv.Program(target = 'm5.prof', - source = make_objs(sources, profEnv)) -profEnv.M5Binary = tlist[0] - -envList = [debugEnv, optEnv, fastEnv, profEnv] +makeEnv('prof', '.po', + CCFLAGS = Split('-O3 -g -pg'), + LINKFLAGS = '-pg') Return('envList') diff --git a/src/arch/SConscript b/src/arch/SConscript index 9c193207f..c90694a68 100644 --- a/src/arch/SConscript +++ b/src/arch/SConscript @@ -25,6 +25,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt import os.path @@ -144,7 +146,6 @@ env.Append(BUILDERS = { 'ISADesc' : isa_desc_builder }) isa = env['TARGET_ISA'] # someday this may be a list of ISAs # Let the target architecture define what additional sources it needs -sources += SConscript(os.path.join(isa, 'SConscript'), - exports = 'env', duplicate = False) +sources += SConscript(os.path.join(isa, 'SConscript'), exports = 'env') Return('sources') diff --git a/src/arch/alpha/SConscript b/src/arch/alpha/SConscript index 1b20f8b1f..216c88cc7 100644 --- a/src/arch/alpha/SConscript +++ b/src/arch/alpha/SConscript @@ -25,6 +25,9 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black +# Steve Reinhardt import os import sys diff --git a/src/arch/alpha/aout_machdep.h b/src/arch/alpha/aout_machdep.h index df9d9ac6a..58991256a 100644 --- a/src/arch/alpha/aout_machdep.h +++ b/src/arch/alpha/aout_machdep.h @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __AOUT_MACHDEP_H__ diff --git a/src/arch/alpha/arguments.cc b/src/arch/alpha/arguments.cc index adc371682..9f9002003 100644 --- a/src/arch/alpha/arguments.cc +++ b/src/arch/alpha/arguments.cc @@ -24,11 +24,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "arch/alpha/arguments.hh" #include "arch/alpha/vtophys.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/vport.hh" using namespace AlphaISA; @@ -54,14 +56,14 @@ AlphaArguments::getArg(bool fp) { if (number < 6) { if (fp) - return xc->readFloatRegBits(16 + number); + return tc->readFloatRegBits(16 + number); else - return xc->readIntReg(16 + number); + return tc->readIntReg(16 + number); } else { - Addr sp = xc->readIntReg(30); - VirtualPort *vp = xc->getVirtPort(xc); + Addr sp = tc->readIntReg(30); + VirtualPort *vp = tc->getVirtPort(tc); uint64_t arg = vp->read<uint64_t>(sp + (number-6) * sizeof(uint64_t)); - xc->delVirtPort(vp); + tc->delVirtPort(vp); return arg; } } diff --git a/src/arch/alpha/arguments.hh b/src/arch/alpha/arguments.hh index bd1c6cb1d..d977d48d6 100644 --- a/src/arch/alpha/arguments.hh +++ b/src/arch/alpha/arguments.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_ARGUMENTS_HH__ @@ -35,14 +37,14 @@ #include "base/refcnt.hh" #include "sim/host.hh" -class ExecContext; +class ThreadContext; namespace AlphaISA { class AlphaArguments { protected: - ExecContext *xc; + ThreadContext *tc; int number; uint64_t getArg(bool fp = false); @@ -63,17 +65,17 @@ class AlphaArguments RefCountingPtr<Data> data; public: - AlphaArguments(ExecContext *ctx, int n = 0) - : xc(ctx), number(n), data(NULL) + AlphaArguments(ThreadContext *ctx, int n = 0) + : tc(ctx), number(n), data(NULL) { assert(number >= 0); data = new Data;} AlphaArguments(const AlphaArguments &args) - : xc(args.xc), number(args.number), data(args.data) {} + : tc(args.tc), number(args.number), data(args.data) {} ~AlphaArguments() {} - ExecContext *getExecContext() const { return xc; } + ThreadContext *getThreadContext() const { return tc; } const AlphaArguments &operator=(const AlphaArguments &args) { - xc = args.xc; + tc = args.tc; number = args.number; data = args.data; return *this; @@ -118,7 +120,7 @@ class AlphaArguments } AlphaArguments operator[](int index) { - return AlphaArguments(xc, index); + return AlphaArguments(tc, index); } template <class T> @@ -131,13 +133,13 @@ class AlphaArguments template <class T> operator T *() { T *buf = (T *)data->alloc(sizeof(T)); - CopyData(xc, buf, getArg(), sizeof(T)); + CopyData(tc, buf, getArg(), sizeof(T)); return buf; } operator char *() { char *buf = data->alloc(2048); - CopyStringOut(xc, buf, getArg(), 2048); + CopyStringOut(tc, buf, getArg(), 2048); return buf; } }; diff --git a/src/arch/alpha/ev5.cc b/src/arch/alpha/ev5.cc index 12f7659e6..7d6894733 100644 --- a/src/arch/alpha/ev5.cc +++ b/src/arch/alpha/ev5.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include "arch/alpha/tlb.hh" @@ -34,11 +37,11 @@ #include "base/stats/events.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "sim/debug.hh" -#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" #if FULL_SYSTEM @@ -49,15 +52,15 @@ using namespace EV5; // Machine dependent functions // void -AlphaISA::initCPU(ExecContext *xc, int cpuId) +AlphaISA::initCPU(ThreadContext *tc, int cpuId) { - initIPRs(xc, cpuId); + initIPRs(tc, cpuId); - xc->setIntReg(16, cpuId); - xc->setIntReg(0, cpuId); + tc->setIntReg(16, cpuId); + tc->setIntReg(0, cpuId); - xc->setPC(xc->readMiscReg(IPR_PAL_BASE) + (new ResetFault)->vect()); - xc->setNextPC(xc->readPC() + sizeof(MachInst)); + tc->setPC(tc->readMiscReg(IPR_PAL_BASE) + (new ResetFault)->vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); } //////////////////////////////////////////////////////////////////////// @@ -65,15 +68,15 @@ AlphaISA::initCPU(ExecContext *xc, int cpuId) // // void -AlphaISA::initIPRs(ExecContext *xc, int cpuId) +AlphaISA::initIPRs(ThreadContext *tc, int cpuId) { for (int i = 0; i < NumInternalProcRegs; ++i) { - xc->setMiscReg(i, 0); + tc->setMiscReg(i, 0); } - xc->setMiscReg(IPR_PAL_BASE, PalBase); - xc->setMiscReg(IPR_MCSR, 0x6); - xc->setMiscReg(IPR_PALtemp16, cpuId); + tc->setMiscReg(IPR_PAL_BASE, PalBase); + tc->setMiscReg(IPR_MCSR, 0x6); + tc->setMiscReg(IPR_PALtemp16, cpuId); } @@ -132,12 +135,12 @@ AlphaISA::zeroRegisters(CPU *cpu) // Insure ISA semantics // (no longer very clean due to the change in setIntReg() in the // cpu model. Consider changing later.) - cpu->cpuXC->setIntReg(ZeroReg, 0); - cpu->cpuXC->setFloatReg(ZeroReg, 0.0); + cpu->thread->setIntReg(ZeroReg, 0); + cpu->thread->setFloatReg(ZeroReg, 0.0); } Fault -CPUExecContext::hwrei() +SimpleThread::hwrei() { if (!inPalMode()) return new UnimplementedOpcodeFault; @@ -145,7 +148,8 @@ CPUExecContext::hwrei() setNextPC(readMiscReg(AlphaISA::IPR_EXC_ADDR)); if (!misspeculating()) { - cpu->kernelStats->hwrei(); + if (kernelStats) + kernelStats->hwrei(); cpu->checkInterrupts = true; } @@ -167,7 +171,7 @@ AlphaISA::MiscRegFile::getDataAsid() } AlphaISA::MiscReg -AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ExecContext *xc) +AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ThreadContext *tc) { uint64_t retval = 0; // return value, default 0 @@ -221,7 +225,7 @@ AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ExecContext *xc) case AlphaISA::IPR_CC: retval |= ipr[idx] & ULL(0xffffffff00000000); - retval |= xc->getCpuPtr()->curCycle() & ULL(0x00000000ffffffff); + retval |= tc->getCpuPtr()->curCycle() & ULL(0x00000000ffffffff); break; case AlphaISA::IPR_VA: @@ -238,7 +242,7 @@ AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ExecContext *xc) case AlphaISA::IPR_DTB_PTE: { - AlphaISA::PTE &pte = xc->getDTBPtr()->index(!xc->misspeculating()); + AlphaISA::PTE &pte = tc->getDTBPtr()->index(!tc->misspeculating()); retval |= ((u_int64_t)pte.ppn & ULL(0x7ffffff)) << 32; retval |= ((u_int64_t)pte.xre & ULL(0xf)) << 8; @@ -278,11 +282,11 @@ int break_ipl = -1; #endif Fault -AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) +AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ThreadContext *tc) { uint64_t old; - if (xc->misspeculating()) + if (tc->misspeculating()) return NoFault; switch (idx) { @@ -335,7 +339,8 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // write entire quad w/ no side-effect old = ipr[idx]; ipr[idx] = val; - xc->getCpuPtr()->kernelStats->context(old, val, xc); + if (tc->getKernelStats()) + tc->getKernelStats()->context(old, val, tc); break; case AlphaISA::IPR_DTB_PTE: @@ -362,14 +367,18 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // only write least significant five bits - interrupt level ipr[idx] = val & 0x1f; - xc->getCpuPtr()->kernelStats->swpipl(ipr[idx]); + if (tc->getKernelStats()) + tc->getKernelStats()->swpipl(ipr[idx]); break; case AlphaISA::IPR_DTB_CM: - if (val & 0x18) - xc->getCpuPtr()->kernelStats->mode(Kernel::user, xc); - else - xc->getCpuPtr()->kernelStats->mode(Kernel::kernel, xc); + if (val & 0x18) { + if (tc->getKernelStats()) + tc->getKernelStats()->mode(Kernel::user, tc); + } else { + if (tc->getKernelStats()) + tc->getKernelStats()->mode(Kernel::kernel, tc); + } case AlphaISA::IPR_ICM: // only write two mode bits - processor mode @@ -443,21 +452,21 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // really a control write ipr[idx] = 0; - xc->getDTBPtr()->flushAll(); + tc->getDTBPtr()->flushAll(); break; case AlphaISA::IPR_DTB_IAP: // really a control write ipr[idx] = 0; - xc->getDTBPtr()->flushProcesses(); + tc->getDTBPtr()->flushProcesses(); break; case AlphaISA::IPR_DTB_IS: // really a control write ipr[idx] = val; - xc->getDTBPtr()->flushAddr(val, + tc->getDTBPtr()->flushAddr(val, DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN])); break; @@ -481,7 +490,7 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) pte.asn = DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN]); // insert new TAG/PTE value into data TLB - xc->getDTBPtr()->insert(val, pte); + tc->getDTBPtr()->insert(val, pte); } break; @@ -505,7 +514,7 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) pte.asn = ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN]); // insert new TAG/PTE value into data TLB - xc->getITBPtr()->insert(ipr[AlphaISA::IPR_ITB_TAG], pte); + tc->getITBPtr()->insert(ipr[AlphaISA::IPR_ITB_TAG], pte); } break; @@ -513,21 +522,21 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // really a control write ipr[idx] = 0; - xc->getITBPtr()->flushAll(); + tc->getITBPtr()->flushAll(); break; case AlphaISA::IPR_ITB_IAP: // really a control write ipr[idx] = 0; - xc->getITBPtr()->flushProcesses(); + tc->getITBPtr()->flushProcesses(); break; case AlphaISA::IPR_ITB_IS: // really a control write ipr[idx] = val; - xc->getITBPtr()->flushAddr(val, + tc->getITBPtr()->flushAddr(val, ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN])); break; @@ -541,7 +550,7 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) } void -AlphaISA::copyIprs(ExecContext *src, ExecContext *dest) +AlphaISA::copyIprs(ThreadContext *src, ThreadContext *dest) { for (int i = IPR_Base_DepTag; i < NumInternalProcRegs; ++i) { dest->setMiscReg(i, src->readMiscReg(i)); @@ -553,15 +562,16 @@ AlphaISA::copyIprs(ExecContext *src, ExecContext *dest) * If return value is false, actual PAL call will be suppressed. */ bool -CPUExecContext::simPalCheck(int palFunc) +SimpleThread::simPalCheck(int palFunc) { - cpu->kernelStats->callpal(palFunc, proxy); + if (kernelStats) + kernelStats->callpal(palFunc, tc); switch (palFunc) { case PAL::halt: halt(); if (--System::numSystemsRunning == 0) - new SimExitEvent("all cpus halted"); + exitSimLoop("all cpus halted"); break; case PAL::bpt: diff --git a/src/arch/alpha/ev5.hh b/src/arch/alpha/ev5.hh index 7c8465cfb..4dd225786 100644 --- a/src/arch/alpha/ev5.hh +++ b/src/arch/alpha/ev5.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Ali Saidi */ #ifndef __ARCH_ALPHA_EV5_HH__ diff --git a/src/arch/alpha/faults.cc b/src/arch/alpha/faults.cc index 0083aa9f3..8493223ff 100644 --- a/src/arch/alpha/faults.cc +++ b/src/arch/alpha/faults.cc @@ -24,10 +24,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Kevin Lim */ #include "arch/alpha/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "base/trace.hh" #if FULL_SYSTEM @@ -107,67 +110,67 @@ FaultStat IntegerOverflowFault::_count; #if FULL_SYSTEM -void AlphaFault::invoke(ExecContext * xc) +void AlphaFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); countStat()++; // exception restart address - if (setRestartAddress() || !xc->inPalMode()) - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, xc->readPC()); + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, tc->readPC()); if (skipFaultingInstruction()) { // traps... skip faulting instruction. - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, - xc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, + tc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); } - xc->setPC(xc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect()); - xc->setNextPC(xc->readPC() + sizeof(MachInst)); + tc->setPC(tc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); } -void ArithmeticFault::invoke(ExecContext * xc) +void ArithmeticFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); panic("Arithmetic traps are unimplemented!"); } -void DtbFault::invoke(ExecContext * xc) +void DtbFault::invoke(ThreadContext * tc) { // Set fault address and flags. Even though we're modeling an // EV5, we use the EV6 technique of not latching fault registers // on VPTE loads (instead of locking the registers until IPR_VA is // read, like the EV5). The EV6 approach is cleaner and seems to // work with EV5 PAL code, but not the other way around. - if (!xc->misspeculating() + if (!tc->misspeculating() && !(reqFlags & VPTE) && !(reqFlags & NO_FAULT)) { // set VA register with faulting address - xc->setMiscReg(AlphaISA::IPR_VA, vaddr); + tc->setMiscReg(AlphaISA::IPR_VA, vaddr); // set MM_STAT register flags - xc->setMiscReg(AlphaISA::IPR_MM_STAT, - (((EV5::Opcode(xc->getInst()) & 0x3f) << 11) - | ((EV5::Ra(xc->getInst()) & 0x1f) << 6) + tc->setMiscReg(AlphaISA::IPR_MM_STAT, + (((EV5::Opcode(tc->getInst()) & 0x3f) << 11) + | ((EV5::Ra(tc->getInst()) & 0x1f) << 6) | (flags & 0x3f))); // set VA_FORM register with faulting formatted address - xc->setMiscReg(AlphaISA::IPR_VA_FORM, - xc->readMiscReg(AlphaISA::IPR_MVPTBR) | (vaddr.vpn() << 3)); + tc->setMiscReg(AlphaISA::IPR_VA_FORM, + tc->readMiscReg(AlphaISA::IPR_MVPTBR) | (vaddr.vpn() << 3)); } - AlphaFault::invoke(xc); + AlphaFault::invoke(tc); } -void ItbFault::invoke(ExecContext * xc) +void ItbFault::invoke(ThreadContext * tc) { - if (!xc->misspeculating()) { - xc->setMiscReg(AlphaISA::IPR_ITB_TAG, pc); - xc->setMiscReg(AlphaISA::IPR_IFAULT_VA_FORM, - xc->readMiscReg(AlphaISA::IPR_IVPTBR) | + if (!tc->misspeculating()) { + tc->setMiscReg(AlphaISA::IPR_ITB_TAG, pc); + tc->setMiscReg(AlphaISA::IPR_IFAULT_VA_FORM, + tc->readMiscReg(AlphaISA::IPR_IVPTBR) | (AlphaISA::VAddr(pc).vpn() << 3)); } - AlphaFault::invoke(xc); + AlphaFault::invoke(tc); } #endif diff --git a/src/arch/alpha/faults.hh b/src/arch/alpha/faults.hh index e8ccc6b79..f952cf9d6 100644 --- a/src/arch/alpha/faults.hh +++ b/src/arch/alpha/faults.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Kevin Lim */ #ifndef __ALPHA_FAULTS_HH__ @@ -46,7 +49,7 @@ class AlphaFault : public FaultBase virtual bool setRestartAddress() {return true;} public: #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif virtual FaultVect vect() = 0; virtual FaultStat & countStat() = 0; @@ -113,7 +116,7 @@ class ArithmeticFault : public AlphaFault FaultVect vect() {return _vect;} FaultStat & countStat() {return _count;} #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; @@ -147,7 +150,7 @@ class DtbFault : public AlphaFault FaultVect vect() = 0; FaultStat & countStat() = 0; #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; @@ -248,7 +251,7 @@ class ItbFault : public AlphaFault FaultVect vect() = 0; FaultStat & countStat() = 0; #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; diff --git a/src/arch/alpha/freebsd/system.cc b/src/arch/alpha/freebsd/system.cc index 3e50fb9a5..7cf68e0db 100644 --- a/src/arch/alpha/freebsd/system.cc +++ b/src/arch/alpha/freebsd/system.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ben Nash */ /** @@ -36,7 +38,7 @@ #include "arch/alpha/system.hh" #include "arch/alpha/freebsd/system.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/physical.hh" #include "mem/port.hh" #include "arch/isa_traits.hh" @@ -70,13 +72,13 @@ FreebsdAlphaSystem::~FreebsdAlphaSystem() void -FreebsdAlphaSystem::doCalibrateClocks(ExecContext *xc) +FreebsdAlphaSystem::doCalibrateClocks(ThreadContext *tc) { Addr ppc_vaddr = 0; Addr timer_vaddr = 0; - ppc_vaddr = (Addr)xc->readIntReg(ArgumentReg1); - timer_vaddr = (Addr)xc->readIntReg(ArgumentReg2); + ppc_vaddr = (Addr)tc->readIntReg(ArgumentReg1); + timer_vaddr = (Addr)tc->readIntReg(ArgumentReg2); virtPort.write(ppc_vaddr, (uint32_t)Clock::Frequency); virtPort.write(timer_vaddr, (uint32_t)TIMER_FREQUENCY); @@ -84,10 +86,10 @@ FreebsdAlphaSystem::doCalibrateClocks(ExecContext *xc) void -FreebsdAlphaSystem::SkipCalibrateClocksEvent::process(ExecContext *xc) +FreebsdAlphaSystem::SkipCalibrateClocksEvent::process(ThreadContext *tc) { - SkipFuncEvent::process(xc); - ((FreebsdAlphaSystem *)xc->getSystemPtr())->doCalibrateClocks(xc); + SkipFuncEvent::process(tc); + ((FreebsdAlphaSystem *)tc->getSystemPtr())->doCalibrateClocks(tc); } @@ -107,10 +109,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<string> binned_fns; - Param<bool> bin_int; - END_DECLARE_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) @@ -125,10 +123,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned"), - INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) END_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) @@ -146,9 +141,6 @@ CREATE_SIM_OBJECT(FreebsdAlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = bin_int; return new FreebsdAlphaSystem(p); } diff --git a/src/arch/alpha/freebsd/system.hh b/src/arch/alpha/freebsd/system.hh index 5d996955e..e0d874e8f 100644 --- a/src/arch/alpha/freebsd/system.hh +++ b/src/arch/alpha/freebsd/system.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ben Nash */ #ifndef __KERN_FREEBSD_FREEBSD_SYSTEM_HH__ @@ -40,7 +42,7 @@ class FreebsdAlphaSystem : public AlphaSystem SkipCalibrateClocksEvent(PCEventQueue *q, const std::string &desc, Addr addr) : SkipFuncEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; SkipFuncEvent *skipDelayEvent; @@ -49,7 +51,7 @@ class FreebsdAlphaSystem : public AlphaSystem public: FreebsdAlphaSystem(Params *p); ~FreebsdAlphaSystem(); - void doCalibrateClocks(ExecContext *xc); + void doCalibrateClocks(ThreadContext *tc); }; diff --git a/src/arch/alpha/isa/branch.isa b/src/arch/alpha/isa/branch.isa index f9a425ed2..7438e7e18 100644 --- a/src/arch/alpha/isa/branch.isa +++ b/src/arch/alpha/isa/branch.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // @@ -104,7 +106,7 @@ output header {{ { } - Addr branchTarget(ExecContext *xc) const; + Addr branchTarget(ThreadContext *tc) const; std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; @@ -119,10 +121,10 @@ output decoder {{ } Addr - Jump::branchTarget(ExecContext *xc) const + Jump::branchTarget(ThreadContext *tc) const { - Addr NPC = xc->readPC() + 4; - uint64_t Rb = xc->readIntReg(_srcRegIdx[0]); + Addr NPC = tc->readPC() + 4; + uint64_t Rb = tc->readIntReg(_srcRegIdx[0]); return (Rb & ~3) | (NPC & 1); } diff --git a/src/arch/alpha/isa/decoder.isa b/src/arch/alpha/isa/decoder.isa index f41c46ac1..fbdb119b6 100644 --- a/src/arch/alpha/isa/decoder.isa +++ b/src/arch/alpha/isa/decoder.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // @@ -78,7 +80,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 = IsStoreConditional); 0x2f: stq_c({{ Mem.uq = Ra; }}, {{ uint64_t tmp = write_result; @@ -90,7 +92,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 = IsStoreConditional); } format IntegerOperate { @@ -596,8 +598,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; }}, IsIprAccess); + 0x025: mf_fpcr({{ Fa.uq = FPCR; }}, IsIprAccess); } } @@ -628,7 +630,7 @@ decode OPCODE default Unknown::unknown() { #else Ra = curTick; #endif - }}); + }}, IsUnverifiable); // All of the barrier instructions below do nothing in // their execute() methods (hence the empty code blocks). @@ -646,8 +648,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); } @@ -695,15 +697,15 @@ decode OPCODE default Unknown::unknown() { 0x00: decode PALFUNC { format EmulatedCallPal { 0x00: halt ({{ - SimExit(curTick, "halt instruction encountered"); + exitSimLoop(curTick, "halt instruction encountered"); }}, IsNonSpeculative); 0x83: callsys({{ xc->syscall(R0); }}, IsNonSpeculative); // Read uniq reg into ABI return value register (r0) - 0x9e: rduniq({{ R0 = Runiq; }}); + 0x9e: rduniq({{ R0 = Runiq; }}, IsIprAccess); // Write uniq reg with value from ABI arg register (r16) - 0x9f: wruniq({{ Runiq = R16; }}); + 0x9f: wruniq({{ Runiq = R16; }}, IsIprAccess); } } #endif @@ -740,7 +742,7 @@ decode OPCODE default Unknown::unknown() { format HwMoveIPR { 1: hw_mfpr({{ Ra = xc->readMiscRegWithEffect(ipr_index, fault); - }}); + }}, IsIprAccess); } } @@ -750,69 +752,69 @@ decode OPCODE default Unknown::unknown() { 1: hw_mtpr({{ xc->setMiscRegWithEffect(ipr_index, Ra); if (traceData) { traceData->setData(Ra); } - }}); + }}, IsIprAccess); } } 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 0x01: decode M5FUNC { 0x00: arm({{ - AlphaPseudo::arm(xc->xcBase()); + AlphaPseudo::arm(xc->tcBase()); }}, IsNonSpeculative); 0x01: quiesce({{ - AlphaPseudo::quiesce(xc->xcBase()); - }}, IsNonSpeculative); + AlphaPseudo::quiesce(xc->tcBase()); + }}, IsNonSpeculative, IsQuiesce); 0x02: quiesceNs({{ - AlphaPseudo::quiesceNs(xc->xcBase(), R16); - }}, IsNonSpeculative); + AlphaPseudo::quiesceNs(xc->tcBase(), R16); + }}, IsNonSpeculative, IsQuiesce); 0x03: quiesceCycles({{ - AlphaPseudo::quiesceCycles(xc->xcBase(), R16); - }}, IsNonSpeculative); + AlphaPseudo::quiesceCycles(xc->tcBase(), R16); + }}, IsNonSpeculative, IsQuiesce); 0x04: quiesceTime({{ - R0 = AlphaPseudo::quiesceTime(xc->xcBase()); + R0 = AlphaPseudo::quiesceTime(xc->tcBase()); }}, IsNonSpeculative); 0x10: ivlb({{ - AlphaPseudo::ivlb(xc->xcBase()); + AlphaPseudo::ivlb(xc->tcBase()); }}, No_OpClass, IsNonSpeculative); 0x11: ivle({{ - AlphaPseudo::ivle(xc->xcBase()); + AlphaPseudo::ivle(xc->tcBase()); }}, No_OpClass, IsNonSpeculative); 0x20: m5exit_old({{ - AlphaPseudo::m5exit_old(xc->xcBase()); + AlphaPseudo::m5exit_old(xc->tcBase()); }}, No_OpClass, IsNonSpeculative); 0x21: m5exit({{ - AlphaPseudo::m5exit(xc->xcBase(), R16); + AlphaPseudo::m5exit(xc->tcBase(), R16); }}, No_OpClass, IsNonSpeculative); - 0x30: initparam({{ Ra = xc->xcBase()->getCpuPtr()->system->init_param; }}); + 0x30: initparam({{ Ra = xc->tcBase()->getCpuPtr()->system->init_param; }}); 0x40: resetstats({{ - AlphaPseudo::resetstats(xc->xcBase(), R16, R17); + AlphaPseudo::resetstats(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x41: dumpstats({{ - AlphaPseudo::dumpstats(xc->xcBase(), R16, R17); + AlphaPseudo::dumpstats(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x42: dumpresetstats({{ - AlphaPseudo::dumpresetstats(xc->xcBase(), R16, R17); + AlphaPseudo::dumpresetstats(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x43: m5checkpoint({{ - AlphaPseudo::m5checkpoint(xc->xcBase(), R16, R17); + AlphaPseudo::m5checkpoint(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x50: m5readfile({{ - R0 = AlphaPseudo::readfile(xc->xcBase(), R16, R17, R18); + R0 = AlphaPseudo::readfile(xc->tcBase(), R16, R17, R18); }}, IsNonSpeculative); 0x51: m5break({{ - AlphaPseudo::debugbreak(xc->xcBase()); + AlphaPseudo::debugbreak(xc->tcBase()); }}, IsNonSpeculative); 0x52: m5switchcpu({{ - AlphaPseudo::switchcpu(xc->xcBase()); + AlphaPseudo::switchcpu(xc->tcBase()); }}, IsNonSpeculative); 0x53: m5addsymbol({{ - AlphaPseudo::addsymbol(xc->xcBase(), R16, R17); + AlphaPseudo::addsymbol(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x54: m5panic({{ panic("M5 panic instruction called at pc=%#x.", xc->readPC()); diff --git a/src/arch/alpha/isa/fp.isa b/src/arch/alpha/isa/fp.isa index f855ca3a9..b4339a1b7 100644 --- a/src/arch/alpha/isa/fp.isa +++ b/src/arch/alpha/isa/fp.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa/int.isa b/src/arch/alpha/isa/int.isa index e097c8467..45e096ebd 100644 --- a/src/arch/alpha/isa/int.isa +++ b/src/arch/alpha/isa/int.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa/main.isa b/src/arch/alpha/isa/main.isa index 25d9cc6e9..1270bf8d8 100644 --- a/src/arch/alpha/isa/main.isa +++ b/src/arch/alpha/isa/main.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // @@ -54,7 +56,7 @@ output decoder {{ #include "base/fenv.hh" #include "base/loader/symtab.hh" #include "config/ss_compatible_fp.hh" -#include "cpu/exec_context.hh" // for Jump::branchTarget() +#include "cpu/thread_context.hh" // for Jump::branchTarget() #include <math.h> diff --git a/src/arch/alpha/isa/mem.isa b/src/arch/alpha/isa/mem.isa index b2e6fc07e..08a0a2343 100644 --- a/src/arch/alpha/isa/mem.isa +++ b/src/arch/alpha/isa/mem.isa @@ -25,6 +25,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt +// Kevin Lim //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa/opcdec.isa b/src/arch/alpha/isa/opcdec.isa index add4704dd..d279ae050 100644 --- a/src/arch/alpha/isa/opcdec.isa +++ b/src/arch/alpha/isa/opcdec.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Kevin Lim //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa/pal.isa b/src/arch/alpha/isa/pal.isa index 93640f274..f4c10da1d 100644 --- a/src/arch/alpha/isa/pal.isa +++ b/src/arch/alpha/isa/pal.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // @@ -264,9 +266,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/src/arch/alpha/isa/unimp.isa b/src/arch/alpha/isa/unimp.isa index c96498764..6cfaa6991 100644 --- a/src/arch/alpha/isa/unimp.isa +++ b/src/arch/alpha/isa/unimp.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa/unknown.isa b/src/arch/alpha/isa/unknown.isa index fb0cee54e..1e95ccf68 100644 --- a/src/arch/alpha/isa/unknown.isa +++ b/src/arch/alpha/isa/unknown.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa/util.isa b/src/arch/alpha/isa/util.isa index b4ee9cc8d..8700d1e0b 100644 --- a/src/arch/alpha/isa/util.isa +++ b/src/arch/alpha/isa/util.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/alpha/isa_traits.hh b/src/arch/alpha/isa_traits.hh index 65c72115b..663b144ab 100644 --- a/src/arch/alpha/isa_traits.hh +++ b/src/arch/alpha/isa_traits.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Gabe Black */ #ifndef __ARCH_ALPHA_ISA_TRAITS_HH__ diff --git a/src/arch/alpha/linux/aligned.hh b/src/arch/alpha/linux/aligned.hh index cabecb283..c4687e348 100644 --- a/src/arch/alpha/linux/aligned.hh +++ b/src/arch/alpha/linux/aligned.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_ALIGNED_HH__ diff --git a/src/arch/alpha/linux/linux.cc b/src/arch/alpha/linux/linux.cc index f123ae1fe..bc0d48e0d 100644 --- a/src/arch/alpha/linux/linux.cc +++ b/src/arch/alpha/linux/linux.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #include "arch/alpha/linux/linux.hh" diff --git a/src/arch/alpha/linux/linux.hh b/src/arch/alpha/linux/linux.hh index f04e2bfa8..09988bab2 100644 --- a/src/arch/alpha/linux/linux.hh +++ b/src/arch/alpha/linux/linux.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ALPHA_ALPHA_LINUX_HH diff --git a/src/arch/alpha/linux/process.cc b/src/arch/alpha/linux/process.cc index 9f4f65db8..997c78ac9 100644 --- a/src/arch/alpha/linux/process.cc +++ b/src/arch/alpha/linux/process.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include "arch/alpha/linux/linux.hh" @@ -31,7 +34,7 @@ #include "arch/alpha/isa_traits.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/linux.hh" #include "sim/process.hh" @@ -45,9 +48,9 @@ using namespace AlphaISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Linux::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "Linux"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -55,7 +58,7 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); strcpy(name->machine, "alpha"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } @@ -64,18 +67,18 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, /// different in practice from those used by Tru64 processes. static SyscallReturn osf_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 45: { // GSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR *fpcr = 0; - fpcr.copyOut(xc->getMemPort()); + fpcr.copyOut(tc->getMemPort()); return 0; } @@ -91,17 +94,17 @@ osf_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target osf_setsysinfo() handler. static SyscallReturn osf_setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 14: { // SSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR - fpcr.copyIn(xc->getMemPort()); + fpcr.copyIn(tc->getMemPort()); DPRINTFR(SyscallVerbose, "osf_setsysinfo(SSI_IEEE_FP_CONTROL): " " setting FPCR to 0x%x\n", gtoh(*(uint64_t*)fpcr)); return 0; diff --git a/src/arch/alpha/linux/process.hh b/src/arch/alpha/linux/process.hh index 2e0566665..9cc7dc7b6 100644 --- a/src/arch/alpha/linux/process.hh +++ b/src/arch/alpha/linux/process.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __ALPHA_LINUX_PROCESS_HH__ diff --git a/src/arch/alpha/linux/system.cc b/src/arch/alpha/linux/system.cc index cdb96096c..bb35f046d 100644 --- a/src/arch/alpha/linux/system.cc +++ b/src/arch/alpha/linux/system.cc @@ -24,6 +24,11 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Lisa Hsu + * Nathan Binkert + * Steve Reinhardt */ /** @@ -41,7 +46,7 @@ #include "arch/alpha/linux/threadinfo.hh" #include "arch/alpha/system.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "dev/platform.hh" #include "kern/linux/printk.hh" @@ -132,24 +137,6 @@ LinuxAlphaSystem::LinuxAlphaSystem(Params *p) } else { printThreadEvent = NULL; } - - if (params()->bin_int) { - intStartEvent = addPalFuncEvent<InterruptStartEvent>("sys_int_21"); - if (!intStartEvent) - panic("could not find symbol: sys_int_21\n"); - - intEndEvent = addPalFuncEvent<InterruptEndEvent>("rti_to_kern"); - if (!intEndEvent) - panic("could not find symbol: rti_to_kern\n"); - - intEndEvent2 = addPalFuncEvent<InterruptEndEvent>("rti_to_user"); - if (!intEndEvent2) - panic("could not find symbol: rti_to_user\n"); - - intEndEvent3 = addKernelFuncEvent<InterruptEndEvent>("do_softirq"); - if (!intEndEvent3) - panic("could not find symbol: do_softirq\n"); - } } LinuxAlphaSystem::~LinuxAlphaSystem() @@ -170,30 +157,33 @@ LinuxAlphaSystem::~LinuxAlphaSystem() void -LinuxAlphaSystem::setDelayLoop(ExecContext *xc) +LinuxAlphaSystem::setDelayLoop(ThreadContext *tc) { Addr addr = 0; if (kernelSymtab->findAddress("loops_per_jiffy", addr)) { - Tick cpuFreq = xc->getCpuPtr()->frequency(); + Tick cpuFreq = tc->getCpuPtr()->frequency(); Tick intrFreq = platform->intrFrequency(); - xc->getVirtPort(xc)->write(addr, - (uint32_t)((cpuFreq / intrFreq) * 0.9988)); + VirtualPort *vp; + + vp = tc->getVirtPort(); + vp->writeHtoG(addr, (uint32_t)((cpuFreq / intrFreq) * 0.9988)); + tc->delVirtPort(vp); } } void -LinuxAlphaSystem::SkipDelayLoopEvent::process(ExecContext *xc) +LinuxAlphaSystem::SkipDelayLoopEvent::process(ThreadContext *tc) { - SkipFuncEvent::process(xc); + SkipFuncEvent::process(tc); // calculate and set loops_per_jiffy - ((LinuxAlphaSystem *)xc->getSystemPtr())->setDelayLoop(xc); + ((LinuxAlphaSystem *)tc->getSystemPtr())->setDelayLoop(tc); } void -LinuxAlphaSystem::PrintThreadInfo::process(ExecContext *xc) +LinuxAlphaSystem::PrintThreadInfo::process(ThreadContext *tc) { - Linux::ThreadInfo ti(xc); + Linux::ThreadInfo ti(tc); DPRINTF(Thread, "Currently Executing Thread %s, pid %d, started at: %d\n", ti.curTaskName(), ti.curTaskPID(), ti.curTaskStart()); @@ -216,10 +206,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(LinuxAlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<string> binned_fns; - Param<bool> bin_int; - END_DECLARE_SIM_OBJECT_PARAMS(LinuxAlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) @@ -234,10 +220,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned"), - INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) END_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) @@ -255,9 +238,6 @@ CREATE_SIM_OBJECT(LinuxAlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = bin_int; return new LinuxAlphaSystem(p); } diff --git a/src/arch/alpha/linux/system.hh b/src/arch/alpha/linux/system.hh index 0c1fb037e..6921ba820 100644 --- a/src/arch/alpha/linux/system.hh +++ b/src/arch/alpha/linux/system.hh @@ -24,12 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Lisa Hsu + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_SYSTEM_HH__ #define __ARCH_ALPHA_LINUX_SYSTEM_HH__ -class ExecContext; +class ThreadContext; class BreakPCEvent; class IdleStartEvent; @@ -41,7 +45,7 @@ using namespace AlphaISA; using namespace Linux; /** - * This class contains linux specific system code (Loading, Events, Binning). + * This class contains linux specific system code (Loading, Events). * It points to objects that are the system binaries to load and patches them * appropriately to work in simulator. */ @@ -53,7 +57,7 @@ class LinuxAlphaSystem : public AlphaSystem public: SkipDelayLoopEvent(PCEventQueue *q, const std::string &desc, Addr addr) : SkipFuncEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class PrintThreadInfo : public PCEvent @@ -61,7 +65,7 @@ class LinuxAlphaSystem : public AlphaSystem public: PrintThreadInfo(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; @@ -120,18 +124,6 @@ class LinuxAlphaSystem : public AlphaSystem */ PrintThreadInfo *printThreadEvent; - /** - * Event to bin Interrupts seperately from kernel code - */ - InterruptStartEvent *intStartEvent; - - /** - * Event to bin Interrupts seperately from kernel code - */ - InterruptEndEvent *intEndEvent; - InterruptEndEvent *intEndEvent2; - InterruptEndEvent *intEndEvent3; - /** Grab the PCBB of the idle process when it starts */ IdleStartEvent *idleStartEvent; @@ -139,7 +131,7 @@ class LinuxAlphaSystem : public AlphaSystem LinuxAlphaSystem(Params *p); ~LinuxAlphaSystem(); - void setDelayLoop(ExecContext *xc); + void setDelayLoop(ThreadContext *tc); }; #endif // __ARCH_ALPHA_LINUX_SYSTEM_HH__ diff --git a/src/arch/alpha/linux/thread_info.hh b/src/arch/alpha/linux/thread_info.hh index 88791b00d..78257da56 100644 --- a/src/arch/alpha/linux/thread_info.hh +++ b/src/arch/alpha/linux/thread_info.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_THREAD_INFO_H__ diff --git a/src/arch/alpha/linux/threadinfo.hh b/src/arch/alpha/linux/threadinfo.hh index 8f03c9314..caeb69f15 100644 --- a/src/arch/alpha/linux/threadinfo.hh +++ b/src/arch/alpha/linux/threadinfo.hh @@ -24,13 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_LINUX_TREADNIFO_HH__ #define __ARCH_ALPHA_LINUX_LINUX_TREADNIFO_HH__ #include "arch/alpha/linux/thread_info.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/sched.hh" #include "sim/vptr.hh" @@ -39,10 +42,10 @@ namespace Linux { class ThreadInfo { private: - ExecContext *xc; + ThreadContext *tc; public: - ThreadInfo(ExecContext *exec) : xc(exec) {} + ThreadInfo(ThreadContext *_tc) : tc(_tc) {} ~ThreadInfo() {} inline VPtr<thread_info> @@ -54,15 +57,15 @@ class ThreadInfo * thread_info struct. So we can get the address by masking off * the lower 14 bits. */ - current = xc->readIntReg(TheISA::StackPointerReg) & ~0x3fff; - return VPtr<thread_info>(xc, current); + current = tc->readIntReg(TheISA::StackPointerReg) & ~0x3fff; + return VPtr<thread_info>(tc, current); } inline VPtr<task_struct> curTaskInfo() { Addr task = curThreadInfo()->task; - return VPtr<task_struct>(xc, task); + return VPtr<task_struct>(tc, task); } std::string diff --git a/src/arch/alpha/osfpal.cc b/src/arch/alpha/osfpal.cc index a48bd28d9..ed1d255a6 100644 --- a/src/arch/alpha/osfpal.cc +++ b/src/arch/alpha/osfpal.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "arch/alpha/osfpal.hh" diff --git a/src/arch/alpha/osfpal.hh b/src/arch/alpha/osfpal.hh index f46d2bce1..cf3940b85 100644 --- a/src/arch/alpha/osfpal.hh +++ b/src/arch/alpha/osfpal.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __OSFPAL_HH__ diff --git a/src/arch/alpha/process.cc b/src/arch/alpha/process.cc index 25ee79692..970292cd8 100644 --- a/src/arch/alpha/process.cc +++ b/src/arch/alpha/process.cc @@ -24,59 +24,22 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi */ #include "arch/alpha/constants.hh" #include "arch/alpha/process.hh" -#include "arch/alpha/linux/process.hh" -#include "arch/alpha/tru64/process.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" -#include "cpu/exec_context.hh" -#include "sim/builder.hh" +#include "cpu/thread_context.hh" #include "sim/system.hh" using namespace AlphaISA; using namespace std; -AlphaLiveProcess * -AlphaLiveProcess::create(const std::string &nm, System *system, int stdin_fd, - int stdout_fd, int stderr_fd, std::string executable, - std::vector<std::string> &argv, std::vector<std::string> &envp) -{ - AlphaLiveProcess *process = NULL; - - ObjectFile *objFile = createObjectFile(executable); - if (objFile == NULL) { - fatal("Can't load object file %s", executable); - } - - - if (objFile->getArch() != ObjectFile::Alpha) - fatal("Object file does not match architecture."); - switch (objFile->getOpSys()) { - case ObjectFile::Tru64: - process = new AlphaTru64Process(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - case ObjectFile::Linux: - process = new AlphaLinuxProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - default: - fatal("Unknown/unsupported operating system."); - } - - if (process == NULL) - fatal("Unknown error creating process object."); - return process; -} - AlphaLiveProcess::AlphaLiveProcess(const std::string &nm, ObjectFile *objFile, System *_system, int stdin_fd, int stdout_fd, int stderr_fd, std::vector<std::string> &argv, std::vector<std::string> &envp) @@ -104,64 +67,7 @@ AlphaLiveProcess::startup() { argsInit(MachineBytes, VMPageSize); - execContexts[0]->setIntReg(GlobalPointerReg, objFile->globalPointer()); + threadContexts[0]->setIntReg(GlobalPointerReg, objFile->globalPointer()); } - - -BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - VectorParam<string> cmd; - Param<string> executable; - Param<string> input; - Param<string> output; - VectorParam<string> env; - SimObjectParam<System *> system; - -END_DECLARE_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - -BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - INIT_PARAM(cmd, "command line (executable plus arguments)"), - INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), - INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), - INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), - INIT_PARAM(env, "environment settings"), - INIT_PARAM(system, "system") - -END_INIT_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - -CREATE_SIM_OBJECT(AlphaLiveProcess) -{ - string in = input; - string out = output; - - // initialize file descriptors to default: same as simulator - int stdin_fd, stdout_fd, stderr_fd; - - if (in == "stdin" || in == "cin") - stdin_fd = STDIN_FILENO; - else - stdin_fd = Process::openInputFile(input); - - if (out == "stdout" || out == "cout") - stdout_fd = STDOUT_FILENO; - else if (out == "stderr" || out == "cerr") - stdout_fd = STDERR_FILENO; - else - stdout_fd = Process::openOutputFile(out); - - stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; - - return AlphaLiveProcess::create(getInstanceName(), system, - stdin_fd, stdout_fd, stderr_fd, - (string)executable == "" ? cmd[0] : executable, - cmd, env); -} - - -REGISTER_SIM_OBJECT("AlphaLiveProcess", AlphaLiveProcess) - diff --git a/src/arch/alpha/process.hh b/src/arch/alpha/process.hh index d97b36e2d..5d5c9a52e 100644 --- a/src/arch/alpha/process.hh +++ b/src/arch/alpha/process.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi */ #ifndef __ALPHA_PROCESS_HH__ @@ -46,18 +49,6 @@ class AlphaLiveProcess : public LiveProcess std::vector<std::string> &envp); void startup(); - - public: - // this function is used to create the LiveProcess object, since - // we can't tell which subclass of LiveProcess to use until we - // open and look at the object file. - static AlphaLiveProcess *create(const std::string &nm, - System *_system, - int stdin_fd, int stdout_fd, int stderr_fd, - std::string executable, - std::vector<std::string> &argv, - std::vector<std::string> &envp); - }; diff --git a/src/arch/alpha/regfile.hh b/src/arch/alpha/regfile.hh index af01b7829..1025412cd 100644 --- a/src/arch/alpha/regfile.hh +++ b/src/arch/alpha/regfile.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black */ #ifndef __ARCH_ALPHA_REGFILE_HH__ @@ -34,7 +36,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; namespace AlphaISA { @@ -60,6 +62,8 @@ namespace AlphaISA void unserialize(Checkpoint *cp, const std::string §ion); + void clear() + { bzero(regs, sizeof(regs)); } }; class FloatRegFile @@ -75,6 +79,8 @@ namespace AlphaISA void unserialize(Checkpoint *cp, const std::string §ion); + void clear() + { bzero(d, sizeof(d)); } }; class MiscRegFile { @@ -88,7 +94,7 @@ namespace AlphaISA MiscReg readReg(int misc_reg); MiscReg readRegWithEffect(int misc_reg, Fault &fault, - ExecContext *xc); + ThreadContext *tc); //These functions should be removed once the simplescalar cpu model //has been replaced. @@ -98,8 +104,14 @@ namespace AlphaISA Fault setReg(int misc_reg, const MiscReg &val); Fault setRegWithEffect(int misc_reg, const MiscReg &val, - ExecContext *xc); + ThreadContext *tc); + void clear() + { + fpcr = uniq = 0; + lock_flag = 0; + lock_addr = 0; + } #if FULL_SYSTEM protected: typedef uint64_t InternalProcReg; @@ -107,9 +119,9 @@ namespace AlphaISA InternalProcReg ipr[NumInternalProcRegs]; // Internal processor regs private: - InternalProcReg readIpr(int idx, Fault &fault, ExecContext *xc); + InternalProcReg readIpr(int idx, Fault &fault, ThreadContext *tc); - Fault setIpr(int idx, InternalProcReg val, ExecContext *xc); + Fault setIpr(int idx, InternalProcReg val, ThreadContext *tc); #endif friend class RegFile; }; @@ -169,9 +181,9 @@ namespace AlphaISA void clear() { - bzero(&intRegFile, sizeof(intRegFile)); - bzero(&floatRegFile, sizeof(floatRegFile)); - bzero(&miscRegFile, sizeof(miscRegFile)); + intRegFile.clear(); + floatRegFile.clear(); + miscRegFile.clear(); } MiscReg readMiscReg(int miscReg) @@ -180,10 +192,10 @@ namespace AlphaISA } MiscReg readMiscRegWithEffect(int miscReg, - Fault &fault, ExecContext *xc) + Fault &fault, ThreadContext *tc) { fault = NoFault; - return miscRegFile.readRegWithEffect(miscReg, fault, xc); + return miscRegFile.readRegWithEffect(miscReg, fault, tc); } Fault setMiscReg(int miscReg, const MiscReg &val) @@ -192,9 +204,9 @@ namespace AlphaISA } Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, - ExecContext * xc) + ThreadContext * tc) { - return miscRegFile.setRegWithEffect(miscReg, val, xc); + return miscRegFile.setRegWithEffect(miscReg, val, tc); } FloatReg readFloatReg(int floatReg) @@ -266,12 +278,12 @@ namespace AlphaISA } }; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); - void copyMiscRegs(ExecContext *src, ExecContext *dest); + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); #if FULL_SYSTEM - void copyIprs(ExecContext *src, ExecContext *dest); + void copyIprs(ThreadContext *src, ThreadContext *dest); #endif } // namespace AlphaISA diff --git a/src/arch/alpha/stacktrace.cc b/src/arch/alpha/stacktrace.cc index 8691e12dc..d70a4d6dd 100644 --- a/src/arch/alpha/stacktrace.cc +++ b/src/arch/alpha/stacktrace.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <string> @@ -34,36 +36,42 @@ #include "base/bitfield.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "sim/system.hh" using namespace std; using namespace AlphaISA; -ProcessInfo::ProcessInfo(ExecContext *_xc) - : xc(_xc) +ProcessInfo::ProcessInfo(ThreadContext *_tc) + : tc(_tc) { Addr addr = 0; - if (!xc->getSystemPtr()->kernelSymtab->findAddress("thread_info_size", addr)) + VirtualPort *vp; + + vp = tc->getVirtPort(); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("thread_info_size", addr)) panic("thread info not compiled into kernel\n"); - thread_info_size = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + thread_info_size = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("task_struct_size", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_size", addr)) panic("thread info not compiled into kernel\n"); - task_struct_size = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + task_struct_size = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("thread_info_task", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("thread_info_task", addr)) panic("thread info not compiled into kernel\n"); - task_off = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + task_off = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("task_struct_pid", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_pid", addr)) panic("thread info not compiled into kernel\n"); - pid_off = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + pid_off = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("task_struct_comm", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_comm", addr)) panic("thread info not compiled into kernel\n"); - name_off = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + name_off = vp->readGtoH<int32_t>(addr); + + tc->delVirtPort(vp); } Addr @@ -73,7 +81,15 @@ ProcessInfo::task(Addr ksp) const if (base == ULL(0xfffffc0000000000)) return 0; - return gtoh(xc->getVirtPort()->read<Addr>(base + task_off)); + Addr tsk; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + tsk = vp->readGtoH<Addr>(base + task_off); + tc->delVirtPort(vp); + + return tsk; } int @@ -83,7 +99,15 @@ ProcessInfo::pid(Addr ksp) const if (!task) return -1; - return gtoh(xc->getVirtPort()->read<uint16_t>(task + pid_off)); + uint16_t pd; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + pd = vp->readGtoH<uint16_t>(task + pid_off); + tc->delVirtPort(vp); + + return pd; } string @@ -94,7 +118,7 @@ ProcessInfo::name(Addr ksp) const return "console"; char comm[256]; - CopyStringOut(xc, comm, task + name_off, sizeof(comm)); + CopyStringOut(tc, comm, task + name_off, sizeof(comm)); if (!comm[0]) return "startup"; @@ -102,14 +126,14 @@ ProcessInfo::name(Addr ksp) const } StackTrace::StackTrace() - : xc(0), stack(64) + : tc(0), stack(64) { } -StackTrace::StackTrace(ExecContext *_xc, StaticInstPtr inst) - : xc(0), stack(64) +StackTrace::StackTrace(ThreadContext *_tc, StaticInstPtr inst) + : tc(0), stack(64) { - trace(_xc, inst); + trace(_tc, inst); } StackTrace::~StackTrace() @@ -117,15 +141,15 @@ StackTrace::~StackTrace() } void -StackTrace::trace(ExecContext *_xc, bool is_call) +StackTrace::trace(ThreadContext *_tc, bool is_call) { - xc = _xc; + tc = _tc; - bool usermode = (xc->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; + bool usermode = (tc->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; - Addr pc = xc->readNextPC(); - bool kernel = xc->getSystemPtr()->kernelStart <= pc && - pc <= xc->getSystemPtr()->kernelEnd; + Addr pc = tc->readNextPC(); + bool kernel = tc->getSystemPtr()->kernelStart <= pc && + pc <= tc->getSystemPtr()->kernelEnd; if (usermode) { stack.push_back(user); @@ -137,8 +161,8 @@ StackTrace::trace(ExecContext *_xc, bool is_call) return; } - SymbolTable *symtab = xc->getSystemPtr()->kernelSymtab; - Addr ksp = xc->readIntReg(TheISA::StackPointerReg); + SymbolTable *symtab = tc->getSystemPtr()->kernelSymtab; + Addr ksp = tc->readIntReg(TheISA::StackPointerReg); Addr bottom = ksp & ~0x3fff; Addr addr; @@ -147,7 +171,7 @@ StackTrace::trace(ExecContext *_xc, bool is_call) panic("could not find address %#x", pc); stack.push_back(addr); - pc = xc->readPC(); + pc = tc->readPC(); } Addr ra; @@ -179,8 +203,8 @@ StackTrace::trace(ExecContext *_xc, bool is_call) return; } - bool kernel = xc->getSystemPtr()->kernelStart <= pc && - pc <= xc->getSystemPtr()->kernelEnd; + bool kernel = tc->getSystemPtr()->kernelStart <= pc && + pc <= tc->getSystemPtr()->kernelEnd; if (!kernel) return; @@ -194,22 +218,22 @@ StackTrace::trace(ExecContext *_xc, bool is_call) bool StackTrace::isEntry(Addr addr) { - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp12)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp12)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp7)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp7)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp11)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp11)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp21)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp21)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp9)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp9)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp2)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp2)) return true; return false; @@ -294,7 +318,7 @@ StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, for (Addr pc = func; pc < callpc; pc += sizeof(MachInst)) { MachInst inst; - CopyOut(xc, (uint8_t *)&inst, pc, sizeof(MachInst)); + CopyOut(tc, (uint8_t *)&inst, pc, sizeof(MachInst)); int reg, disp; if (decodeStack(inst, disp)) { @@ -305,7 +329,7 @@ StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, size += disp; } else if (decodeSave(inst, reg, disp)) { if (!ra && reg == ReturnAddressReg) { - CopyOut(xc, (uint8_t *)&ra, sp + disp, sizeof(Addr)); + CopyOut(tc, (uint8_t *)&ra, sp + disp, sizeof(Addr)); if (!ra) { // panic("no return address value pc=%#x\n", pc); return false; @@ -321,8 +345,8 @@ StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, void StackTrace::dump() { - StringWrap name(xc->getCpuPtr()->name()); - SymbolTable *symtab = xc->getSystemPtr()->kernelSymtab; + StringWrap name(tc->getCpuPtr()->name()); + SymbolTable *symtab = tc->getSystemPtr()->kernelSymtab; DPRINTFN("------ Stack ------\n"); diff --git a/src/arch/alpha/stacktrace.hh b/src/arch/alpha/stacktrace.hh index 1d8d97a79..d12aee211 100644 --- a/src/arch/alpha/stacktrace.hh +++ b/src/arch/alpha/stacktrace.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_STACKTRACE_HH__ @@ -32,13 +34,13 @@ #include "base/trace.hh" #include "cpu/static_inst.hh" -class ExecContext; +class ThreadContext; class StackTrace; class ProcessInfo { private: - ExecContext *xc; + ThreadContext *tc; int thread_info_size; int task_struct_size; @@ -47,7 +49,7 @@ class ProcessInfo int name_off; public: - ProcessInfo(ExecContext *_xc); + ProcessInfo(ThreadContext *_tc); Addr task(Addr ksp) const; int pid(Addr ksp) const; @@ -59,7 +61,7 @@ class StackTrace protected: typedef TheISA::MachInst MachInst; private: - ExecContext *xc; + ThreadContext *tc; std::vector<Addr> stack; private: @@ -68,21 +70,21 @@ class StackTrace bool decodeSave(MachInst inst, int ®, int &disp); bool decodeStack(MachInst inst, int &disp); - void trace(ExecContext *xc, bool is_call); + void trace(ThreadContext *tc, bool is_call); public: StackTrace(); - StackTrace(ExecContext *xc, StaticInstPtr inst); + StackTrace(ThreadContext *tc, StaticInstPtr inst); ~StackTrace(); void clear() { - xc = 0; + tc = 0; stack.clear(); } - bool valid() const { return xc != NULL; } - bool trace(ExecContext *xc, StaticInstPtr inst); + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); public: const std::vector<Addr> &getstack() const { return stack; } @@ -104,7 +106,7 @@ class StackTrace }; inline bool -StackTrace::trace(ExecContext *xc, StaticInstPtr inst) +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) { if (!inst->isCall() && !inst->isReturn()) return false; @@ -112,7 +114,7 @@ StackTrace::trace(ExecContext *xc, StaticInstPtr inst) if (valid()) clear(); - trace(xc, !inst->isReturn()); + trace(tc, !inst->isReturn()); return true; } diff --git a/src/arch/alpha/system.cc b/src/arch/alpha/system.cc index 4234019cd..3aaba7d58 100644 --- a/src/arch/alpha/system.cc +++ b/src/arch/alpha/system.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include "arch/alpha/ev5.hh" @@ -230,10 +232,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<std::string> binned_fns; - Param<bool> bin_int; - END_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem) @@ -248,10 +246,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned"), - INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) END_INIT_SIM_OBJECT_PARAMS(AlphaSystem) @@ -269,9 +264,6 @@ CREATE_SIM_OBJECT(AlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = bin_int; return new AlphaSystem(p); } diff --git a/src/arch/alpha/system.hh b/src/arch/alpha/system.hh index 924e16826..b26a5e301 100644 --- a/src/arch/alpha/system.hh +++ b/src/arch/alpha/system.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __ARCH_ALPHA_SYSTEM_HH__ diff --git a/src/arch/alpha/tlb.cc b/src/arch/alpha/tlb.cc index 05b02d74b..c6684274b 100644 --- a/src/arch/alpha/tlb.cc +++ b/src/arch/alpha/tlb.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Andrew Schultz */ #include <string> @@ -34,7 +38,7 @@ #include "base/str.hh" #include "base/trace.hh" #include "config/alpha_tlaser.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "sim/builder.hh" using namespace std; @@ -282,7 +286,7 @@ AlphaITB::regStats() Fault -AlphaITB::translate(RequestPtr &req, ExecContext *xc) const +AlphaITB::translate(RequestPtr &req, ThreadContext *tc) const { if (AlphaISA::PcPAL(req->getVaddr())) { // strip off PAL PC marker (lsb is 1) @@ -304,13 +308,13 @@ AlphaITB::translate(RequestPtr &req, ExecContext *xc) const // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13> for EV5 // VA<47:41> == 0x7e, VA<40:13> maps directly to PA<40:13> for EV6 #if ALPHA_TLASER - if ((MCSR_SP(xc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && + if ((MCSR_SP(tc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && VAddrSpaceEV5(req->getVaddr()) == 2) { #else if (VAddrSpaceEV6(req->getVaddr()) == 0x7e) { #endif // only valid in kernel mode - if (ICM_CM(xc->readMiscReg(AlphaISA::IPR_ICM)) != + if (ICM_CM(tc->readMiscReg(AlphaISA::IPR_ICM)) != AlphaISA::mode_kernel) { acv++; return new ItbAcvFault(req->getVaddr()); @@ -328,7 +332,7 @@ AlphaITB::translate(RequestPtr &req, ExecContext *xc) const } else { // not a physical address: need to look up pte - int asn = DTB_ASN_ASN(xc->readMiscReg(AlphaISA::IPR_DTB_ASN)); + int asn = DTB_ASN_ASN(tc->readMiscReg(AlphaISA::IPR_DTB_ASN)); AlphaISA::PTE *pte = lookup(AlphaISA::VAddr(req->getVaddr()).vpn(), asn); @@ -343,7 +347,7 @@ AlphaITB::translate(RequestPtr &req, ExecContext *xc) const // check permissions for this access if (!(pte->xre & - (1 << ICM_CM(xc->readMiscReg(AlphaISA::IPR_ICM))))) { + (1 << ICM_CM(tc->readMiscReg(AlphaISA::IPR_ICM))))) { // instruction access fault acv++; return new ItbAcvFault(req->getVaddr()); @@ -439,12 +443,12 @@ AlphaDTB::regStats() } Fault -AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const +AlphaDTB::translate(RequestPtr &req, ThreadContext *tc, bool write) const { - Addr pc = xc->readPC(); + Addr pc = tc->readPC(); AlphaISA::mode_type mode = - (AlphaISA::mode_type)DTB_CM_CM(xc->readMiscReg(AlphaISA::IPR_DTB_CM)); + (AlphaISA::mode_type)DTB_CM_CM(tc->readMiscReg(AlphaISA::IPR_DTB_CM)); /** @@ -460,7 +464,7 @@ AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const if (pc & 0x1) { mode = (req->getFlags() & ALTMODE) ? (AlphaISA::mode_type)ALT_MODE_AM( - xc->readMiscReg(AlphaISA::IPR_ALT_MODE)) + tc->readMiscReg(AlphaISA::IPR_ALT_MODE)) : AlphaISA::mode_kernel; } @@ -478,14 +482,14 @@ AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const // Check for "superpage" mapping #if ALPHA_TLASER - if ((MCSR_SP(xc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && + if ((MCSR_SP(tc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && VAddrSpaceEV5(req->getVaddr()) == 2) { #else if (VAddrSpaceEV6(req->getVaddr()) == 0x7e) { #endif // only valid in kernel mode - if (DTB_CM_CM(xc->readMiscReg(AlphaISA::IPR_DTB_CM)) != + if (DTB_CM_CM(tc->readMiscReg(AlphaISA::IPR_DTB_CM)) != AlphaISA::mode_kernel) { if (write) { write_acv++; } else { read_acv++; } uint64_t flags = ((write ? MM_STAT_WR_MASK : 0) | @@ -509,7 +513,7 @@ AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const else read_accesses++; - int asn = DTB_ASN_ASN(xc->readMiscReg(AlphaISA::IPR_DTB_ASN)); + int asn = DTB_ASN_ASN(tc->readMiscReg(AlphaISA::IPR_DTB_ASN)); // not a physical address: need to look up pte AlphaISA::PTE *pte = lookup(AlphaISA::VAddr(req->getVaddr()).vpn(), diff --git a/src/arch/alpha/tlb.hh b/src/arch/alpha/tlb.hh index f6256020e..07d01fa5c 100644 --- a/src/arch/alpha/tlb.hh +++ b/src/arch/alpha/tlb.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ALPHA_MEMORY_HH__ @@ -38,7 +41,7 @@ #include "mem/request.hh" #include "sim/sim_object.hh" -class ExecContext; +class ThreadContext; class AlphaTLB : public SimObject { @@ -92,7 +95,7 @@ class AlphaITB : public AlphaTLB AlphaITB(const std::string &name, int size); virtual void regStats(); - Fault translate(RequestPtr &req, ExecContext *xc) const; + Fault translate(RequestPtr &req, ThreadContext *tc) const; }; class AlphaDTB : public AlphaTLB @@ -115,7 +118,7 @@ class AlphaDTB : public AlphaTLB AlphaDTB(const std::string &name, int size); virtual void regStats(); - Fault translate(RequestPtr &req, ExecContext *xc, bool write) const; + Fault translate(RequestPtr &req, ThreadContext *tc, bool write) const; }; #endif // __ALPHA_MEMORY_HH__ diff --git a/src/arch/alpha/tru64/process.cc b/src/arch/alpha/tru64/process.cc index 55f75f7d0..82e44b9e7 100644 --- a/src/arch/alpha/tru64/process.cc +++ b/src/arch/alpha/tru64/process.cc @@ -24,13 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include "arch/alpha/tru64/tru64.hh" #include "arch/alpha/isa_traits.hh" #include "arch/alpha/tru64/process.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/tru64/tru64.hh" #include "sim/process.hh" @@ -42,9 +45,9 @@ using namespace AlphaISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<AlphaTru64::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<AlphaTru64::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "OSF1"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -52,43 +55,43 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "732"); strcpy(name->machine, "alpha"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } /// Target getsysyinfo() handler. static SyscallReturn getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + unsigned nbytes = tc->getSyscallArg(2); switch (op) { case AlphaTru64::GSI_MAX_CPU: { - TypedBufferArg<uint32_t> max_cpu(xc->getSyscallArg(1)); + TypedBufferArg<uint32_t> max_cpu(tc->getSyscallArg(1)); *max_cpu = htog((uint32_t)process->numCpus()); - max_cpu.copyOut(xc->getMemPort()); + max_cpu.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_CPUS_IN_BOX: { - TypedBufferArg<uint32_t> cpus_in_box(xc->getSyscallArg(1)); + TypedBufferArg<uint32_t> cpus_in_box(tc->getSyscallArg(1)); *cpus_in_box = htog((uint32_t)process->numCpus()); - cpus_in_box.copyOut(xc->getMemPort()); + cpus_in_box.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_PHYSMEM: { - TypedBufferArg<uint64_t> physmem(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> physmem(tc->getSyscallArg(1)); *physmem = htog((uint64_t)1024 * 1024); // physical memory in KB - physmem.copyOut(xc->getMemPort()); + physmem.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_CPU_INFO: { - TypedBufferArg<AlphaTru64::cpu_info> infop(xc->getSyscallArg(1)); + TypedBufferArg<AlphaTru64::cpu_info> infop(tc->getSyscallArg(1)); infop->current_cpu = htog(0); infop->cpus_in_box = htog(process->numCpus()); @@ -100,30 +103,30 @@ getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, infop->cpu_ex_binding = htog(0); infop->mhz = htog(667); - infop.copyOut(xc->getMemPort()); + infop.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_PROC_TYPE: { - TypedBufferArg<uint64_t> proc_type(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> proc_type(tc->getSyscallArg(1)); *proc_type = htog((uint64_t)11); - proc_type.copyOut(xc->getMemPort()); + proc_type.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_PLATFORM_NAME: { - BufferArg bufArg(xc->getSyscallArg(1), nbytes); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); strncpy((char *)bufArg.bufferPtr(), "COMPAQ Professional Workstation XP1000", nbytes); - bufArg.copyOut(xc->getMemPort()); + bufArg.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_CLK_TCK: { - TypedBufferArg<uint64_t> clk_hz(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> clk_hz(tc->getSyscallArg(1)); *clk_hz = htog((uint64_t)1024); - clk_hz.copyOut(xc->getMemPort()); + clk_hz.copyOut(tc->getMemPort()); return 1; } @@ -138,14 +141,14 @@ getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target setsysyinfo() handler. static SyscallReturn setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); + unsigned op = tc->getSyscallArg(0); switch (op) { case AlphaTru64::SSI_IEEE_FP_CONTROL: warn("setsysinfo: ignoring ieee_set_fp_control() arg 0x%x\n", - xc->getSyscallArg(1)); + tc->getSyscallArg(1)); break; default: @@ -160,22 +163,22 @@ setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target table() handler. static SyscallReturn tableFunc(SyscallDesc *desc, int callnum,Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; using namespace TheISA; - int id = xc->getSyscallArg(0); // table ID - int index = xc->getSyscallArg(1); // index into table + int id = tc->getSyscallArg(0); // table ID + int index = tc->getSyscallArg(1); // index into table // arg 2 is buffer pointer; type depends on table ID - int nel = xc->getSyscallArg(3); // number of elements - int lel = xc->getSyscallArg(4); // expected element size + int nel = tc->getSyscallArg(3); // number of elements + int lel = tc->getSyscallArg(4); // expected element size switch (id) { case AlphaTru64::TBL_SYSINFO: { if (index != 0 || nel != 1 || lel != sizeof(Tru64::tbl_sysinfo)) return -EINVAL; - TypedBufferArg<Tru64::tbl_sysinfo> elp(xc->getSyscallArg(2)); + TypedBufferArg<Tru64::tbl_sysinfo> elp(tc->getSyscallArg(2)); const int clk_hz = one_million; elp->si_user = htog(curTick / (Clock::Frequency / clk_hz)); @@ -187,7 +190,7 @@ SyscallReturn tableFunc(SyscallDesc *desc, int callnum,Process *process, elp->si_phz = htog(clk_hz); elp->si_boottime = htog(seconds_since_epoch); // seconds since epoch? elp->si_max_procs = htog(process->numCpus()); - elp.copyOut(xc->getMemPort()); + elp.copyOut(tc->getMemPort()); return 0; } diff --git a/src/arch/alpha/tru64/process.hh b/src/arch/alpha/tru64/process.hh index 1cde4cac0..5d91f6ac1 100644 --- a/src/arch/alpha/tru64/process.hh +++ b/src/arch/alpha/tru64/process.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __ALPHA_TRU64_PROCESS_HH__ diff --git a/src/arch/alpha/tru64/system.cc b/src/arch/alpha/tru64/system.cc index 2ad06d679..6c0edc1ee 100644 --- a/src/arch/alpha/tru64/system.cc +++ b/src/arch/alpha/tru64/system.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Lisa Hsu */ #include "arch/alpha/tru64/system.hh" @@ -32,7 +35,7 @@ #include "base/loader/symtab.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/tru64/tru64_events.hh" #include "kern/system_events.hh" #include "mem/physical.hh" @@ -104,9 +107,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tru64AlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<string> binned_fns; - END_DECLARE_SIM_OBJECT_PARAMS(Tru64AlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) @@ -121,9 +121,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 12), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 2<<1), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned") + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 2<<1) END_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) @@ -141,9 +139,6 @@ CREATE_SIM_OBJECT(Tru64AlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = false; return new Tru64AlphaSystem(p); } diff --git a/src/arch/alpha/tru64/system.hh b/src/arch/alpha/tru64/system.hh index 0e0cc1bc8..947e92f50 100644 --- a/src/arch/alpha/tru64/system.hh +++ b/src/arch/alpha/tru64/system.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Lisa Hsu */ #ifndef __ARCH_ALPHA_TRU64_SYSTEM_HH__ @@ -33,7 +36,7 @@ #include "arch/isa_traits.hh" #include "sim/system.hh" -class ExecContext; +class ThreadContext; class BreakPCEvent; class BadAddrEvent; diff --git a/src/arch/alpha/tru64/tru64.cc b/src/arch/alpha/tru64/tru64.cc index 4a3e653c1..56b04846f 100644 --- a/src/arch/alpha/tru64/tru64.cc +++ b/src/arch/alpha/tru64/tru64.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #include "arch/alpha/tru64/tru64.hh" diff --git a/src/arch/alpha/tru64/tru64.hh b/src/arch/alpha/tru64/tru64.hh index 19343ba23..f0711b995 100644 --- a/src/arch/alpha/tru64/tru64.hh +++ b/src/arch/alpha/tru64/tru64.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ALPHA_ALPHA_TRU64_HH diff --git a/src/arch/alpha/types.hh b/src/arch/alpha/types.hh index d4cb482d8..5859052e9 100644 --- a/src/arch/alpha/types.hh +++ b/src/arch/alpha/types.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ARCH_ALPHA_TYPES_HH__ diff --git a/src/arch/alpha/utility.hh b/src/arch/alpha/utility.hh index 6cc916307..ec136091c 100644 --- a/src/arch/alpha/utility.hh +++ b/src/arch/alpha/utility.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ARCH_ALPHA_UTILITY_HH__ @@ -106,10 +109,10 @@ namespace AlphaISA /** * Function to insure ISA semantics about 0 registers. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void zeroRegisters(XC *xc); + template <class TC> + void zeroRegisters(TC *tc); #if FULL_SYSTEM // Alpha IPR register accessors @@ -140,15 +143,15 @@ namespace AlphaISA RoundPage(Addr addr) { return (addr + PageBytes - 1) & ~(PageBytes - 1); } - void initCPU(ExecContext *xc, int cpuId); - void initIPRs(ExecContext *xc, int cpuId); + void initCPU(ThreadContext *tc, int cpuId); + void initIPRs(ThreadContext *tc, int cpuId); /** * Function to check for and process any interrupts. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void processInterrupts(XC *xc); + template <class TC> + void processInterrupts(TC *tc); #endif } // namespace AlphaISA diff --git a/src/arch/alpha/vtophys.cc b/src/arch/alpha/vtophys.cc index 41e9b80a3..f7fd92c15 100644 --- a/src/arch/alpha/vtophys.cc +++ b/src/arch/alpha/vtophys.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi */ #include <string> @@ -32,7 +36,7 @@ #include "arch/alpha/vtophys.hh" #include "base/chunk_generator.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/vport.hh" using namespace std; @@ -81,10 +85,10 @@ AlphaISA::vtophys(Addr vaddr) } Addr -AlphaISA::vtophys(ExecContext *xc, Addr addr) +AlphaISA::vtophys(ThreadContext *tc, Addr addr) { AlphaISA::VAddr vaddr = addr; - Addr ptbr = xc->readMiscReg(AlphaISA::IPR_PALtemp20); + Addr ptbr = tc->readMiscReg(AlphaISA::IPR_PALtemp20); Addr paddr = 0; //@todo Andrew couldn't remember why he commented some of this code //so I put it back in. Perhaps something to do with gdb debugging? @@ -97,7 +101,7 @@ AlphaISA::vtophys(ExecContext *xc, Addr addr) paddr = vaddr; } else { AlphaISA::PageTableEntry pte = - kernel_pte_lookup(xc->getPhysPort(), ptbr, vaddr); + kernel_pte_lookup(tc->getPhysPort(), ptbr, vaddr); if (pte.valid()) paddr = pte.paddr() | vaddr.offset(); } @@ -111,52 +115,52 @@ AlphaISA::vtophys(ExecContext *xc, Addr addr) void -AlphaISA::CopyOut(ExecContext *xc, void *dest, Addr src, size_t cplen) +AlphaISA::CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) { uint8_t *dst = (uint8_t *)dest; - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); vp->readBlob(src, dst, cplen); - xc->delVirtPort(vp); + tc->delVirtPort(vp); } void -AlphaISA::CopyIn(ExecContext *xc, Addr dest, void *source, size_t cplen) +AlphaISA::CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) { uint8_t *src = (uint8_t *)source; - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); vp->writeBlob(dest, src, cplen); - xc->delVirtPort(vp); + tc->delVirtPort(vp); } void -AlphaISA::CopyStringOut(ExecContext *xc, char *dst, Addr vaddr, size_t maxlen) +AlphaISA::CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) { int len = 0; - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); do { vp->readBlob(vaddr++, (uint8_t*)dst++, 1); len++; } while (len < maxlen && dst[len] != 0 ); - xc->delVirtPort(vp); + tc->delVirtPort(vp); dst[len] = 0; } void -AlphaISA::CopyStringIn(ExecContext *xc, char *src, Addr vaddr) +AlphaISA::CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) { - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); for (ChunkGenerator gen(vaddr, strlen(src), AlphaISA::PageBytes); !gen.done(); gen.next()) { vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); src += gen.size(); } - xc->delVirtPort(vp); + tc->delVirtPort(vp); } diff --git a/src/arch/alpha/vtophys.hh b/src/arch/alpha/vtophys.hh index 7ab14bc5b..472c694ff 100644 --- a/src/arch/alpha/vtophys.hh +++ b/src/arch/alpha/vtophys.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ARCH_ALPHA_VTOPHYS_H__ @@ -31,7 +34,7 @@ #include "arch/alpha/isa_traits.hh" -class ExecContext; +class ThreadContext; class FunctionalPort; namespace AlphaISA { @@ -40,12 +43,12 @@ PageTableEntry kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr); Addr vtophys(Addr vaddr); -Addr vtophys(ExecContext *xc, Addr vaddr); +Addr vtophys(ThreadContext *tc, Addr vaddr); -void CopyOut(ExecContext *xc, void *dst, Addr src, size_t len); -void CopyIn(ExecContext *xc, Addr dst, void *src, size_t len); -void CopyStringOut(ExecContext *xc, char *dst, Addr vaddr, size_t maxlen); -void CopyStringIn(ExecContext *xc, char *src, Addr vaddr); +void CopyOut(ThreadContext *tc, void *dst, Addr src, size_t len); +void CopyIn(ThreadContext *tc, Addr dst, void *src, size_t len); +void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); +void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); }; #endif // __ARCH_ALPHA_VTOPHYS_H__ diff --git a/src/arch/isa_parser.py b/src/arch/isa_parser.py index c0758da50..4d522e18a 100755 --- a/src/arch/isa_parser.py +++ b/src/arch/isa_parser.py @@ -23,6 +23,9 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt +# Korey Sewell import os import sys @@ -1179,6 +1182,11 @@ class IntRegOperand(Operand): if (self.size == self.dflt_size): return '%s = xc->readIntReg(this, %d);\n' % \ (self.base_name, self.src_reg_idx) + elif (self.size > self.dflt_size): + int_reg_val = 'xc->readIntReg(this, %d)' % (self.src_reg_idx) + if (self.is_signed): + int_reg_val = 'sext<%d>(%s)' % (self.dflt_size, int_reg_val) + return '%s = %s;\n' % (self.base_name, int_reg_val) else: return '%s = bits(xc->readIntReg(this, %d), %d, 0);\n' % \ (self.base_name, self.src_reg_idx, self.size-1) diff --git a/src/arch/isa_specific.hh b/src/arch/isa_specific.hh index 91c9ffb68..181e81302 100644 --- a/src/arch/isa_specific.hh +++ b/src/arch/isa_specific.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black */ #ifndef __ARCH_ISA_SPECIFIC_HH__ diff --git a/src/arch/mips/SConscript b/src/arch/mips/SConscript index ef1ef25d6..6295a6c11 100644 --- a/src/arch/mips/SConscript +++ b/src/arch/mips/SConscript @@ -25,6 +25,10 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black +# Steve Reinhardt +# Korey Sewell import os import sys @@ -43,16 +47,13 @@ Import('env') base_sources = Split(''' faults.cc isa_traits.cc + utility.cc ''') # Full-system sources full_system_sources = Split(''' memory.cc - arguments.cc mips34k.cc - osfpal.cc - stacktrace.cc - vtophys.cc ''') # Syscall emulation (non-full-system) sources diff --git a/src/arch/mips/faults.cc b/src/arch/mips/faults.cc index 1b31dfa69..810c3fed4 100644 --- a/src/arch/mips/faults.cc +++ b/src/arch/mips/faults.cc @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #include "arch/mips/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "base/trace.hh" @@ -100,28 +102,28 @@ FaultStat IntegerOverflowFault::_count; #if FULL_SYSTEM -void MipsFault::invoke(ExecContext * xc) +void MipsFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); countStat()++; // exception restart address - if (setRestartAddress() || !xc->inPalMode()) - xc->setMiscReg(MipsISA::IPR_EXC_ADDR, xc->readPC()); + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(MipsISA::IPR_EXC_ADDR, tc->readPC()); if (skipFaultingInstruction()) { // traps... skip faulting instruction. - xc->setMiscReg(MipsISA::IPR_EXC_ADDR, - xc->readMiscReg(MipsISA::IPR_EXC_ADDR) + 4); + tc->setMiscReg(MipsISA::IPR_EXC_ADDR, + tc->readMiscReg(MipsISA::IPR_EXC_ADDR) + 4); } - xc->setPC(xc->readMiscReg(MipsISA::IPR_PAL_BASE) + vect()); - xc->setNextPC(xc->readPC() + sizeof(MachInst)); + tc->setPC(tc->readMiscReg(MipsISA::IPR_PAL_BASE) + vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); } -void ArithmeticFault::invoke(ExecContext * xc) +void ArithmeticFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); panic("Arithmetic traps are unimplemented!"); } diff --git a/src/arch/mips/faults.hh b/src/arch/mips/faults.hh index 0bdabe29e..d8bf59cc1 100644 --- a/src/arch/mips/faults.hh +++ b/src/arch/mips/faults.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __MIPS_FAULTS_HH__ @@ -45,7 +47,7 @@ class MipsFault : public FaultBase virtual bool setRestartAddress() {return true;} public: #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif virtual FaultVect vect() = 0; virtual FaultStat & countStat() = 0; @@ -112,7 +114,7 @@ class ArithmeticFault : public MipsFault FaultVect vect() {return _vect;} FaultStat & countStat() {return _count;} #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; diff --git a/src/arch/mips/isa/base.isa b/src/arch/mips/isa/base.isa index b2a31c018..b733da7da 100644 --- a/src/arch/mips/isa/base.isa +++ b/src/arch/mips/isa/base.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Base class for MIPS instructions, and some support functions @@ -18,16 +46,6 @@ output header {{ { protected: - /// Make MipsISA register dependence tags directly visible in - /// this class and derived classes. Maybe these should really - /// live here and not in the MipsISA namespace. - /*enum DependenceTags { - FP_Base_DepTag = MipsISA::FP_Base_DepTag, - Fpcr_DepTag = MipsISA::Fpcr_DepTag, - Uniq_DepTag = MipsISA::Uniq_DepTag, - IPR_Base_DepTag = MipsISA::IPR_Base_DepTag - };*/ - // Constructor MipsStaticInst(const char *mnem, MachInst _machInst, OpClass __opClass) : StaticInst(mnem, _machInst, __opClass) @@ -67,12 +85,12 @@ output decoder {{ } if(_numSrcRegs > 0) { - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[0]); } if(_numSrcRegs > 1) { - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[1]); } diff --git a/src/arch/mips/isa/bitfields.isa b/src/arch/mips/isa/bitfields.isa index e1124a591..e8d4578c7 100644 --- a/src/arch/mips/isa/bitfields.isa +++ b/src/arch/mips/isa/bitfields.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Bitfield definitions. @@ -17,20 +45,19 @@ def bitfield FUNCTION < 5: 0>; def bitfield FUNCTION_HI < 5: 3>; def bitfield FUNCTION_LO < 2: 0>; -// Integer operate format -def bitfield RT <20:16>; -def bitfield RT_HI <20:19>; -def bitfield RT_LO <18:16>; - def bitfield RS <25:21>; def bitfield RS_MSB <25:25>; def bitfield RS_HI <25:24>; def bitfield RS_LO <23:21>; -def bitfield RS_SRL <25:22>; - +def bitfield RS_SRL <25:22>; +def bitfield RS_RT <25:16>; +def bitfield RT <20:16>; +def bitfield RT_HI <20:19>; +def bitfield RT_LO <18:16>; +def bitfield RT_RD <20:11>; def bitfield RD <15:11>; -def bitfield INTIMM <15: 0>; // integer immediate (literal) +def bitfield INTIMM <15: 0>; // Floating-point operate format def bitfield FMT <25:21>; @@ -67,5 +94,9 @@ def bitfield HINT <10: 6>; def bitfield SYSCALLCODE <25: 6>; def bitfield TRAPCODE <15:13>; +// EXT/INS instructions +def bitfield MSB <15:11>; +def bitfield LSB <10: 6>; + // M5 instructions def bitfield M5FUNC <7:0>; diff --git a/src/arch/mips/isa/decoder.isa b/src/arch/mips/isa/decoder.isa index 1454aba39..a64f74c4f 100644 --- a/src/arch/mips/isa/decoder.isa +++ b/src/arch/mips/isa/decoder.isa @@ -1,4 +1,32 @@ - // -*- mode:c++ -*- +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell //////////////////////////////////////////////////////////////////// // @@ -6,41 +34,33 @@ // ----------------------------- // The following instructions are specified in the MIPS32 ISA // Specification. Decoding closely follows the style specified -// in the MIPS32 ISAthe specification document starting with Table +// in the MIPS32 ISA specification document starting with Table // A-2 (document available @ www.mips.com) // -//@todo: Distinguish "unknown/future" use insts from "reserved" -// ones decode OPCODE_HI default Unknown::unknown() { - - // Derived From ... Table A-2 MIPS32 ISA Manual + //Table A-2 0x0: decode OPCODE_LO { - 0x0: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { 0x1: decode MOVCI { format BasicOp { - 0: movf({{ if (getFPConditionCode(FCSR, CC) == 0) Rd = Rs}}); - 1: movt({{ if (getFPConditionCode(FCSR, CC) == 1) Rd = Rs}}); + 0: movf({{ Rd = (getCondCode(FCSR, CC) == 0) ? Rd : Rs; }}); + 1: movt({{ Rd = (getCondCode(FCSR, CC) == 1) ? Rd : Rs; }}); } } format BasicOp { - - //Table A-3 Note: "1. Specific encodings of the rt, rd, and sa fields - //are used to distinguish among the SLL, NOP, SSNOP and EHB functions. + //Table A-3 Note: "Specific encodings of the rd, rs, and + //rt fields are used to distinguish SLL, SSNOP, and EHB + //functions 0x0: decode RS { - 0x0: decode RT { //fix Nop traditional vs. Nop converted disassembly later - 0x0: decode RD default Nop::nop(){ - 0x0: decode SA { - 0x1: ssnop({{ ; }}); //really sll r0,r0,1 - 0x3: ehb({{ ; }}); //really sll r0,r0,3 - } - } - - default: sll({{ Rd = Rt.uw << SA; }}); + 0x0: decode RT_RD { + 0x0: decode SA default Nop::nop(){ + 0x1: WarnUnimpl::ssnop(); + 0x3: WarnUnimpl::ehb(); + } + default: sll({{ Rd = Rt.uw << SA; }}); } - } 0x2: decode RS_SRL { @@ -55,7 +75,6 @@ decode OPCODE_HI default Unknown::unknown() { 0x3: decode RS { 0x0: sra({{ uint32_t temp = Rt >> SA; - if ( (Rt & 0x80000000) > 0 ) { uint32_t mask = 0x80000000; for(int i=0; i < SA; i++) { @@ -63,7 +82,6 @@ decode OPCODE_HI default Unknown::unknown() { mask = mask >> 1; } } - Rd = temp; }}); } @@ -96,37 +114,36 @@ decode OPCODE_HI default Unknown::unknown() { } 0x1: decode FUNCTION_LO { - - //Table A-3 Note: "Specific encodings of the hint field are used - //to distinguish JR from JR.HB and JALR from JALR.HB" + //Table A-3 Note: "Specific encodings of the hint field are + //used to distinguish JR from JR.HB and JALR from JALR.HB" format Jump { 0x0: decode HINT { - 0:jr({{ NNPC = Rs & ~1; }},IsReturn); - - 1:jr_hb({{ NNPC = Rs & ~1; clear_exe_inst_hazards(); }},IsReturn); + 0x1: jr_hb({{ NNPC = Rs & ~1; }}, IsReturn, ClearHazards); + default: jr({{ NNPC = Rs & ~1; }}, IsReturn); } 0x1: decode HINT { - 0: jalr({{ Rd = NNPC; NNPC = Rs; }},IsCall,IsReturn); - - 1: jalr_hb({{ Rd = NNPC; NNPC = Rs; clear_exe_inst_hazards();}},IsCall,IsReturn); + 0x1: jalr_hb({{ Rd = NNPC; NNPC = Rs; }}, IsCall, Link + , ClearHazards); + default: jalr({{ Rd = NNPC; NNPC = Rs; }}, IsCall, + Link); } } format BasicOp { - 0x2: movz({{ if (Rt == 0) Rd = Rs; }}); - 0x3: movn({{ if (Rt != 0) Rd = Rs; }}); + 0x2: movz({{ Rd = (Rt == 0) ? Rs : Rd; }}); + 0x3: movn({{ Rd = (Rt != 0) ? Rs : Rd; }}); + 0x4: syscall({{ xc->syscall(R2); }}, IsNonSpeculative); + 0x7: sync({{ ; }}, IsMemBarrier); } - format BasicOp { - 0x4: syscall({{ xc->syscall(R2); }},IsNonSpeculative); - 0x5: break({{ panic("Not implemented break yet"); }},IsNonSpeculative); - 0x7: sync({{ panic("Not implemented sync yet"); }},IsNonSpeculative); + format FailUnimpl { + 0x5: break(); } } 0x2: decode FUNCTION_LO { - format BasicOp { + format HiLoMiscOp { 0x0: mfhi({{ Rd = HI; }}); 0x1: mthi({{ HI = Rs; }}); 0x2: mflo({{ Rd = LO; }}); @@ -135,24 +152,16 @@ decode OPCODE_HI default Unknown::unknown() { } 0x3: decode FUNCTION_LO { - format IntOp { - 0x0: mult({{ - int64_t temp1 = Rs.sd * Rt.sd; - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x1: multu({{ - uint64_t temp1 = Rs.ud * Rt.ud; - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); + format HiLoOp { + 0x0: mult({{ val = Rs.sd * Rt.sd; }}); + 0x1: multu({{ val = Rs.ud * Rt.ud; }}); + } + format HiLoMiscOp { 0x2: div({{ HI = Rs.sd % Rt.sd; LO = Rs.sd / Rt.sd; }}); - 0x3: divu({{ HI = Rs.ud % Rt.ud; LO = Rs.ud / Rt.ud; @@ -201,11 +210,8 @@ decode OPCODE_HI default Unknown::unknown() { format Branch { 0x0: bltz({{ cond = (Rs.sw < 0); }}); 0x1: bgez({{ cond = (Rs.sw >= 0); }}); - } - - format BranchLikely { - 0x2: bltzl({{ cond = (Rs.sw < 0); }}); - 0x3: bgezl({{ cond = (Rs.sw >= 0); }}); + 0x2: bltzl({{ cond = (Rs.sw < 0); }}, Likely); + 0x3: bgezl({{ cond = (Rs.sw >= 0); }}, Likely); } } @@ -222,13 +228,13 @@ decode OPCODE_HI default Unknown::unknown() { 0x2: decode REGIMM_LO { format Branch { - 0x0: bltzal({{ cond = (Rs.sw < 0); }}, IsCall,IsReturn); - 0x1: bgezal({{ cond = (Rs.sw >= 0); }}, IsCall,IsReturn); - } - - format BranchLikely { - 0x2: bltzall({{ cond = (Rs.sw < 0); }}, IsCall, IsReturn); - 0x3: bgezall({{ cond = (Rs.sw >= 0); }}, IsCall, IsReturn); + 0x0: bltzal({{ cond = (Rs.sw < 0); }}, Link); + 0x1: decode RS { + 0x0: bal ({{ cond = 1; }}, IsCall, Link); + default: bgezal({{ cond = (Rs.sw >= 0); }}, Link); + } + 0x2: bltzall({{ cond = (Rs.sw < 0); }}, Link, Likely); + 0x3: bgezall({{ cond = (Rs.sw >= 0); }}, Link, Likely); } } @@ -241,25 +247,23 @@ decode OPCODE_HI default Unknown::unknown() { format Jump { 0x2: j({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2);}}); - - 0x3: jal({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2); }},IsCall,IsReturn); + 0x3: jal({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2); }}, IsCall, + Link); } format Branch { - 0x4: beq({{ cond = (Rs.sw == Rt.sw); }}); - 0x5: bne({{ cond = (Rs.sw != Rt.sw); }}); - 0x6: decode RT { - 0x0: blez({{ cond = (Rs.sw <= 0); }}); - } - - 0x7: decode RT { - 0x0: bgtz({{ cond = (Rs.sw > 0); }}); + 0x4: decode RS_RT { + 0x0: b({{ cond = 1; }}); + default: beq({{ cond = (Rs.sw == Rt.sw); }}); } + 0x5: bne({{ cond = (Rs.sw != Rt.sw); }}); + 0x6: blez({{ cond = (Rs.sw <= 0); }}); + 0x7: bgtz({{ cond = (Rs.sw > 0); }}); } } 0x1: decode OPCODE_LO { - format IntOp { + format IntImmOp { 0x0: addi({{ Rt.sw = Rs.sw + imm; /*Trap If Overflow*/}}); 0x1: addiu({{ Rt.sw = Rs.sw + imm;}}); 0x2: slti({{ Rt.sw = ( Rs.sw < imm) ? 1 : 0 }}); @@ -275,112 +279,47 @@ decode OPCODE_HI default Unknown::unknown() { } 0x2: decode OPCODE_LO { - //Table A-11 MIPS32 COP0 Encoding of rs Field 0x0: decode RS_MSB { 0x0: decode RS { - format System { - 0x0: mfc0({{ - //uint64_t reg_num = Rd.uw; - - Rt = xc->readMiscReg(RD << 5 | SEL); - }}); - - 0x4: mtc0({{ - //uint64_t reg_num = Rd.uw; - - xc->setMiscReg(RD << 5 | SEL,Rt); - }}); - - 0x8: mftr({{ - //The contents of the coprocessor 0 register specified by the - //combination of rd and sel are loaded into general register - //rt. Note that not all coprocessor 0 registers support the - //sel field. In those instances, the sel field must be zero. - - //MT Code Needed Here - - }}); - - 0xC: mttr({{ - //The contents of the coprocessor 0 register specified by the - //combination of rd and sel are loaded into general register - //rt. Note that not all coprocessor 0 registers support the - //sel field. In those instances, the sel field must be zero. - - //MT Code Needed Here - }}); - - - 0xA: rdpgpr({{ - //Accessing Previous Shadow Set Register Number - //uint64_t prev = xc->readMiscReg(SRSCtl)/*[PSS]*/; - //uint64_t reg_num = Rt.uw; - - //Rd = xc->regs.IntRegFile[prev]; - //Rd = xc->shadowIntRegFile[prev][reg_num]; - }}); + format CP0Control { + 0x0: mfc0({{ Rt = xc->readMiscReg(RD << 5 | SEL); }}); + 0x4: mtc0({{ xc->setMiscReg(RD << 5 | SEL, Rt); }}); + } + format MipsMT { + 0x8: mftr(); + 0xC: mttr(); 0xB: decode RD { - 0x0: decode SC { - 0x0: dvpe({{ - Rt.sw = xc->readMiscReg(MVPControl); - xc->setMiscReg(MVPControl,0); - }}); - - 0x1: evpe({{ - Rt.sw = xc->readMiscReg(MVPControl); - xc->setMiscReg(MVPControl,1); - }}); + 0x0: dvpe(); + 0x1: evpe(); } - 0x1: decode SC { - 0x0: dmt({{ - Rt.sw = xc->readMiscReg(VPEControl); - xc->setMiscReg(VPEControl,0); - }}); - - 0x1: emt({{ - Rt.sw = xc->readMiscReg(VPEControl); - xc->setMiscReg(VPEControl,1); - }}); - } - - 0xC: decode SC { - 0x0: di({{ - Rt.sw = xc->readMiscReg(Status); - xc->setMiscReg(Status,0); - }}); - - 0x1: ei({{ - Rt.sw = xc->readMiscReg(Status); - xc->setMiscReg(Status,1); - }}); + 0x0: dmt(); + 0x1: emt(); + 0xC: decode SC { + 0x0: di(); + 0x1: ei(); + } } } + } - 0xE: wrpgpr({{ - //Accessing Previous Shadow Set Register Number - //uint64_t prev = xc->readMiscReg(SRSCtl/*[PSS]*/); - //uint64_t reg_num = Rd.uw; - - //xc->regs.IntRegFile[prev]; - //xc->shadowIntRegFile[prev][reg_num] = Rt; - }}); + format FailUnimpl { + 0xA: rdpgpr(); + 0xE: wrpgpr(); } } //Table A-12 MIPS32 COP0 Encoding of Function Field When rs=CO 0x1: decode FUNCTION { - format System { - 0x01: tlbr({{ }}); - 0x02: tlbwi({{ }}); - 0x06: tlbwr({{ }}); - 0x08: tlbp({{ }}); - } + format FailUnimpl { + 0x01: tlbr(); + 0x02: tlbwi(); + 0x06: tlbwr(); + 0x08: tlbp(); - format WarnUnimpl { 0x18: eret(); 0x1F: deret(); 0x20: wait(); @@ -393,18 +332,9 @@ decode OPCODE_HI default Unknown::unknown() { 0x0: decode RS_HI { 0x0: decode RS_LO { - format FloatOp { + format CP1Control { 0x0: mfc1 ({{ Rt.uw = Fs.uw<31:0>; }}); - 0x3: mfhc1({{ Rt.uw = Fs.ud<63:32>;}}); - 0x4: mtc1 ({{ Fs.uw = Rt.uw; }}); - 0x7: mthc1({{ - uint64_t fs_hi = Rt.uw; - uint64_t fs_lo = Fs.ud & 0x0000FFFF; - Fs.ud = fs_hi << 32 | fs_lo; - }}); - } - format System { 0x2: cfc1({{ switch (FS) { @@ -424,11 +354,14 @@ decode OPCODE_HI default Unknown::unknown() { Rt = FCSR; break; default: - panic("FP Control Value (%d) Not Available. Ignoring Access to" - "Floating Control Status Register",FS); + panic("FP Control Value (%d) Not Valid"); } }}); + 0x3: mfhc1({{ Rt.uw = Fs.ud<63:32>;}}); + + 0x4: mtc1 ({{ Fs.uw = Rt.uw; }}); + 0x6: ctc1({{ switch (FS) { @@ -464,21 +397,29 @@ decode OPCODE_HI default Unknown::unknown() { "Floating Control Status Register", FS); } }}); + + 0x7: mthc1({{ + uint64_t fs_hi = Rt.uw; + uint64_t fs_lo = Fs.ud & 0x0FFFFFFFF; + Fs.ud = (fs_hi << 32) | fs_lo; + }}); + } } 0x1: decode ND { - 0x0: decode TF { - format Branch { - 0x0: bc1f({{ cond = (getFPConditionCode(FCSR,CC) == 0); }}); - 0x1: bc1t({{ cond = (getFPConditionCode(FCSR,CC) == 1); }}); + format Branch { + 0x0: decode TF { + 0x0: bc1f({{ cond = getCondCode(FCSR, BRANCH_CC) == 0; + }}); + 0x1: bc1t({{ cond = getCondCode(FCSR, BRANCH_CC) == 1; + }}); } - } - - 0x1: decode TF { - format BranchLikely { - 0x0: bc1fl({{ cond = (getFPConditionCode(FCSR,CC) == 0); }}); - 0x1: bc1tl({{ cond = (getFPConditionCode(FCSR,CC) == 1); }}); + 0x1: decode TF { + 0x0: bc1fl({{ cond = getCondCode(FCSR, BRANCH_CC) == 0; + }}, Likely); + 0x1: bc1tl({{ cond = getCondCode(FCSR, BRANCH_CC) == 1; + }}, Likely); } } } @@ -486,9 +427,8 @@ decode OPCODE_HI default Unknown::unknown() { 0x1: decode RS_HI { 0x2: decode RS_LO { - //Table A-14 MIPS32 COP1 Encoding of Function Field When rs=S - //(( single-word )) + //(( single-precision floating point)) 0x0: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { format FloatOp { @@ -499,194 +439,102 @@ decode OPCODE_HI default Unknown::unknown() { 0x4: sqrt_s({{ Fd.sf = sqrt(Fs.sf);}}); 0x5: abs_s({{ Fd.sf = fabs(Fs.sf);}}); 0x6: mov_s({{ Fd.sf = Fs.sf;}}); - 0x7: neg_s({{ Fd.sf = -1 * Fs.sf;}}); + 0x7: neg_s({{ Fd.sf = -Fs.sf;}}); } } 0x1: decode FUNCTION_LO { - format Float64Op { - 0x0: round_l_s({{ - Fd.ud = fpConvert(roundFP(Fs.sf,0), SINGLE_TO_LONG); - }}); - - 0x1: trunc_l_s({{ - Fd.ud = fpConvert(truncFP(Fs.sf), SINGLE_TO_LONG); - }}); - - 0x2: ceil_l_s({{ - Fd.ud = fpConvert(ceil(Fs.sf), SINGLE_TO_LONG); - }}); - - 0x3: floor_l_s({{ - Fd.ud = fpConvert(floor(Fs.sf), SINGLE_TO_LONG); - }}); - } - - format FloatOp { - 0x4: round_w_s({{ - Fd.uw = fpConvert(roundFP(Fs.sf,0), SINGLE_TO_WORD); - }}); - - 0x5: trunc_w_s({{ - Fd.uw = fpConvert(truncFP(Fs.sf), SINGLE_TO_WORD); - }}); - - 0x6: ceil_w_s({{ - Fd.uw = fpConvert(ceil(Fs.sf), SINGLE_TO_WORD); - }}); - - 0x7: floor_w_s({{ - Fd.uw = fpConvert(floor(Fs.sf), SINGLE_TO_WORD); - }}); + format FloatConvertOp { + 0x0: round_l_s({{ val = Fs.sf; }}, ToLong, + Round); + 0x1: trunc_l_s({{ val = Fs.sf; }}, ToLong, + Trunc); + 0x2: ceil_l_s({{ val = Fs.sf; }}, ToLong, + Ceil); + 0x3: floor_l_s({{ val = Fs.sf; }}, ToLong, + Floor); + 0x4: round_w_s({{ val = Fs.sf; }}, ToWord, + Round); + 0x5: trunc_w_s({{ val = Fs.sf; }}, ToWord, + Trunc); + 0x6: ceil_w_s({{ val = Fs.sf; }}, ToWord, + Ceil); + 0x7: floor_w_s({{ val = Fs.sf; }}, ToWord, + Floor); } } 0x2: decode FUNCTION_LO { 0x1: decode MOVCF { - format FloatOp { - 0x0: movf_s({{if (getFPConditionCode(FCSR,CC) == 0) Fd = Fs;}}); - 0x1: movt_s({{if (getFPConditionCode(FCSR,CC) == 1) Fd = Fs;}}); + format BasicOp { + 0x0: movf_s({{ Fd = (getCondCode(FCSR,CC) == 0) ? Fs : Fd; }}); + 0x1: movt_s({{ Fd = (getCondCode(FCSR,CC) == 1) ? Fs : Fd; }}); } } + format BasicOp { + 0x2: movz_s({{ Fd = (Rt == 0) ? Fs : Fd; }}); + 0x3: movn_s({{ Fd = (Rt != 0) ? Fs : Fd; }}); + } + format FloatOp { - 0x2: movz_s({{ if (Rt == 0) Fd = Fs; }}); - 0x3: movn_s({{ if (Rt != 0) Fd = Fs; }}); 0x5: recip_s({{ Fd = 1 / Fs; }}); 0x6: rsqrt_s({{ Fd = 1 / sqrt(Fs);}}); } } 0x4: decode FUNCTION_LO { - format FloatConvertOp { - 0x1: cvt_d_s({{ - Fd.ud = fpConvert(Fs.sf, SINGLE_TO_DOUBLE); - }}); - - 0x4: cvt_w_s({{ - Fd.uw = fpConvert(Fs.sf, SINGLE_TO_WORD); - }}); + 0x1: cvt_d_s({{ val = Fs.sf; }}, ToDouble); + 0x4: cvt_w_s({{ val = Fs.sf; }}, ToWord); + 0x5: cvt_l_s({{ val = Fs.sf; }}, ToLong); } - format FloatConvertOp { - 0x5: cvt_l_s({{ - Fd.ud = fpConvert(Fs.sf, SINGLE_TO_LONG); + 0x6: FloatOp::cvt_ps_s({{ + Fd.ud = (uint64_t) Fs.uw << 32 | + (uint64_t) Ft.uw; }}); - - 0x6: cvt_ps_st({{ - Fd.ud = (uint64_t)Fs.uw << 32 | (uint64_t)Ft.uw; - }}); - } } 0x6: decode FUNCTION_LO { format FloatCompareOp { - 0x0: c_f_s({{ cond = 0; }}); - - 0x1: c_un_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_eq_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x3: c_ueq_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x4: c_olt_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x5: c_ult_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x6: c_ole_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf <= Ft.sf); - }}); - - 0x7: c_ule_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf <= Ft.sf); - }}); + 0x0: c_f_s({{ cond = 0; }}, SinglePrecision, + UnorderedFalse); + 0x1: c_un_s({{ cond = 0; }}, SinglePrecision, + UnorderedTrue); + 0x2: c_eq_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedFalse); + 0x3: c_ueq_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedTrue); + 0x4: c_olt_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedFalse); + 0x5: c_ult_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedTrue); + 0x6: c_ole_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedFalse); + 0x7: c_ule_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedTrue); } } 0x7: decode FUNCTION_LO { - format FloatCompareWithXcptOp { - 0x0: c_sf_s({{ cond = 0; }}); - - 0x1: c_ngle_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_seq_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x3: c_ngl_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x4: c_lt_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x5: c_nge_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x6: c_le_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf <= Ft.sf); - }}); - - 0x7: c_ngt_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf <= Ft.sf); - }}); + format FloatCompareOp { + 0x0: c_sf_s({{ cond = 0; }}, SinglePrecision, + UnorderedFalse, QnanException); + 0x1: c_ngle_s({{ cond = 0; }}, SinglePrecision, + UnorderedTrue, QnanException); + 0x2: c_seq_s({{ cond = (Fs.sf == Ft.sf);}}, + UnorderedFalse, QnanException); + 0x3: c_ngl_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedTrue, QnanException); + 0x6: c_le_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedTrue, QnanException); } } } @@ -695,197 +543,108 @@ decode OPCODE_HI default Unknown::unknown() { 0x1: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { format FloatOp { - 0x0: add_d({{ Fd.df = Fs.df + Ft.df;}}); - 0x1: sub_d({{ Fd.df = Fs.df - Ft.df;}}); - 0x2: mul_d({{ Fd.df = Fs.df * Ft.df;}}); - 0x3: div_d({{ Fd.df = Fs.df / Ft.df;}}); - 0x4: sqrt_d({{ Fd.df = sqrt(Fs.df);}}); - 0x5: abs_d({{ Fd.df = fabs(Fs.df);}}); - 0x6: mov_d({{ Fd.ud = Fs.ud;}}); - 0x7: neg_d({{ Fd.df = -1 * Fs.df;}}); + 0x0: add_d({{ Fd.df = Fs.df + Ft.df; }}); + 0x1: sub_d({{ Fd.df = Fs.df - Ft.df; }}); + 0x2: mul_d({{ Fd.df = Fs.df * Ft.df; }}); + 0x3: div_d({{ Fd.df = Fs.df / Ft.df; }}); + 0x4: sqrt_d({{ Fd.df = sqrt(Fs.df); }}); + 0x5: abs_d({{ Fd.df = fabs(Fs.df); }}); + 0x6: mov_d({{ Fd.df = Fs.df; }}); + 0x7: neg_d({{ Fd.df = -1 * Fs.df; }}); } } 0x1: decode FUNCTION_LO { - format FloatOp { - 0x0: round_l_d({{ - Fd.ud = fpConvert(roundFP(Fs.df,0), DOUBLE_TO_LONG); - }}); - - 0x1: trunc_l_d({{ - Fd.ud = fpConvert(truncFP(Fs.df), DOUBLE_TO_LONG); - }}); - - 0x2: ceil_l_d({{ - Fd.ud = fpConvert(ceil(Fs.df), DOUBLE_TO_LONG); - }}); - - 0x3: floor_l_d({{ - Fd.ud = fpConvert(floor(Fs.df), DOUBLE_TO_LONG); - }}); - } - - format FloatOp { - 0x4: round_w_d({{ - Fd.uw = fpConvert(roundFP(Fs.df,0), DOUBLE_TO_WORD); - }}); - - 0x5: trunc_w_d({{ - Fd.uw = fpConvert(truncFP(Fs.df), DOUBLE_TO_WORD); - }}); - - 0x6: ceil_w_d({{ - Fd.uw = fpConvert(ceil(Fs.df), DOUBLE_TO_WORD); - }}); - - 0x7: floor_w_d({{ - Fd.uw = fpConvert(floor(Fs.df), DOUBLE_TO_WORD); - }}); + format FloatConvertOp { + 0x0: round_l_d({{ val = Fs.df; }}, ToLong, + Round); + 0x1: trunc_l_d({{ val = Fs.df; }}, ToLong, + Trunc); + 0x2: ceil_l_d({{ val = Fs.df; }}, ToLong, + Ceil); + 0x3: floor_l_d({{ val = Fs.df; }}, ToLong, + Floor); + 0x4: round_w_d({{ val = Fs.df; }}, ToWord, + Round); + 0x5: trunc_w_d({{ val = Fs.df; }}, ToWord, + Trunc); + 0x6: ceil_w_d({{ val = Fs.df; }}, ToWord, + Ceil); + 0x7: floor_w_d({{ val = Fs.df; }}, ToWord, + Floor); } } 0x2: decode FUNCTION_LO { 0x1: decode MOVCF { - format FloatOp { - 0x0: movf_d({{if (getFPConditionCode(FCSR,CC) == 0) Fd.df = Fs.df; }}); - 0x1: movt_d({{if (getFPConditionCode(FCSR,CC) == 1) Fd.df = Fs.df; }}); + format BasicOp { + 0x0: movf_d({{ Fd.df = (getCondCode(FCSR,CC) == 0) ? + Fs.df : Fd.df; + }}); + 0x1: movt_d({{ Fd.df = (getCondCode(FCSR,CC) == 1) ? + Fs.df : Fd.df; + }}); } } format BasicOp { - 0x2: movz_d({{ if (Rt == 0) Fd.df = Fs.df; }}); - 0x3: movn_d({{ if (Rt != 0) Fd.df = Fs.df; }}); + 0x2: movz_d({{ Fd.df = (Rt == 0) ? Fs.df : Fd.df; }}); + 0x3: movn_d({{ Fd.df = (Rt != 0) ? Fs.df : Fd.df; }}); } format FloatOp { - 0x5: recip_d({{ Fd.df = 1 / Fs.df}}); + 0x5: recip_d({{ Fd.df = 1 / Fs.df }}); 0x6: rsqrt_d({{ Fd.df = 1 / sqrt(Fs.df) }}); } } 0x4: decode FUNCTION_LO { - format FloatOp { - 0x0: cvt_s_d({{ - Fd.uw = fpConvert(Fs.df, DOUBLE_TO_SINGLE); - }}); - - 0x4: cvt_w_d({{ - Fd.uw = fpConvert(Fs.df, DOUBLE_TO_WORD); - }}); - - 0x5: cvt_l_d({{ - Fd.ud = fpConvert(Fs.df, DOUBLE_TO_LONG); - }}); + format FloatConvertOp { + 0x0: cvt_s_d({{ val = Fs.df; }}, ToSingle); + 0x4: cvt_w_d({{ val = Fs.df; }}, ToWord); + 0x5: cvt_l_d({{ val = Fs.df; }}, ToLong); } } 0x6: decode FUNCTION_LO { format FloatCompareOp { - 0x0: c_f_d({{ cond = 0; }}); - - 0x1: c_un_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_eq_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df == Ft.df); - }}); - - 0x3: c_ueq_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df == Ft.df); - }}); - - 0x4: c_olt_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df < Ft.df); - }}); - - 0x5: c_ult_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df < Ft.df); - }}); - - 0x6: c_ole_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df <= Ft.df); - }}); - - 0x7: c_ule_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df <= Ft.df); - }}); + 0x0: c_f_d({{ cond = 0; }}, DoublePrecision, + UnorderedFalse); + 0x1: c_un_d({{ cond = 0; }}, DoublePrecision, + UnorderedTrue); + 0x2: c_eq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedFalse); + 0x3: c_ueq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedTrue); + 0x4: c_olt_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedFalse); + 0x5: c_ult_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedTrue); + 0x6: c_ole_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedFalse); + 0x7: c_ule_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedTrue); } } 0x7: decode FUNCTION_LO { - format FloatCompareWithXcptOp { - 0x0: c_sf_d({{ cond = 0; }}); - - 0x1: c_ngle_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_seq_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df == Ft.df); - }}); - - 0x3: c_ngl_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df == Ft.df); - }}); - - 0x4: c_lt_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df < Ft.df); - }}); - - 0x5: c_nge_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df < Ft.df); - }}); - - 0x6: c_le_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df <= Ft.df); - }}); - - 0x7: c_ngt_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df <= Ft.df); - }}); + format FloatCompareOp { + 0x0: c_sf_d({{ cond = 0; }}, DoublePrecision, + UnorderedFalse, QnanException); + 0x1: c_ngle_d({{ cond = 0; }}, DoublePrecision, + UnorderedTrue, QnanException); + 0x2: c_seq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedFalse, QnanException); + 0x3: c_ngl_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedTrue, QnanException); + 0x6: c_le_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedTrue, QnanException); } } } @@ -893,19 +652,9 @@ decode OPCODE_HI default Unknown::unknown() { //Table A-16 MIPS32 COP1 Encoding of Function Field When rs=W 0x4: decode FUNCTION { format FloatConvertOp { - 0x20: cvt_s_w({{ - Fd.uw = fpConvert(Fs.sf, WORD_TO_SINGLE); - }}); - - 0x21: cvt_d_w({{ - Fd.ud = fpConvert(Fs.sf, WORD_TO_DOUBLE); - }}); - } - - format Float64ConvertOp { - 0x26: cvt_ps_pw({{ - Fd.ud = fpConvert(Fs.ud, WORD_TO_PS); - }}); + 0x20: cvt_s_w({{ val = Fs.uw; }}, ToSingle); + 0x21: cvt_d_w({{ val = Fs.uw; }}, ToDouble); + 0x26: FailUnimpl::cvt_ps_w(); } } @@ -913,18 +662,10 @@ decode OPCODE_HI default Unknown::unknown() { //Note: "1. Format type L is legal only if 64-bit floating point operations //are enabled." 0x5: decode FUNCTION_HI { - format Float64ConvertOp { - 0x20: cvt_s_l({{ - Fd.uw = fpConvert(Fs.ud, LONG_TO_SINGLE); - }}); - - 0x21: cvt_d_l({{ - Fd.ud = fpConvert(Fs.ud, LONG_TO_DOUBLE); - }}); - - 0x26: cvt_ps_l({{ - Fd.ud = fpConvert(Fs.ud, LONG_TO_PS); - }}); + format FloatConvertOp { + 0x20: cvt_s_l({{ val = Fs.ud; }}, ToSingle); + 0x21: cvt_d_l({{ val = Fs.ud; }}, ToDouble); + 0x26: FailUnimpl::cvt_ps_l(); } } @@ -938,30 +679,25 @@ decode OPCODE_HI default Unknown::unknown() { Fd1.sf = Fs1.sf + Ft2.sf; Fd2.sf = Fs2.sf + Ft2.sf; }}); - 0x1: sub_ps({{ Fd1.sf = Fs1.sf - Ft2.sf; Fd2.sf = Fs2.sf - Ft2.sf; }}); - 0x2: mul_ps({{ Fd1.sf = Fs1.sf * Ft2.sf; Fd2.sf = Fs2.sf * Ft2.sf; }}); - 0x5: abs_ps({{ Fd1.sf = fabs(Fs1.sf); Fd2.sf = fabs(Fs2.sf); }}); - 0x6: mov_ps({{ Fd1.sf = Fs1.sf; Fd2.sf = Fs2.sf; }}); - 0x7: neg_ps({{ - Fd1.sf = -1 * Fs1.sf; - Fd2.sf = -1 * Fs2.sf; + Fd1.sf = -(Fs1.sf); + Fd2.sf = -(Fs2.sf); }}); } } @@ -970,236 +706,112 @@ decode OPCODE_HI default Unknown::unknown() { 0x1: decode MOVCF { format Float64Op { 0x0: movf_ps({{ - if (getFPConditionCode(FCSR, CC) == 0) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC+1) == 0) - Fd2 = Fs2; + Fd1 = (getCondCode(FCSR, CC) == 0) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC+1) == 0) ? + Fs2 : Fd2; }}); - 0x1: movt_ps({{ - if (getFPConditionCode(FCSR, CC) == 1) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC+1) == 1) - Fd2 = Fs2; + Fd2 = (getCondCode(FCSR, CC) == 1) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC+1) == 1) ? + Fs2 : Fd2; }}); } } format Float64Op { 0x2: movz_ps({{ - if (getFPConditionCode(FCSR, CC) == 0) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC) == 0) - Fd2 = Fs2; + Fd1 = (getCondCode(FCSR, CC) == 0) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC) == 0) ? + Fs2 : Fd2; }}); - 0x3: movn_ps({{ - if (getFPConditionCode(FCSR, CC) == 1) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC) == 1) - Fd2 = Fs2; + Fd1 = (getCondCode(FCSR, CC) == 1) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC) == 1) ? + Fs2 : Fd2; }}); } } 0x4: decode FUNCTION_LO { - 0x0: Float64Op::cvt_s_pu({{ - Fd.uw = fpConvert(Fs2.uw, PU_TO_SINGLE); - }}); + 0x0: FloatOp::cvt_s_pu({{ Fd.sf = Fs2.sf; }}); } 0x5: decode FUNCTION_LO { - format Float64Op { - 0x0: cvt_s_pl({{ - Fd.uw = fpConvert(Fs1.uw, PL_TO_SINGLE); - }}); + 0x0: FloatOp::cvt_s_pl({{ Fd.sf = Fs1.sf; }}); - 0x4: pll({{ Fd.ud = (uint64_t) Fs1.uw << 32 | Ft1.uw; }}); - 0x5: plu({{ Fd.ud = (uint64_t) Fs1.uw << 32 | Ft2.uw; }}); - 0x6: pul({{ Fd.ud = (uint64_t) Fs2.uw << 32 | Ft1.uw; }}); - 0x7: puu({{ Fd.ud = (uint64_t) Fs2.uw << 32 | Ft2.uw; }}); + format Float64Op { + 0x4: pll({{ Fd.ud = (uint64_t) Fs1.uw << 32 | + Ft1.uw; + }}); + 0x5: plu({{ Fd.ud = (uint64_t) Fs1.uw << 32 | + Ft2.uw; + }}); + 0x6: pul({{ Fd.ud = (uint64_t) Fs2.uw << 32 | + Ft1.uw; + }}); + 0x7: puu({{ Fd.ud = (uint64_t) Fs2.uw << 32 | + Ft2.uw; + }}); } } 0x6: decode FUNCTION_LO { format FloatPSCompareOp { - 0x0: c_f_ps({{ cond1 = 0; cond2 = 0; }}); - - 0x1: c_un_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = 0; - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = 0; - - }}); - - 0x2: c_eq_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x3: c_ueq_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x4: c_olt_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf < Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x5: c_ult_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs.sf < Ft.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x6: c_ole_ps({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond1 = 0; - else - cond1 = (Fs.sf <= Ft.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); - - 0x7: c_ule_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf <= Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); + 0x0: c_f_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedFalse); + 0x1: c_un_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedTrue); + 0x2: c_eq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedFalse); + 0x3: c_ueq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedTrue); + 0x4: c_olt_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedFalse); + 0x5: c_ult_ps({{ cond1 = (Fs.sf < Ft.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedTrue); + 0x6: c_ole_ps({{ cond1 = (Fs.sf <= Ft.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedFalse); + 0x7: c_ule_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedTrue); } } 0x7: decode FUNCTION_LO { - format FloatPSCompareWithXcptOp { - 0x0: c_sf_ps({{ cond1 = 0; cond2 = 0; }}); - - 0x1: c_ngle_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = 0; - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = 0; - }}); - - 0x2: c_seq_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x3: c_ngl_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x4: c_lt_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf < Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x5: c_nge_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf < Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x6: c_le_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf <= Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); - - 0x7: c_ngt_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf <= Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); + format FloatPSCompareOp { + 0x0: c_sf_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedFalse, QnanException); + 0x1: c_ngle_ps({{ cond1 = 0; }}, + {{ cond2 = 0; }}, + UnorderedTrue, QnanException); + 0x2: c_seq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x3: c_ngl_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedTrue, QnanException); + 0x6: c_le_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedTrue, QnanException); } } } @@ -1209,9 +821,9 @@ decode OPCODE_HI default Unknown::unknown() { //Table A-19 MIPS32 COP2 Encoding of rs Field 0x2: decode RS_MSB { - 0x0: decode RS_HI { - 0x0: decode RS_LO { - format WarnUnimpl { + format FailUnimpl { + 0x0: decode RS_HI { + 0x0: decode RS_LO { 0x0: mfc2(); 0x2: cfc2(); 0x3: mfhc2(); @@ -1219,18 +831,14 @@ decode OPCODE_HI default Unknown::unknown() { 0x6: ctc2(); 0x7: mftc2(); } - } - 0x1: decode ND { - 0x0: decode TF { - format WarnUnimpl { + 0x1: decode ND { + 0x0: decode TF { 0x0: bc2f(); 0x1: bc2t(); } - } - 0x1: decode TF { - format WarnUnimpl { + 0x1: decode TF { 0x0: bc2fl(); 0x1: bc2tl(); } @@ -1244,441 +852,240 @@ decode OPCODE_HI default Unknown::unknown() { //operations are enabled." 0x3: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { - format LoadFloatMemory { - 0x0: lwxc1({{ Ft.uw = Mem.uw;}}, {{ EA = Rs + Rt; }}); - 0x1: ldxc1({{ Ft.ud = Mem.ud;}}, {{ EA = Rs + Rt; }}); - 0x5: luxc1({{ Ft.uw = Mem.ud;}}, {{ EA = Rs + Rt; }}); + format LoadIndexedMemory { + 0x0: lwxc1({{ Ft.uw = Mem.uw;}}); + 0x1: ldxc1({{ Ft.ud = Mem.ud;}}); + 0x5: luxc1({{ Ft.uw = Mem.ud;}}); } } 0x1: decode FUNCTION_LO { - format StoreFloatMemory { - 0x0: swxc1({{ Mem.uw = Ft.uw;}}, {{ EA = Rs + Rt; }}); - 0x1: sdxc1({{ Mem.ud = Ft.ud;}}, {{ EA = Rs + Rt; }}); - 0x5: suxc1({{ Mem.ud = Ft.ud;}}, {{ EA = Rs + Rt; }}); + format StoreIndexedMemory { + 0x0: swxc1({{ Mem.uw = Ft.uw;}}); + 0x1: sdxc1({{ Mem.ud = Ft.ud;}}); + 0x5: suxc1({{ Mem.ud = Ft.ud;}}); } - 0x7: WarnUnimpl::prefx(); + 0x7: Prefetch::prefx({{ EA = Rs + Rt; }}); } - format FloatOp { - 0x3: WarnUnimpl::alnv_ps(); + 0x3: decode FUNCTION_LO { + 0x6: Float64Op::alnv_ps({{ if (Rs<2:0> == 0) { + Fd.ud = Fs.ud; + } else if (Rs<2:0> == 4) { + #if BYTE_ORDER == BIG_ENDIAN + Fd.ud = Fs.ud<31:0> << 32 | + Ft.ud<63:32>; + #elif BYTE_ORDER == LITTLE_ENDIAN + Fd.ud = Ft.ud<31:0> << 32 | + Fs.ud<63:32>; + #endif + } else { + Fd.ud = Fd.ud; + } + }}); + } - format BasicOp { - 0x4: decode FUNCTION_LO { - 0x0: madd_s({{ Fd.sf = (Fs.sf * Ft.sf) + Fr.sf; }}); - 0x1: madd_d({{ Fd.df = (Fs.df * Ft.df) + Fr.df; }}); - 0x6: madd_ps({{ - Fd1.sf = (Fs1.df * Ft1.df) + Fr1.df; - Fd2.sf = (Fs2.df * Ft2.df) + Fr2.df; - }}); - } + format FloatAccOp { + 0x4: decode FUNCTION_LO { + 0x0: madd_s({{ Fd.sf = (Fs.sf * Ft.sf) + Fr.sf; }}); + 0x1: madd_d({{ Fd.df = (Fs.df * Ft.df) + Fr.df; }}); + 0x6: madd_ps({{ + Fd1.sf = (Fs1.df * Ft1.df) + Fr1.df; + Fd2.sf = (Fs2.df * Ft2.df) + Fr2.df; + }}); + } - 0x5: decode FUNCTION_LO { - 0x0: msub_s({{ Fd.sf = (Fs.sf * Ft.sf) - Fr.sf; }}); - 0x1: msub_d({{ Fd.df = (Fs.df * Ft.df) - Fr.df; }}); - 0x6: msub_ps({{ - Fd1.sf = (Fs1.df * Ft1.df) - Fr1.df; - Fd2.sf = (Fs2.df * Ft2.df) - Fr2.df; - }}); - } + 0x5: decode FUNCTION_LO { + 0x0: msub_s({{ Fd.sf = (Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: msub_d({{ Fd.df = (Fs.df * Ft.df) - Fr.df; }}); + 0x6: msub_ps({{ + Fd1.sf = (Fs1.df * Ft1.df) - Fr1.df; + Fd2.sf = (Fs2.df * Ft2.df) - Fr2.df; + }}); + } - 0x6: decode FUNCTION_LO { - 0x0: nmadd_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); - 0x1: nmadd_d({{ Fd.df = (-1 * Fs.df * Ft.df) + Fr.df; }}); - 0x6: nmadd_ps({{ - Fd1.sf = -1 * ((Fs1.df * Ft1.df) + Fr1.df); - Fd2.sf = -1 * ((Fs2.df * Ft2.df) + Fr2.df); - }}); - } + 0x6: decode FUNCTION_LO { + 0x0: nmadd_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: nmadd_d({{ Fd.df = (-1 * Fs.df * Ft.df) + Fr.df; }}); + 0x6: nmadd_ps({{ + Fd1.sf = -((Fs1.df * Ft1.df) + Fr1.df); + Fd2.sf = -((Fs2.df * Ft2.df) + Fr2.df); + }}); + } - 0x7: decode FUNCTION_LO { - 0x0: nmsub_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); - 0x1: nmsub_d({{ Fd.df = (-1 * Fs.df * Ft.df) - Fr.df; }}); - 0x6: nmsub_ps({{ - Fd1.sf = -1 * ((Fs1.df * Ft1.df) - Fr1.df); - Fd2.sf = -1 * ((Fs2.df * Ft2.df) - Fr2.df); - }}); - } + 0x7: decode FUNCTION_LO { + 0x0: nmsub_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: nmsub_d({{ Fd.df = (-1 * Fs.df * Ft.df) - Fr.df; }}); + 0x6: nmsub_ps({{ + Fd1.sf = -((Fs1.df * Ft1.df) - Fr1.df); + Fd2.sf = -((Fs2.df * Ft2.df) - Fr2.df); + }}); } + } } - format BranchLikely { - 0x4: beql({{ cond = (Rs.sw == 0); }}); - 0x5: bnel({{ cond = (Rs.sw != 0); }}); - 0x6: blezl({{ cond = (Rs.sw <= 0); }}); - 0x7: bgtzl({{ cond = (Rs.sw > 0); }}); + format Branch { + 0x4: beql({{ cond = (Rs.sw == Rt.sw); }}, Likely); + 0x5: bnel({{ cond = (Rs.sw != Rt.sw); }}, Likely); + 0x6: blezl({{ cond = (Rs.sw <= 0); }}, Likely); + 0x7: bgtzl({{ cond = (Rs.sw > 0); }}, Likely); } } - 0x3: decode OPCODE_LO default FailUnimpl::reserved() { - + 0x3: decode OPCODE_LO { //Table A-5 MIPS32 SPECIAL2 Encoding of Function Field 0x4: decode FUNCTION_HI { - 0x0: decode FUNCTION_LO { - format IntOp { - 0x0: madd({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 + (Rs.sw * Rt.sw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x1: maddu({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 + (Rs.uw * Rt.uw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x2: mul({{ Rd.sw = Rs.sw * Rt.sw; }}); - - 0x4: msub({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 - (Rs.sw * Rt.sw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); + 0x2: IntOp::mul({{ int64_t temp1 = Rs.sd * Rt.sd; + Rd.sw = temp1<31:0> + }}); - 0x5: msubu({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 - (Rs.uw * Rt.uw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); + format HiLoOp { + 0x0: madd({{ val = ((int64_t) HI << 32 | LO) + + (Rs.sd * Rt.sd); + }}); + 0x1: maddu({{ val = ((uint64_t) HI << 32 | LO) + + (Rs.ud * Rt.ud); + }}); + 0x4: msub({{ val = ((int64_t) HI << 32 | LO) - + (Rs.sd * Rt.sd); + }}); + 0x5: msubu({{ val = ((uint64_t) HI << 32 | LO) - + (Rs.ud * Rt.ud); + }}); } } 0x4: decode FUNCTION_LO { format BasicOp { - 0x0: clz({{ - int cnt = 0; - uint32_t mask = 0x80000000; - for (int i=0; i < 32; i++) { - if( (Rs & mask) == 0) { - cnt++; - } else { - break; - } - } - Rd.uw = cnt; - }}); - - 0x1: clo({{ - int cnt = 0; - uint32_t mask = 0x80000000; - for (int i=0; i < 32; i++) { - if( (Rs & mask) != 0) { - cnt++; - } else { - break; - } - } - Rd.uw = cnt; - }}); + 0x0: clz({{ int cnt = 32; + for (int idx = 31; idx >= 0; idx--) { + if( Rs<idx:idx> == 1) { + cnt = 31 - idx; + break; + } + } + Rd.uw = cnt; + }}); + 0x1: clo({{ int cnt = 32; + for (int idx = 31; idx >= 0; idx--) { + if( Rs<idx:idx> == 0) { + cnt = 31 - idx; + break; + } + } + Rd.uw = cnt; + }}); } } 0x7: decode FUNCTION_LO { - 0x7: WarnUnimpl::sdbbp(); + 0x7: FailUnimpl::sdbbp(); } } - //Table A-6 MIPS32 SPECIAL3 Encoding of Function Field for Release 2 of the Architecture + //Table A-6 MIPS32 SPECIAL3 Encoding of Function Field for Release 2 + //of the Architecture 0x7: decode FUNCTION_HI { - 0x0: decode FUNCTION_LO { - format FailUnimpl { - 0x1: ext(); - 0x4: ins(); + format BasicOp { + 0x1: ext({{ Rt.uw = bits(Rs.uw, MSB+LSB, LSB); }}); + 0x4: ins({{ Rt.uw = bits(Rt.uw, 31, MSB+1) << (MSB+1) | + bits(Rs.uw, MSB-LSB, 0) << LSB | + bits(Rt.uw, LSB-1, 0); + }}); } } 0x1: decode FUNCTION_LO { - format FailUnimpl { + format MipsMT { 0x0: fork(); 0x1: yield(); } } - //Table A-10 MIPS32 BSHFL Encoding of sa Field 0x4: decode SA { - - 0x02: FailUnimpl::wsbh(); - format BasicOp { + 0x02: wsbh({{ Rd.uw = Rt.uw<23:16> << 24 | + Rt.uw<31:24> << 16 | + Rt.uw<7:0> << 8 | + Rt.uw<15:8>; + }}); 0x10: seb({{ Rd.sw = Rt.sw<7:0>}}); 0x18: seh({{ Rd.sw = Rt.sw<15:0>}}); } } 0x6: decode FUNCTION_LO { - 0x7: FailUnimpl::rdhwr();//{{ /*Rt = xc->hwRegs[RD];*/ }} + 0x7: FailUnimpl::rdhwr(); } } } - 0x4: decode OPCODE_LO default FailUnimpl::reserved() { + 0x4: decode OPCODE_LO { format LoadMemory { 0x0: lb({{ Rt.sw = Mem.sb; }}); 0x1: lh({{ Rt.sw = Mem.sh; }}); - - 0x2: lwl({{ - uint32_t mem_word = Mem.uw; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: - Rt = mem_word; - break; - - case 1: - Rt &= 0x000F; - Rt |= (mem_word << 4); - break; - - case 2: - Rt &= 0x00FF; - Rt |= (mem_word << 8); - break; - - case 3: - Rt &= 0x0FFF; - Rt |= (mem_word << 12); - break; - - default: - panic("lwl: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: - Rt &= 0x0FFF; - Rt |= (mem_word << 12); - break; - - case 1: - Rt &= 0x00FF; - Rt |= (mem_word << 8); - break; - - case 2: - Rt &= 0x000F; - Rt |= (mem_word << 4); - break; - - case 3: - Rt = mem_word; - break; - - default: - panic("lwl: bad offset"); - } -#endif - }}, {{ EA = (Rs + disp) & ~3; }}); - 0x3: lw({{ Rt.sw = Mem.sw; }}); 0x4: lbu({{ Rt.uw = Mem.ub; }}); 0x5: lhu({{ Rt.uw = Mem.uh; }}); - 0x6: lwr({{ - uint32_t mem_word = Mem.uw; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; - -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: Rt &= 0xFFF0; Rt |= (mem_word >> 12); break; - case 1: Rt &= 0xFF00; Rt |= (mem_word >> 8); break; - case 2: Rt &= 0xF000; Rt |= (mem_word >> 4); break; - case 3: Rt = mem_word; break; - default: panic("lwr: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: Rt = mem_word; break; - case 1: Rt &= 0xF000; Rt |= (mem_word >> 4); break; - case 2: Rt &= 0xFF00; Rt |= (mem_word >> 8); break; - case 3: Rt &= 0xFFF0; Rt |= (mem_word >> 12); break; - default: panic("lwr: bad offset"); - } -#endif - }}, - {{ EA = (Rs + disp) & ~3; }}); + } + + format LoadUnalignedMemory { + 0x2: lwl({{ uint32_t mem_shift = 24 - (8 * byte_offset); + Rt.uw = mem_word << mem_shift | + Rt.uw & mask(mem_shift); + }}); + 0x6: lwr({{ uint32_t mem_shift = 8 * byte_offset; + Rt.uw = Rt.uw & (mask(mem_shift) << (32 - mem_shift)) | + mem_word >> mem_shift; + }}); } } - 0x5: decode OPCODE_LO default FailUnimpl::reserved() { + 0x5: decode OPCODE_LO { format StoreMemory { 0x0: sb({{ Mem.ub = Rt<7:0>; }}); 0x1: sh({{ Mem.uh = Rt<15:0>; }}); - 0x2: swl({{ - uint32_t mem_word = 0; - uint32_t aligned_addr = (Rs + disp) & ~3; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; - - DPRINTF(IEW,"Execute: aligned=0x%x unaligned=0x%x\n offset=0x%x", - aligned_addr,unalign_addr,offset); - - fault = xc->read(aligned_addr, (uint32_t&)mem_word, memAccessFlags); - -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: - Mem = Rt; - break; - - case 1: - mem_word &= 0xF000; - mem_word |= (Rt >> 4); - Mem = mem_word; - break; - - case 2: - mem_word &= 0xFF00; - mem_word |= (Rt >> 8); - Mem = mem_word; - break; - - case 3: - mem_word &= 0xFFF0; - mem_word |= (Rt >> 12); - Mem = mem_word; - break; - - default: - panic("swl: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: - mem_word &= 0xFFF0; - mem_word |= (Rt >> 12); - Mem = mem_word; - break; - - case 1: - mem_word &= 0xFF00; - mem_word |= (Rt >> 8); - Mem = mem_word; - break; - - case 2: - mem_word &= 0xF000; - mem_word |= (Rt >> 4); - Mem = mem_word; - break; - - case 3: - Mem = Rt; - break; - - default: - panic("swl: bad offset"); - } -#endif - }},{{ EA = (Rs + disp) & ~3; }},mem_flags = NO_ALIGN_FAULT); - 0x3: sw({{ Mem.uw = Rt<31:0>; }}); - - 0x6: swr({{ - uint32_t mem_word = 0; - uint32_t aligned_addr = (Rs + disp) & ~3; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; - - fault = xc->read(aligned_addr, (uint32_t&)mem_word, memAccessFlags); - -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: - mem_word &= 0x0FFF; - mem_word |= (Rt << 12); - Mem = mem_word; - break; - - case 1: - mem_word &= 0x00FF; - mem_word |= (Rt << 8); - Mem = mem_word; - break; - - case 2: - mem_word &= 0x000F; - mem_word |= (Rt << 4); - Mem = mem_word; - break; - - case 3: - Mem = Rt; - break; - - default: - panic("swr: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: - Mem = Rt; - break; - - case 1: - mem_word &= 0x000F; - mem_word |= (Rt << 4); - Mem = mem_word; - break; - - case 2: - mem_word &= 0x00FF; - mem_word |= (Rt << 8); - Mem = mem_word; - break; - - case 3: - mem_word &= 0x0FFF; - mem_word |= (Rt << 12); - Mem = mem_word; - break; - - default: - panic("swr: bad offset"); - } -#endif - }},{{ EA = (Rs + disp) & ~3;}},mem_flags = NO_ALIGN_FAULT); } - format WarnUnimpl { - 0x7: cache(); + format StoreUnalignedMemory { + 0x2: swl({{ uint32_t reg_shift = 24 - (8 * byte_offset); + uint32_t mem_shift = 32 - reg_shift; + mem_word = mem_word & (mask(reg_shift) << mem_shift) | + Rt.uw >> reg_shift; + }}); + 0x6: swr({{ uint32_t reg_shift = 8 * byte_offset; + mem_word = Rt.uw << reg_shift | + mem_word & (mask(reg_shift)); + }}); } + 0x7: FailUnimpl::cache(); } - 0x6: decode OPCODE_LO default FailUnimpl::reserved() { - 0x0: LoadMemory::ll({{Rt.uw = Mem.uw}},mem_flags=LOCKED); - - format LoadFloatMemory { - 0x1: lwc1({{ Ft.uw = Mem.uw; }}); + 0x6: decode OPCODE_LO { + format LoadMemory { + 0x0: ll({{ Rt.uw = Mem.uw; }}, mem_flags=LOCKED); + 0x1: lwc1({{ Ft.uw = Mem.uw; }}); 0x5: ldc1({{ Ft.ud = Mem.ud; }}); } + + 0x3: Prefetch::pref(); } - 0x7: decode OPCODE_LO default FailUnimpl::reserved() { - 0x0: StoreMemory::sc({{ Mem.uw = Rt.uw; Rt.uw = 1; }}); + 0x7: decode OPCODE_LO { + 0x0: StoreCond::sc({{ Mem.uw = Rt.uw;}}, + {{ uint64_t tmp = write_result; + Rt.uw = (tmp == 0 || tmp == 1) ? tmp : Rt.uw; + }}, mem_flags=LOCKED); - format StoreFloatMemory { + format StoreMemory { 0x1: swc1({{ Mem.uw = Ft.uw; }}); 0x5: sdc1({{ Mem.ud = Ft.ud; }}); } diff --git a/src/arch/mips/isa/formats/basic.isa b/src/arch/mips/isa/formats/basic.isa index c02af7ddc..35ce09205 100644 --- a/src/arch/mips/isa/formats/basic.isa +++ b/src/arch/mips/isa/formats/basic.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + // Declarations for execute() methods. def template BasicExecDeclare {{ Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *) const; @@ -12,11 +40,11 @@ def template BasicDeclare {{ */ class %(class_name)s : public %(base_class)s { - public: + public: /// Constructor. %(class_name)s(MachInst machInst); %(BasicExecDeclare)s - }; + }; }}; // Basic instruction class constructor template. @@ -27,6 +55,7 @@ def template BasicConstructor {{ } }}; + // Basic instruction class execute method template. def template BasicExecute {{ Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const diff --git a/src/arch/mips/isa/formats/branch.isa b/src/arch/mips/isa/formats/branch.isa index 8cfa37a20..827e3ccf0 100644 --- a/src/arch/mips/isa/formats/branch.isa +++ b/src/arch/mips/isa/formats/branch.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Control transfer instructions @@ -68,29 +96,6 @@ output header {{ }; /** - * Base class for branch likely branches (PC-relative control transfers), - */ - class BranchLikely : public PCDependentDisassembly - { - protected: - /// target address (signed) Displacement . - int32_t disp; - - /// Constructor. - BranchLikely(const char *mnem, MachInst _machInst, OpClass __opClass) - : PCDependentDisassembly(mnem, _machInst, __opClass), - disp(OFFSET << 2) - { - - } - - Addr branchTarget(Addr branchPC) const; - - std::string - generateDisassembly(Addr pc, const SymbolTable *symtab) const; - }; - - /** * Base class for jumps (register-indirect control transfers). In * the Mips ISA, these are always unconditional. */ @@ -111,7 +116,7 @@ output header {{ { } - Addr branchTarget(ExecContext *xc) const; + Addr branchTarget(ThreadContext *tc) const; std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; @@ -126,16 +131,10 @@ output decoder {{ } Addr - BranchLikely::branchTarget(Addr branchPC) const - { - return branchPC + 4 + disp; - } - - Addr - Jump::branchTarget(ExecContext *xc) const + Jump::branchTarget(ThreadContext *tc) const { - Addr NPC = xc->readPC() + 4; - uint64_t Rb = xc->readIntReg(_srcRegIdx[0]); + Addr NPC = tc->readPC() + 4; + uint64_t Rb = tc->readIntReg(_srcRegIdx[0]); return (Rb & ~3) | (NPC & 1); } @@ -171,49 +170,12 @@ output decoder {{ // unconditional branches) if (_numSrcRegs == 1) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; } else if(_numSrcRegs == 2) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[1]); - ss << ","; - } - - Addr target = pc + 4 + disp; - - std::string str; - if (symtab && symtab->findSymbol(target, str)) - ss << str; - else - ccprintf(ss, "0x%x", target); - - string inst_name = mnemonic; - - if (inst_name.substr(inst_name.length()-2,inst_name.length()) == "al"){ - ccprintf(ss, " (r31=0x%x)",pc+8); - } - - return ss.str(); - } - - std::string - BranchLikely::generateDisassembly(Addr pc, const SymbolTable *symtab) const - { - std::stringstream ss; - - ccprintf(ss, "%-10s ", mnemonic); - - // There's only one register arg (RA), but it could be - // either a source (the condition for conditional - // branches) or a destination (the link reg for - // unconditional branches) - if (_numSrcRegs > 0) { - printReg(ss, _srcRegIdx[0]); - ss << ","; - } - else if (_numDestRegs > 0) { - printReg(ss, _destRegIdx[0]); - ss << ","; + ss << ", "; } Addr target = pc + 4 + disp; @@ -247,72 +209,64 @@ output decoder {{ printReg(ss, _srcRegIdx[0]); } else if(_numSrcRegs == 2) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[1]); - } else { - panic(">= 3 Source Registers!!!"); } return ss.str(); } }}; -def format Branch(code,*flags) {{ - #Add Link Code if Link instruction - strlen = len(name) - if name[strlen-2:] == 'al': - code += 'R31 = NNPC;\n' +def format Branch(code,*opt_flags) {{ + not_taken_code = ' NNPC = NNPC;\n' + not_taken_code += '} \n' + + #Build Instruction Flags + #Use Link & Likely Flags to Add Link/Condition Code + inst_flags = ('IsDirectControl', ) + for x in opt_flags: + if x == 'Link': + code += 'R31 = NNPC;\n' + elif x == 'Likely': + not_taken_code = ' NPC = NNPC;\n' + not_taken_code += ' NNPC = NNPC + 4;\n' + not_taken_code += '} \n' + inst_flags = ('IsCondDelaySlot', ) + else: + inst_flags += (x, ) + + if 'cond == 1' in code: + inst_flags += ('IsCondControl', ) + else: + inst_flags += ('IsUncondControl', ) #Condition code code = 'bool cond;\n' + code code += 'if (cond) {\n' code += ' NNPC = NPC + disp;\n' code += '} else {\n' - code += ' NNPC = NNPC;\n' - code += '} \n' - - iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), - ('IsDirectControl', 'IsCondControl')) - - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; - - -def format BranchLikely(code,*flags) {{ - #Add Link Code if Link instruction - strlen = len(name) - if name[strlen-3:] == 'all': - code += 'R31 = NNPC;\n' - - #Condition code - code = 'bool cond;\n' + code - code += 'if (cond) {' - code += 'NNPC = NPC + disp;\n' - code += '} \n' - - - iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), - ('IsDirectControl', 'IsCondControl','IsCondDelaySlot')) + code += not_taken_code + iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), inst_flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) exec_output = BasicExecute.subst(iop) }}; -def format Jump(code,*flags) {{ - #Add Link Code if Link instruction - strlen = len(name) - if strlen > 1 and name[1:] == 'al': +def format Jump(code, *opt_flags) {{ + #Build Instruction Flags + #Use Link Flag to Add Link Code + inst_flags = ('IsIndirectControl', 'IsUncondControl') + for x in opt_flags: + if x == 'Link': code = 'R31 = NNPC;\n' + code + elif x == 'ClearHazards': + code += '/* Code Needed to Clear Execute & Inst Hazards */\n' + else: + inst_flags += (x, ) - - iop = InstObjParams(name, Name, 'Jump', CodeBlock(code),\ - ('IsIndirectControl', 'IsUncondControl')) - + iop = InstObjParams(name, Name, 'Jump', CodeBlock(code), inst_flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) diff --git a/src/arch/mips/isa/formats/control.isa b/src/arch/mips/isa/formats/control.isa new file mode 100644 index 000000000..509ee7e87 --- /dev/null +++ b/src/arch/mips/isa/formats/control.isa @@ -0,0 +1,156 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// + +//Outputs to decoder.hh +output header {{ + + class Control : public MipsStaticInst + { + protected: + + /// Constructor + Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class CP0Control : public Control + { + protected: + + /// Constructor + CP0Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + Control(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class CP1Control : public Control + { + protected: + + /// Constructor + CP1Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + Control(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + +}}; + +//Outputs to decoder.cc +output decoder {{ + std::string Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (mnemonic == "mfc0" || mnemonic == "mtc0") { + ccprintf(ss, "%-10s %d,%d,%d", mnemonic,RT,RD,SEL); + } else { + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + } + + ss << ", "; + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + } + + return ss.str(); + } + + std::string CP0Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + ccprintf(ss, "%-10s r%d, r%d, %d", mnemonic, RT, RD, SEL); + return ss.str(); + } + + std::string CP1Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + ccprintf(ss, "%-10s r%d, f%d", mnemonic, RT, FS); + return ss.str(); + } + +}}; + +def format System(code, *flags) {{ + iop = InstObjParams(name, Name, 'Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format CP0Control(code, *flags) {{ + iop = InstObjParams(name, Name, 'CP0Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format CP1Control(code, *flags) {{ + iop = InstObjParams(name, Name, 'CP1Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + diff --git a/src/arch/mips/isa/formats/formats.isa b/src/arch/mips/isa/formats/formats.isa index 7d493ffae..4c3eec132 100644 --- a/src/arch/mips/isa/formats/formats.isa +++ b/src/arch/mips/isa/formats/formats.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //Templates from this format are used later //Include the basic format ##include "basic.isa" @@ -10,8 +38,8 @@ //Include utility functions ##include "util.isa" -//Include the cop0 formats -##include "cop0.isa" +//Include the control/cp0/cp1 formats +##include "control.isa" //Include the integer formats ##include "int.isa" @@ -22,6 +50,9 @@ //Include the mem format ##include "mem.isa" +//Include the mem format +##include "mt.isa" + //Include the trap format ##include "trap.isa" diff --git a/src/arch/mips/isa/formats/fp.isa b/src/arch/mips/isa/formats/fp.isa index 9f2c24755..d05b04d0e 100644 --- a/src/arch/mips/isa/formats/fp.isa +++ b/src/arch/mips/isa/formats/fp.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Floating Point operate instructions @@ -18,49 +46,264 @@ output header {{ { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + //std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + //needs function to check for fpEnable or not + }; + + class FPCompareOp : public FPOp + { + protected: + FPCompareOp(const char *mnem, MachInst _machInst, OpClass __opClass) : FPOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; }}; output decoder {{ - std::string FPOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + std::string FPCompareOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const { - return "Disassembly of integer instruction\n"; + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + ccprintf(ss,"%d",CC); + + if(_numSrcRegs > 0) { + ss << ", "; + printReg(ss, _srcRegIdx[0]); + } + + if(_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); } }}; +output exec {{ -// Primary format for float operate instructions: -def format FloatOp(code, *flags) {{ - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + //If any operand is Nan return the appropriate QNaN + template <class T> + bool + fpNanOperands(FPOp *inst, %(CPU_exec_context)s *xc, const T &src_type, + Trace::InstRecord *traceData) + { + uint64_t mips_nan = 0; + T src_op = 0; + int size = sizeof(src_op) * 8; + + for (int i = 0; i < inst->numSrcRegs(); i++) { + uint64_t src_bits = xc->readFloatRegBits(inst, 0, size); + + if (isNan(&src_bits, size) ) { + if (isSnan(&src_bits, size)) { + switch (size) + { + case 32: mips_nan = MIPS32_QNAN; break; + case 64: mips_nan = MIPS64_QNAN; break; + default: panic("Unsupported Floating Point Size (%d)", size); + } + } else { + mips_nan = src_bits; + } + + xc->setFloatRegBits(inst, 0, mips_nan, size); + if (traceData) { traceData->setData(mips_nan); } + return true; + } + } + return false; + } + + template <class T> + bool + fpInvalidOp(FPOp *inst, %(CPU_exec_context)s *cpu, const T dest_val, + Trace::InstRecord *traceData) + { + uint64_t mips_nan = 0; + T src_op = dest_val; + int size = sizeof(src_op) * 8; + + if (isNan(&src_op, size)) { + switch (size) + { + case 32: mips_nan = MIPS32_QNAN; break; + case 64: mips_nan = MIPS64_QNAN; break; + default: panic("Unsupported Floating Point Size (%d)", size); + } + + //Set value to QNAN + cpu->setFloatRegBits(inst, 0, mips_nan, size); + + //Read FCSR from FloatRegFile + uint32_t fcsr_bits = cpu->tc->readFloatRegBits(FCSR); + + //Write FCSR from FloatRegFile + cpu->tc->setFloatRegBits(FCSR, genInvalidVector(fcsr_bits)); + + if (traceData) { traceData->setData(mips_nan); } + return true; + } + + return false; + } + + void + fpResetCauseBits(%(CPU_exec_context)s *cpu) + { + //Read FCSR from FloatRegFile + uint32_t fcsr = cpu->tc->readFloatRegBits(FCSR); + + fcsr = bits(fcsr, 31, 18) << 18 | bits(fcsr, 11, 0); + + //Write FCSR from FloatRegFile + cpu->tc->setFloatRegBits(FCSR, fcsr); + } }}; -def format FloatCompareOp(code, *flags) {{ - code = 'bool cond;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC,cond);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) +def template FloatingPointExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + + //When is the right time to reset cause bits? + //start of every instruction or every cycle? + fpResetCauseBits(xc); + + %(op_decl)s; + %(op_rd)s; + + //Check if any FP operand is a NaN value + if (!fpNanOperands((FPOp*)this, xc, Fd, traceData)) { + %(code)s; + + //Change this code for Full-System/Sycall Emulation + //separation + //---- + //Should Full System-Mode throw a fault here? + //---- + //Check for IEEE 754 FP Exceptions + //fault = fpNanOperands((FPOp*)this, xc, Fd, traceData); + if (!fpInvalidOp((FPOp*)this, xc, Fd, traceData) && + fault == NoFault) + { + %(op_wb)s; + } + } + + return fault; + } }}; -def format FloatCompareWithXcptOp(code, *flags) {{ - code = 'bool cond;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC,cond);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) +// Primary format for float point operate instructions: +def format FloatOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code), flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + exec_output = FloatingPointExecute.subst(iop) +}}; + +def format FloatCompareOp(cond_code, *flags) {{ + import sys + + code = 'bool cond;\n' + if '.sf' in cond_code or 'SinglePrecision' in flags: + if 'QnanException' in flags: + code += 'if (isQnan(&Fs.sf, 32) || isQnan(&Ft.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += '\treturn NoFault;' + code += '}\n else ' + code += 'if (isNan(&Fs.sf, 32) || isNan(&Ft.sf, 32)) {\n' + elif '.df' in cond_code or 'DoublePrecision' in flags: + if 'QnanException' in flags: + code += 'if (isQnan(&Fs.df, 64) || isQnan(&Ft.df, 64)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += '\treturn NoFault;' + code += '}\n else ' + code += 'if (isNan(&Fs.df, 64) || isNan(&Ft.df, 64)) {\n' + else: + sys.exit('Decoder Failed: Can\'t Determine Operand Type\n') + + if 'UnorderedTrue' in flags: + code += 'cond = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + + code += '} else {\n' + code += cond_code + '}' + code += 'FCSR = genCCVector(FCSR, CC, cond);\n' + + iop = InstObjParams(name, Name, 'FPCompareOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; def format FloatConvertOp(code, *flags) {{ - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) + import sys + + #Determine Source Type + convert = 'fpConvert(' + if '.sf' in code: + code = 'float ' + code + '\n' + convert += 'SINGLE_TO_' + elif '.df' in code: + code = 'double ' + code + '\n' + convert += 'DOUBLE_TO_' + elif '.uw' in code: + code = 'uint32_t ' + code + '\n' + convert += 'WORD_TO_' + elif '.ud' in code: + code = 'uint64_t ' + code + '\n' + convert += 'LONG_TO_' + else: + sys.exit("Error Determining Source Type for Conversion") + + #Determine Destination Type + if 'ToSingle' in flags: + code += 'Fd.uw = ' + convert + 'SINGLE, ' + elif 'ToDouble' in flags: + code += 'Fd.ud = ' + convert + 'DOUBLE, ' + elif 'ToWord' in flags: + code += 'Fd.uw = ' + convert + 'WORD, ' + elif 'ToLong' in flags: + code += 'Fd.ud = ' + convert + 'LONG, ' + else: + sys.exit("Error Determining Destination Type for Conversion") + + #Figure out how to round value + if 'Ceil' in flags: + code += 'ceil(val)); ' + elif 'Floor' in flags: + code += 'floor(val)); ' + elif 'Round' in flags: + code += 'roundFP(val, 0)); ' + elif 'Trunc' in flags: + code += 'truncFP(val));' + else: + code += 'val); ' + + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format FloatAccOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code), flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) @@ -76,34 +319,51 @@ def format Float64Op(code, *flags) {{ exec_output = BasicExecute.subst(iop) }}; -def format Float64ConvertOp(code, *flags) {{ - code = 'bool cond;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC,cond);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; +def format FloatPSCompareOp(cond_code1, cond_code2, *flags) {{ + import sys -def format FloatPSCompareOp(code, *flags) {{ - code = 'bool cond1;\nbool cond2;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC+1, cond1);\n' - code += 'FCSR = makeCCVector(FCSR, CC, cond2);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; + code = 'bool cond1, cond2;\n' + code += 'bool code_block1, code_block2;\n' + code += 'code_block1 = code_block2 = true;\n' -def format FloatPSCompareWithXcptOp(code, *flags) {{ - code = 'bool cond1;\nbool cond2;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC+1, cond1);\n' - code += 'FCSR = makeCCVector(FCSR, CC, cond2);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + if 'QnanException' in flags: + code += 'if (isQnan(&Fs1.sf, 32) || isQnan(&Ft1.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += 'code_block1 = false;' + code += '}\n' + code += 'if (isQnan(&Fs2.sf, 32) || isQnan(&Ft2.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += 'code_block2 = false;' + code += '}\n' + + code += 'if (code_block1) {' + code += '\tif (isNan(&Fs1.sf, 32) || isNan(&Ft1.sf, 32)) {\n' + if 'UnorderedTrue' in flags: + code += 'cond1 = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond1 = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + code += '} else {\n' + code += cond_code1 + code += 'FCSR = genCCVector(FCSR, CC, cond1);}\n}\n' + + code += 'if (code_block2) {' + code += '\tif (isNan(&Fs2.sf, 32) || isNan(&Ft2.sf, 32)) {\n' + if 'UnorderedTrue' in flags: + code += 'cond2 = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond2 = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + code += '} else {\n' + code += cond_code2 + code += 'FCSR = genCCVector(FCSR, CC, cond2);}\n}' + + iop = InstObjParams(name, Name, 'FPCompareOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; + diff --git a/src/arch/mips/isa/formats/int.isa b/src/arch/mips/isa/formats/int.isa index 7d38b9ff5..7b5affb5c 100644 --- a/src/arch/mips/isa/formats/int.isa +++ b/src/arch/mips/isa/formats/int.isa @@ -1,11 +1,37 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Integer operate instructions // - -//Outputs to decoder.hh output header {{ #include <iostream> using namespace std; @@ -25,6 +51,34 @@ output header {{ std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; }; + + class HiLoOp: public IntOp + { + protected: + + /// Constructor + HiLoOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + IntOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class HiLoMiscOp: public HiLoOp + { + protected: + + /// Constructor + HiLoMiscOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + HiLoOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class IntImmOp : public MipsStaticInst { protected: @@ -52,6 +106,33 @@ output header {{ }}; +// HiLo<Misc> instruction class execute method template. +// Mainly to get instruction trace data to print out +// correctly +def template HiLoExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if(fault == NoFault) + { + %(op_wb)s; + //If there are 2 Destination Registers then + //concatenate the values for the traceData + if(traceData && _numDestRegs == 2) { + uint64_t hilo_final_val = (uint64_t)HI << 32 | LO; + traceData->setData(hilo_final_val); + } + } + return fault; + } +}}; + //Outputs to decoder.cc output decoder {{ std::string IntOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const @@ -64,7 +145,7 @@ output decoder {{ // it's generally implicit if (_numDestRegs > 0) { printReg(ss, _destRegIdx[0]); - ss << ","; + ss << ", "; } // just print the first two source regs... if there's @@ -75,13 +156,47 @@ output decoder {{ } if (_numSrcRegs > 1) { - ss << ","; + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); + } + + std::string HiLoOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + //Destination Registers are implicit for HI/LO ops + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; printReg(ss, _srcRegIdx[1]); } return ss.str(); } + std::string HiLoMiscOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (_numDestRegs > 0 && _destRegIdx[0] < 32) { + printReg(ss, _destRegIdx[0]); + } else if (_numSrcRegs > 0 && _srcRegIdx[0] < 32) { + printReg(ss, _srcRegIdx[0]); + } + + return ss.str(); + } + std::string IntImmOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const { std::stringstream ss; @@ -92,15 +207,15 @@ output decoder {{ printReg(ss, _destRegIdx[0]); } - ss << ","; + ss << ", "; if (_numSrcRegs > 0) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; } if( mnemonic == "lui") - ccprintf(ss, "%08p ", sextImm); + ccprintf(ss, "0x%x ", sextImm); else ss << (int) sextImm; @@ -109,23 +224,47 @@ output decoder {{ }}; -//Used by decoder.isa def format IntOp(code, *opt_flags) {{ - orig_code = code - cblk = CodeBlock(code) - - # Figure out if we are creating a IntImmOp or a IntOp - # by looking at the instruction name - iop = InstObjParams(name, Name, 'IntOp', cblk, opt_flags) - strlen = len(name) - if name[strlen-1] == 'i' or name[strlen-2:] == 'iu': - iop = InstObjParams(name, Name, 'IntImmOp', cblk, opt_flags) - - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = OperateNopCheckDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + iop = InstObjParams(name, Name, 'IntOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format IntImmOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'IntImmOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format HiLoOp(code, *opt_flags) {{ + if '.sd' in code: + code = 'int64_t ' + code + elif '.ud' in code: + code = 'uint64_t ' + code + + code += 'HI = val<63:32>;\n' + code += 'LO = val<31:0>;\n' + + iop = InstObjParams(name, Name, 'HiLoOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = HiLoExecute.subst(iop) }}; +def format HiLoMiscOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'HiLoMiscOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = HiLoExecute.subst(iop) +}}; + + + diff --git a/src/arch/mips/isa/formats/mem.isa b/src/arch/mips/isa/formats/mem.isa index 922cdb9a2..f52247056 100644 --- a/src/arch/mips/isa/formats/mem.isa +++ b/src/arch/mips/isa/formats/mem.isa @@ -25,10 +25,13 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Gabe Black +// Korey Sewell //////////////////////////////////////////////////////////////////// // -// Memory-format instructions: LoadAddress, Load, Store +// Memory-format instructions // output header {{ @@ -87,15 +90,6 @@ output decoder {{ }}; -def format LoadAddress(code) {{ - iop = InstObjParams(name, Name, 'MemoryDisp32', CodeBlock(code)) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; - - def template LoadStoreDeclare {{ /** * Static instruction class for "%(mnemonic)s". @@ -423,8 +417,70 @@ def template StoreCompleteAcc {{ } }}; + +def template MiscMemAccExecute {{ + Fault %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + %(code)s; + } + + return NoFault; + } +}}; + +def template MiscExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + return NoFault; + } +}}; + +def template MiscInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("Misc instruction does not support split access method!"); + return NoFault; + } +}}; + + +def template MiscCompleteAcc {{ + Fault %(class_name)s::completeAcc(uint8_t *data, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("Misc instruction does not support split access method!"); + + return NoFault; + } +}}; + // load instructions use Rt as dest, so check for -// Rt == 31 to detect nops +// Rt == 0 to detect nops def template LoadNopCheckDecode {{ { MipsStaticInst *i = new %(class_name)s(machInst); @@ -443,7 +499,6 @@ def format LoadMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, exec_template_base = 'Load') }}; - def format StoreMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ @@ -451,26 +506,70 @@ def format StoreMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, exec_template_base = 'Store') }}; -//FP loads are offloaded to these formats for now ... -def format LoadFloatMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, +def format LoadIndexedMemory(memacc_code, ea_code = {{ EA = Rs + Rt; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, - decode_template = BasicDecode, + decode_template = LoadNopCheckDecode, exec_template_base = 'Load') }}; +def format StoreIndexedMemory(memacc_code, ea_code = {{ EA = Rs + Rt; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + exec_template_base = 'Store') +}}; + +def format LoadUnalignedMemory(memacc_code, ea_code = {{ EA = (Rs + disp) & ~3; }}, + mem_flags = [], inst_flags = []) {{ + decl_code = 'uint32_t mem_word = Mem.uw;\n' + decl_code += 'uint32_t unalign_addr = Rs + disp;\n' + decl_code += 'uint32_t byte_offset = unalign_addr & 3;\n' + decl_code += '#if BYTE_ORDER == BIG_ENDIAN\n' + decl_code += '\tbyte_offset ^= 3;\n' + decl_code += '#endif\n' + + memacc_code = decl_code + memacc_code -def format StoreFloatMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = LoadNopCheckDecode, + exec_template_base = 'Load') +}}; + +def format StoreUnalignedMemory(memacc_code, ea_code = {{ EA = (Rs + disp) & ~3; }}, mem_flags = [], inst_flags = []) {{ + decl_code = 'uint32_t mem_word = 0;\n' + decl_code += 'uint32_t unaligned_addr = Rs + disp;\n' + decl_code += 'uint32_t byte_offset = unaligned_addr & 3;\n' + decl_code += '#if BYTE_ORDER == BIG_ENDIAN\n' + decl_code += '\tbyte_offset ^= 3;\n' + decl_code += '#endif\n' + decl_code += 'fault = xc->read(EA, (uint32_t&)mem_word, memAccessFlags);\n' + memacc_code = decl_code + memacc_code + '\nMem = mem_word;\n' + (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = LoadNopCheckDecode, exec_template_base = 'Store') }}; +def format Prefetch(ea_code = {{ EA = Rs + disp; }}, + mem_flags = [], pf_flags = [], inst_flags = []) {{ + pf_mem_flags = mem_flags + pf_flags + ['NO_FAULT'] + pf_inst_flags = inst_flags + ['IsMemRef', 'IsLoad', + 'IsDataPrefetch', 'MemReadOp'] + + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, + 'xc->prefetch(EA, memAccessFlags);', + pf_mem_flags, pf_inst_flags, exec_template_base = 'Misc') + +}}; -def format UnalignedStore(memacc_code, postacc_code, - ea_code = {{ EA = Rb + disp; }}, +def format StoreCond(memacc_code, postacc_code, + ea_code = {{ EA = Rs + disp; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, diff --git a/src/arch/mips/isa/formats/mt.isa b/src/arch/mips/isa/formats/mt.isa new file mode 100644 index 000000000..521b01123 --- /dev/null +++ b/src/arch/mips/isa/formats/mt.isa @@ -0,0 +1,81 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// MT instructions +// + +output header {{ + /** + * Base class for integer operations. + */ + class MT : public MipsStaticInst + { + protected: + + /// Constructor + MT(const char *mnem, MachInst _machInst, OpClass __opClass) : MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + //Edit This Template When MT is Implemented + std::string MT::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return "Disassembly of MT instruction\n"; + } +}}; + +def template MTExecute {{ + //Edit This Template When MT is Implemented + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + //Write the resulting state to the execution context + %(op_wb)s; + + //Call into the trap handler with the appropriate fault + return No_Fault; + } +}}; + +// Primary format for integer operate instructions: +def format MipsMT() {{ + code = 'panic(\"Mips MT Is Currently Unimplemented.\");\n' + iop = InstObjParams(name, Name, 'MT', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; diff --git a/src/arch/mips/isa/formats/noop.isa b/src/arch/mips/isa/formats/noop.isa index 2aa4816e3..4fd8235e4 100644 --- a/src/arch/mips/isa/formats/noop.isa +++ b/src/arch/mips/isa/formats/noop.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Nop @@ -36,11 +64,7 @@ output decoder {{ std::string Nop::generateDisassembly(Addr pc, const SymbolTable *symtab) const { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return originalDisassembly; -#else - return csprintf("%-10s (%s)", "nop", originalDisassembly); -#endif + return csprintf("%-10s %s", "nop", originalDisassembly); } /// Helper function for decoding nops. Substitute Nop object @@ -89,6 +113,6 @@ def format BasicOperateWithNopCheck(code, *opt_args) {{ }}; def format Nop() {{ - decode_block = 'return new Nop(\"sll r0,r0,0\",machInst);\n' + decode_block = 'return new Nop(\"\",machInst);\n' }}; diff --git a/src/arch/mips/isa/formats/tlbop.isa b/src/arch/mips/isa/formats/tlbop.isa index f5e4076f2..75ab71c48 100644 --- a/src/arch/mips/isa/formats/tlbop.isa +++ b/src/arch/mips/isa/formats/tlbop.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // TlbOp instructions @@ -30,13 +60,10 @@ output decoder {{ def template TlbOpExecute {{ Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const { - //Call into the trap handler with the appropriate fault - return No_Fault; - } - //Write the resulting state to the execution context %(op_wb)s; + //Call into the trap handler with the appropriate fault return No_Fault; } }}; diff --git a/src/arch/mips/isa/formats/trap.isa b/src/arch/mips/isa/formats/trap.isa index 6884d4fa8..574b808cc 100644 --- a/src/arch/mips/isa/formats/trap.isa +++ b/src/arch/mips/isa/formats/trap.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Trap instructions @@ -23,27 +53,26 @@ output header {{ output decoder {{ std::string Trap::generateDisassembly(Addr pc, const SymbolTable *symtab) const { - return "Disassembly of integer instruction\n"; + return "Disassembly of trap instruction\n"; } }}; def template TrapExecute {{ + //Edit This Template When Traps Are Implemented Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const { - //Call into the trap handler with the appropriate fault - return No_Fault; - } - //Write the resulting state to the execution context %(op_wb)s; + //Call into the trap handler with the appropriate fault return No_Fault; } }}; -// Primary format for integer operate instructions: def format Trap(code, *flags) {{ - code = 'bool cond;\n' + code; + code = 'panic(\"' + code += 'Trap Exception Handler Is Currently Not Implemented.' + code += '\");' iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) diff --git a/src/arch/mips/isa/formats/unimp.isa b/src/arch/mips/isa/formats/unimp.isa index ea0c5b15e..e17b5f832 100644 --- a/src/arch/mips/isa/formats/unimp.isa +++ b/src/arch/mips/isa/formats/unimp.isa @@ -1,5 +1,6 @@ // -*- mode:c++ -*- + // Copyright (c) 2003-2005 The Regents of The University of Michigan // All rights reserved. // @@ -25,6 +26,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell //////////////////////////////////////////////////////////////////// // @@ -101,11 +104,7 @@ output decoder {{ WarnUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab) const { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return csprintf("%-10s", mnemonic); -#else return csprintf("%-10s (unimplemented)", mnemonic); -#endif } }}; @@ -125,7 +124,7 @@ output exec {{ Trace::InstRecord *traceData) const { if (!warned) { - warn("instruction '%s' unimplemented\n", mnemonic); + warn("\tinstruction '%s' unimplemented\n", mnemonic); warned = true; } @@ -144,28 +143,3 @@ def format WarnUnimpl() {{ decode_block = BasicDecodeWithMnemonic.subst(iop) }}; -output header {{ - /** - * Static instruction class for unknown (illegal) instructions. - * These cause simulator termination if they are executed in a - * non-speculative mode. This is a leaf class. - */ - class Unknown : public MipsStaticInst - { - public: - /// Constructor - Unknown(MachInst _machInst) - : MipsStaticInst("unknown", _machInst, No_OpClass) - { - // don't call execute() (which panics) if we're on a - // speculative path - flags[IsNonSpeculative] = true; - } - - %(BasicExecDeclare)s - - std::string - generateDisassembly(Addr pc, const SymbolTable *symtab) const; - }; -}}; - diff --git a/src/arch/mips/isa/formats/unknown.isa b/src/arch/mips/isa/formats/unknown.isa index 6f88e630c..41387adca 100644 --- a/src/arch/mips/isa/formats/unknown.isa +++ b/src/arch/mips/isa/formats/unknown.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2003-2005 The Regents of The University of Michigan +// Copyright (c) 2003-2006 The Regents of The University of Michigan // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell //////////////////////////////////////////////////////////////////// // @@ -32,28 +34,31 @@ // output header {{ - std::string inst2string(MachInst machInst); -}}; -output decoder {{ - -std::string inst2string(MachInst machInst) -{ - string str = ""; - uint32_t mask = 0x80000000; - - for(int i=0; i < 32; i++) { - if ((machInst & mask) == 0) { - str += "0"; - } else { - str += "1"; + /** + * Static instruction class for unknown (illegal) instructions. + * These cause simulator termination if they are executed in a + * non-speculative mode. This is a leaf class. + */ + class Unknown : public MipsStaticInst + { + public: + /// Constructor + Unknown(MachInst _machInst) + : MipsStaticInst("unknown", _machInst, No_OpClass) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; } - mask = mask >> 1; - } + %(BasicExecDeclare)s - return str; -} + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; +output decoder {{ std::string Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab) const { diff --git a/src/arch/mips/isa/formats/util.isa b/src/arch/mips/isa/formats/util.isa index 615160931..b67a02d07 100644 --- a/src/arch/mips/isa/formats/util.isa +++ b/src/arch/mips/isa/formats/util.isa @@ -1,5 +1,34 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 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. +// +// Authors: Steve Reinhardt +// Korey Sewell + let {{ def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, postacc_code = '', base_class = 'Memory', @@ -90,7 +119,31 @@ def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + completeAccTemplate.subst(completeacc_iop)) }}; +output header {{ + std::string inst2string(MachInst machInst); +}}; + +output decoder {{ + +std::string inst2string(MachInst machInst) +{ + string str = ""; + uint32_t mask = 0x80000000; + + for(int i=0; i < 32; i++) { + if ((machInst & mask) == 0) { + str += "0"; + } else { + str += "1"; + } + mask = mask >> 1; + } + + return str; +} + +}}; output exec {{ using namespace MipsISA; @@ -124,6 +177,7 @@ output exec {{ #endif + }}; diff --git a/src/arch/mips/isa/includes.isa b/src/arch/mips/isa/includes.isa index 9c370fbe3..555cec255 100644 --- a/src/arch/mips/isa/includes.isa +++ b/src/arch/mips/isa/includes.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Output include file directives. @@ -16,9 +46,10 @@ output decoder {{ #include "arch/mips/isa_traits.hh" #include "base/cprintf.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" // for Jump::branchTarget() +#include "cpu/thread_context.hh" #include "arch/mips/faults.hh" #include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" #include <math.h> #if defined(linux) @@ -31,6 +62,8 @@ using namespace MipsISA; output exec {{ #include "arch/mips/faults.hh" #include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" + #include <math.h> #if defined(linux) #include <fenv.h> diff --git a/src/arch/mips/isa/main.isa b/src/arch/mips/isa/main.isa index e6f43c3e7..9da3fc0db 100644 --- a/src/arch/mips/isa/main.isa +++ b/src/arch/mips/isa/main.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/mips/isa/operands.isa b/src/arch/mips/isa/operands.isa index 0f9c74b48..316552ef4 100644 --- a/src/arch/mips/isa/operands.isa +++ b/src/arch/mips/isa/operands.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 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. +// +// Authors: Korey Sewell + def operand_types {{ 'sb' : ('signed int', 8), 'ub' : ('unsigned int', 8), diff --git a/src/arch/mips/isa_traits.cc b/src/arch/mips/isa_traits.cc index 216a6e2ec..9f3817a60 100644 --- a/src/arch/mips/isa_traits.cc +++ b/src/arch/mips/isa_traits.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Korey Sewell */ #include "arch/mips/isa_traits.hh" @@ -37,8 +40,9 @@ using namespace std; void -MipsISA::copyRegs(ExecContext *src, ExecContext *dest) +MipsISA::copyRegs(ThreadContext *src, ThreadContext *dest) { + panic("Copy Regs Not Implemented Yet\n"); /*fpcr = xc->readMiscReg(MipsISA::Fpcr_DepTag); uniq = xc->readMiscReg(MipsISA::Uniq_DepTag); lock_flag = xc->readMiscReg(MipsISA::Lock_Flag_DepTag); @@ -50,8 +54,9 @@ MipsISA::copyRegs(ExecContext *src, ExecContext *dest) } void -MipsISA::MiscRegFile::copyMiscRegs(ExecContext *xc) +MipsISA::MiscRegFile::copyMiscRegs(ThreadContext *tc) { + panic("Copy Misc. Regs Not Implemented Yet\n"); /*fpcr = xc->readMiscReg(MipsISA::Fpcr_DepTag); uniq = xc->readMiscReg(MipsISA::Uniq_DepTag); lock_flag = xc->readMiscReg(MipsISA::Lock_Flag_DepTag); @@ -60,77 +65,6 @@ MipsISA::MiscRegFile::copyMiscRegs(ExecContext *xc) #endif*/ } -uint64_t -MipsISA::fpConvert(double fp_val, ConvertType cvt_type) -{ - - switch (cvt_type) - { - case SINGLE_TO_DOUBLE: - double sdouble_val = fp_val; - void *sdouble_ptr = &sdouble_val; - uint64_t sdp_bits = *(uint64_t *) sdouble_ptr; - return sdp_bits; - - case SINGLE_TO_WORD: - int32_t sword_val = (int32_t) fp_val; - void *sword_ptr = &sword_val; - uint64_t sword_bits= *(uint32_t *) sword_ptr; - return sword_bits; - - case WORD_TO_SINGLE: - float wfloat_val = fp_val; - void *wfloat_ptr = &wfloat_val; - uint64_t wfloat_bits = *(uint32_t *) wfloat_ptr; - return wfloat_bits; - - case WORD_TO_DOUBLE: - double wdouble_val = fp_val; - void *wdouble_ptr = &wdouble_val; - uint64_t wdp_bits = *(uint64_t *) wdouble_ptr; - return wdp_bits; - - default: - panic("Invalid Floating Point Conversion Type (%d). See \"types.hh\" for List of Conversions\n",cvt_type); - return 0; - } -} - -double -MipsISA::roundFP(double val, int digits) -{ - double digit_offset = pow(10.0,digits); - val = val * digit_offset; - val = val + 0.5; - val = floor(val); - val = val / digit_offset; - return val; -} - -double -MipsISA::truncFP(double val) -{ - int trunc_val = (int) val; - return (double) trunc_val; -} - -bool -MipsISA::getFPConditionCode(uint32_t fcsr_reg, int cc) -{ - //uint32_t cc_bits = xc->readFloatReg(35); - return false;//regFile.floatRegfile.getConditionCode(cc); -} - -uint32_t -MipsISA::makeCCVector(uint32_t fcsr, int num, bool val) -{ - int shift = (num == 0) ? 22 : num + 23; - - fcsr = fcsr | (val << shift); - - return fcsr; -} - #if FULL_SYSTEM static inline Addr diff --git a/src/arch/mips/isa_traits.hh b/src/arch/mips/isa_traits.hh index 148c405df..dc8b6758a 100644 --- a/src/arch/mips/isa_traits.hh +++ b/src/arch/mips/isa_traits.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Korey Sewell */ #ifndef __ARCH_MIPS_ISA_TRAITS_HH__ @@ -45,7 +48,7 @@ class FastCPU; class FullCPU; class Checkpoint; -class ExecContext; +class ThreadContext; namespace LittleEndianGuest {}; @@ -128,14 +131,14 @@ namespace MipsISA /** * Function to insure ISA semantics about 0 registers. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void zeroRegisters(XC *xc); + template <class TC> + void zeroRegisters(TC *tc); const Addr MaxAddr = (Addr)-1; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); uint64_t fpConvert(double fp_val, ConvertType cvt_type); double roundFP(double val, int digits); diff --git a/src/arch/mips/linux/process.cc b/src/arch/mips/linux/process.cc index ffc5da2e1..17e735527 100644 --- a/src/arch/mips/linux/process.cc +++ b/src/arch/mips/linux/process.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #include "arch/mips/linux/linux.hh" @@ -31,7 +33,7 @@ #include "arch/mips/isa_traits.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/linux.hh" #include "sim/process.hh" @@ -43,9 +45,9 @@ using namespace MipsISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Linux::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "Linux"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -53,7 +55,7 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); strcpy(name->machine, "mips"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } @@ -62,18 +64,18 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, /// different in practice from those used by Tru64 processes. static SyscallReturn sys_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 45: { // GSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR *fpcr = 0; - fpcr.copyOut(xc->getMemPort()); + fpcr.copyOut(tc->getMemPort()); return 0; } @@ -89,17 +91,17 @@ sys_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target sys_setsysinfo() handler. static SyscallReturn sys_setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 14: { // SSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR - fpcr.copyIn(xc->getMemPort()); + fpcr.copyIn(tc->getMemPort()); DPRINTFR(SyscallVerbose, "sys_setsysinfo(SSI_IEEE_FP_CONTROL): " " setting FPCR to 0x%x\n", gtoh(*(uint64_t*)fpcr)); return 0; @@ -133,7 +135,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 14 */ SyscallDesc("mknod", unimplementedFunc), /* 15 */ SyscallDesc("chmod", chmodFunc<MipsLinux>), /* 16 */ SyscallDesc("lchown", chownFunc), - /* 17 */ SyscallDesc("break", obreakFunc), /*obreak*/ + /* 17 */ SyscallDesc("break", obreakFunc), /* 18 */ SyscallDesc("unused#18", unimplementedFunc), /* 19 */ SyscallDesc("lseek", lseekFunc), /* 20 */ SyscallDesc("getpid", getpidFunc), @@ -161,7 +163,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 42 */ SyscallDesc("pipe", unimplementedFunc), /* 43 */ SyscallDesc("times", unimplementedFunc), /* 44 */ SyscallDesc("prof", unimplementedFunc), - /* 45 */ SyscallDesc("brk", obreakFunc),/*openFunc<MipsLinux>*/ + /* 45 */ SyscallDesc("brk", obreakFunc), /* 46 */ SyscallDesc("setgid", unimplementedFunc), /* 47 */ SyscallDesc("getgid", getgidFunc), /* 48 */ SyscallDesc("signal", ignoreFunc), @@ -171,7 +173,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 52 */ SyscallDesc("umount2", unimplementedFunc), /* 53 */ SyscallDesc("lock", unimplementedFunc), /* 54 */ SyscallDesc("ioctl", ioctlFunc<MipsLinux>), - /* 55 */ SyscallDesc("fcntl", unimplementedFunc), + /* 55 */ SyscallDesc("fcntl", fcntlFunc), /* 56 */ SyscallDesc("mpx", unimplementedFunc), /* 57 */ SyscallDesc("setpgid", unimplementedFunc), /* 58 */ SyscallDesc("ulimit", unimplementedFunc), @@ -208,8 +210,8 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 89 */ SyscallDesc("readdir", unimplementedFunc), /* 90 */ SyscallDesc("mmap", mmapFunc<MipsLinux>), /* 91 */ SyscallDesc("munmap",munmapFunc), - /* 92 */ SyscallDesc("truncate", fcntlFunc), - /* 93 */ SyscallDesc("ftruncate", unimplementedFunc), + /* 92 */ SyscallDesc("truncate", truncateFunc), + /* 93 */ SyscallDesc("ftruncate", ftruncateFunc), /* 94 */ SyscallDesc("fchmod", unimplementedFunc), /* 95 */ SyscallDesc("fchown", unimplementedFunc), /* 96 */ SyscallDesc("getpriority", unimplementedFunc), @@ -260,7 +262,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 141 */ SyscallDesc("getdents", unimplementedFunc), /* 142 */ SyscallDesc("newselect", unimplementedFunc), /* 143 */ SyscallDesc("flock", unimplementedFunc), - /* 144 */ SyscallDesc("msync", unimplementedFunc),/*getrlimitFunc<MipsLinux>*/ + /* 144 */ SyscallDesc("msync", unimplementedFunc), /* 145 */ SyscallDesc("readv", unimplementedFunc), /* 146 */ SyscallDesc("writev", writevFunc<MipsLinux>), /* 147 */ SyscallDesc("cacheflush", unimplementedFunc), @@ -336,7 +338,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 217 */ SyscallDesc("mincore", unimplementedFunc), /* 218 */ SyscallDesc("madvise", unimplementedFunc), /* 219 */ SyscallDesc("getdents64", unimplementedFunc), - /* 220 */ SyscallDesc("fcntl64", fcntlFunc), + /* 220 */ SyscallDesc("fcntl64", fcntl64Func), /* 221 */ SyscallDesc("reserved#221", unimplementedFunc), /* 222 */ SyscallDesc("gettid", unimplementedFunc), /* 223 */ SyscallDesc("readahead", unimplementedFunc), @@ -412,9 +414,7 @@ MipsLinuxProcess::MipsLinuxProcess(const std::string &name, : MipsLiveProcess(name, objFile, system, stdin_fd, stdout_fd, stderr_fd, argv, envp), Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)) -{ - //init_regs->intRegFile[0] = 0; -} +{ } SyscallDesc* MipsLinuxProcess::getDesc(int callnum) diff --git a/src/arch/mips/process.cc b/src/arch/mips/process.cc index 7831551be..7762c2fa0 100644 --- a/src/arch/mips/process.cc +++ b/src/arch/mips/process.cc @@ -24,74 +24,41 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi + * Korey Sewell */ #include "arch/mips/isa_traits.hh" #include "arch/mips/process.hh" -#include "arch/mips/linux/process.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" -#include "cpu/exec_context.hh" -#include "sim/builder.hh" +#include "cpu/thread_context.hh" #include "sim/system.hh" using namespace std; using namespace MipsISA; - -MipsLiveProcess * -MipsLiveProcess::create(const std::string &nm, System *system, int stdin_fd, - int stdout_fd, int stderr_fd, std::string executable, - std::vector<std::string> &argv, std::vector<std::string> &envp) -{ - MipsLiveProcess *process = NULL; - - ObjectFile *objFile = createObjectFile(executable); - if (objFile == NULL) { - fatal("Can't load object file %s", executable); - } - - - if (objFile->getArch() != ObjectFile::Mips) - fatal("Object file does not match architecture."); - switch (objFile->getOpSys()) { - case ObjectFile::Linux: - process = new MipsLinuxProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - default: - fatal("Unknown/unsupported operating system."); - } - - if (process == NULL) - fatal("Unknown error creating process object."); - return process; -} - MipsLiveProcess::MipsLiveProcess(const std::string &nm, ObjectFile *objFile, System *_system, int stdin_fd, int stdout_fd, int stderr_fd, std::vector<std::string> &argv, std::vector<std::string> &envp) : LiveProcess(nm, objFile, _system, stdin_fd, stdout_fd, stderr_fd, argv, envp) { + // Set up stack. On MIPS, stack starts at the top of kuseg + // user address space. MIPS stack grows down from here + stack_base = 0x7FFFFFFF; - // XXX all the below need to be updated for SPARC - Ali + // Set pointer for next thread stack. Reserve 8M for main stack. + next_thread_stack_base = stack_base - (8 * 1024 * 1024); + + // Set up break point (Top of Heap) brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); brk_point = roundUp(brk_point, VMPageSize); - // Set up stack. On Alpha, stack goes below text section. This - // code should get moved to some architecture-specific spot. - stack_base = objFile->textBase() - (409600+4096); - - // Set up region for mmaps. Tru64 seems to start just above 0 and - // grow up from there. + // Set up region for mmaps. For now, start at bottom of kuseg space. mmap_start = mmap_end = 0x10000; - - // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); - } void @@ -99,63 +66,3 @@ MipsLiveProcess::startup() { argsInit(MachineBytes, VMPageSize); } - - - - -BEGIN_DECLARE_SIM_OBJECT_PARAMS(MipsLiveProcess) - - VectorParam<string> cmd; - Param<string> executable; - Param<string> input; - Param<string> output; - VectorParam<string> env; - SimObjectParam<System *> system; - -END_DECLARE_SIM_OBJECT_PARAMS(MipsLiveProcess) - - -BEGIN_INIT_SIM_OBJECT_PARAMS(MipsLiveProcess) - - INIT_PARAM(cmd, "command line (executable plus arguments)"), - INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), - INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), - INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), - INIT_PARAM(env, "environment settings"), - INIT_PARAM(system, "system") - -END_INIT_SIM_OBJECT_PARAMS(MipsLiveProcess) - - -CREATE_SIM_OBJECT(MipsLiveProcess) -{ - string in = input; - string out = output; - - // initialize file descriptors to default: same as simulator - int stdin_fd, stdout_fd, stderr_fd; - - if (in == "stdin" || in == "cin") - stdin_fd = STDIN_FILENO; - else - stdin_fd = Process::openInputFile(input); - - if (out == "stdout" || out == "cout") - stdout_fd = STDOUT_FILENO; - else if (out == "stderr" || out == "cerr") - stdout_fd = STDERR_FILENO; - else - stdout_fd = Process::openOutputFile(out); - - stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; - - return MipsLiveProcess::create(getInstanceName(), system, - stdin_fd, stdout_fd, stderr_fd, - (string)executable == "" ? cmd[0] : executable, - cmd, env); -} - - -REGISTER_SIM_OBJECT("MipsLiveProcess", MipsLiveProcess) - - diff --git a/src/arch/mips/process.hh b/src/arch/mips/process.hh index 2a13dc955..b0ef20399 100644 --- a/src/arch/mips/process.hh +++ b/src/arch/mips/process.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi + * Korey Sewell */ #ifndef __MIPS_PROCESS_HH__ @@ -46,18 +50,6 @@ class MipsLiveProcess : public LiveProcess std::vector<std::string> &envp); void startup(); - - public: - // this function is used to create the LiveProcess object, since - // we can't tell which subclass of LiveProcess to use until we - // open and look at the object file. - static MipsLiveProcess *create(const std::string &nm, - System *_system, - int stdin_fd, int stdout_fd, int stderr_fd, - std::string executable, - std::vector<std::string> &argv, - std::vector<std::string> &envp); - }; diff --git a/src/arch/mips/regfile/float_regfile.hh b/src/arch/mips/regfile/float_regfile.hh index 15c6f97f4..d1a60298a 100644 --- a/src/arch/mips/regfile/float_regfile.hh +++ b/src/arch/mips/regfile/float_regfile.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_FLOAT_REGFILE_HH__ @@ -38,7 +40,7 @@ #include "sim/host.hh" class Checkpoint; -class ExecContext; +class ThreadContext; class Regfile; namespace MipsISA @@ -60,13 +62,17 @@ namespace MipsISA switch(width) { case SingleWidth: - void *float_ptr = ®s[floatReg]; - return *(float *) float_ptr; + { + void *float_ptr = ®s[floatReg]; + return *(float *) float_ptr; + } case DoubleWidth: - uint64_t double_val = (FloatReg64)regs[floatReg + 1] << 32 | regs[floatReg]; - void *double_ptr = &double_val; - return *(double *) double_ptr; + { + uint64_t double_val = (FloatReg64)regs[floatReg + 1] << 32 | regs[floatReg]; + void *double_ptr = &double_val; + return *(double *) double_ptr; + } default: panic("Attempted to read a %d bit floating point register!", width); @@ -97,21 +103,24 @@ namespace MipsISA Fault setReg(int floatReg, const FloatReg &val, int width) { - switch(width) { case SingleWidth: - float temp = val; - void *float_ptr = &temp; - regs[floatReg] = *(FloatReg32 *) float_ptr; - break; + { + float temp = val; + void *float_ptr = &temp; + regs[floatReg] = *(FloatReg32 *) float_ptr; + break; + } case DoubleWidth: - const void *double_ptr = &val; - FloatReg64 temp_double = *(FloatReg64 *) double_ptr; - regs[floatReg + 1] = temp_double >> 32; - regs[floatReg] = temp_double; - break; + { + const void *double_ptr = &val; + FloatReg64 temp_double = *(FloatReg64 *) double_ptr; + regs[floatReg + 1] = temp_double >> 32; + regs[floatReg] = 0x0000FFFF & temp_double; + break; + } default: panic("Attempted to read a %d bit floating point register!", width); @@ -146,14 +155,6 @@ namespace MipsISA void unserialize(Checkpoint *cp, const std::string §ion); }; - enum MiscFloatRegNums { - FIR = NumFloatArchRegs, - FCCR, - FEXR, - FENR, - FCSR - }; - } // namespace MipsISA #endif diff --git a/src/arch/mips/regfile/int_regfile.hh b/src/arch/mips/regfile/int_regfile.hh index 3cd87734d..dc82a3c26 100644 --- a/src/arch/mips/regfile/int_regfile.hh +++ b/src/arch/mips/regfile/int_regfile.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_INT_REGFILE_HH__ @@ -35,7 +37,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; class Regfile; namespace MipsISA diff --git a/src/arch/mips/regfile/misc_regfile.hh b/src/arch/mips/regfile/misc_regfile.hh index 9f054e5f7..f8aeab8cb 100644 --- a/src/arch/mips/regfile/misc_regfile.hh +++ b/src/arch/mips/regfile/misc_regfile.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_MISC_REGFILE_HH__ @@ -34,7 +36,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; class Regfile; namespace MipsISA @@ -55,14 +57,14 @@ namespace MipsISA int getInstAsid(); int getDataAsid(); - void copyMiscRegs(ExecContext *xc); + void copyMiscRegs(ThreadContext *tc); MiscReg readReg(int misc_reg) { return miscRegFile[misc_reg]; } - MiscReg readRegWithEffect(int misc_reg, Fault &fault, ExecContext *xc) + MiscReg readRegWithEffect(int misc_reg, Fault &fault, ThreadContext *tc) { return miscRegFile[misc_reg]; } @@ -73,7 +75,7 @@ namespace MipsISA } Fault setRegWithEffect(int misc_reg, const MiscReg &val, - ExecContext *xc) + ThreadContext *tc) { miscRegFile[misc_reg] = val; return NoFault; } @@ -85,9 +87,9 @@ namespace MipsISA InternalProcReg ipr[NumInternalProcRegs]; // Internal processor regs private: - MiscReg readIpr(int idx, Fault &fault, ExecContext *xc) { } + MiscReg readIpr(int idx, Fault &fault, ThreadContext *tc) { } - Fault setIpr(int idx, uint64_t val, ExecContext *xc) { } + Fault setIpr(int idx, uint64_t val, ThreadContext *tc) { } #endif friend class RegFile; }; diff --git a/src/arch/mips/regfile/regfile.hh b/src/arch/mips/regfile/regfile.hh index e77571b33..af61e62cd 100644 --- a/src/arch/mips/regfile/regfile.hh +++ b/src/arch/mips/regfile/regfile.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_REGFILE_HH__ @@ -37,7 +39,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; namespace MipsISA { @@ -62,10 +64,10 @@ namespace MipsISA } MiscReg readMiscRegWithEffect(int miscReg, - Fault &fault, ExecContext *xc) + Fault &fault, ThreadContext *tc) { fault = NoFault; - return miscRegFile.readRegWithEffect(miscReg, fault, xc); + return miscRegFile.readRegWithEffect(miscReg, fault, tc); } Fault setMiscReg(int miscReg, const MiscReg &val) @@ -74,9 +76,9 @@ namespace MipsISA } Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, - ExecContext * xc) + ThreadContext * tc) { - return miscRegFile.setRegWithEffect(miscReg, val, xc); + return miscRegFile.setRegWithEffect(miscReg, val, tc); } FloatReg readFloatReg(int floatReg) @@ -187,12 +189,12 @@ namespace MipsISA } }; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); - void copyMiscRegs(ExecContext *src, ExecContext *dest); + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); #if FULL_SYSTEM - void copyIprs(ExecContext *src, ExecContext *dest); + void copyIprs(ThreadContext *src, ThreadContext *dest); #endif } // namespace MipsISA diff --git a/src/arch/mips/stacktrace.hh b/src/arch/mips/stacktrace.hh index 1d8d97a79..38767cef7 100644 --- a/src/arch/mips/stacktrace.hh +++ b/src/arch/mips/stacktrace.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ARCH_ALPHA_STACKTRACE_HH__ @@ -32,13 +34,13 @@ #include "base/trace.hh" #include "cpu/static_inst.hh" -class ExecContext; +class ThreadContext; class StackTrace; class ProcessInfo { private: - ExecContext *xc; + ThreadContext *tc; int thread_info_size; int task_struct_size; @@ -47,7 +49,7 @@ class ProcessInfo int name_off; public: - ProcessInfo(ExecContext *_xc); + ProcessInfo(ThreadContext *_tc); Addr task(Addr ksp) const; int pid(Addr ksp) const; @@ -59,7 +61,7 @@ class StackTrace protected: typedef TheISA::MachInst MachInst; private: - ExecContext *xc; + ThreadContext *tc; std::vector<Addr> stack; private: @@ -68,21 +70,21 @@ class StackTrace bool decodeSave(MachInst inst, int ®, int &disp); bool decodeStack(MachInst inst, int &disp); - void trace(ExecContext *xc, bool is_call); + void trace(ThreadContext *tc, bool is_call); public: StackTrace(); - StackTrace(ExecContext *xc, StaticInstPtr inst); + StackTrace(ThreadContext *tc, StaticInstPtr inst); ~StackTrace(); void clear() { - xc = 0; + tc = 0; stack.clear(); } - bool valid() const { return xc != NULL; } - bool trace(ExecContext *xc, StaticInstPtr inst); + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); public: const std::vector<Addr> &getstack() const { return stack; } @@ -104,7 +106,7 @@ class StackTrace }; inline bool -StackTrace::trace(ExecContext *xc, StaticInstPtr inst) +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) { if (!inst->isCall() && !inst->isReturn()) return false; @@ -112,7 +114,7 @@ StackTrace::trace(ExecContext *xc, StaticInstPtr inst) if (valid()) clear(); - trace(xc, !inst->isReturn()); + trace(tc, !inst->isReturn()); return true; } diff --git a/src/arch/mips/types.hh b/src/arch/mips/types.hh index 4d5fb3456..7cd2eed0c 100644 --- a/src/arch/mips/types.hh +++ b/src/arch/mips/types.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_TYPES_HH__ diff --git a/src/arch/mips/utility.cc b/src/arch/mips/utility.cc new file mode 100644 index 000000000..e7455fdbf --- /dev/null +++ b/src/arch/mips/utility.cc @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2003-2006 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. + * + * Authors: Korey Sewell + */ + +#include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" +#include "config/full_system.hh" +#include "cpu/static_inst.hh" +#include "sim/serialize.hh" +#include "base/bitfield.hh" + +using namespace MipsISA; +using namespace std; + +uint64_t +MipsISA::fpConvert(ConvertType cvt_type, double fp_val) +{ + + switch (cvt_type) + { + case SINGLE_TO_DOUBLE: + { + double sdouble_val = fp_val; + void *sdouble_ptr = &sdouble_val; + uint64_t sdp_bits = *(uint64_t *) sdouble_ptr; + return sdp_bits; + } + + case SINGLE_TO_WORD: + { + int32_t sword_val = (int32_t) fp_val; + void *sword_ptr = &sword_val; + uint64_t sword_bits= *(uint32_t *) sword_ptr; + return sword_bits; + } + + case WORD_TO_SINGLE: + { + float wfloat_val = fp_val; + void *wfloat_ptr = &wfloat_val; + uint64_t wfloat_bits = *(uint32_t *) wfloat_ptr; + return wfloat_bits; + } + + case WORD_TO_DOUBLE: + { + double wdouble_val = fp_val; + void *wdouble_ptr = &wdouble_val; + uint64_t wdp_bits = *(uint64_t *) wdouble_ptr; + return wdp_bits; + } + + default: + panic("Invalid Floating Point Conversion Type (%d). See \"types.hh\" for List of Conversions\n",cvt_type); + return 0; + } +} + +double +MipsISA::roundFP(double val, int digits) +{ + double digit_offset = pow(10.0,digits); + val = val * digit_offset; + val = val + 0.5; + val = floor(val); + val = val / digit_offset; + return val; +} + +double +MipsISA::truncFP(double val) +{ + int trunc_val = (int) val; + return (double) trunc_val; +} + +bool +MipsISA::getCondCode(uint32_t fcsr, int cc_idx) +{ + int shift = (cc_idx == 0) ? 23 : cc_idx + 24; + bool cc_val = (fcsr >> shift) & 0x00000001; + return cc_val; +} + +uint32_t +MipsISA::genCCVector(uint32_t fcsr, int cc_num, uint32_t cc_val) +{ + int cc_idx = (cc_num == 0) ? 23 : cc_num + 24; + + fcsr = bits(fcsr, 31, cc_idx + 1) << cc_idx + 1 | + cc_val << cc_idx | + bits(fcsr, cc_idx - 1, 0); + + return fcsr; +} + +uint32_t +MipsISA::genInvalidVector(uint32_t fcsr_bits) +{ + //Set FCSR invalid in "flag" field + int invalid_offset = Invalid + Flag_Field; + fcsr_bits = fcsr_bits | (1 << invalid_offset); + + //Set FCSR invalid in "cause" flag + int cause_offset = Invalid + Cause_Field; + fcsr_bits = fcsr_bits | (1 << cause_offset); + + return fcsr_bits; +} + +bool +MipsISA::isNan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 23) == 0xFF); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 52) == 0x7FF); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} + + +bool +MipsISA::isQnan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 22) == 0x1FE); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 51) == 0xFFE); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} + +bool +MipsISA::isSnan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 22) == 0x1FF); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 51) == 0xFFF); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} diff --git a/src/arch/mips/utility.hh b/src/arch/mips/utility.hh index 5c7dc3ea4..c5c69ddcd 100644 --- a/src/arch/mips/utility.hh +++ b/src/arch/mips/utility.hh @@ -39,6 +39,19 @@ namespace MipsISA { + //Floating Point Utility Functions + uint64_t fpConvert(ConvertType cvt_type, double fp_val); + double roundFP(double val, int digits); + double truncFP(double val); + + bool getCondCode(uint32_t fcsr, int cc); + uint32_t genCCVector(uint32_t fcsr, int num, uint32_t cc_val); + uint32_t genInvalidVector(uint32_t fcsr); + + bool isNan(void *val_ptr, int size); + bool isQnan(void *val_ptr, int size); + bool isSnan(void *val_ptr, int size); }; + #endif diff --git a/src/arch/sparc/SConscript b/src/arch/sparc/SConscript index 7362c9275..e7a8278db 100644 --- a/src/arch/sparc/SConscript +++ b/src/arch/sparc/SConscript @@ -25,6 +25,9 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black +# Steve Reinhardt import os import sys @@ -47,12 +50,8 @@ base_sources = Split(''' # Full-system sources full_system_sources = Split(''' - tlb.cc - arguments.cc - ev5.cc - osfpal.cc - stacktrace.cc vtophys.cc + ua2005.cc ''') # Syscall emulation (non-full-system) sources diff --git a/src/arch/sparc/faults.cc b/src/arch/sparc/faults.cc index 67a89ab0e..57b4d4d86 100644 --- a/src/arch/sparc/faults.cc +++ b/src/arch/sparc/faults.cc @@ -24,10 +24,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Kevin Lim */ #include "arch/sparc/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "base/trace.hh" @@ -217,36 +220,36 @@ FaultStat TrapInstruction::_count; #if FULL_SYSTEM -void SparcFault::invoke(ExecContext * xc) +void SparcFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); countStat()++; //Use the SPARC trap state machine /*// exception restart address - if (setRestartAddress() || !xc->inPalMode()) - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, xc->regs.pc); + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, tc->regs.pc); if (skipFaultingInstruction()) { // traps... skip faulting instruction. - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, - xc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, + tc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); } - if (!xc->inPalMode()) - AlphaISA::swap_palshadow(&(xc->regs), true); + if (!tc->inPalMode()) + AlphaISA::swap_palshadow(&(tc->regs), true); - xc->regs.pc = xc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect(); - xc->regs.npc = xc->regs.pc + sizeof(MachInst);*/ + tc->regs.pc = tc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect(); + tc->regs.npc = tc->regs.pc + sizeof(MachInst);*/ } #endif #if !FULL_SYSTEM -void TrapInstruction::invoke(ExecContext * xc) +void TrapInstruction::invoke(ThreadContext * tc) { - xc->syscall(syscall_num); + tc->syscall(syscall_num); } #endif diff --git a/src/arch/sparc/faults.hh b/src/arch/sparc/faults.hh index e8fb8dfc5..9f595a28b 100644 --- a/src/arch/sparc/faults.hh +++ b/src/arch/sparc/faults.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Kevin Lim */ #ifndef __ALPHA_FAULTS_HH__ @@ -43,7 +46,7 @@ class SparcFault : public FaultBase { public: #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif virtual TrapType trapType() = 0; virtual FaultPriority priority() = 0; @@ -582,7 +585,7 @@ class TrapInstruction : public EnumeratedFault FaultPriority priority() {return _priority;} FaultStat & countStat() {return _count;} #if !FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; diff --git a/src/arch/sparc/isa/includes.isa b/src/arch/sparc/isa/includes.isa index 762de243a..40afb3722 100644 --- a/src/arch/sparc/isa/includes.isa +++ b/src/arch/sparc/isa/includes.isa @@ -48,7 +48,7 @@ output header {{ output decoder {{ #include "base/cprintf.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" // for Jump::branchTarget() +#include "cpu/thread_context.hh" // for Jump::branchTarget() #include <math.h> #if defined(linux) diff --git a/src/arch/sparc/isa/main.isa b/src/arch/sparc/isa/main.isa index 79be0e2a3..14acf54fa 100644 --- a/src/arch/sparc/isa/main.isa +++ b/src/arch/sparc/isa/main.isa @@ -25,6 +25,8 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell //////////////////////////////////////////////////////////////////// // diff --git a/src/arch/sparc/isa_traits.hh b/src/arch/sparc/isa_traits.hh index 2b4d89441..346f7b730 100644 --- a/src/arch/sparc/isa_traits.hh +++ b/src/arch/sparc/isa_traits.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + * Gabe Black */ #ifndef __ARCH_SPARC_ISA_TRAITS_HH__ @@ -33,7 +36,7 @@ #include "config/full_system.hh" #include "sim/host.hh" -class ExecContext; +class ThreadContext; class FastCPU; //class FullCPU; class Checkpoint; @@ -80,6 +83,9 @@ class SyscallReturn #endif +#if FULL_SYSTEM +#include "arch/sparc/isa_fullsys_traits.hh" +#endif namespace SparcISA { diff --git a/src/arch/sparc/linux/linux.cc b/src/arch/sparc/linux/linux.cc index c7ed29358..ae6ffbc2a 100644 --- a/src/arch/sparc/linux/linux.cc +++ b/src/arch/sparc/linux/linux.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black */ #include "arch/sparc/linux/linux.hh" diff --git a/src/arch/sparc/linux/linux.hh b/src/arch/sparc/linux/linux.hh index 9cde5bb9c..926c2cb77 100644 --- a/src/arch/sparc/linux/linux.hh +++ b/src/arch/sparc/linux/linux.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black */ #ifndef __ARCH_SPARC_LINUX_LINUX_HH__ diff --git a/src/arch/sparc/linux/process.cc b/src/arch/sparc/linux/process.cc index 71be6a83a..e27255e67 100644 --- a/src/arch/sparc/linux/process.cc +++ b/src/arch/sparc/linux/process.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Gabe Black + * Ali Saidi */ #include "arch/sparc/isa_traits.hh" @@ -31,7 +35,7 @@ #include "arch/sparc/regfile.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/linux.hh" #include "sim/process.hh" @@ -44,9 +48,9 @@ using namespace SparcISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Linux::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "Linux"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -54,40 +58,40 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); strcpy(name->machine, "sparc"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } SyscallReturn SparcISA::getresuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc) + Process *p, ThreadContext *tc) { const IntReg id = htog(100); - Addr ruid = xc->getSyscallArg(0); - Addr euid = xc->getSyscallArg(1); - Addr suid = xc->getSyscallArg(2); + Addr ruid = tc->getSyscallArg(0); + Addr euid = tc->getSyscallArg(1); + Addr suid = tc->getSyscallArg(2); //Handle the EFAULT case //Set the ruid if(ruid) { BufferArg ruidBuff(ruid, sizeof(IntReg)); memcpy(ruidBuff.bufferPtr(), &id, sizeof(IntReg)); - ruidBuff.copyOut(xc->getMemPort()); + ruidBuff.copyOut(tc->getMemPort()); } //Set the euid if(euid) { BufferArg euidBuff(euid, sizeof(IntReg)); memcpy(euidBuff.bufferPtr(), &id, sizeof(IntReg)); - euidBuff.copyOut(xc->getMemPort()); + euidBuff.copyOut(tc->getMemPort()); } //Set the suid if(suid) { BufferArg suidBuff(suid, sizeof(IntReg)); memcpy(suidBuff.bufferPtr(), &id, sizeof(IntReg)); - suidBuff.copyOut(xc->getMemPort()); + suidBuff.copyOut(tc->getMemPort()); } return 0; } diff --git a/src/arch/sparc/linux/process.hh b/src/arch/sparc/linux/process.hh index 23ce66d02..f4819ba84 100644 --- a/src/arch/sparc/linux/process.hh +++ b/src/arch/sparc/linux/process.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __SPARC_LINUX_PROCESS_HH__ @@ -59,7 +61,7 @@ class SparcLinuxProcess : public SparcLiveProcess }; SyscallReturn getresuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); } // namespace SparcISA #endif // __ALPHA_LINUX_PROCESS_HH__ diff --git a/src/arch/sparc/process.cc b/src/arch/sparc/process.cc index fe6692cc3..75f01e038 100644 --- a/src/arch/sparc/process.cc +++ b/src/arch/sparc/process.cc @@ -24,60 +24,23 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi */ #include "arch/sparc/isa_traits.hh" #include "arch/sparc/process.hh" -#include "arch/sparc/linux/process.hh" -#include "arch/sparc/solaris/process.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/page_table.hh" #include "mem/translating_port.hh" -#include "sim/builder.hh" #include "sim/system.hh" using namespace std; using namespace SparcISA; -SparcLiveProcess * -SparcLiveProcess::create(const std::string &nm, System *system, int stdin_fd, - int stdout_fd, int stderr_fd, std::string executable, - std::vector<std::string> &argv, std::vector<std::string> &envp) -{ - SparcLiveProcess *process = NULL; - - ObjectFile *objFile = createObjectFile(executable); - if (objFile == NULL) { - fatal("Can't load object file %s", executable); - } - - - if (objFile->getArch() != ObjectFile::SPARC) - fatal("Object file with arch %x does not match architecture %x.", - objFile->getArch(), ObjectFile::SPARC); - switch (objFile->getOpSys()) { - case ObjectFile::Linux: - process = new SparcLinuxProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - - case ObjectFile::Solaris: - process = new SparcSolarisProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - default: - fatal("Unknown/unsupported operating system."); - } - - if (process == NULL) - fatal("Unknown error creating process object."); - return process; -} SparcLiveProcess::SparcLiveProcess(const std::string &nm, ObjectFile *objFile, System *_system, int stdin_fd, int stdout_fd, int stderr_fd, @@ -110,27 +73,27 @@ SparcLiveProcess::startup() //From the SPARC ABI //The process runs in user mode - execContexts[0]->setMiscRegWithEffect(MISCREG_PSTATE, 0x02); + threadContexts[0]->setMiscRegWithEffect(MISCREG_PSTATE, 0x02); //Setup default FP state - execContexts[0]->setMiscReg(MISCREG_FSR, 0); + threadContexts[0]->setMiscReg(MISCREG_FSR, 0); - execContexts[0]->setMiscReg(MISCREG_TICK, 0); + threadContexts[0]->setMiscReg(MISCREG_TICK, 0); // /* * Register window management registers */ //No windows contain info from other programs - execContexts[0]->setMiscRegWithEffect(MISCREG_OTHERWIN, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_OTHERWIN, 0); //There are no windows to pop - execContexts[0]->setMiscRegWithEffect(MISCREG_CANRESTORE, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CANRESTORE, 0); //All windows are available to save into - execContexts[0]->setMiscRegWithEffect(MISCREG_CANSAVE, NWindows - 2); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CANSAVE, NWindows - 2); //All windows are "clean" - execContexts[0]->setMiscRegWithEffect(MISCREG_CLEANWIN, NWindows); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CLEANWIN, NWindows); //Start with register window 0 - execContexts[0]->setMiscRegWithEffect(MISCREG_CWP, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CWP, 0); } m5_auxv_t buildAuxVect(int64_t type, int64_t val) @@ -308,72 +271,14 @@ SparcLiveProcess::argsInit(int intSize, int pageSize) initVirtMem->writeBlob(argc_base, (uint8_t*)&guestArgc, intSize); - execContexts[0]->setIntReg(ArgumentReg0, argc); - execContexts[0]->setIntReg(ArgumentReg1, argv_array_base); - execContexts[0]->setIntReg(StackPointerReg, stack_min - StackBias); + threadContexts[0]->setIntReg(ArgumentReg0, argc); + threadContexts[0]->setIntReg(ArgumentReg1, argv_array_base); + threadContexts[0]->setIntReg(StackPointerReg, stack_min - StackBias); Addr prog_entry = objFile->entryPoint(); - execContexts[0]->setPC(prog_entry); - execContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); - execContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); + threadContexts[0]->setPC(prog_entry); + threadContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); + threadContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); // num_processes++; } - - -BEGIN_DECLARE_SIM_OBJECT_PARAMS(SparcLiveProcess) - - VectorParam<string> cmd; - Param<string> executable; - Param<string> input; - Param<string> output; - VectorParam<string> env; - SimObjectParam<System *> system; - -END_DECLARE_SIM_OBJECT_PARAMS(SparcLiveProcess) - - -BEGIN_INIT_SIM_OBJECT_PARAMS(SparcLiveProcess) - - INIT_PARAM(cmd, "command line (executable plus arguments)"), - INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), - INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), - INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), - INIT_PARAM(env, "environment settings"), - INIT_PARAM(system, "system") - -END_INIT_SIM_OBJECT_PARAMS(SparcLiveProcess) - - -CREATE_SIM_OBJECT(SparcLiveProcess) -{ - string in = input; - string out = output; - - // initialize file descriptors to default: same as simulator - int stdin_fd, stdout_fd, stderr_fd; - - if (in == "stdin" || in == "cin") - stdin_fd = STDIN_FILENO; - else - stdin_fd = Process::openInputFile(input); - - if (out == "stdout" || out == "cout") - stdout_fd = STDOUT_FILENO; - else if (out == "stderr" || out == "cerr") - stdout_fd = STDERR_FILENO; - else - stdout_fd = Process::openOutputFile(out); - - stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; - - return SparcLiveProcess::create(getInstanceName(), system, - stdin_fd, stdout_fd, stderr_fd, - (string)executable == "" ? cmd[0] : executable, - cmd, env); -} - - -REGISTER_SIM_OBJECT("SparcLiveProcess", SparcLiveProcess) - - diff --git a/src/arch/sparc/process.hh b/src/arch/sparc/process.hh index c177f20a5..7ba8d7109 100644 --- a/src/arch/sparc/process.hh +++ b/src/arch/sparc/process.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi */ #ifndef __SPARC_PROCESS_HH__ @@ -62,15 +65,6 @@ class SparcLiveProcess : public LiveProcess void startup(); public: - // this function is used to create the LiveProcess object, since - // we can't tell which subclass of LiveProcess to use until we - // open and look at the object file. - static SparcLiveProcess *create(const std::string &nm, - System *_system, - int stdin_fd, int stdout_fd, int stderr_fd, - std::string executable, - std::vector<std::string> &argv, - std::vector<std::string> &envp); void argsInit(int intSize, int pageSize); diff --git a/src/arch/sparc/regfile.hh b/src/arch/sparc/regfile.hh index 4e5494730..e5192d684 100644 --- a/src/arch/sparc/regfile.hh +++ b/src/arch/sparc/regfile.hh @@ -24,15 +24,19 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + * Ali Saidi */ #ifndef __ARCH_SPARC_REGFILE_HH__ #define __ARCH_SPARC_REGFILE_HH__ +#include "arch/sparc/exceptions.hh" #include "arch/sparc/faults.hh" #include "base/trace.hh" #include "sim/byteswap.hh" -#include "sim/eventq.hh" +#include "cpu/cpuevent.hh" #include "sim/host.hh" class Checkpoint; @@ -56,6 +60,7 @@ namespace SparcISA const int HprStart = 64; const int MiscStart = 96; + const uint64_t Bit64 = (1ULL << 63); class IntRegFile { protected: @@ -237,17 +242,22 @@ namespace SparcISA //In each of these cases, we have to copy the value into a temporary //variable. This is because we may otherwise try to access an //unaligned portion of memory. + + uint32_t result32; + uint64_t result64; switch(width) { case SingleWidth: - uint32_t result32 = gtoh((uint32_t)val); + result32 = gtoh((uint32_t)val); memcpy(regSpace + 4 * floatReg, &result32, width); + break; case DoubleWidth: - uint64_t result64 = gtoh((uint64_t)val); + result64 = gtoh((uint64_t)val); memcpy(regSpace + 4 * floatReg, &result64, width); + break; case QuadWidth: - uint64_t result128 = gtoh((uint64_t)val); - memcpy(regSpace + 4 * floatReg, &result128, width); + panic("Quad width FP not implemented."); + break; default: panic("Attempted to read a %d bit floating point register!", width); } @@ -259,17 +269,21 @@ namespace SparcISA //In each of these cases, we have to copy the value into a temporary //variable. This is because we may otherwise try to access an //unaligned portion of memory. + uint32_t result32; + uint64_t result64; switch(width) { case SingleWidth: - uint32_t result32 = gtoh((uint32_t)val); + result32 = gtoh((uint32_t)val); memcpy(regSpace + 4 * floatReg, &result32, width); + break; case DoubleWidth: - uint64_t result64 = gtoh((uint64_t)val); + result64 = gtoh((uint64_t)val); memcpy(regSpace + 4 * floatReg, &result64, width); + break; case QuadWidth: - uint64_t result128 = gtoh((uint64_t)val); - memcpy(regSpace + 4 * floatReg, &result128, width); + panic("Quad width FP not implemented."); + break; default: panic("Attempted to read a %d bit floating point register!", width); } @@ -402,7 +416,7 @@ namespace SparcISA union { uint64_t stick; // Hardware clock-tick counter struct { - int64_t counter:63; // Clock-tick count + int64_t :63; // Not used, storage in SparcSystem uint64_t npt:1; // Non-priveleged trap } stickFields; }; @@ -552,22 +566,31 @@ namespace SparcISA // These need to check the int_dis field and if 0 then // set appropriate bit in softint and checkinterrutps on the cpu - void processTickCompare() { panic("tick compare not implemented\n"); } - void processSTickCompare(){ panic("tick compare not implemented\n"); } - void processHSTickCompare(){ panic("tick compare not implemented\n"); } +#if FULL_SYSTEM + /** Process a tick compare event and generate an interrupt on the cpu if + * appropriate. */ + void processTickCompare(ThreadContext *tc); + void processSTickCompare(ThreadContext *tc); + void processHSTickCompare(ThreadContext *tc); - typedef EventWrapper<MiscRegFile, + typedef CpuEventWrapper<MiscRegFile, &MiscRegFile::processTickCompare> TickCompareEvent; - TickCompareEvent tickCompare; + TickCompareEvent *tickCompare; - typedef EventWrapper<MiscRegFile, + typedef CpuEventWrapper<MiscRegFile, &MiscRegFile::processSTickCompare> STickCompareEvent; - STickCompareEvent sTickCompare; + STickCompareEvent *sTickCompare; - typedef EventWrapper<MiscRegFile, + typedef CpuEventWrapper<MiscRegFile, &MiscRegFile::processHSTickCompare> HSTickCompareEvent; - HSTickCompareEvent hSTickCompare; + HSTickCompareEvent *hSTickCompare; + /** Fullsystem only register version of ReadRegWithEffect() */ + MiscReg readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); + /** Fullsystem only register version of SetRegWithEffect() */ + Fault setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc); +#endif public: void reset() @@ -580,25 +603,29 @@ namespace SparcISA //Bits that aren't set aren't defined on startup. tl = MaxTL; gl = MaxGL; - tt[tl] = PowerOnReset.trapType(); - pstateFields.mm = 0; //Total Store Order - pstateFields.red = 1; //Enter RED_State - pstateFields.am = 0; //Address Masking is turned off - pstateFields.priv = 1; //Processor enters privileged mode - pstateFields.ie = 0; //Interrupts are disabled - pstateFields.ag = 1; //Globals are replaced with alternate globals - pstateFields.tle = 0; //Big Endian mode for traps - pstateFields.cle = 0; //Big Endian mode for non-traps + tickFields.counter = 0; //The TICK register is unreadable bya tickFields.npt = 1; //The TICK register is unreadable by by !priv - tick_cmpr.int_dis = 1; // disable timer compare interrupts - stickFields.counter = 0; //The TICK register is unreadable by + + softint = 0; // Clear all the soft interrupt bits + tick_cmprFields.int_dis = 1; // disable timer compare interrupts + tick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing stickFields.npt = 1; //The TICK register is unreadable by by !priv - hpstateFields.id = 1; - hpstateFields.ibe = 0; + stick_cmprFields.int_dis = 1; // disable timer compare interrupts + stick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing + + + tt[tl] = power_on_reset; + pstate = 0; // fields 0 but pef + pstateFields.pef = 1; + + hpstate = 0; hpstateFields.red = 1; hpstateFields.hpriv = 1; hpstateFields.tlz = 0; // this is a guess + hintp = 0; // no interrupts pending + hstick_cmprFields.int_dis = 1; // disable timer compare interrupts + hstick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing #else /* //This sets up the initial state of the processor for usermode processes pstateFields.priv = 0; //Process runs in user mode @@ -619,25 +646,42 @@ namespace SparcISA } MiscRegFile() - : tickCompare(this), sTickCompare(this), hSTickCompare(this) { reset(); } + /** read a value out of an either an SE or FS IPR. No checking is done + * about SE vs. FS as this is mostly used to copy the regfile. Thus more + * register are copied that are necessary for FS. However this prevents + * a bunch of ifdefs and is rarely called so is not performance + * criticial. */ MiscReg readReg(int miscReg); - MiscReg readRegWithEffect(int miscReg, Fault &fault, ExecContext *xc); - + /** Read a value from an IPR. Only the SE iprs are here and the rest + * are are readFSRegWithEffect (which is called by readRegWithEffect()). + * Checking is done for permission based on state bits in the miscreg + * file. */ + MiscReg readRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); + + /** write a value into an either an SE or FS IPR. No checking is done + * about SE vs. FS as this is mostly used to copy the regfile. Thus more + * register are copied that are necessary for FS. However this prevents + * a bunch of ifdefs and is rarely called so is not performance + * criticial.*/ Fault setReg(int miscReg, const MiscReg &val); + /** Write a value into an IPR. Only the SE iprs are here and the rest + * are are setFSRegWithEffect (which is called by setRegWithEffect()). + * Checking is done for permission based on state bits in the miscreg + * file. */ Fault setRegWithEffect(int miscReg, - const MiscReg &val, ExecContext * xc); + const MiscReg &val, ThreadContext * tc); void serialize(std::ostream & os); void unserialize(Checkpoint * cp, const std::string & section); - void copyMiscRegs(ExecContext * xc); + void copyMiscRegs(ThreadContext * tc); protected: @@ -715,9 +759,9 @@ namespace SparcISA } MiscReg readMiscRegWithEffect(int miscReg, - Fault &fault, ExecContext *xc) + Fault &fault, ThreadContext *tc) { - return miscRegFile.readRegWithEffect(miscReg, fault, xc); + return miscRegFile.readRegWithEffect(miscReg, fault, tc); } Fault setMiscReg(int miscReg, const MiscReg &val) @@ -726,9 +770,9 @@ namespace SparcISA } Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, - ExecContext * xc) + ThreadContext * tc) { - return miscRegFile.setRegWithEffect(miscReg, val, xc); + return miscRegFile.setRegWithEffect(miscReg, val, tc); } FloatReg readFloatReg(int floatReg, int width) @@ -815,9 +859,9 @@ namespace SparcISA } }; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); - void copyMiscRegs(ExecContext *src, ExecContext *dest); + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); int InterruptLevel(uint64_t softint); diff --git a/src/arch/sparc/solaris/process.cc b/src/arch/sparc/solaris/process.cc index 95cdb0bd5..af0550910 100644 --- a/src/arch/sparc/solaris/process.cc +++ b/src/arch/sparc/solaris/process.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include "arch/sparc/isa_traits.hh" @@ -31,7 +33,7 @@ #include "arch/sparc/regfile.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/solaris/solaris.hh" #include "sim/process.hh" @@ -44,9 +46,9 @@ using namespace SparcISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Solaris::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Solaris::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "SunOS"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -54,7 +56,7 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "Generic_118558-21"); strcpy(name->machine, "sun4u"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } diff --git a/src/arch/sparc/solaris/process.hh b/src/arch/sparc/solaris/process.hh index 24dffdaf0..3c0d7eba7 100644 --- a/src/arch/sparc/solaris/process.hh +++ b/src/arch/sparc/solaris/process.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __SPARC_SOLARIS_PROCESS_HH__ diff --git a/src/arch/sparc/solaris/solaris.cc b/src/arch/sparc/solaris/solaris.cc index a56f10740..c588925b0 100644 --- a/src/arch/sparc/solaris/solaris.cc +++ b/src/arch/sparc/solaris/solaris.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include "arch/sparc/solaris/solaris.hh" diff --git a/src/arch/sparc/solaris/solaris.hh b/src/arch/sparc/solaris/solaris.hh index 6833a2d6a..0564faba4 100644 --- a/src/arch/sparc/solaris/solaris.hh +++ b/src/arch/sparc/solaris/solaris.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __ARCH_SPARC_SOLARIS_SOLARIS_HH__ diff --git a/src/arch/sparc/stacktrace.hh b/src/arch/sparc/stacktrace.hh index 1d8d97a79..d12aee211 100644 --- a/src/arch/sparc/stacktrace.hh +++ b/src/arch/sparc/stacktrace.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_STACKTRACE_HH__ @@ -32,13 +34,13 @@ #include "base/trace.hh" #include "cpu/static_inst.hh" -class ExecContext; +class ThreadContext; class StackTrace; class ProcessInfo { private: - ExecContext *xc; + ThreadContext *tc; int thread_info_size; int task_struct_size; @@ -47,7 +49,7 @@ class ProcessInfo int name_off; public: - ProcessInfo(ExecContext *_xc); + ProcessInfo(ThreadContext *_tc); Addr task(Addr ksp) const; int pid(Addr ksp) const; @@ -59,7 +61,7 @@ class StackTrace protected: typedef TheISA::MachInst MachInst; private: - ExecContext *xc; + ThreadContext *tc; std::vector<Addr> stack; private: @@ -68,21 +70,21 @@ class StackTrace bool decodeSave(MachInst inst, int ®, int &disp); bool decodeStack(MachInst inst, int &disp); - void trace(ExecContext *xc, bool is_call); + void trace(ThreadContext *tc, bool is_call); public: StackTrace(); - StackTrace(ExecContext *xc, StaticInstPtr inst); + StackTrace(ThreadContext *tc, StaticInstPtr inst); ~StackTrace(); void clear() { - xc = 0; + tc = 0; stack.clear(); } - bool valid() const { return xc != NULL; } - bool trace(ExecContext *xc, StaticInstPtr inst); + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); public: const std::vector<Addr> &getstack() const { return stack; } @@ -104,7 +106,7 @@ class StackTrace }; inline bool -StackTrace::trace(ExecContext *xc, StaticInstPtr inst) +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) { if (!inst->isCall() && !inst->isReturn()) return false; @@ -112,7 +114,7 @@ StackTrace::trace(ExecContext *xc, StaticInstPtr inst) if (valid()) clear(); - trace(xc, !inst->isReturn()); + trace(tc, !inst->isReturn()); return true; } diff --git a/src/arch/sparc/system.cc b/src/arch/sparc/system.cc index 1e2882607..e197e7918 100644 --- a/src/arch/sparc/system.cc +++ b/src/arch/sparc/system.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include "arch/sparc/system.hh" @@ -40,7 +42,8 @@ using namespace BigEndianGuest; SparcSystem::SparcSystem(Params *p) - : System(p) + : System(p), sysTick(0) + { resetSymtab = new SymbolTable; hypervisorSymtab = new SymbolTable; diff --git a/src/arch/sparc/system.hh b/src/arch/sparc/system.hh index 27aa8768a..614707f6c 100644 --- a/src/arch/sparc/system.hh +++ b/src/arch/sparc/system.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __ARCH_SPARC_SYSTEM_HH__ @@ -53,7 +55,7 @@ class SparcSystem : public System SparcSystem(Params *p); - ~SparcaSystem(); + ~SparcSystem(); virtual bool breakpoint(); @@ -82,6 +84,9 @@ class SparcSystem : public System /** Object pointer for the openboot code */ ObjectFile *openboot; + /** System Tick for syncronized tick across all cpus. */ + Tick sysTick; + protected: const Params *params() const { return (const Params *)_params; } diff --git a/src/arch/sparc/tlb.hh b/src/arch/sparc/tlb.hh new file mode 100644 index 000000000..35ff08b43 --- /dev/null +++ b/src/arch/sparc/tlb.hh @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_SPARC_TLB_HH__ +#define __ARCH_SPARC_TLB_HH__ + + +#endif // __ARCH_SPARC_TLB_HH__ diff --git a/src/arch/sparc/ua2005.cc b/src/arch/sparc/ua2005.cc new file mode 100644 index 000000000..b89d48663 --- /dev/null +++ b/src/arch/sparc/ua2005.cc @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Ali Saidi + */ + +#include "arch/sparc/regfile.hh" + +Fault +SparcISA::MiscRegFile::setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext *tc) +{ + int64_t time; + SparcSystem *sys; + switch (miscReg) { + /** Full system only ASRs */ + case MISCREG_SOFTINT: + if (isNonPriv()) + return new PrivilegedOpcode; + // Check if we are going to interrupt because of something + int oldLevel = InterruptLevel(softint); + int newLevel = InterruptLevel(val); + setReg(miscReg, val); + if (newLevel > oldLevel) + ; // MUST DO SOMETHING HERE TO TELL CPU TO LOOK FOR INTERRUPTS XXX + //tc->getCpuPtr()->checkInterrupts = true; + return NoFault; + + case MISCREG_SOFTINT_CLR: + return setRegWithEffect(miscReg, ~val & softint, tc); + case MISCREG_SOFTINT_SET: + return setRegWithEffect(miscReg, val | softint, tc); + + case MISCREG_TICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (tickCompare == NULL) + tickCompare = new TickCompareEvent(this, tc); + setReg(miscReg, val); + if (tick_cmprFields.int_dis && tickCompare.scheduled()) + tickCompare.deschedule(); + time = tick_cmprFields.tick_cmpr - tickFields.counter; + if (!tick_cmprFields.int_dis && time > 0) + tickCompare.schedule(time * tc->getCpuPtr()->cycles(1)); + return NoFault; + + case MISCREG_STICK: + if (isNonPriv()) + return new PrivilegedOpcode; + if (isPriv()) + return new PrivilegedAction; + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + sys->sysTick = curTick/Clock::Int::ns - val & ~Bit64; + stickFields.npt = val & Bit64 ? 1 : 0; + return NoFault; + + case MISCREG_STICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (sTickCompare == NULL) + sTickCompare = new STickCompareEvent(this, tc); + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + setReg(miscReg, val); + if (stick_cmprFields.int_dis && sTickCompare.scheduled()) + sTickCompare.deschedule(); + time = stick_cmprFields.tick_cmpr - sys->sysTick; + if (!stick_cmprFields.int_dis && time > 0) + sTickCompare.schedule(time * Clock::Int::ns); + return NoFault; + + /** Fullsystem only Priv registers. */ + case MISCREG_PIL: + if (FULL_SYSTEM) { + setReg(miscReg, val); + //tc->getCpuPtr()->checkInterrupts; + // MUST DO SOMETHING HERE TO TELL CPU TO LOOK FOR INTERRUPTS XXX + return NoFault; + } else + panic("PIL not implemented for syscall emulation\n"); + + /** Hyper privileged registers */ + case MISCREG_HPSTATE: + case MISCREG_HINTP: + setReg(miscReg, val); + return NoFault; + case MISCREG_HTSTATE: + if (tl == 0) + return new IllegalInstruction; + setReg(miscReg, val); + return NoFault; + + case MISCREG_HTBA: + // clear lower 7 bits on writes. + setReg(miscReg, val & ULL(~0x7FFF)); + return NoFault; + + case MISCREG_STRAND_STS_REG: + setReg(miscReg, strandStatusReg); + return NoFault; + case MISCREG_HSTICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (hSTickCompare == NULL) + hSTickCompare = new HSTickCompareEvent(this, tc); + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + setReg(miscReg, val); + if (hstick_cmprFields.int_dis && hSTickCompare.scheduled()) + hSTickCompare.deschedule(); + int64_t time = hstick_cmprFields.tick_cmpr - sys->sysTick; + if (!hstick_cmprFields.int_dis && time > 0) + hSTickCompare.schedule(time * Clock::Int::ns); + return NoFault; + default: + return new IllegalInstruction; + } +} + +MiscReg +MiscRegFile::readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext * tc) +{ + switch (miscReg) { + + /** Privileged registers. */ + case MISCREG_SOFTINT: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + case MISCREG_TICK_CMPR: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + case MISCREG_STICK: + SparcSystem *sys; + if (stickFields.npt && !isNonPriv()) { + fault = new PrivilegedAction; + return 0; + } + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + return curTick/Clock::Int::ns - sys->sysTick | stickFields.npt << 63; + case MISCREG_STICK_CMPR: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + + + /** Hyper privileged registers */ + case MISCREG_HPSTATE: + case MISCREG_HINTP: + return readReg(miscReg); + case MISCREG_HTSTATE: + if (tl == 0) { + fault = new IllegalInstruction; + return 0; + } + return readReg(miscReg); + + case MISCREG_HTBA: + return readReg(miscReg) & ULL(~0x7FFF); + case MISCREG_HVER: + return NWindows | MaxTL << 8 | MaxGL << 16; + case MISCREG_STRAND_STS_REG: + return strandStatusReg; + case MISCREG_HSTICK_CMPR: + return hstick_cmpr; + + default: + fault = new IllegalInstruction; + return 0; + } +} + +void +MiscRegFile::processTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +void +MiscRegFile::processSTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +void +MiscRegFile::processHSTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +}; // namespace SparcISA diff --git a/src/arch/sparc/utility.hh b/src/arch/sparc/utility.hh index 1e67b3370..f1c071148 100644 --- a/src/arch/sparc/utility.hh +++ b/src/arch/sparc/utility.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black */ #ifndef __ARCH_SPARC_UTILITY_HH__ @@ -79,10 +81,10 @@ namespace SparcISA /** * Function to insure ISA semantics about 0 registers. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void zeroRegisters(XC *xc); + template <class TC> + void zeroRegisters(TC *tc); } // namespace SparcISA diff --git a/src/arch/sparc/vtophys.cc b/src/arch/sparc/vtophys.cc new file mode 100644 index 000000000..f7fd92c15 --- /dev/null +++ b/src/arch/sparc/vtophys.cc @@ -0,0 +1,166 @@ +/* + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi + */ + +#include <string> + +#include "arch/alpha/ev5.hh" +#include "arch/alpha/vtophys.hh" +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "mem/vport.hh" + +using namespace std; +using namespace AlphaISA; + +AlphaISA::PageTableEntry +AlphaISA::kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr) +{ + Addr level1_pte = ptbr + vaddr.level1(); + AlphaISA::PageTableEntry level1 = mem->read<uint64_t>(level1_pte); + if (!level1.valid()) { + DPRINTF(VtoPhys, "level 1 PTE not valid, va = %#\n", vaddr); + return 0; + } + + Addr level2_pte = level1.paddr() + vaddr.level2(); + AlphaISA::PageTableEntry level2 = mem->read<uint64_t>(level2_pte); + if (!level2.valid()) { + DPRINTF(VtoPhys, "level 2 PTE not valid, va = %#x\n", vaddr); + return 0; + } + + Addr level3_pte = level2.paddr() + vaddr.level3(); + AlphaISA::PageTableEntry level3 = mem->read<uint64_t>(level3_pte); + if (!level3.valid()) { + DPRINTF(VtoPhys, "level 3 PTE not valid, va = %#x\n", vaddr); + return 0; + } + return level3; +} + +Addr +AlphaISA::vtophys(Addr vaddr) +{ + Addr paddr = 0; + if (AlphaISA::IsUSeg(vaddr)) + DPRINTF(VtoPhys, "vtophys: invalid vaddr %#x", vaddr); + else if (AlphaISA::IsK0Seg(vaddr)) + paddr = AlphaISA::K0Seg2Phys(vaddr); + else + panic("vtophys: ptbr is not set on virtual lookup"); + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + +Addr +AlphaISA::vtophys(ThreadContext *tc, Addr addr) +{ + AlphaISA::VAddr vaddr = addr; + Addr ptbr = tc->readMiscReg(AlphaISA::IPR_PALtemp20); + Addr paddr = 0; + //@todo Andrew couldn't remember why he commented some of this code + //so I put it back in. Perhaps something to do with gdb debugging? + if (AlphaISA::PcPAL(vaddr) && (vaddr < EV5::PalMax)) { + paddr = vaddr & ~ULL(1); + } else { + if (AlphaISA::IsK0Seg(vaddr)) { + paddr = AlphaISA::K0Seg2Phys(vaddr); + } else if (!ptbr) { + paddr = vaddr; + } else { + AlphaISA::PageTableEntry pte = + kernel_pte_lookup(tc->getPhysPort(), ptbr, vaddr); + if (pte.valid()) + paddr = pte.paddr() | vaddr.offset(); + } + } + + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + + +void +AlphaISA::CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) +{ + uint8_t *dst = (uint8_t *)dest; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->readBlob(src, dst, cplen); + + tc->delVirtPort(vp); + +} + +void +AlphaISA::CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) +{ + uint8_t *src = (uint8_t *)source; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->writeBlob(dest, src, cplen); + + tc->delVirtPort(vp); +} + +void +AlphaISA::CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) +{ + int len = 0; + VirtualPort *vp = tc->getVirtPort(tc); + + do { + vp->readBlob(vaddr++, (uint8_t*)dst++, 1); + len++; + } while (len < maxlen && dst[len] != 0 ); + + tc->delVirtPort(vp); + dst[len] = 0; +} + +void +AlphaISA::CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) +{ + VirtualPort *vp = tc->getVirtPort(tc); + for (ChunkGenerator gen(vaddr, strlen(src), AlphaISA::PageBytes); !gen.done(); + gen.next()) + { + vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); + src += gen.size(); + } + tc->delVirtPort(vp); +} diff --git a/src/arch/sparc/vtophys.hh b/src/arch/sparc/vtophys.hh new file mode 100644 index 000000000..bf2b757d6 --- /dev/null +++ b/src/arch/sparc/vtophys.hh @@ -0,0 +1,55 @@ +/* + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_SPARC_VTOPHYS_H__ +#define __ARCH_SPARC_VTOPHYS_H__ + +#include "arch/sparc/isa_traits.hh" + +class ThreadContext; +class FunctionalPort; + +namespace SparcISA { + +PageTableEntry +kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, SparcISA::VAddr vaddr); + +Addr vtophys(Addr vaddr); +Addr vtophys(ThreadContext *tc, Addr vaddr); + +void CopyOut(ThreadContext *tc, void *dst, Addr src, size_t len); +void CopyIn(ThreadContext *tc, Addr dst, void *src, size_t len); +void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); +void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); + +}; +#endif // __ARCH_SPARC_VTOPHYS_H__ + diff --git a/src/base/bitfield.hh b/src/base/bitfield.hh index c59354c7d..f4e49aa8d 100644 --- a/src/base/bitfield.hh +++ b/src/base/bitfield.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __BASE_BITFIELD_HH__ diff --git a/src/base/callback.hh b/src/base/callback.hh index 7b3023505..a39d7df20 100644 --- a/src/base/callback.hh +++ b/src/base/callback.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __CALLBACK_HH__ diff --git a/src/base/chunk_generator.hh b/src/base/chunk_generator.hh index 4f708bd4b..e9d5355ca 100644 --- a/src/base/chunk_generator.hh +++ b/src/base/chunk_generator.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __BASE__CHUNK_GENERATOR_HH__ diff --git a/src/base/circlebuf.cc b/src/base/circlebuf.cc index 89bbfd822..a0c015671 100644 --- a/src/base/circlebuf.cc +++ b/src/base/circlebuf.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <algorithm> diff --git a/src/base/circlebuf.hh b/src/base/circlebuf.hh index 8a64cb5f5..66583b7db 100644 --- a/src/base/circlebuf.hh +++ b/src/base/circlebuf.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __CIRCLEBUF_HH__ diff --git a/src/base/compression/lzss_compression.cc b/src/base/compression/lzss_compression.cc index 3ffdf7e95..eb35fb8f1 100644 --- a/src/base/compression/lzss_compression.cc +++ b/src/base/compression/lzss_compression.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** @file diff --git a/src/base/compression/lzss_compression.hh b/src/base/compression/lzss_compression.hh index c136c6d60..35e4dcb3f 100644 --- a/src/base/compression/lzss_compression.hh +++ b/src/base/compression/lzss_compression.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ #ifndef __LZSS_COMPRESSION_HH__ diff --git a/src/base/compression/null_compression.hh b/src/base/compression/null_compression.hh index 5fbcf562b..5a582d828 100644 --- a/src/base/compression/null_compression.hh +++ b/src/base/compression/null_compression.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Nathan Binkert */ #ifndef __BASE_COMPRESSION_NULL_COMPRESSION_HH__ diff --git a/src/base/cprintf.cc b/src/base/cprintf.cc index cf332ebf2..dd8ce858b 100644 --- a/src/base/cprintf.cc +++ b/src/base/cprintf.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <cassert> diff --git a/src/base/cprintf.hh b/src/base/cprintf.hh index c468c375f..6b2a77f90 100644 --- a/src/base/cprintf.hh +++ b/src/base/cprintf.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __CPRINTF_HH__ diff --git a/src/base/cprintf_formats.hh b/src/base/cprintf_formats.hh index 05a8723a4..58ee7f795 100644 --- a/src/base/cprintf_formats.hh +++ b/src/base/cprintf_formats.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __CPRINTF_FORMATS_HH__ diff --git a/src/base/crc.hh b/src/base/crc.hh index 6ede07748..16dce79aa 100644 --- a/src/base/crc.hh +++ b/src/base/crc.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_CRC_HH__ diff --git a/src/base/date.cc b/src/base/date.cc index ba7698c29..9ec8a0b33 100644 --- a/src/base/date.cc +++ b/src/base/date.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ const char *compileDate = __DATE__ " " __TIME__; diff --git a/src/base/dbl_list.hh b/src/base/dbl_list.hh index 1d06ff576..8fca72998 100644 --- a/src/base/dbl_list.hh +++ b/src/base/dbl_list.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __DBL_LIST_HH__ diff --git a/src/base/endian.hh b/src/base/endian.hh index 499eb50c5..d2b9dc65c 100644 --- a/src/base/endian.hh +++ b/src/base/endian.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __ENDIAN_HH__ diff --git a/src/base/fast_alloc.cc b/src/base/fast_alloc.cc index 6504e07c2..455fb8ed7 100644 --- a/src/base/fast_alloc.cc +++ b/src/base/fast_alloc.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ /* diff --git a/src/base/fast_alloc.hh b/src/base/fast_alloc.hh index 54e35f8e0..3e22e59c1 100644 --- a/src/base/fast_alloc.hh +++ b/src/base/fast_alloc.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ /* diff --git a/src/base/fenv.hh b/src/base/fenv.hh index 3234f5dd3..013d2f09b 100644 --- a/src/base/fenv.hh +++ b/src/base/fenv.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __BASE_FENV_HH__ diff --git a/src/base/fifo_buffer.cc b/src/base/fifo_buffer.cc index 85b306c25..4bafa88bd 100644 --- a/src/base/fifo_buffer.cc +++ b/src/base/fifo_buffer.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Nathan Binkert */ #include "base/fifo_buffer.hh" diff --git a/src/base/fifo_buffer.hh b/src/base/fifo_buffer.hh index 03ce057c7..f6205330b 100644 --- a/src/base/fifo_buffer.hh +++ b/src/base/fifo_buffer.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Nathan Binkert */ #ifndef __FIFO_BUFFER_HH__ diff --git a/src/base/hashmap.hh b/src/base/hashmap.hh index 712366829..570cbc152 100644 --- a/src/base/hashmap.hh +++ b/src/base/hashmap.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __HASHMAP_HH__ diff --git a/src/base/hostinfo.cc b/src/base/hostinfo.cc index d42c96732..a7c93e712 100644 --- a/src/base/hostinfo.cc +++ b/src/base/hostinfo.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <ctype.h> diff --git a/src/base/hostinfo.hh b/src/base/hostinfo.hh index 21a6e5475..b6663ea69 100644 --- a/src/base/hostinfo.hh +++ b/src/base/hostinfo.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __HOSTINFO_HH__ diff --git a/src/base/hybrid_pred.cc b/src/base/hybrid_pred.cc index 21cbdb0fd..73216489e 100644 --- a/src/base/hybrid_pred.cc +++ b/src/base/hybrid_pred.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Lisa Hsu */ #include <string> diff --git a/src/base/hybrid_pred.hh b/src/base/hybrid_pred.hh index ea4a9d04c..cb1d6003b 100644 --- a/src/base/hybrid_pred.hh +++ b/src/base/hybrid_pred.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Steve Reinhardt */ //========================================================================== diff --git a/src/base/inet.cc b/src/base/inet.cc index f2665bd2b..b8da12a99 100644 --- a/src/base/inet.cc +++ b/src/base/inet.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sstream> diff --git a/src/base/inet.hh b/src/base/inet.hh index e5d0473f9..1bf7c585f 100644 --- a/src/base/inet.hh +++ b/src/base/inet.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __BASE_INET_HH__ diff --git a/src/base/inifile.cc b/src/base/inifile.cc index eb5a1335f..4d504d04f 100644 --- a/src/base/inifile.cc +++ b/src/base/inifile.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #define USE_CPP diff --git a/src/base/inifile.hh b/src/base/inifile.hh index 3c6894978..631b29b87 100644 --- a/src/base/inifile.hh +++ b/src/base/inifile.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __INIFILE_HH__ diff --git a/src/base/intmath.cc b/src/base/intmath.cc index f1c1651ba..22414ea4b 100644 --- a/src/base/intmath.cc +++ b/src/base/intmath.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include "base/intmath.hh" diff --git a/src/base/intmath.hh b/src/base/intmath.hh index 51baddb91..227012e30 100644 --- a/src/base/intmath.hh +++ b/src/base/intmath.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __INTMATH_HH__ diff --git a/src/base/loader/aout_object.cc b/src/base/loader/aout_object.cc index 564898ca3..6691bd4ae 100644 --- a/src/base/loader/aout_object.cc +++ b/src/base/loader/aout_object.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include <string> diff --git a/src/base/loader/aout_object.hh b/src/base/loader/aout_object.hh index aeb710427..d180d69f3 100644 --- a/src/base/loader/aout_object.hh +++ b/src/base/loader/aout_object.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __AOUT_OBJECT_HH__ diff --git a/src/base/loader/ecoff_object.cc b/src/base/loader/ecoff_object.cc index 80917ee9c..134f2d98d 100644 --- a/src/base/loader/ecoff_object.cc +++ b/src/base/loader/ecoff_object.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include <string> diff --git a/src/base/loader/ecoff_object.hh b/src/base/loader/ecoff_object.hh index 603c70bec..05c604b2b 100644 --- a/src/base/loader/ecoff_object.hh +++ b/src/base/loader/ecoff_object.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __ECOFF_OBJECT_HH__ diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc index 79601e9d1..00d218b76 100644 --- a/src/base/loader/elf_object.cc +++ b/src/base/loader/elf_object.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include <string> diff --git a/src/base/loader/elf_object.hh b/src/base/loader/elf_object.hh index 72c265edd..46dbfe37b 100644 --- a/src/base/loader/elf_object.hh +++ b/src/base/loader/elf_object.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __ELF_OBJECT_HH__ diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc index c6dfced1d..42c74d418 100644 --- a/src/base/loader/object_file.cc +++ b/src/base/loader/object_file.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <list> diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh index b43989cb5..79fa394c6 100644 --- a/src/base/loader/object_file.hh +++ b/src/base/loader/object_file.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __OBJECT_FILE_HH__ diff --git a/src/base/loader/symtab.cc b/src/base/loader/symtab.cc index 25f54f9bf..3e73eb7a3 100644 --- a/src/base/loader/symtab.cc +++ b/src/base/loader/symtab.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/base/loader/symtab.hh b/src/base/loader/symtab.hh index ebcda1345..55ff0c86f 100644 --- a/src/base/loader/symtab.hh +++ b/src/base/loader/symtab.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __SYMTAB_HH__ @@ -32,7 +35,7 @@ #include <iosfwd> #include <map> -#include "arch/isa_traits.hh" // for Addr +#include "sim/host.hh" // for Addr class Checkpoint; class SymbolTable diff --git a/src/base/match.cc b/src/base/match.cc index 4f1f49b57..994209864 100644 --- a/src/base/match.cc +++ b/src/base/match.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "base/match.hh" diff --git a/src/base/match.hh b/src/base/match.hh index 1b0a083a7..6e1f03b80 100644 --- a/src/base/match.hh +++ b/src/base/match.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/base/misc.cc b/src/base/misc.cc index f3c86827b..991a33736 100644 --- a/src/base/misc.cc +++ b/src/base/misc.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/base/misc.hh b/src/base/misc.hh index 9255c69c6..87faf20e6 100644 --- a/src/base/misc.hh +++ b/src/base/misc.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Dave Greene */ #ifndef __MISC_HH__ diff --git a/src/base/mod_num.hh b/src/base/mod_num.hh index fabbb56a9..6962dc0fd 100644 --- a/src/base/mod_num.hh +++ b/src/base/mod_num.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch */ template<class T, T MV> diff --git a/src/base/mysql.cc b/src/base/mysql.cc index c8d6e933a..2416c766a 100644 --- a/src/base/mysql.cc +++ b/src/base/mysql.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/base/mysql.hh b/src/base/mysql.hh index ae28a9dfb..272a0f07c 100644 --- a/src/base/mysql.hh +++ b/src/base/mysql.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_MYSQL_HH__ diff --git a/src/base/output.cc b/src/base/output.cc index 2b1733f21..afcac03a5 100644 --- a/src/base/output.cc +++ b/src/base/output.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <errno.h> diff --git a/src/base/output.hh b/src/base/output.hh index 3bbe73e3b..0aae4ae81 100644 --- a/src/base/output.hh +++ b/src/base/output.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_OUTPUT_HH__ diff --git a/src/base/pollevent.cc b/src/base/pollevent.cc index 99044fc09..2743cd95d 100644 --- a/src/base/pollevent.cc +++ b/src/base/pollevent.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/ioctl.h> diff --git a/src/base/pollevent.hh b/src/base/pollevent.hh index d39931797..5b84650cb 100644 --- a/src/base/pollevent.hh +++ b/src/base/pollevent.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __POLLEVENT_H__ diff --git a/src/base/predictor.hh b/src/base/predictor.hh index 37aa29989..94f19ca28 100644 --- a/src/base/predictor.hh +++ b/src/base/predictor.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Nathan Binkert */ // diff --git a/src/base/random.cc b/src/base/random.cc index 4aac14101..e135b55f5 100644 --- a/src/base/random.cc +++ b/src/base/random.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ali Saidi */ #include <cstdlib> diff --git a/src/base/random.hh b/src/base/random.hh index def7a4bce..b5eb39f94 100644 --- a/src/base/random.hh +++ b/src/base/random.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __BASE_RANDOM_HH__ diff --git a/src/base/range.cc b/src/base/range.cc index a4e50fc4f..442e5fdf8 100644 --- a/src/base/range.cc +++ b/src/base/range.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "base/intmath.hh" diff --git a/src/base/range.hh b/src/base/range.hh index 4e3e0fd6e..d9542c0ca 100644 --- a/src/base/range.hh +++ b/src/base/range.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __BASE_RANGE_HH__ diff --git a/src/base/refcnt.hh b/src/base/refcnt.hh index de589f7c5..6672d4a5f 100644 --- a/src/base/refcnt.hh +++ b/src/base/refcnt.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __REFCNT_HH__ diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc index 41d0c1471..0d3b73b1e 100644 --- a/src/base/remote_gdb.cc +++ b/src/base/remote_gdb.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @@ -126,7 +128,7 @@ #include "base/socket.hh" #include "base/trace.hh" #include "config/full_system.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/static_inst.hh" #include "mem/physical.hh" #include "mem/port.hh" @@ -250,7 +252,7 @@ RemoteGDB::Event::process(int revent) gdb->detach(); } -RemoteGDB::RemoteGDB(System *_system, ExecContext *c) +RemoteGDB::RemoteGDB(System *_system, ThreadContext *c) : event(NULL), listener(NULL), number(-1), fd(-1), active(false), attached(false), system(_system), pmem(_system->physmem), context(c) @@ -640,7 +642,9 @@ RemoteGDB::read(Addr vaddr, size_t size, char *data) DPRINTF(GDBRead, "read: addr=%#x, size=%d", vaddr, size); - context->getVirtPort(context)->readBlob(vaddr, (uint8_t*)data, size); + VirtualPort *vp = context->getVirtPort(context); + vp->readBlob(vaddr, (uint8_t*)data, size); + context->delVirtPort(vp); #if TRACING_ON if (DTRACE(GDBRead)) { @@ -677,8 +681,9 @@ RemoteGDB::write(Addr vaddr, size_t size, const char *data) } else DPRINTFNR("\n"); } - - context->getVirtPort(context)->writeBlob(vaddr, (uint8_t*)data, size); + VirtualPort *vp = context->getVirtPort(context); + vp->writeBlob(vaddr, (uint8_t*)data, size); + context->delVirtPort(vp); #ifdef IMB alpha_pal_imb(); @@ -702,11 +707,11 @@ RemoteGDB::HardBreakpoint::HardBreakpoint(RemoteGDB *_gdb, Addr pc) } void -RemoteGDB::HardBreakpoint::process(ExecContext *xc) +RemoteGDB::HardBreakpoint::process(ThreadContext *tc) { DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc()); - if (xc == gdb->context) + if (tc == gdb->context) gdb->trap(ALPHA_KENTRY_INT); } diff --git a/src/base/remote_gdb.hh b/src/base/remote_gdb.hh index b7abf5116..90b53e53f 100644 --- a/src/base/remote_gdb.hh +++ b/src/base/remote_gdb.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __REMOTE_GDB_HH__ @@ -37,7 +39,7 @@ #include "base/socket.hh" class System; -class ExecContext; +class ThreadContext; class PhysicalMemory; class GDBListener; @@ -78,7 +80,7 @@ class RemoteGDB System *system; PhysicalMemory *pmem; - ExecContext *context; + ThreadContext *context; protected: uint8_t getbyte(); @@ -96,10 +98,10 @@ class RemoteGDB template <class T> void write(Addr addr, T data); public: - RemoteGDB(System *system, ExecContext *context); + RemoteGDB(System *system, ThreadContext *context); ~RemoteGDB(); - void replaceExecContext(ExecContext *xc) { context = xc; } + void replaceThreadContext(ThreadContext *tc) { context = tc; } void attach(int fd); void detach(); @@ -131,7 +133,7 @@ class RemoteGDB HardBreakpoint(RemoteGDB *_gdb, Addr addr); std::string name() { return gdb->name() + ".hwbkpt"; } - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; friend class HardBreakpoint; diff --git a/src/base/res_list.hh b/src/base/res_list.hh index 960ed108e..442280e15 100644 --- a/src/base/res_list.hh +++ b/src/base/res_list.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Nathan Binkert */ #ifndef __RES_LIST_HH__ diff --git a/src/base/sat_counter.cc b/src/base/sat_counter.cc index 7920f6c81..8980275eb 100644 --- a/src/base/sat_counter.cc +++ b/src/base/sat_counter.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Lisa Hsu */ #include <sstream> diff --git a/src/base/sat_counter.hh b/src/base/sat_counter.hh index d7be17b6f..79de11156 100644 --- a/src/base/sat_counter.hh +++ b/src/base/sat_counter.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Steve Reinhardt */ #ifndef __SAT_COUNTER_HH__ diff --git a/src/base/sched_list.hh b/src/base/sched_list.hh index f794e3514..56ee2f8ec 100644 --- a/src/base/sched_list.hh +++ b/src/base/sched_list.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch */ #ifndef SCHED_LIST_HH diff --git a/src/base/socket.cc b/src/base/socket.cc index 45a60e7e3..adcc48735 100644 --- a/src/base/socket.cc +++ b/src/base/socket.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/base/socket.hh b/src/base/socket.hh index 848405c09..8e55eae72 100644 --- a/src/base/socket.hh +++ b/src/base/socket.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __SOCKET_HH__ diff --git a/src/base/statistics.cc b/src/base/statistics.cc index 20de46347..2acef83c5 100644 --- a/src/base/statistics.cc +++ b/src/base/statistics.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iomanip> @@ -41,7 +43,6 @@ #include "base/time.hh" #include "base/trace.hh" #include "base/stats/statdb.hh" -#include "config/stats_binning.hh" using namespace std; @@ -172,12 +173,6 @@ FormulaBase::size() const return root->size(); } -bool -FormulaBase::binned() const -{ - return root && root->binned(); -} - void FormulaBase::reset() { @@ -236,33 +231,6 @@ Formula::operator+=(Temp r) return *this; } -MainBin::MainBin(const string &name) - : _name(name), mem(NULL), memsize(-1) -{ - Database::regBin(this, name); -} - -MainBin::~MainBin() -{ - if (mem) - delete [] mem; -} - -char * -MainBin::memory(off_t off) -{ - if (memsize == -1) - memsize = ceilPow2((size_t) offset()); - - if (!mem) { - mem = new char[memsize]; - memset(mem, 0, memsize); - } - - assert(offset() <= size()); - return mem + off; -} - void check() { @@ -285,13 +253,6 @@ check() Database::stats().sort(StatData::less); -#if STATS_BINNING - if (MainBin::curBin() == NULL) { - static MainBin mainBin("main bin"); - mainBin.activate(); - } -#endif - if (i == end) return; @@ -311,39 +272,14 @@ CallbackQueue resetQueue; void reset() { - // reset non-binned stats Database::stat_list_t::iterator i = Database::stats().begin(); Database::stat_list_t::iterator end = Database::stats().end(); while (i != end) { StatData *data = *i; - if (!data->binned()) - data->reset(); + data->reset(); ++i; } - // save the bin so we can go back to where we were - MainBin *orig = MainBin::curBin(); - - // reset binned stats - Database::bin_list_t::iterator bi = Database::bins().begin(); - Database::bin_list_t::iterator be = Database::bins().end(); - while (bi != be) { - MainBin *bin = *bi; - bin->activate(); - - i = Database::stats().begin(); - while (i != end) { - StatData *data = *i; - if (data->binned()) - data->reset(); - ++i; - } - ++bi; - } - - // restore bin - MainBin::curBin() = orig; - resetQueue.process(); } diff --git a/src/base/statistics.hh b/src/base/statistics.hh index dd507c091..59f219c07 100644 --- a/src/base/statistics.hh +++ b/src/base/statistics.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Erik Hallnor */ /** @file @@ -57,11 +60,9 @@ #include "base/intmath.hh" #include "base/refcnt.hh" #include "base/str.hh" -#include "base/stats/bin.hh" #include "base/stats/flags.hh" #include "base/stats/visit.hh" #include "base/stats/types.hh" -#include "config/stats_binning.hh" #include "sim/host.hh" class Callback; @@ -100,11 +101,6 @@ struct StatData virtual ~StatData(); /** - * @return true if the stat is binned. - */ - virtual bool binned() const = 0; - - /** * Reset the corresponding stat to the default state. */ virtual void reset() = 0; @@ -157,7 +153,6 @@ class ScalarStatData : public ScalarData public: ScalarStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool check() const { return s.check(); } virtual Counter value() const { return s.value(); } virtual Result result() const { return s.result(); } @@ -200,7 +195,6 @@ class VectorStatData : public VectorData public: VectorStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool check() const { return s.check(); } virtual bool zero() const { return s.zero(); } virtual void reset() { s.reset(); } @@ -258,7 +252,6 @@ class DistStatData : public DistData public: DistStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool check() const { return s.check(); } virtual void reset() { s.reset(); } virtual bool zero() const { return s.zero(); } @@ -297,12 +290,10 @@ class VectorDistStatData : public VectorDistData { protected: Stat &s; - typedef typename Stat::bin_t bin_t; public: VectorDistStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return bin_t::binned; } virtual bool check() const { return s.check(); } virtual void reset() { s.reset(); } virtual size_t size() const { return s.size(); } @@ -339,12 +330,10 @@ class Vector2dStatData : public Vector2dData { protected: Stat &s; - typedef typename Stat::bin_t bin_t; public: Vector2dStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return bin_t::binned; } virtual bool check() const { return s.check(); } virtual void reset() { s.reset(); } virtual bool zero() const { return s.zero(); } @@ -356,7 +345,6 @@ class Vector2dStatData : public Vector2dData } }; - class DataAccess { protected: @@ -612,22 +600,16 @@ struct StatStor * changes. This allows the quick calculation of a per cycle count of the item * being watched. This is good for keeping track of residencies in structures * among other things. - * @todo add lateny to the stat and fix binning. */ struct AvgStor { public: /** The paramaters for this storage type */ - struct Params - { - /** - * The current count. We stash this here because the current - * value is not a binned value. - */ - Counter current; - }; + struct Params { }; private: + /** The current count. */ + Counter current; /** The total count for all cycles. */ mutable Result total; /** The cycle that current last changed. */ @@ -637,7 +619,7 @@ struct AvgStor /** * Build and initializes this stat storage. */ - AvgStor(Params &p) : total(0), last(0) { p.current = Counter(); } + AvgStor(Params &p) : current(0), total(0), last(0) { } /** * Set the current count to the one provided, update the total and last @@ -646,9 +628,9 @@ struct AvgStor * @param p The parameters for this storage. */ void set(Counter val, Params &p) { - total += p.current * (curTick - last); + total += current * (curTick - last); last = curTick; - p.current = val; + current = val; } /** @@ -656,21 +638,21 @@ struct AvgStor * @param val The amount to increment. * @param p The parameters for this storage. */ - void inc(Counter val, Params &p) { set(p.current + val, p); } + void inc(Counter val, Params &p) { set(current + val, p); } /** * Deccrement the current count by the provided value, calls set. * @param val The amount to decrement. * @param p The parameters for this storage. */ - void dec(Counter val, Params &p) { set(p.current - val, p); } + void dec(Counter val, Params &p) { set(current - val, p); } /** * Return the current count. * @param p The parameters for this storage. * @return The current count. */ - Counter value(const Params &p) const { return p.current; } + Counter value(const Params &p) const { return current; } /** * Return the current average. @@ -679,9 +661,9 @@ struct AvgStor */ Result result(const Params &p) const { - total += p.current * (curTick - last); + total += current * (curTick - last); last = curTick; - return (Result)(total + p.current) / (Result)(curTick + 1); + return (Result)(total + current) / (Result)(curTick + 1); } /** @@ -701,39 +683,53 @@ struct AvgStor /** * Implementation of a scalar stat. The type of stat is determined by the - * Storage template. The storage for this stat is held within the Bin class. - * This allows for breaking down statistics across multiple bins easily. + * Storage template. */ -template <class Storage, class Bin> +template <class Stor> class ScalarBase : public DataAccess { public: + typedef Stor Storage; + /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template Bin<Storage> bin_t; + typedef typename Storage::Params Params; protected: - /** The bin of this stat. */ - bin_t bin; + /** The storage of this stat. */ + char storage[sizeof(Storage)]; + /** The parameters for this stat. */ - params_t params; + Params params; protected: /** - * Retrieve the storage from the bin. - * @return The storage object for this stat. + * Retrieve the storage. + * @param index The vector index to access. + * @return The storage object at the given index. */ - Storage *data() { return bin.data(params); } + Storage * + data() + { + return reinterpret_cast<Storage *>(storage); + } + /** - * Retrieve a const pointer to the storage from the bin. - * @return A const pointer to the storage object for this stat. + * Retrieve a const pointer to the storage. + * for the given index. + * @param index The vector index to access. + * @return A const pointer to the storage object at the given index. */ - const Storage *data() const + const Storage * + data() const + { + return reinterpret_cast<const Storage *>(storage); + } + + void + doInit() { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(*_params); + new (storage) Storage(params); + setInit(); } public: @@ -748,9 +744,7 @@ class ScalarBase : public DataAccess * Create and initialize this stat, register it with the database. */ ScalarBase() - { - bin.init(params); - } + { } public: // Common operators for stats @@ -799,18 +793,13 @@ class ScalarBase : public DataAccess * @return 1. */ size_t size() const { return 1; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } - bool check() const { return bin.initialized(); } + bool check() const { return true; } /** * Reset stat value to default */ - void reset() { bin.reset(); } + void reset() { data()->reset(); } Counter value() { return data()->value(params); } @@ -826,7 +815,6 @@ class ProxyData : public ScalarData { public: virtual void visit(Visit &visitor) { visitor.visit(*this); } - virtual bool binned() const { return false; } virtual std::string str() const { return to_string(value()); } virtual size_t size() const { return 1; } virtual bool zero() const { return value() == 0; } @@ -888,7 +876,6 @@ class ValueBase : public DataAccess Result total() const { return proxy->total(); }; size_t size() const { return proxy->size(); } - bool binned() const { return proxy->binned(); } std::string str() const { return proxy->str(); } bool zero() const { return proxy->zero(); } bool check() const { return proxy != NULL; } @@ -900,195 +887,62 @@ class ValueBase : public DataAccess // Vector Statistics // ////////////////////////////////////////////////////////////////////// -template <class Storage, class Bin> -class ScalarProxy; - -/** - * Implementation of a vector of stats. The type of stat is determined by the - * Storage class. @sa ScalarBase - */ -template <class Storage, class Bin> -class VectorBase : public DataAccess -{ - public: - /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template VectorBin<Storage> bin_t; - - protected: - /** The bin of this stat. */ - bin_t bin; - /** The parameters for this stat. */ - params_t params; - - protected: - /** - * Retrieve the storage from the bin for the given index. - * @param index The vector index to access. - * @return The storage object at the given index. - */ - Storage *data(int index) { return bin.data(index, params); } - /** - * Retrieve a const pointer to the storage from the bin - * for the given index. - * @param index The vector index to access. - * @return A const pointer to the storage object at the given index. - */ - const Storage *data(int index) const - { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(index, *_params); - } - - public: - void value(VCounter &vec) const - { - vec.resize(size()); - for (int i = 0; i < size(); ++i) - vec[i] = data(i)->value(params); - } - - /** - * Copy the values to a local vector and return a reference to it. - * @return A reference to a vector of the stat values. - */ - void result(VResult &vec) const - { - vec.resize(size()); - for (int i = 0; i < size(); ++i) - vec[i] = data(i)->result(params); - } - - /** - * @return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } - - /** - * Return a total of all entries in this vector. - * @return The total of all vector entries. - */ - Result total() const { - Result total = 0.0; - for (int i = 0; i < size(); ++i) - total += data(i)->result(params); - return total; - } - - /** - * @return the number of elements in this vector. - */ - size_t size() const { return bin.size(); } - - bool zero() const - { - for (int i = 0; i < size(); ++i) - if (data(i)->zero()) - return true; - return false; - } - - bool check() const { return bin.initialized(); } - void reset() { bin.reset(); } - - public: - VectorBase() {} - - /** Friend this class with the associated scalar proxy. */ - friend class ScalarProxy<Storage, Bin>; - - /** - * Return a reference (ScalarProxy) to the stat at the given index. - * @param index The vector index to access. - * @return A reference of the stat. - */ - ScalarProxy<Storage, Bin> operator[](int index); - - void update(StatData *data) {} -}; - -const StatData * getStatData(const void *stat); /** * A proxy class to access the stat at a given index in a VectorBase stat. * Behaves like a ScalarBase. */ -template <class Storage, class Bin> +template <class Stat> class ScalarProxy { - public: - /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template VectorBin<Storage> bin_t; - private: - /** Pointer to the bin in the parent VectorBase. */ - bin_t *bin; - /** Pointer to the params in the parent VectorBase. */ - params_t *params; + /** Pointer to the parent Vector. */ + Stat *stat; + /** The index to access in the parent VectorBase. */ int index; - /** Keep a pointer to the original stat so was can get data */ - void *stat; - - protected: - /** - * Retrieve the storage from the bin. - * @return The storage from the bin for this stat. - */ - Storage *data() { return bin->data(index, *params); } - /** - * Retrieve a const pointer to the storage from the bin. - * @return A const pointer to the storage for this stat. - */ - const Storage *data() const - { - bin_t *_bin = const_cast<bin_t *>(bin); - params_t *_params = const_cast<params_t *>(params); - return _bin->data(index, *_params); - } public: /** * Return the current value of this stat as its base type. * @return The current value. */ - Counter value() const { return data()->value(*params); } + Counter value() const { return stat->data(index)->value(stat->params); } /** * Return the current value of this statas a result type. * @return The current value. */ - Result result() const { return data()->result(*params); } + Result result() const { return stat->data(index)->result(stat->params); } public: /** * Create and initialize this proxy, do not register it with the database. - * @param b The bin to use. * @param p The params to use. * @param i The index to access. */ - ScalarProxy(bin_t &b, params_t &p, int i, void *s) - : bin(&b), params(&p), index(i), stat(s) {} + ScalarProxy(Stat *s, int i) + : stat(s), index(i) + { + assert(stat); + } + /** * Create a copy of the provided ScalarProxy. * @param sp The proxy to copy. */ ScalarProxy(const ScalarProxy &sp) - : bin(sp.bin), params(sp.params), index(sp.index), stat(sp.stat) {} + : stat(sp.stat), index(sp.index) + {} + /** * Set this proxy equal to the provided one. * @param sp The proxy to copy. * @return A reference to this proxy. */ const ScalarProxy &operator=(const ScalarProxy &sp) { - bin = sp.bin; - params = sp.params; - index = sp.index; stat = sp.stat; + index = sp.index; return *this; } @@ -1098,12 +952,12 @@ class ScalarProxy * Increment the stat by 1. This calls the associated storage object inc * function. */ - void operator++() { data()->inc(1, *params); } + void operator++() { stat->data(index)->inc(1, stat->params); } /** * Decrement the stat by 1. This calls the associated storage object dec * function. */ - void operator--() { data()->dec(1, *params); } + void operator--() { stat->data(index)->dec(1, stat->params); } /** Increment the stat by 1. */ void operator++(int) { ++*this; } @@ -1116,7 +970,7 @@ class ScalarProxy * @param v The new value. */ template <typename U> - void operator=(const U &v) { data()->set(v, *params); } + void operator=(const U &v) { stat->data(index)->set(v, stat->params); } /** * Increment the stat by the given value. This calls the associated @@ -1124,7 +978,7 @@ class ScalarProxy * @param v The value to add. */ template <typename U> - void operator+=(const U &v) { data()->inc(v, *params); } + void operator+=(const U &v) { stat->data(index)->inc(v, stat->params); } /** * Decrement the stat by the given value. This calls the associated @@ -1132,7 +986,7 @@ class ScalarProxy * @param v The value to substract. */ template <typename U> - void operator-=(const U &v) { data()->dec(v, *params); } + void operator-=(const U &v) { stat->data(index)->dec(v, stat->params); } /** * Return the number of elements, always 1 for a scalar. @@ -1141,192 +995,356 @@ class ScalarProxy size_t size() const { return 1; } /** - * Return true if stat is binned. - *@return false since Proxies aren't printed/binned - */ - bool binned() const { return false; } - - /** * This stat has no state. Nothing to reset */ void reset() { } public: - const StatData *statData() const { return getStatData(stat); } - std::string str() const + std::string + str() const { - return csprintf("%s[%d]", this->statData()->name, index); + return csprintf("%s[%d]", stat->str(), index); } }; -template <class Storage, class Bin> -inline ScalarProxy<Storage, Bin> -VectorBase<Storage, Bin>::operator[](int index) +/** + * Implementation of a vector of stats. The type of stat is determined by the + * Storage class. @sa ScalarBase + */ +template <class Stor> +class VectorBase : public DataAccess { - assert (index >= 0 && index < size()); - return ScalarProxy<Storage, Bin>(bin, params, index, this); -} + public: + typedef Stor Storage; -template <class Storage, class Bin> -class VectorProxy; + /** Define the params of the storage class. */ + typedef typename Storage::Params Params; -template <class Storage, class Bin> -class Vector2dBase : public DataAccess -{ - public: - typedef typename Storage::Params params_t; - typedef typename Bin::template VectorBin<Storage> bin_t; + /** Proxy type */ + typedef ScalarProxy<VectorBase<Storage> > Proxy; + + friend class ScalarProxy<VectorBase<Storage> >; protected: - size_t x; - size_t y; - bin_t bin; - params_t params; + /** The storage of this stat. */ + Storage *storage; + size_t _size; + + /** The parameters for this stat. */ + Params params; protected: - Storage *data(int index) { return bin.data(index, params); } - const Storage *data(int index) const + /** + * Retrieve the storage. + * @param index The vector index to access. + * @return The storage object at the given index. + */ + Storage *data(int index) { return &storage[index]; } + + /** + * Retrieve a const pointer to the storage. + * @param index The vector index to access. + * @return A const pointer to the storage object at the given index. + */ + const Storage *data(int index) const { return &storage[index]; } + + void + doInit(int s) { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(index, *_params); + assert(s > 0 && "size must be positive!"); + assert(!storage && "already initialized"); + _size = s; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); } public: - Vector2dBase() {} + void value(VCounter &vec) const + { + vec.resize(size()); + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->value(params); + } - void update(Vector2dData *data) + /** + * Copy the values to a local vector and return a reference to it. + * @return A reference to a vector of the stat values. + */ + void result(VResult &vec) const { - int size = this->size(); - data->cvec.resize(size); - for (int i = 0; i < size; ++i) - data->cvec[i] = this->data(i)->value(params); + vec.resize(size()); + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->result(params); } - std::string ysubname(int i) const { return (*this->y_subnames)[i]; } + /** + * Return a total of all entries in this vector. + * @return The total of all vector entries. + */ + Result total() const { + Result total = 0.0; + for (int i = 0; i < size(); ++i) + total += data(i)->result(params); + return total; + } + + /** + * @return the number of elements in this vector. + */ + size_t size() const { return _size; } + + bool + zero() const + { + for (int i = 0; i < size(); ++i) + if (data(i)->zero()) + return false; + return true; + } + + bool + check() const + { + return storage != NULL; + } + + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } - friend class VectorProxy<Storage, Bin>; - VectorProxy<Storage, Bin> operator[](int index); + public: + VectorBase() + : storage(NULL) + {} - size_t size() const { return bin.size(); } - bool zero() const { return data(0)->value(params) == 0.0; } + ~VectorBase() + { + if (!storage) + return; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } /** - * Reset stat value to default + * Return a reference (ScalarProxy) to the stat at the given index. + * @param index The vector index to access. + * @return A reference of the stat. */ - void reset() { bin.reset(); } + Proxy + operator[](int index) + { + assert (index >= 0 && index < size()); + return Proxy(this, index); + } - bool check() { return bin.initialized(); } + void update(StatData *data) {} }; -template <class Storage, class Bin> +template <class Stat> class VectorProxy { - public: - typedef typename Storage::Params params_t; - typedef typename Bin::template VectorBin<Storage> bin_t; - private: - bin_t *bin; - params_t *params; + Stat *stat; int offset; int len; - void *stat; private: - mutable VResult *vec; + mutable VResult vec; - Storage *data(int index) { + typename Stat::Storage * + data(int index) + { assert(index < len); - return bin->data(offset + index, *params); + return stat->data(offset + index); } - const Storage *data(int index) const { - bin_t *_bin = const_cast<bin_t *>(bin); - params_t *_params = const_cast<params_t *>(params); - return _bin->data(offset + index, *_params); + const typename Stat::Storage * + data(int index) const + { + assert(index < len); + return const_cast<Stat *>(stat)->data(offset + index); } public: - const VResult &result() const { - if (vec) - vec->resize(size()); - else - vec = new VResult(size()); + const VResult & + result() const + { + vec.resize(size()); for (int i = 0; i < size(); ++i) - (*vec)[i] = data(i)->result(*params); + vec[i] = data(i)->result(stat->params); - return *vec; + return vec; } - Result total() const { - Result total = 0.0; + Result + total() const + { + Result total = 0; for (int i = 0; i < size(); ++i) - total += data(i)->result(*params); + total += data(i)->result(stat->params); return total; } public: - VectorProxy(bin_t &b, params_t &p, int o, int l, void *s) - : bin(&b), params(&p), offset(o), len(l), stat(s), vec(NULL) + VectorProxy(Stat *s, int o, int l) + : stat(s), offset(o), len(l) { } VectorProxy(const VectorProxy &sp) - : bin(sp.bin), params(sp.params), offset(sp.offset), len(sp.len), - stat(sp.stat), vec(NULL) + : stat(sp.stat), offset(sp.offset), len(sp.len) { } - ~VectorProxy() + const VectorProxy & + operator=(const VectorProxy &sp) { - if (vec) - delete vec; - } - - const VectorProxy &operator=(const VectorProxy &sp) - { - bin = sp.bin; - params = sp.params; + stat = sp.stat; offset = sp.offset; len = sp.len; - stat = sp.stat; - if (vec) - delete vec; - vec = NULL; return *this; } - ScalarProxy<Storage, Bin> operator[](int index) + ScalarProxy<Stat> operator[](int index) { assert (index >= 0 && index < size()); - return ScalarProxy<Storage, Bin>(*bin, *params, offset + index, stat); + return ScalarProxy<Stat>(stat, offset + index); } size_t size() const { return len; } /** - * Return true if stat is binned. - *@return false since Proxies aren't printed/binned - */ - bool binned() const { return false; } - - /** * This stat has no state. Nothing to reset. */ void reset() { } }; -template <class Storage, class Bin> -inline VectorProxy<Storage, Bin> -Vector2dBase<Storage, Bin>::operator[](int index) +template <class Stor> +class Vector2dBase : public DataAccess { - int offset = index * y; - assert (index >= 0 && offset < size()); - return VectorProxy<Storage, Bin>(bin, params, offset, y, this); -} + public: + typedef Stor Storage; + typedef typename Storage::Params Params; + typedef VectorProxy<Vector2dBase<Storage> > Proxy; + friend class ScalarProxy<Vector2dBase<Storage> >; + friend class VectorProxy<Vector2dBase<Storage> >; + + protected: + size_t x; + size_t y; + size_t _size; + Storage *storage; + Params params; + + protected: + Storage *data(int index) { return &storage[index]; } + const Storage *data(int index) const { return &storage[index]; } + + void + doInit(int _x, int _y) + { + assert(_x > 0 && _y > 0 && "sizes must be positive!"); + assert(!storage && "already initialized"); + + Vector2dData *statdata = dynamic_cast<Vector2dData *>(find()); + + x = _x; + y = _y; + statdata->x = _x; + statdata->y = _y; + _size = x * y; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); + } + + public: + Vector2dBase() + : storage(NULL) + {} + + ~Vector2dBase() + { + if (!storage) + return; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + void + update(Vector2dData *newdata) + { + int size = this->size(); + newdata->cvec.resize(size); + for (int i = 0; i < size; ++i) + newdata->cvec[i] = data(i)->value(params); + } + + std::string ysubname(int i) const { return (*this->y_subnames)[i]; } + + Proxy + operator[](int index) + { + int offset = index * y; + assert (index >= 0 && offset + index < size()); + return Proxy(this, offset, y); + } + + + size_t + size() const + { + return _size; + } + + bool + zero() const + { + return data(0)->zero(); +#if 0 + for (int i = 0; i < size(); ++i) + if (!data(i)->zero()) + return false; + return true; +#endif + } + + /** + * Reset stat value to default + */ + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } + + bool + check() + { + return storage != NULL; + } +}; ////////////////////////////////////////////////////////////////////// // @@ -1373,14 +1391,8 @@ struct DistStor VCounter cvec; public: - /** - * Construct this storage with the supplied params. - * @param params The parameters. - */ DistStor(const Params ¶ms) - : min_val(INT_MAX), max_val(INT_MIN), underflow(Counter()), - overflow(Counter()), sum(Counter()), squares(Counter()), - samples(Counter()), cvec(params.size) + : cvec(params.size) { reset(); } @@ -1615,36 +1627,46 @@ struct AvgFancy * Implementation of a distribution stat. The type of distribution is * determined by the Storage template. @sa ScalarBase */ -template <class Storage, class Bin> +template <class Stor> class DistBase : public DataAccess { public: + typedef Stor Storage; /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template Bin<Storage> bin_t; + typedef typename Storage::Params Params; protected: - /** The bin of this stat. */ - bin_t bin; + /** The storage for this stat. */ + char storage[sizeof(Storage)]; + /** The parameters for this stat. */ - params_t params; + Params params; protected: /** - * Retrieve the storage from the bin. + * Retrieve the storage. * @return The storage object for this stat. */ - Storage *data() { return bin.data(params); } + Storage *data() + { + return reinterpret_cast<Storage *>(storage); + } + /** - * Retrieve a const pointer to the storage from the bin. + * Retrieve a const pointer to the storage. * @return A const pointer to the storage object for this stat. */ - const Storage *data() const + const Storage * + data() const { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(*_params); + return reinterpret_cast<const Storage *>(storage); + } + + void + doInit() + { + new (storage) Storage(params); + setInit(); } public: @@ -1675,65 +1697,122 @@ class DistBase : public DataAccess base->data.fancy = Storage::fancy; data()->update(&(base->data), params); } - /** - * @return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } + /** * Reset stat value to default */ - void reset() + void + reset() { - bin.reset(); + data()->reset(); } - bool check() { return bin.initialized(); } + bool + check() + { + return true; + } }; -template <class Storage, class Bin> +template <class Stat> class DistProxy; -template <class Storage, class Bin> +template <class Stor> class VectorDistBase : public DataAccess { public: - typedef typename Storage::Params params_t; - typedef typename Bin::template VectorBin<Storage> bin_t; + typedef Stor Storage; + typedef typename Storage::Params Params; + typedef DistProxy<VectorDistBase<Storage> > Proxy; + friend class DistProxy<VectorDistBase<Storage> >; protected: - bin_t bin; - params_t params; + Storage *storage; + size_t _size; + Params params; protected: - Storage *data(int index) { return bin.data(index, params); } - const Storage *data(int index) const + Storage * + data(int index) { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(index, *_params); + return &storage[index]; + } + + const Storage * + data(int index) const + { + return &storage[index]; + } + + void + doInit(int s) + { + assert(s > 0 && "size must be positive!"); + assert(!storage && "already initialized"); + _size = s; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); } public: - VectorDistBase() {} + VectorDistBase() + : storage(NULL) + {} + + ~VectorDistBase() + { + if (!storage) + return ; - friend class DistProxy<Storage, Bin>; - DistProxy<Storage, Bin> operator[](int index); - const DistProxy<Storage, Bin> operator[](int index) const; + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + Proxy operator[](int index); + + size_t + size() const + { + return _size; + } + + bool + zero() const + { + return false; +#if 0 + for (int i = 0; i < size(); ++i) + if (!data(i)->zero(params)) + return false; + return true; +#endif + } - size_t size() const { return bin.size(); } - bool zero() const { return false; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } /** * Reset stat value to default */ - void reset() { bin.reset(); } + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } - bool check() { return bin.initialized(); } - void update(VectorDistData *base) + bool + check() + { + return storage != NULL; + } + + void + update(VectorDistData *base) { int size = this->size(); base->data.resize(size); @@ -1744,75 +1823,75 @@ class VectorDistBase : public DataAccess } }; -template <class Storage, class Bin> +template <class Stat> class DistProxy { - public: - typedef typename Storage::Params params_t; - typedef typename Bin::template Bin<Storage> bin_t; - typedef VectorDistBase<Storage, Bin> base_t; - private: - union { - base_t *stat; - const base_t *cstat; - }; + Stat *stat; int index; protected: - Storage *data() { return stat->data(index); } - const Storage *data() const { return cstat->data(index); } + typename Stat::Storage *data() { return stat->data(index); } + const typename Stat::Storage *data() const { return stat->data(index); } public: - DistProxy(const VectorDistBase<Storage, Bin> &s, int i) - : cstat(&s), index(i) {} + DistProxy(Stat *s, int i) + : stat(s), index(i) + {} + DistProxy(const DistProxy &sp) - : cstat(sp.cstat), index(sp.index) {} - const DistProxy &operator=(const DistProxy &sp) { - cstat = sp.cstat; index = sp.index; return *this; + : stat(sp.stat), index(sp.index) + {} + + const DistProxy &operator=(const DistProxy &sp) + { + stat = sp.stat; + index = sp.index; + return *this; } public: template <typename U> - void sample(const U &v, int n = 1) { data()->sample(v, n, cstat->params); } + void + sample(const U &v, int n = 1) + { + data()->sample(v, n, stat->params); + } + + size_t + size() const + { + return 1; + } + + bool + zero() const + { + return data()->zero(stat->params); + } - size_t size() const { return 1; } - bool zero() const { return data()->zero(cstat->params); } - /** - * Return true if stat is binned. - *@return false since Proxies are not binned/printed. - */ - bool binned() const { return false; } /** * Proxy has no state. Nothing to reset. */ void reset() { } }; -template <class Storage, class Bin> -inline DistProxy<Storage, Bin> -VectorDistBase<Storage, Bin>::operator[](int index) -{ - assert (index >= 0 && index < size()); - return DistProxy<Storage, Bin>(*this, index); -} - -template <class Storage, class Bin> -inline const DistProxy<Storage, Bin> -VectorDistBase<Storage, Bin>::operator[](int index) const +template <class Storage> +inline typename VectorDistBase<Storage>::Proxy +VectorDistBase<Storage>::operator[](int index) { assert (index >= 0 && index < size()); - return DistProxy<Storage, Bin>(*this, index); + return typename VectorDistBase<Storage>::Proxy(this, index); } #if 0 -template <class Storage, class Bin> +template <class Storage> Result -VectorDistBase<Storage, Bin>::total(int index) const +VectorDistBase<Storage>::total(int index) const { int total = 0; - for (int i=0; i < x_size(); ++i) { - total += data(i)->result(*params); + for (int i = 0; i < x_size(); ++i) { + total += data(i)->result(stat->params); } } #endif @@ -1845,11 +1924,6 @@ class Node : public RefCounted * @return The total of the result vector. */ virtual Result total() const = 0; - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const = 0; /** * @@ -1876,11 +1950,6 @@ class ScalarStatNode : public Node virtual Result total() const { return data->result(); }; virtual size_t size() const { return 1; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const { return data->binned(); } /** * @@ -1888,34 +1957,45 @@ class ScalarStatNode : public Node virtual std::string str() const { return data->name; } }; -template <class Storage, class Bin> +template <class Stat> class ScalarProxyNode : public Node { private: - const ScalarProxy<Storage, Bin> proxy; + const ScalarProxy<Stat> proxy; mutable VResult vresult; public: - ScalarProxyNode(const ScalarProxy<Storage, Bin> &p) - : proxy(p), vresult(1) { } - virtual const VResult &result() const + ScalarProxyNode(const ScalarProxy<Stat> &p) + : proxy(p), vresult(1) + { } + + virtual const VResult & + result() const { vresult[0] = proxy.result(); return vresult; } - virtual Result total() const { return proxy.result(); }; - virtual size_t size() const { return 1; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const { return proxy.binned(); } + virtual Result + total() const + { + return proxy.result(); + } + + virtual size_t + size() const + { + return 1; + } /** * */ - virtual std::string str() const { return proxy.str(); } + virtual std::string + str() const + { + return proxy.str(); + } }; class VectorStatNode : public Node @@ -1929,11 +2009,6 @@ class VectorStatNode : public Node virtual Result total() const { return data->total(); }; virtual size_t size() const { return data->size(); } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const { return data->binned(); } virtual std::string str() const { return data->name; } }; @@ -1949,13 +2024,6 @@ class ConstNode : public Node const VResult &result() const { return vresult; } virtual Result total() const { return vresult[0]; }; virtual size_t size() const { return 1; } - - /** - * Return true if stat is binned. - *@return False since constants aren't binned. - */ - virtual bool binned() const { return false; } - virtual std::string str() const { return to_string(vresult[0]); } }; @@ -2029,11 +2097,6 @@ class UnaryNode : public Node } virtual size_t size() const { return l->size(); } - /** - * Return true if child of node is binned. - *@return True if child of node is binned. - */ - virtual bool binned() const { return l->binned(); } virtual std::string str() const { @@ -2100,11 +2163,6 @@ class BinaryNode : public Node return ls; } } - /** - * Return true if any children of node are binned - *@return True if either child of node is binned. - */ - virtual bool binned() const { return (l->binned() || r->binned()); } virtual std::string str() const { @@ -2153,11 +2211,6 @@ class SumNode : public Node } virtual size_t size() const { return 1; } - /** - * Return true if child of node is binned. - *@return True if child of node is binned. - */ - virtual bool binned() const { return l->binned(); } virtual std::string str() const { @@ -2173,40 +2226,24 @@ class SumNode : public Node ////////////////////////////////////////////////////////////////////// /** * @defgroup VisibleStats "Statistic Types" - * These are the statistics that are used in the simulator. By default these - * store counters and don't use binning, but are templatized to accept any type - * and any Bin class. + * These are the statistics that are used in the simulator. * @{ */ /** - * This is an easy way to assign all your stats to be binned or not - * binned. If the typedef is NoBin, nothing is binned. If it is - * MainBin, then all stats are binned under that Bin. - */ -#if STATS_BINNING -typedef MainBin DefaultBin; -#else -typedef NoBin DefaultBin; -#endif - -/** * This is a simple scalar statistic, like a counter. * @sa Stat, ScalarBase, StatStor */ -template <class Bin = DefaultBin> -class Scalar - : public Wrap<Scalar<Bin>, - ScalarBase<StatStor, Bin>, - ScalarStatData> +template<int N = 0> +class Scalar : public Wrap<Scalar<N>, ScalarBase<StatStor>, ScalarStatData> { public: /** The base implementation. */ - typedef ScalarBase<StatStor, Bin> Base; + typedef ScalarBase<StatStor> Base; Scalar() { - this->setInit(); + this->doInit(); } /** @@ -2218,10 +2255,7 @@ class Scalar void operator=(const U &v) { Base::operator=(v); } }; -class Value - : public Wrap<Value, - ValueBase, - ScalarStatData> +class Value : public Wrap<Value, ValueBase, ScalarStatData> { public: /** The base implementation. */ @@ -2246,19 +2280,16 @@ class Value * A stat that calculates the per cycle average of a value. * @sa Stat, ScalarBase, AvgStor */ -template <class Bin = DefaultBin> -class Average - : public Wrap<Average<Bin>, - ScalarBase<AvgStor, Bin>, - ScalarStatData> +template<int N = 0> +class Average : public Wrap<Average<N>, ScalarBase<AvgStor>, ScalarStatData> { public: /** The base implementation. */ - typedef ScalarBase<AvgStor, Bin> Base; + typedef ScalarBase<AvgStor> Base; Average() { - this->setInit(); + this->doInit(); } /** @@ -2274,15 +2305,12 @@ class Average * A vector of scalar stats. * @sa Stat, VectorBase, StatStor */ -template <class Bin = DefaultBin> -class Vector - : public WrapVec<Vector<Bin>, - VectorBase<StatStor, Bin>, - VectorStatData> +template<int N = 0> +class Vector : public WrapVec<Vector<N>, VectorBase<StatStor>, VectorStatData> { public: /** The base implementation. */ - typedef ScalarBase<StatStor, Bin> Base; + typedef ScalarBase<StatStor> Base; /** * Set this vector to have the given size. @@ -2290,9 +2318,7 @@ class Vector * @return A reference to this stat. */ Vector &init(size_t size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2301,11 +2327,9 @@ class Vector * A vector of Average stats. * @sa Stat, VectorBase, AvgStor */ -template <class Bin = DefaultBin> +template<int N = 0> class AverageVector - : public WrapVec<AverageVector<Bin>, - VectorBase<AvgStor, Bin>, - VectorStatData> + : public WrapVec<AverageVector<N>, VectorBase<AvgStor>, VectorStatData> { public: /** @@ -2314,9 +2338,7 @@ class AverageVector * @return A reference to this stat. */ AverageVector &init(size_t size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2325,19 +2347,13 @@ class AverageVector * A 2-Dimensional vecto of scalar stats. * @sa Stat, Vector2dBase, StatStor */ -template <class Bin = DefaultBin> +template<int N = 0> class Vector2d - : public WrapVec2d<Vector2d<Bin>, - Vector2dBase<StatStor, Bin>, - Vector2dStatData> + : public WrapVec2d<Vector2d<N>, Vector2dBase<StatStor>, Vector2dStatData> { public: - Vector2d &init(size_t _x, size_t _y) { - this->statData()->x = this->x = _x; - this->statData()->y = this->y = _y; - this->bin.init(this->x * this->y, this->params); - this->setInit(); - + Vector2d &init(size_t x, size_t y) { + this->doInit(x, y); return *this; } }; @@ -2346,17 +2362,15 @@ class Vector2d * A simple distribution stat. * @sa Stat, DistBase, DistStor */ -template <class Bin = DefaultBin> +template<int N = 0> class Distribution - : public Wrap<Distribution<Bin>, - DistBase<DistStor, Bin>, - DistStatData> + : public Wrap<Distribution<N>, DistBase<DistStor>, DistStatData> { public: /** Base implementation. */ - typedef DistBase<DistStor, Bin> Base; + typedef DistBase<DistStor> Base; /** The Parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2371,9 +2385,7 @@ class Distribution this->params.max = max; this->params.bucket_size = bkt; this->params.size = (int)rint((max - min) / bkt + 1.0); - this->bin.init(this->params); - this->setInit(); - + this->doInit(); return *this; } }; @@ -2382,25 +2394,22 @@ class Distribution * Calculates the mean and variance of all the samples. * @sa Stat, DistBase, FancyStor */ -template <class Bin = DefaultBin> +template<int N = 0> class StandardDeviation - : public Wrap<StandardDeviation<Bin>, - DistBase<FancyStor, Bin>, - DistStatData> + : public Wrap<StandardDeviation<N>, DistBase<FancyStor>, DistStatData> { public: /** The base implementation */ - typedef DistBase<DistStor, Bin> Base; + typedef DistBase<DistStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** * Construct and initialize this distribution. */ StandardDeviation() { - this->bin.init(this->params); - this->setInit(); + this->doInit(); } }; @@ -2408,17 +2417,15 @@ class StandardDeviation * Calculates the per cycle mean and variance of the samples. * @sa Stat, DistBase, AvgFancy */ -template <class Bin = DefaultBin> +template<int N = 0> class AverageDeviation - : public Wrap<AverageDeviation<Bin>, - DistBase<AvgFancy, Bin>, - DistStatData> + : public Wrap<AverageDeviation<N>, DistBase<AvgFancy>, DistStatData> { public: /** The base implementation */ - typedef DistBase<DistStor, Bin> Base; + typedef DistBase<DistStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2426,8 +2433,7 @@ class AverageDeviation */ AverageDeviation() { - this->bin.init(this->params); - this->setInit(); + this->doInit(); } }; @@ -2435,17 +2441,17 @@ class AverageDeviation * A vector of distributions. * @sa Stat, VectorDistBase, DistStor */ -template <class Bin = DefaultBin> +template<int N = 0> class VectorDistribution - : public WrapVec<VectorDistribution<Bin>, - VectorDistBase<DistStor, Bin>, + : public WrapVec<VectorDistribution<N>, + VectorDistBase<DistStor>, VectorDistStatData> { public: /** The base implementation */ - typedef VectorDistBase<DistStor, Bin> Base; + typedef VectorDistBase<DistStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2461,9 +2467,7 @@ class VectorDistribution this->params.max = max; this->params.bucket_size = bkt; this->params.size = (int)rint((max - min) / bkt + 1.0); - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2472,17 +2476,17 @@ class VectorDistribution * This is a vector of StandardDeviation stats. * @sa Stat, VectorDistBase, FancyStor */ -template <class Bin = DefaultBin> +template<int N = 0> class VectorStandardDeviation - : public WrapVec<VectorStandardDeviation<Bin>, - VectorDistBase<FancyStor, Bin>, + : public WrapVec<VectorStandardDeviation<N>, + VectorDistBase<FancyStor>, VectorDistStatData> { public: /** The base implementation */ - typedef VectorDistBase<FancyStor, Bin> Base; + typedef VectorDistBase<FancyStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2491,9 +2495,7 @@ class VectorStandardDeviation * @return A reference to this distribution. */ VectorStandardDeviation &init(int size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2502,17 +2504,17 @@ class VectorStandardDeviation * This is a vector of AverageDeviation stats. * @sa Stat, VectorDistBase, AvgFancy */ -template <class Bin = DefaultBin> +template<int N = 0> class VectorAverageDeviation - : public WrapVec<VectorAverageDeviation<Bin>, - VectorDistBase<AvgFancy, Bin>, + : public WrapVec<VectorAverageDeviation<N>, + VectorDistBase<AvgFancy>, VectorDistStatData> { public: /** The base implementation */ - typedef VectorDistBase<AvgFancy, Bin> Base; + typedef VectorDistBase<AvgFancy> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2521,9 +2523,7 @@ class VectorAverageDeviation * @return A reference to this distribution. */ VectorAverageDeviation &init(int size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2567,13 +2567,6 @@ class FormulaBase : public DataAccess */ size_t size() const; - /** - * Return true if Formula is binned. i.e. any of its children - * nodes are binned - * @return True if Formula is binned. - */ - bool binned() const; - bool check() const { return true; } /** @@ -2612,7 +2605,6 @@ class FormulaStatData : public FormulaData public: FormulaStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool zero() const { return s.zero(); } virtual void reset() { s.reset(); } @@ -2679,7 +2671,6 @@ class FormulaNode : public Node virtual size_t size() const { return formula.size(); } virtual const VResult &result() const { formula.result(vec); return vec; } virtual Result total() const { return formula.total(); } - virtual bool binned() const { return formula.binned(); } virtual std::string str() const { return formula.str(); } }; @@ -2713,8 +2704,8 @@ class Temp * Create a new ScalarStatNode. * @param s The ScalarStat to place in a node. */ - template <class Bin> - Temp(const Scalar<Bin> &s) + template <int N> + Temp(const Scalar<N> &s) : node(new ScalarStatNode(s.statData())) { } /** @@ -2728,16 +2719,16 @@ class Temp * Create a new ScalarStatNode. * @param s The ScalarStat to place in a node. */ - template <class Bin> - Temp(const Average<Bin> &s) + template <int N> + Temp(const Average<N> &s) : node(new ScalarStatNode(s.statData())) { } /** * Create a new VectorStatNode. * @param s The VectorStat to place in a node. */ - template <class Bin> - Temp(const Vector<Bin> &s) + template <int N> + Temp(const Vector<N> &s) : node(new VectorStatNode(s.statData())) { } /** @@ -2750,9 +2741,9 @@ class Temp * Create a new ScalarProxyNode. * @param p The ScalarProxy to place in a node. */ - template <class Storage, class Bin> - Temp(const ScalarProxy<Storage, Bin> &p) - : node(new ScalarProxyNode<Storage, Bin>(p)) { } + template <class Stat> + Temp(const ScalarProxy<Stat> &p) + : node(new ScalarProxyNode<Stat>(p)) { } /** * Create a ConstNode diff --git a/src/base/stats/events.cc b/src/base/stats/events.cc index 3191aec13..6ecc5434c 100644 --- a/src/base/stats/events.cc +++ b/src/base/stats/events.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <vector> diff --git a/src/base/stats/events.hh b/src/base/stats/events.hh index 2a23240b4..b09b91c7c 100644 --- a/src/base/stats/events.hh +++ b/src/base/stats/events.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_EVENTS_HH__ diff --git a/src/base/stats/flags.hh b/src/base/stats/flags.hh index 00db95bc1..ada1a4a87 100644 --- a/src/base/stats/flags.hh +++ b/src/base/stats/flags.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_FLAGS_HH__ diff --git a/src/base/stats/mysql.cc b/src/base/stats/mysql.cc index 6d12b4fc1..0fb31f4ce 100644 --- a/src/base/stats/mysql.cc +++ b/src/base/stats/mysql.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <cassert> @@ -156,14 +158,6 @@ MySqlRun::cleanup() if (mysql.commit()) panic("could not commit transaction\n%s\n", mysql.error); - mysql.query("DELETE bins " - "FROM bins " - "LEFT JOIN data ON bn_id=dt_bin " - "WHERE dt_bin IS NULL"); - - if (mysql.commit()) - panic("could not commit transaction\n%s\n", mysql.error); - mysql.query("DELETE events" "FROM events" "LEFT JOIN runs ON ev_run=rn_id" @@ -307,52 +301,6 @@ SetupStat::setup() return statid; } -unsigned -SetupBin(const string &bin) -{ - static map<string, int> binmap; - - using namespace MySQL; - map<string,int>::const_iterator i = binmap.find(bin); - if (i != binmap.end()) - return (*i).second; - - Connection &mysql = MySqlDB.conn(); - assert(mysql.connected()); - - uint16_t bin_id; - - stringstream select; - stringstream insert; - ccprintf(select, "SELECT bn_id FROM bins WHERE bn_name=\"%s\"", bin); - - mysql.query(select); - MySQL::Result result = mysql.store_result(); - if (result) { - assert(result.num_fields() == 1); - MySQL::Row row = result.fetch_row(); - if (row) { - to_number(row[0], bin_id); - goto exit; - } - } - - ccprintf(insert, "INSERT INTO bins(bn_name) values(\"%s\")", bin); - - mysql.query(insert); - if (mysql.error) - panic("could not get a bin\n%s\n", mysql.error); - - bin_id = mysql.insert_id(); - if (mysql.commit()) - panic("could not commit transaction\n%s\n", mysql.error); - - binmap.insert(make_pair(bin, bin_id)); - - exit: - return bin_id; -} - InsertData::InsertData() { query = new char[maxsize + 1]; @@ -382,7 +330,7 @@ InsertData::flush() size = 0; first = true; strcpy(query, "INSERT INTO " - "data(dt_stat,dt_x,dt_y,dt_run,dt_tick,dt_bin,dt_data) " + "data(dt_stat,dt_x,dt_y,dt_run,dt_tick,dt_data) " "values"); size = strlen(query); } @@ -400,9 +348,9 @@ InsertData::insert() first = false; - size += sprintf(query + size, "(%u,%d,%d,%u,%llu,%u,\"%f\")", + size += sprintf(query + size, "(%u,%d,%d,%u,%llu,\"%f\")", stat, x, y, MySqlDB.run(), (unsigned long long)tick, - bin, data); + data); } struct InsertSubData @@ -654,29 +602,6 @@ MySql::configure(const FormulaData &data) InsertFormula(find(data.id), data.str()); } -void -MySql::output(MainBin *bin) -{ - MySQL::Connection &mysql = MySqlDB.conn(); - - if (bin) { - bin->activate(); - newdata.bin = SetupBin(bin->name()); - } else { - newdata.bin = 0; - } - - Database::stat_list_t::const_iterator i, end = Database::stats().end(); - for (i = Database::stats().begin(); i != end; ++i) { - StatData *stat = *i; - if (bin && stat->binned() || !bin && !stat->binned()) { - stat->visit(*this); - if (mysql.commit()) - panic("could not commit transaction\n%s\n", mysql.error); - } - } -} - bool MySql::valid() const { @@ -695,11 +620,14 @@ MySql::output() // store sample # newdata.tick = curTick; - output(NULL); - if (!bins().empty()) { - bin_list_t::iterator i, end = bins().end(); - for (i = bins().begin(); i != end; ++i) - output(*i); + MySQL::Connection &mysql = MySqlDB.conn(); + + Database::stat_list_t::const_iterator i, end = Database::stats().end(); + for (i = Database::stats().begin(); i != end; ++i) { + StatData *stat = *i; + stat->visit(*this); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); } newdata.flush(); diff --git a/src/base/stats/mysql.hh b/src/base/stats/mysql.hh index 25ea22b97..50f7d9e97 100644 --- a/src/base/stats/mysql.hh +++ b/src/base/stats/mysql.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_MYSQL_HH__ @@ -37,7 +39,6 @@ namespace MySQL { class Connection; } namespace Stats { -class MainBin; class DistDataData; class MySqlRun; bool MySqlConnected(); @@ -80,7 +81,6 @@ class InsertData uint64_t tick; double data; uint16_t stat; - uint16_t bin; int16_t x; int16_t y; @@ -131,7 +131,6 @@ class MySql : public Output protected: // Output helper - void output(MainBin *bin); void output(const DistDataData &data); void output(const ScalarData &data); void output(const VectorData &data); diff --git a/src/base/stats/mysql_run.hh b/src/base/stats/mysql_run.hh index d8dcb7594..487224551 100644 --- a/src/base/stats/mysql_run.hh +++ b/src/base/stats/mysql_run.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_MYSQL_RUN_HH__ diff --git a/src/base/stats/output.hh b/src/base/stats/output.hh index ee6b38d63..4fe93791f 100644 --- a/src/base/stats/output.hh +++ b/src/base/stats/output.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_OUTPUT_HH__ diff --git a/src/base/stats/statdb.cc b/src/base/stats/statdb.cc index a6b00ab8a..f9136807a 100644 --- a/src/base/stats/statdb.cc +++ b/src/base/stats/statdb.cc @@ -24,12 +24,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "base/misc.hh" #include "base/trace.hh" #include "base/statistics.hh" -#include "base/stats/bin.hh" #include "base/stats/statdb.hh" using namespace std; @@ -49,17 +50,6 @@ find(void *stat) } void -regBin(MainBin *bin, const std::string &_name) -{ - bin_list_t::iterator i, end = bins().end(); - for (i = bins().begin(); i != end; ++i) - if ((*i)->name() == _name) - panic("re-registering bin %s", _name); - bins().push_back(bin); - DPRINTF(Stats, "registering %s\n", _name); -} - -void regStat(void *stat, StatData *data) { if (map().find(stat) != map().end()) diff --git a/src/base/stats/statdb.hh b/src/base/stats/statdb.hh index eb56d8fac..a5b9be7eb 100644 --- a/src/base/stats/statdb.hh +++ b/src/base/stats/statdb.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_STATDB_HH__ @@ -38,31 +40,25 @@ class Python; namespace Stats { -class MainBin; class StatData; namespace Database { typedef std::map<void *, StatData *> stat_map_t; typedef std::list<StatData *> stat_list_t; -typedef std::list<MainBin *> bin_list_t; // We wrap the database in a struct to make sure it is built in time. struct TheDatabase { stat_map_t map; stat_list_t stats; - bin_list_t bins; - }; TheDatabase &db(); inline stat_map_t &map() { return db().map; } inline stat_list_t &stats() { return db().stats; } -inline bin_list_t &bins() { return db().bins; } StatData *find(void *stat); -void regBin(MainBin *bin, const std::string &name); void regStat(void *stat, StatData *data); void regPrint(void *stat); diff --git a/src/base/stats/text.cc b/src/base/stats/text.cc index 300737c60..c4448efc9 100644 --- a/src/base/stats/text.cc +++ b/src/base/stats/text.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #if defined(__APPLE__) @@ -127,23 +129,9 @@ Text::output() using namespace Database; ccprintf(*stream, "\n---------- Begin Simulation Statistics ----------\n"); - if (bins().empty() || bins().size() == 1) { - stat_list_t::const_iterator i, end = stats().end(); - for (i = stats().begin(); i != end; ++i) - (*i)->visit(*this); - } else { - ccprintf(*stream, "PRINTING BINNED STATS\n"); - bin_list_t::iterator i, end = bins().end(); - for (i = bins().begin(); i != end; ++i) { - MainBin *bin = *i; - bin->activate(); - ccprintf(*stream,"---%s Bin------------\n", bin->name()); - stat_list_t::const_iterator i, end = stats().end(); - for (i = stats().begin(); i != end; ++i) - (*i)->visit(*this); - ccprintf(*stream, "---------------------------------\n"); - } - } + stat_list_t::const_iterator i, end = stats().end(); + for (i = stats().begin(); i != end; ++i) + (*i)->visit(*this); ccprintf(*stream, "\n---------- End Simulation Statistics ----------\n"); stream->flush(); } diff --git a/src/base/stats/text.hh b/src/base/stats/text.hh index 125cb79fa..b3faf5ad5 100644 --- a/src/base/stats/text.hh +++ b/src/base/stats/text.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_TEXT_HH__ @@ -44,7 +46,6 @@ class Text : public Output protected: bool noOutput(const StatData &data); - void binout(); public: bool compat; diff --git a/src/base/stats/types.hh b/src/base/stats/types.hh index 57f1564a5..b64e8fb17 100644 --- a/src/base/stats/types.hh +++ b/src/base/stats/types.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_TYPES_HH__ diff --git a/src/base/stats/visit.cc b/src/base/stats/visit.cc index dd4d49502..870e4fb77 100644 --- a/src/base/stats/visit.cc +++ b/src/base/stats/visit.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "base/stats/visit.hh" diff --git a/src/base/stats/visit.hh b/src/base/stats/visit.hh index c0593c670..0087c227c 100644 --- a/src/base/stats/visit.hh +++ b/src/base/stats/visit.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_VISIT_HH__ diff --git a/src/base/str.cc b/src/base/str.cc index 5f7f50286..0a517dff5 100644 --- a/src/base/str.cc +++ b/src/base/str.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <ctype.h> diff --git a/src/base/str.hh b/src/base/str.hh index 79e33a1be..08953caf3 100644 --- a/src/base/str.hh +++ b/src/base/str.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __STR_HH__ diff --git a/src/base/time.cc b/src/base/time.cc index 5827c9a85..cbc7256ee 100644 --- a/src/base/time.cc +++ b/src/base/time.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/base/time.hh b/src/base/time.hh index 5731e3029..24e8a8a53 100644 --- a/src/base/time.hh +++ b/src/base/time.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __SIM_TIME_HH__ diff --git a/src/base/timebuf.hh b/src/base/timebuf.hh index f6b5b2781..6a326d25a 100644 --- a/src/base/timebuf.hh +++ b/src/base/timebuf.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #ifndef __BASE_TIMEBUF_HH__ diff --git a/src/base/trace.cc b/src/base/trace.cc index 90db7f045..50426b992 100644 --- a/src/base/trace.cc +++ b/src/base/trace.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <ctype.h> diff --git a/src/base/trace.hh b/src/base/trace.hh index 5e14f1bff..8df5dd893 100644 --- a/src/base/trace.hh +++ b/src/base/trace.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __BASE_TRACE_HH__ diff --git a/src/base/traceflags.py b/src/base/traceflags.py index 3b7dd0f81..7ff68bcaf 100644 --- a/src/base/traceflags.py +++ b/src/base/traceflags.py @@ -25,6 +25,9 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert +# Steve Reinhardt # # This file generates the header and source files for the flags @@ -53,6 +56,7 @@ baseFlags = [ 'BusBridge', 'Cache', 'Chains', + 'Checker', 'Clock', 'Commit', 'CommitRate', @@ -103,14 +107,19 @@ baseFlags = [ 'IdeDisk', 'InstExec', 'Interrupt', - 'LDSTQ', + 'LSQ', + 'LSQUnit', 'Loader', 'MC146818', 'MMU', 'MSHR', 'Mbox', 'MemDepUnit', - 'OoOCPU', + 'OzoneCPU', + 'FE', + 'IBE', + 'BE', + 'OzoneLSQ', 'PCEvent', 'PCIA', 'PCIDEV', @@ -146,6 +155,9 @@ baseFlags = [ 'Uart', 'VtoPhys', 'WriteBarrier', + 'Activity', + 'Scoreboard', + 'Writeback', ] # @@ -163,7 +175,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/src/base/userinfo.cc b/src/base/userinfo.cc index 15bd72224..62f7b7b5c 100644 --- a/src/base/userinfo.cc +++ b/src/base/userinfo.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/base/userinfo.hh b/src/base/userinfo.hh index d8ebd443c..a2838ab3e 100644 --- a/src/base/userinfo.hh +++ b/src/base/userinfo.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BASE_USERINFO_HH__ diff --git a/src/cpu/SConscript b/src/cpu/SConscript index 34fb6df78..34bad132c 100644 --- a/src/cpu/SConscript +++ b/src/cpu/SConscript @@ -25,6 +25,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt import os import os.path @@ -58,6 +60,14 @@ virtual Fault completeAcc(Packet *pkt, %s *xc, { panic("completeAcc not defined!"); }; ''' +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') @@ -108,6 +118,7 @@ if 'FastCPU' in env['CPU_MODELS']: if 'AlphaFullCPU' in env['CPU_MODELS']: sources += Split(''' + base_dyn_inst.cc o3/2bit_local_pred.cc o3/alpha_dyn_inst.cc o3/alpha_cpu.cc @@ -118,20 +129,46 @@ 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/lsq_unit.cc + ozone/lw_back_end.cc + ozone/lw_lsq.cc + ''') + +if 'CheckerCPU' in env['CPU_MODELS']: + sources += Split(''' + checker/cpu.cc + checker/o3_cpu_builder.cc + ''') + # FullCPU sources are included from m5/SConscript since they're not # below this point in the file hierarchy. diff --git a/src/cpu/activity.cc b/src/cpu/activity.cc new file mode 100644 index 000000000..9a0f6d98d --- /dev/null +++ b/src/cpu/activity.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "base/timebuf.hh" +#include "cpu/activity.hh" + +ActivityRecorder::ActivityRecorder(int num_stages, int longest_latency, + int activity) + : activityBuffer(longest_latency, 0), longestLatency(longest_latency), + activityCount(activity), numStages(num_stages) +{ + stageActive = new bool[numStages]; + memset(stageActive, 0, numStages); +} + +void +ActivityRecorder::activity() +{ + // If we've already recorded activity for this cycle, we don't + // want to increment the count any more. + if (activityBuffer[0]) { + return; + } + + activityBuffer[0] = true; + + ++activityCount; + + DPRINTF(Activity, "Activity: %i\n", activityCount); +} + +void +ActivityRecorder::advance() +{ + // If there's a 1 in the slot that is about to be erased once the + // time buffer advances, then decrement the activityCount. + if (activityBuffer[-longestLatency]) { + --activityCount; + + assert(activityCount >= 0); + + DPRINTF(Activity, "Activity: %i\n", activityCount); + + if (activityCount == 0) { + DPRINTF(Activity, "No activity left!\n"); + } + } + + activityBuffer.advance(); +} + +void +ActivityRecorder::activateStage(const int idx) +{ + // Increment the activity count if this stage wasn't already active. + if (!stageActive[idx]) { + ++activityCount; + + stageActive[idx] = true; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already active.\n", idx); + } + +// assert(activityCount < longestLatency + numStages + 1); +} + +void +ActivityRecorder::deactivateStage(const int idx) +{ + // Decrement the activity count if this stage was active. + 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); +} + +void +ActivityRecorder::reset() +{ + activityCount = 0; + memset(stageActive, 0, numStages); + for (int i = 0; i < longestLatency + 1; ++i) + activityBuffer.advance(); +} + +void +ActivityRecorder::dump() +{ + for (int i = 0; i <= longestLatency; ++i) { + cprintf("[Idx:%i %i] ", i, activityBuffer[-i]); + } + + cprintf("\n"); + + for (int i = 0; i < numStages; ++i) { + cprintf("[Stage:%i %i]\n", i, stageActive[i]); + } + + cprintf("\n"); + + cprintf("Activity count: %i\n", activityCount); +} + +void +ActivityRecorder::validate() +{ + int count = 0; + for (int i = 0; i <= longestLatency; ++i) { + if (activityBuffer[-i]) { + count++; + } + } + + for (int i = 0; i < numStages; ++i) { + if (stageActive[i]) { + count++; + } + } + + assert(count == activityCount); +} diff --git a/src/cpu/activity.hh b/src/cpu/activity.hh new file mode 100644 index 000000000..e99927339 --- /dev/null +++ b/src/cpu/activity.hh @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_ACTIVITY_HH__ +#define __CPU_ACTIVITY_HH__ + +#include "base/timebuf.hh" +#include "base/trace.hh" + +/** + * ActivityRecorder helper class that informs the CPU if it can switch + * over to being idle or not. It works by having a time buffer as + * long as any time buffer in the CPU, and the CPU and all of its + * stages inform the ActivityRecorder when they write to any time + * buffer. The ActivityRecorder marks a 1 in the "0" slot of the time + * buffer any time a stage writes to a time buffer, and it advances + * its time buffer at the same time as all other stages. The + * ActivityRecorder also records if a stage has activity to do next + * cycle. The recorder keeps a count of these two. Thus any time the + * count is non-zero, there is either communication still in flight, + * or activity that still must be done, meaning that the CPU can not + * idle. If count is zero, then the CPU can safely idle as it has no + * more outstanding work to do. + */ +class ActivityRecorder { + public: + ActivityRecorder(int num_stages, int longest_latency, int count); + + /** Records that there is activity this cycle. */ + void activity(); + + /** Advances the activity buffer, decrementing the activityCount + * if active communication just left the time buffer, and + * determining if there is no activity. + */ + void advance(); + + /** Marks a stage as active. */ + void activateStage(const int idx); + + /** Deactivates a stage. */ + void deactivateStage(const int idx); + + /** Returns how many things are active within the recorder. */ + int getActivityCount() { return activityCount; } + + /** Sets the count to a starting value. Can be used to disable + * the idling option. + */ + void setActivityCount(int count) + { activityCount = count; } + + /** Returns if the CPU should be active. */ + bool active() { return activityCount; } + + /** Clears the time buffer and the activity count. */ + void reset(); + + /** Debug function to dump the contents of the time buffer. */ + void dump(); + + /** Debug function to ensure that the activity count matches the + * contents of the time buffer. + */ + void validate(); + + 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 have a 1 somewhere in it only if there + * is active communication in a time buffer. + */ + TimeBuffer<bool> activityBuffer; + + /** Longest latency time buffer in the CPU. */ + int longestLatency; + + /** 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; + + /** Number of stages that can be marked as active or inactive. */ + int numStages; + + /** Records which stages are active/inactive. */ + bool *stageActive; +}; + +#endif // __CPU_ACTIVITY_HH__ diff --git a/src/cpu/base.cc b/src/cpu/base.cc index 9ce458c64..55c04c498 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include <iostream> @@ -35,7 +38,8 @@ #include "base/misc.hh" #include "base/output.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/cpuevent.hh" +#include "cpu/thread_context.hh" #include "cpu/profile.hh" #include "cpu/sampler/sampler.hh" #include "sim/param.hh" @@ -45,10 +49,6 @@ #include "base/trace.hh" -#if FULL_SYSTEM -#include "kern/kernel_stats.hh" -#endif - using namespace std; vector<BaseCPU *> BaseCPU::cpuList; @@ -89,8 +89,8 @@ BaseCPU::BaseCPU(Params *p) // if (p->max_insts_any_thread != 0) for (int i = 0; i < number_of_threads; ++i) - new SimExitEvent(comInstEventQueue[i], p->max_insts_any_thread, - "a thread reached the max instruction count"); + new SimLoopExitEvent(comInstEventQueue[i], p->max_insts_any_thread, + "a thread reached the max instruction count"); if (p->max_insts_all_threads != 0) { // allocate & initialize shared downcounter: each event will @@ -114,8 +114,8 @@ BaseCPU::BaseCPU(Params *p) // if (p->max_loads_any_thread != 0) for (int i = 0; i < number_of_threads; ++i) - new SimExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, - "a thread reached the max load count"); + new SimLoopExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, + "a thread reached the max load count"); if (p->max_loads_all_threads != 0) { // allocate & initialize shared downcounter: each event will @@ -153,8 +153,6 @@ BaseCPU::BaseCPU(Params *p) profileEvent = NULL; if (params->profile) profileEvent = new ProfileEvent(this, params->profile); - - kernelStats = new Kernel::Statistics(system); #endif } @@ -164,6 +162,7 @@ BaseCPU::Params::Params() #if FULL_SYSTEM profile = false; #endif + checker = NULL; } void @@ -174,17 +173,13 @@ BaseCPU::enableFunctionTrace() BaseCPU::~BaseCPU() { -#if FULL_SYSTEM - if (kernelStats) - delete kernelStats; -#endif } void BaseCPU::init() { if (!params->deferRegistration) - registerExecContexts(); + registerThreadContexts(); } void @@ -207,37 +202,35 @@ BaseCPU::regStats() .desc("number of cpu cycles simulated") ; - int size = execContexts.size(); + int size = threadContexts.size(); if (size > 1) { for (int i = 0; i < size; ++i) { stringstream namestr; ccprintf(namestr, "%s.ctx%d", name(), i); - execContexts[i]->regStats(namestr.str()); + threadContexts[i]->regStats(namestr.str()); } } else if (size == 1) - execContexts[0]->regStats(name()); + threadContexts[0]->regStats(name()); #if FULL_SYSTEM - if (kernelStats) - kernelStats->regStats(name() + ".kern"); #endif } void -BaseCPU::registerExecContexts() +BaseCPU::registerThreadContexts() { - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; #if FULL_SYSTEM int id = params->cpu_id; if (id != -1) id += i; - xc->setCpuId(system->registerExecContext(xc, id)); + tc->setCpuId(system->registerThreadContext(tc, id)); #else - xc->setCpuId(xc->getProcessPtr()->registerExecContext(xc)); + tc->setCpuId(tc->getProcessPtr()->registerThreadContext(tc)); #endif } } @@ -252,19 +245,22 @@ BaseCPU::switchOut(Sampler *sampler) void BaseCPU::takeOverFrom(BaseCPU *oldCPU) { - assert(execContexts.size() == oldCPU->execContexts.size()); + assert(threadContexts.size() == oldCPU->threadContexts.size()); + + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *newTC = threadContexts[i]; + ThreadContext *oldTC = oldCPU->threadContexts[i]; - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *newXC = execContexts[i]; - ExecContext *oldXC = oldCPU->execContexts[i]; + newTC->takeOverFrom(oldTC); - newXC->takeOverFrom(oldXC); - assert(newXC->readCpuId() == oldXC->readCpuId()); + CpuEvent::replaceThreadContext(oldTC, newTC); + + assert(newTC->readCpuId() == oldTC->readCpuId()); #if FULL_SYSTEM - system->replaceExecContext(newXC, newXC->readCpuId()); + system->replaceThreadContext(newTC, newTC->readCpuId()); #else - assert(newXC->getProcessPtr() == oldXC->getProcessPtr()); - newXC->getProcessPtr()->replaceExecContext(newXC, newXC->readCpuId()); + assert(newTC->getProcessPtr() == oldTC->getProcessPtr()); + newTC->getProcessPtr()->replaceThreadContext(newTC, newTC->readCpuId()); #endif } @@ -273,8 +269,8 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU) interrupts[i] = oldCPU->interrupts[i]; intstatus = oldCPU->intstatus; - for (int i = 0; i < execContexts.size(); ++i) - execContexts[i]->profileClear(); + for (int i = 0; i < threadContexts.size(); ++i) + threadContexts[i]->profileClear(); if (profileEvent) profileEvent->schedule(curTick); @@ -290,9 +286,9 @@ BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval) void BaseCPU::ProfileEvent::process() { - for (int i = 0, size = cpu->execContexts.size(); i < size; ++i) { - ExecContext *xc = cpu->execContexts[i]; - xc->profileSample(); + for (int i = 0, size = cpu->threadContexts.size(); i < size; ++i) { + ThreadContext *tc = cpu->threadContexts[i]; + tc->profileSample(); } schedule(curTick + interval); @@ -345,12 +341,6 @@ BaseCPU::serialize(std::ostream &os) { SERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels); SERIALIZE_SCALAR(intstatus); - -#if FULL_SYSTEM - if (kernelStats) - kernelStats->serialize(os); -#endif - } void @@ -358,11 +348,6 @@ BaseCPU::unserialize(Checkpoint *cp, const std::string §ion) { UNSERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels); UNSERIALIZE_SCALAR(intstatus); - -#if FULL_SYSTEM - if (kernelStats) - kernelStats->unserialize(cp, section); -#endif } #endif // FULL_SYSTEM diff --git a/src/cpu/base.hh b/src/cpu/base.hh index 79700c117..43122f238 100644 --- a/src/cpu/base.hh +++ b/src/cpu/base.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __CPU_BASE_HH__ @@ -38,10 +41,10 @@ #include "sim/sim_object.hh" #include "arch/isa_traits.hh" -class System; -namespace Kernel { class Statistics; } class BranchPred; -class ExecContext; +class CheckerCPU; +class ThreadContext; +class System; class BaseCPU : public SimObject { @@ -89,7 +92,7 @@ class BaseCPU : public SimObject #endif protected: - std::vector<ExecContext *> execContexts; + std::vector<ThreadContext *> threadContexts; public: @@ -125,6 +128,7 @@ class BaseCPU : public SimObject int cpu_id; Tick profile; #endif + BaseCPU *checker; Params(); }; @@ -140,7 +144,7 @@ class BaseCPU : public SimObject virtual void activateWhenReady(int tid) {}; - void registerExecContexts(); + void registerThreadContexts(); /// Prepare for another CPU to take over execution. When it is /// is ready (drained pipe) it signals the sampler. @@ -232,10 +236,6 @@ class BaseCPU : public SimObject public: // Number of CPU cycles simulated Stats::Scalar<> numCycles; - -#if FULL_SYSTEM - Kernel::Statistics *kernelStats; -#endif }; #endif // __CPU_BASE_HH__ diff --git a/src/cpu/base_dyn_inst.cc b/src/cpu/base_dyn_inst.cc index bf7c35cad..30fa10a6b 100644 --- a/src/cpu/base_dyn_inst.cc +++ b/src/cpu/base_dyn_inst.cc @@ -24,12 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#ifndef __CPU_BASE_DYN_INST_CC__ -#define __CPU_BASE_DYN_INST_CC__ - #include <iostream> +#include <set> #include <string> #include <sstream> @@ -38,11 +38,13 @@ #include "arch/faults.hh" #include "cpu/exetrace.hh" -#include "mem/mem_req.hh" +#include "mem/request.hh" #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 +56,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<const BaseDynInst *, const BaseDynInst *, MyHashFunc> my_hash_t; +typedef m5::hash_map<const BaseDynInst *, const BaseDynInst *, MyHashFunc> +my_hash_t; + my_hash_t thishash; #endif template <class Impl> -BaseDynInst<Impl>::BaseDynInst(MachInst machInst, Addr inst_PC, +BaseDynInst<Impl>::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 +87,7 @@ template <class Impl> BaseDynInst<Impl>::BaseDynInst(StaticInstPtr &_staticInst) : staticInst(_staticInst), traceData(NULL) { + seqNum = 0; initVars(); } @@ -90,46 +95,111 @@ template <class Impl> void BaseDynInst<Impl>::initVars() { - effAddr = MemReq::inval_addr; - physEffAddr = MemReq::inval_addr; + req = NULL; + memData = NULL; + effAddr = 0; + physEffAddr = 0; + storeSize = 0; readyRegs = 0; + // May want to turn this into a bit vector or something. completed = false; + resultReady = false; canIssue = false; 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; // Also make this a parameter, or perhaps get it from xc or cpu. asid = 0; - // Initialize the fault to be unimplemented opcode. - fault = new UnimplementedOpcodeFault; + // Initialize the fault to be NoFault. + 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 <class Impl> BaseDynInst<Impl>::~BaseDynInst() { + if (req) { + delete req; + } + + if (memData) { + delete [] memData; + } + + if (traceData) { + delete traceData; + } + + fault = NoFault; + --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 <class Impl> +void +BaseDynInst<Impl>::dumpSNList() +{ + std::set<InstSeqNum>::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 <class Impl> void BaseDynInst<Impl>::prefetch(Addr addr, unsigned flags) @@ -137,16 +207,16 @@ BaseDynInst<Impl>::prefetch(Addr addr, unsigned flags) // This is the "functional" implementation of prefetch. Not much // happens here since prefetches don't affect the architectural // 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,18 +232,10 @@ BaseDynInst<Impl>::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); } +*/ } template <class Impl> @@ -184,10 +246,11 @@ BaseDynInst<Impl>::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 @@ -203,6 +266,7 @@ BaseDynInst<Impl>::writeHint(Addr addr, int size, unsigned flags) storeSize = size; storeData = 0; +*/ } /** @@ -212,20 +276,23 @@ template <class Impl> Fault BaseDynInst<Impl>::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; +*/ + return NoFault; } /** @@ -235,24 +302,27 @@ template <class Impl> Fault BaseDynInst<Impl>::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); + 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); } return fault; +*/ + return NoFault; } template <class Impl> @@ -275,68 +345,28 @@ BaseDynInst<Impl>::dump(std::string &outstring) outstring = s.str(); } - -#if 0 template <class Impl> -Fault -BaseDynInst<Impl>::mem_access(mem_cmd cmd, Addr addr, void *p, int nbytes) +void +BaseDynInst<Impl>::markSrcRegReady() { - Fault fault; - - // check alignments, even speculative this test should always pass - if ((nbytes & nbytes - 1) != 0 || (addr & nbytes - 1) != 0) { - for (int i = 0; i < nbytes; i++) - ((char *) p)[i] = 0; - - // I added the following because according to the comment above, - // we should never get here. The comment lies -#if 0 - panic("unaligned access. Cycle = %n", curTick); -#endif - return NoFault; + if (++readyRegs == numSrcRegs()) { + canIssue = true; } +} - MemReqPtr req = new MemReq(addr, thread, nbytes); - switch(cmd) { - case Read: - fault = spec_mem->read(req, (uint8_t *)p); - break; - - case Write: - fault = spec_mem->write(req, (uint8_t *)p); - if (fault != NoFault) - break; - - specMemWrite = true; - storeSize = nbytes; - switch(nbytes) { - case sizeof(uint8_t): - *(uint8_t)&storeData = (uint8_t *)p; - break; - case sizeof(uint16_t): - *(uint16_t)&storeData = (uint16_t *)p; - break; - case sizeof(uint32_t): - *(uint32_t)&storeData = (uint32_t *)p; - break; - case sizeof(uint64_t): - *(uint64_t)&storeData = (uint64_t *)p; - break; - } - break; - - default: - fault = genMachineCheckFault(); - break; - } +template <class Impl> +void +BaseDynInst<Impl>::markSrcRegReady(RegIndex src_idx) +{ + ++readyRegs; - trace_mem(fault, cmd, addr, p, nbytes); + _readySrcRegIdx[src_idx] = true; - return fault; + if (readyRegs == numSrcRegs()) { + canIssue = true; + } } -#endif - template <class Impl> bool BaseDynInst<Impl>::eaSrcsReady() @@ -345,8 +375,7 @@ BaseDynInst<Impl>::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; } @@ -360,5 +389,18 @@ template class BaseDynInst<AlphaSimpleImpl>; template <> int BaseDynInst<AlphaSimpleImpl>::instcount = 0; +/* +// Forward declaration +template class BaseDynInst<SimpleImpl>; -#endif // __CPU_BASE_DYN_INST_CC__ +template <> +int +BaseDynInst<SimpleImpl>::instcount = 0; + +// Forward declaration +template class BaseDynInst<OzoneImpl>; + +template <> +int +BaseDynInst<OzoneImpl>::instcount = 0; +*/ diff --git a/src/cpu/base_dyn_inst.hh b/src/cpu/base_dyn_inst.hh index 3a7852f79..948ee058a 100644 --- a/src/cpu/base_dyn_inst.hh +++ b/src/cpu/base_dyn_inst.hh @@ -24,26 +24,32 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #ifndef __CPU_BASE_DYN_INST_HH__ #define __CPU_BASE_DYN_INST_HH__ +#include <list> #include <string> -#include <vector> +#include "arch/faults.hh" #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/op_class.hh" #include "cpu/static_inst.hh" +#include "mem/packet.hh" +#include "sim/system.hh" +/* #include "encumbered/cpu/full/bpred_update.hh" -#include "encumbered/cpu/full/op_class.hh" #include "encumbered/cpu/full/spec_memory.hh" #include "encumbered/cpu/full/spec_state.hh" #include "encumbered/mem/functional/main.hh" +*/ /** * @file @@ -59,20 +65,31 @@ 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 type. typedef TheISA::IntReg IntReg; + // Floating point register type. + typedef TheISA::FloatReg FloatReg; + + // The DynInstPtr type. + typedef typename Impl::DynInstPtr DynInstPtr; + + // The list of instructions iterator type. + typedef typename std::list<DynInstPtr>::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 +97,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 <class T> 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 <class T> Fault write(T data, Addr addr, unsigned flags, uint64_t *res); @@ -96,18 +129,24 @@ 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; + /** Is the instruction's result ready. */ + bool resultReady; + /** Can this instruction issue. */ bool canIssue; @@ -120,12 +159,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 +189,25 @@ 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; + /** Pointer to the thread state. */ + ImplState *thread; /** The kind of fault this instruction has generated. */ Fault fault; + /** The memory request. */ +// MemReqPtr req; + Request *req; +// Packet pkt; + + uint8_t *memData; + /** The effective virtual address (lds & stores only). */ Addr effAddr; @@ -197,17 +255,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 +288,20 @@ class BaseDynInst : public FastAlloc, public RefCounted void initVars(); public: - 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 + /** + * @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, +// MemCmd cmd, +// Addr addr, +// void *p, +// int nbytes); /** Dumps out contents of this BaseDynInst. */ void dump(); @@ -237,6 +315,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 +331,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. @@ -266,6 +343,8 @@ class BaseDynInst : public FastAlloc, public RefCounted bool isMemRef() const { return staticInst->isMemRef(); } bool isLoad() const { return staticInst->isLoad(); } bool isStore() const { return staticInst->isStore(); } + bool isStoreConditional() const + { return staticInst->isStoreConditional(); } bool isInstPrefetch() const { return staticInst->isInstPrefetch(); } bool isDataPrefetch() const { return staticInst->isDataPrefetch(); } bool isCopy() const { return staticInst->isCopy(); } @@ -280,9 +359,53 @@ 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(); } + bool isIprAccess() const { return staticInst->isIprAccess(); } + bool isUnverifiable() const { return staticInst->isUnverifiable(); } + + /** 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 +413,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 +425,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; } @@ -322,30 +439,42 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns the result of a floating point (double) instruction. */ double readDoubleResult() { return instResult.dbl; } - //Push to .cc file. - /** Records that one of the source registers is ready. */ - void markSrcRegReady() + void setIntReg(const StaticInst *si, int idx, uint64_t val) { - ++readyRegs; - if(readyRegs == numSrcRegs()) { - canIssue = true; - } + instResult.integer = val; } - /** Marks a specific register as ready. - * @todo: Move this to .cc file. - */ - void markSrcRegReady(RegIndex src_idx) + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) { - ++readyRegs; + if (width == 32) + instResult.fp = val; + else if (width == 64) + instResult.dbl = val; + else + panic("Unsupported width!"); + } - _readySrcRegIdx[src_idx] = 1; + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + instResult.fp = val; + } - if(readyRegs == numSrcRegs()) { - canIssue = true; - } + void setFloatRegBits(const StaticInst *si, int idx, uint64_t val, int width) + { + instResult.integer = val; } + void setFloatRegBits(const StaticInst *si, int idx, uint64_t val) + { + instResult.integer = val; + } + + /** Records that one of the source registers is ready. */ + void markSrcRegReady(); + + /** Marks a specific register as ready. */ + void markSrcRegReady(RegIndex src_idx); + /** Returns if a source register is ready. */ bool isReadySrcRegIdx(int idx) const { @@ -355,9 +484,13 @@ 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; } + void setResultReady() { resultReady = true; } + + bool isResultReady() const { return resultReady; } + /** Sets this instruction as ready to issue. */ void setCanIssue() { canIssue = true; } @@ -385,34 +518,97 @@ 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 setNextPC(uint64_t val) + { + nextPC = val; +// instResult.integer = 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. + /** Returns the thread context. */ - ExecContext *xcBase() { return cpuXC->getProxy(); } + ThreadContext *tcBase() { return thread->getTC(); } 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. */ @@ -431,12 +627,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<class Impl> @@ -444,34 +654,47 @@ template<class T> inline Fault BaseDynInst<Impl>::read(Addr addr, T &data, unsigned flags) { - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), sizeof(T), flags); - req->asid = asid; + // Sometimes reads will get retried, so they may come through here + // twice. + if (!req) { + req = new Request(); + req->setVirt(asid, addr, sizeof(T), flags, this->PC); + req->setThreadContext(thread->readCpuId(), threadNumber); + } else { + assert(addr == req->getVaddr()); + } - fault = cpu->translateDataReadReq(req); + if ((req->getVaddr() & (TheISA::VMPageSize - 1)) + req->getSize() > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } - // 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 + fault = cpu->translateDataReadReq(req, thread); if (fault == NoFault) { + effAddr = req->getVaddr(); + physEffAddr = req->getPaddr(); + memReqFlags = req->getFlags(); + +#if 0 + 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,36 +715,38 @@ BaseDynInst<Impl>::write(T data, Addr addr, unsigned flags, uint64_t *res) traceData->setData(data); } - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), sizeof(T), flags); - - req->asid = asid; + assert(req == NULL); - fault = cpu->translateDataWriteReq(req); + req = new Request(); + req->setVirt(asid, addr, sizeof(T), flags, this->PC); + req->setThreadContext(thread->readCpuId(), threadNumber); - // 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; + if ((req->getVaddr() & (TheISA::VMPageSize - 1)) + req->getSize() > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif + fault = cpu->translateDataWriteReq(req, thread); if (fault == NoFault) { + effAddr = req->getVaddr(); + physEffAddr = req->getPaddr(); + memReqFlags = req->getFlags(); +#if 0 + 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) { // always return some result to keep misspeculated paths // (which will ignore faults) deterministic - *res = (fault == NoFault) ? req->result : 0; + *res = (fault == NoFault) ? req->getScResult() : 0; } return fault; diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc new file mode 100644 index 000000000..ebc02f7be --- /dev/null +++ b/src/cpu/checker/cpu.cc @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include <list> +#include <string> + +#include "base/refcnt.hh" +#include "cpu/base.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" +#include "sim/byteswap.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha_impl.hh" + +//#include "cpu/ozone/dyn_inst.hh" +//#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +#if FULL_SYSTEM +#include "sim/system.hh" +#include "arch/vtophys.hh" +#endif // FULL_SYSTEM + +using namespace std; +//The CheckerCPU does alpha only +using namespace AlphaISA; + +void +CheckerCPU::init() +{ +} + +CheckerCPU::CheckerCPU(Params *p) + : BaseCPU(p), thread(NULL), tc(NULL) +{ + memReq = NULL; + + numInst = 0; + startNumInst = 0; + numLoad = 0; + startNumLoad = 0; + youngestSN = 0; + + changedPC = willChangePC = changedNextPC = false; + + exitOnError = p->exitOnError; +#if FULL_SYSTEM + itb = p->itb; + dtb = p->dtb; + systemPtr = NULL; +#else + process = p->process; +#endif +} + +CheckerCPU::~CheckerCPU() +{ +} + +void +CheckerCPU::setMemory(MemObject *mem) +{ +#if !FULL_SYSTEM + memPtr = mem; + thread = new SimpleThread(this, /* thread_num */ 0, process, + /* asid */ 0, mem); + + thread->setStatus(ThreadContext::Suspended); + tc = thread->getTC(); + threadContexts.push_back(tc); +#endif +} + +void +CheckerCPU::setSystem(System *system) +{ +#if FULL_SYSTEM + systemPtr = system; + + thread = new SimpleThread(this, 0, systemPtr, itb, dtb, false); + + thread->setStatus(ThreadContext::Suspended); + tc = thread->getTC(); + threadContexts.push_back(tc); + delete thread->kernelStats; + thread->kernelStats = NULL; +#endif +} + +void +CheckerCPU::setIcachePort(Port *icache_port) +{ + icachePort = icache_port; +} + +void +CheckerCPU::setDcachePort(Port *dcache_port) +{ + dcachePort = dcache_port; +} + +void +CheckerCPU::serialize(ostream &os) +{ +/* + BaseCPU::serialize(os); + SERIALIZE_SCALAR(inst); + nameOut(os, csprintf("%s.xc", name())); + thread->serialize(os); + cacheCompletionEvent.serialize(os); +*/ +} + +void +CheckerCPU::unserialize(Checkpoint *cp, const string §ion) +{ +/* + BaseCPU::unserialize(cp, section); + UNSERIALIZE_SCALAR(inst); + thread->unserialize(cp, csprintf("%s.xc", section)); +*/ +} + +Fault +CheckerCPU::copySrcTranslate(Addr src) +{ + panic("Unimplemented!"); +} + +Fault +CheckerCPU::copy(Addr dest) +{ + panic("Unimplemented!"); +} + +template <class T> +Fault +CheckerCPU::read(Addr addr, T &data, unsigned flags) +{ + // need to fill in CPU & thread IDs here + memReq = new Request(); + + memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + translateDataReadReq(memReq); + + Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast); + + pkt->dataStatic(&data); + + if (!(memReq->getFlags() & UNCACHEABLE)) { + // Access memory to see if we have the same data + dcachePort->sendFunctional(pkt); + } else { + // Assume the data is correct if it's an uncached access + memcpy(&data, &unverifiedResult.integer, sizeof(T)); + } + + delete pkt; + + return NoFault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +CheckerCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + +template <class T> +Fault +CheckerCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + // need to fill in CPU & thread IDs here + memReq = new Request(); + + memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + thread->translateDataWriteReq(memReq); + + // Can compare the write data and result only if it's cacheable, + // not a store conditional, or is a store conditional that + // succeeded. + // @todo: Verify that actual memory matches up with these values. + // Right now it only verifies that the instruction data is the + // same as what was in the request that got sent to memory; there + // is no verification that it is the same as what is in memory. + // This is because the LSQ would have to be snooped in the CPU to + // verify this data. + if (unverifiedReq && + !(unverifiedReq->getFlags() & UNCACHEABLE) && + (!(unverifiedReq->getFlags() & LOCKED) || + ((unverifiedReq->getFlags() & LOCKED) && + unverifiedReq->getScResult() == 1))) { + T inst_data; +/* + // This code would work if the LSQ allowed for snooping. + Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast); + pkt.dataStatic(&inst_data); + + dcachePort->sendFunctional(pkt); + + delete pkt; +*/ + memcpy(&inst_data, unverifiedMemData, sizeof(T)); + + if (data != inst_data) { + warn("%lli: Store value does not match value in memory! " + "Instruction: %#x, memory: %#x", + curTick, inst_data, data); + handleError(); + } + } + + // Assume the result was the same as the one passed in. This checker + // doesn't check if the SC should succeed or fail, it just checks the + // value. + if (res && unverifiedReq->scResultValid()) + *res = unverifiedReq->getScResult(); + + return NoFault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +CheckerCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +#if FULL_SYSTEM +Addr +CheckerCPU::dbg_vtophys(Addr addr) +{ + return vtophys(tc, addr); +} +#endif // FULL_SYSTEM + +bool +CheckerCPU::translateInstReq(Request *req) +{ +#if FULL_SYSTEM + return (thread->translateInstReq(req) == NoFault); +#else + thread->translateInstReq(req); + return true; +#endif +} + +void +CheckerCPU::translateDataReadReq(Request *req) +{ + thread->translateDataReadReq(req); + + if (req->getVaddr() != unverifiedReq->getVaddr()) { + warn("%lli: Request virtual addresses do not match! Inst: %#x, " + "checker: %#x", + curTick, unverifiedReq->getVaddr(), req->getVaddr()); + handleError(); + } + req->setPaddr(unverifiedReq->getPaddr()); + + if (checkFlags(req)) { + warn("%lli: Request flags do not match! Inst: %#x, checker: %#x", + curTick, unverifiedReq->getFlags(), req->getFlags()); + handleError(); + } +} + +void +CheckerCPU::translateDataWriteReq(Request *req) +{ + thread->translateDataWriteReq(req); + + if (req->getVaddr() != unverifiedReq->getVaddr()) { + warn("%lli: Request virtual addresses do not match! Inst: %#x, " + "checker: %#x", + curTick, unverifiedReq->getVaddr(), req->getVaddr()); + handleError(); + } + req->setPaddr(unverifiedReq->getPaddr()); + + if (checkFlags(req)) { + warn("%lli: Request flags do not match! Inst: %#x, checker: %#x", + curTick, unverifiedReq->getFlags(), req->getFlags()); + handleError(); + } +} + +bool +CheckerCPU::checkFlags(Request *req) +{ + // Remove any dynamic flags that don't have to do with the request itself. + unsigned flags = unverifiedReq->getFlags(); + unsigned mask = LOCKED | PHYSICAL | VPTE | ALTMODE | UNCACHEABLE | NO_FAULT; + flags = flags & (mask); + if (flags == req->getFlags()) { + return false; + } else { + return true; + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::tick(DynInstPtr &completed_inst) +{ + DynInstPtr inst; + + // Either check this instruction, or add it to a list of + // instructions waiting to be checked. Instructions must be + // checked in program order, so if a store has committed yet not + // completed, there may be some instructions that are waiting + // behind it that have completed and must be checked. + if (!instList.empty()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + + if (!instList.front()->isCompleted()) { + return; + } else { + inst = instList.front(); + instList.pop_front(); + } + } else { + if (!completed_inst->isCompleted()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + return; + } else { + if (youngestSN < completed_inst->seqNum) { + inst = completed_inst; + youngestSN = completed_inst->seqNum; + } else { + return; + } + } + } + + // Try to check all instructions that are completed, ending if we + // run out of instructions to check or if an instruction is not + // yet completed. + while (1) { + DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%#x.\n", + inst->seqNum, inst->readPC()); + unverifiedResult.integer = inst->readIntResult(); + unverifiedReq = inst->req; + unverifiedMemData = inst->memData; + numCycles++; + + Fault fault = NoFault; + + // maintain $r0 semantics + thread->setIntReg(ZeroReg, 0); +#ifdef TARGET_ALPHA + thread->setFloatRegDouble(ZeroReg, 0.0); +#endif // TARGET_ALPHA + + // Check if any recent PC changes match up with anything we + // expect to happen. This is mostly to check if traps or + // PC-based events have occurred in both the checker and CPU. + if (changedPC) { + DPRINTF(Checker, "Changed PC recently to %#x\n", + thread->readPC()); + if (willChangePC) { + if (newPC == thread->readPC()) { + DPRINTF(Checker, "Changed PC matches expected PC\n"); + } else { + warn("%lli: Changed PC does not match expected PC, " + "changed: %#x, expected: %#x", + curTick, thread->readPC(), newPC); + handleError(); + } + willChangePC = false; + } + changedPC = false; + } + if (changedNextPC) { + DPRINTF(Checker, "Changed NextPC recently to %#x\n", + thread->readNextPC()); + changedNextPC = false; + } + + // Try to fetch the instruction + +#if FULL_SYSTEM +#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 +#else +#define IFETCH_FLAGS(pc) 0 +#endif + + uint64_t fetch_PC = thread->readPC() & ~3; + + // set up memory request for instruction fetch + memReq = new Request(inst->threadNumber, fetch_PC, + sizeof(uint32_t), + IFETCH_FLAGS(thread->readPC()), + fetch_PC, thread->readCpuId(), inst->threadNumber); + + bool succeeded = translateInstReq(memReq); + + if (!succeeded) { + if (inst->getFault() == NoFault) { + // In this case the instruction was not a dummy + // instruction carrying an ITB fault. In the single + // threaded case the ITB should still be able to + // translate this instruction; in the SMT case it's + // possible that its ITB entry was kicked out. + warn("%lli: Instruction PC %#x was not found in the ITB!", + curTick, thread->readPC()); + handleError(); + + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); + + return; + } else { + // The instruction is carrying an ITB fault. Handle + // the fault and see if our results match the CPU on + // the next tick(). + fault = inst->getFault(); + } + } + + if (fault == NoFault) { + Packet *pkt = new Packet(memReq, Packet::ReadReq, + Packet::Broadcast); + + pkt->dataStatic(&machInst); + + icachePort->sendFunctional(pkt); + + delete pkt; + + // keep an instruction count + numInst++; + + // decode the instruction + machInst = gtoh(machInst); + // Checks that the instruction matches what we expected it to be. + // Checks both the machine instruction and the PC. + validateInst(inst); + + curStaticInst = StaticInst::decode(makeExtMI(machInst, + thread->readPC())); + +#if FULL_SYSTEM + thread->setInst(machInst); +#endif // FULL_SYSTEM + + fault = inst->getFault(); + } + + // Discard fetch's memReq. + delete memReq; + memReq = NULL; + + // Either the instruction was a fault and we should process the fault, + // or we should just go ahead execute the instruction. This assumes + // that the instruction is properly marked as a fault. + if (fault == NoFault) { + + thread->funcExeInst++; + + fault = curStaticInst->execute(this, NULL); + + // Checks to make sure instrution results are correct. + validateExecution(inst); + + if (curStaticInst->isLoad()) { + ++numLoad; + } + } + + if (fault != NoFault) { +#if FULL_SYSTEM + fault->invoke(tc); + willChangePC = true; + newPC = thread->readPC(); + DPRINTF(Checker, "Fault, PC is now %#x\n", newPC); +#else // !FULL_SYSTEM + fatal("fault (%d) detected @ PC 0x%08p", fault, thread->readPC()); +#endif // FULL_SYSTEM + } else { +#if THE_ISA != MIPS_ISA + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); +#else + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); +#endif + + } + +#if FULL_SYSTEM + // @todo: Determine if these should happen only if the + // instruction hasn't faulted. In the SimpleCPU case this may + // not be true, but in the O3 or Ozone case this may be true. + Addr oldpc; + int count = 0; + do { + oldpc = thread->readPC(); + system->pcEventQueue.service(tc); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + willChangePC = true; + newPC = thread->readPC(); + DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC); + } +#endif + + // @todo: Optionally can check all registers. (Or just those + // that have been modified). + validateState(); + + if (memReq) { + delete memReq; + memReq = NULL; + } + + // Continue verifying instructions if there's another completed + // instruction waiting to be verified. + if (instList.empty()) { + break; + } else if (instList.front()->isCompleted()) { + inst = instList.front(); + instList.pop_front(); + } else { + break; + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::switchOut(Sampler *s) +{ + instList.clear(); +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU) +{ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateInst(DynInstPtr &inst) +{ + if (inst->readPC() != thread->readPC()) { + warn("%lli: PCs do not match! Inst: %#x, checker: %#x", + curTick, inst->readPC(), thread->readPC()); + if (changedPC) { + warn("%lli: Changed PCs recently, may not be an error", + curTick); + } else { + handleError(); + } + } + + MachInst mi = static_cast<MachInst>(inst->staticInst->machInst); + + if (mi != machInst) { + warn("%lli: Binary instructions do not match! Inst: %#x, " + "checker: %#x", + curTick, mi, machInst); + handleError(); + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateExecution(DynInstPtr &inst) +{ + if (inst->numDestRegs()) { + // @todo: Support more destination registers. + if (inst->isUnverifiable()) { + // Unverifiable instructions assume they were executed + // properly by the CPU. Grab the result from the + // instruction and write it to the register. + RegIndex idx = inst->destRegIdx(0); + if (idx < TheISA::FP_Base_DepTag) { + thread->setIntReg(idx, inst->readIntResult()); + } else if (idx < TheISA::Fpcr_DepTag) { + thread->setFloatRegBits(idx, inst->readIntResult()); + } else { + thread->setMiscReg(idx, inst->readIntResult()); + } + } else if (result.integer != inst->readIntResult()) { + warn("%lli: Instruction results do not match! (Values may not " + "actually be integers) Inst: %#x, checker: %#x", + curTick, inst->readIntResult(), result.integer); + handleError(); + } + } + + if (inst->readNextPC() != thread->readNextPC()) { + warn("%lli: Instruction next PCs do not match! Inst: %#x, " + "checker: %#x", + curTick, inst->readNextPC(), thread->readNextPC()); + handleError(); + } + + // Checking side effect registers can be difficult if they are not + // checked simultaneously with the execution of the instruction. + // This is because other valid instructions may have modified + // these registers in the meantime, and their values are not + // stored within the DynInst. + while (!miscRegIdxs.empty()) { + int misc_reg_idx = miscRegIdxs.front(); + miscRegIdxs.pop(); + + if (inst->tcBase()->readMiscReg(misc_reg_idx) != + thread->readMiscReg(misc_reg_idx)) { + warn("%lli: Misc reg idx %i (side effect) does not match! " + "Inst: %#x, checker: %#x", + curTick, misc_reg_idx, + inst->tcBase()->readMiscReg(misc_reg_idx), + thread->readMiscReg(misc_reg_idx)); + handleError(); + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateState() +{ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::dumpInsts() +{ + int num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Completed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isCompleted()); + + cprintf("\n"); + + inst_list_it--; + ++num; + } + +} + +//template +//class Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >; + +template +class Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >; diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh new file mode 100644 index 000000000..c9986d228 --- /dev/null +++ b/src/cpu/checker/cpu.hh @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_CHECKER_CPU_HH__ +#define __CPU_CHECKER_CPU_HH__ + +#include <list> +#include <queue> +#include <map> + +#include "arch/types.hh" +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/simple_thread.hh" +#include "cpu/pc_event.hh" +#include "cpu/static_inst.hh" +#include "sim/eventq.hh" + +// forward declarations +#if FULL_SYSTEM +class Processor; +class AlphaITB; +class AlphaDTB; +class PhysicalMemory; + +class RemoteGDB; +class GDBListener; + +#else + +class Process; + +#endif // FULL_SYSTEM +template <class> +class BaseDynInst; +class ThreadContext; +class MemInterface; +class Checkpoint; +class Request; +class Sampler; + +/** + * CheckerCPU class. Dynamically verifies instructions as they are + * completed by making sure that the instruction and its results match + * the independent execution of the benchmark inside the checker. The + * checker verifies instructions in order, regardless of the order in + * which instructions complete. There are certain results that can + * not be verified, specifically the result of a store conditional or + * the values of uncached accesses. In these cases, and with + * instructions marked as "IsUnverifiable", the checker assumes that + * the value from the main CPU's execution is correct and simply + * copies that value. It provides a CheckerThreadContext (see + * checker/thread_context.hh) that provides hooks for updating the + * Checker's state through any ThreadContext accesses. This allows the + * checker to be able to correctly verify instructions, even with + * external accesses to the ThreadContext that change state. + */ +class CheckerCPU : public BaseCPU +{ + protected: + typedef TheISA::MachInst MachInst; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + public: + virtual void init(); + + struct Params : public BaseCPU::Params + { +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#else + Process *process; +#endif + bool exitOnError; + }; + + public: + CheckerCPU(Params *p); + virtual ~CheckerCPU(); + + Process *process; + + void setMemory(MemObject *mem); + + MemObject *memPtr; + + void setSystem(System *system); + + System *systemPtr; + + void setIcachePort(Port *icache_port); + + Port *icachePort; + + void setDcachePort(Port *dcache_port); + + Port *dcachePort; + + public: + // Primary thread being run. + SimpleThread *thread; + + ThreadContext *tc; + + AlphaITB *itb; + AlphaDTB *dtb; + +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); +#endif + + union Result { + uint64_t integer; + float fp; + double dbl; + }; + + Result result; + + // current instruction + MachInst machInst; + + // Pointer to the one memory request. + RequestPtr memReq; + + StaticInstPtr curStaticInst; + + // number of simulated instructions + Counter numInst; + Counter startNumInst; + + std::queue<int> miscRegIdxs; + + virtual Counter totalInstructions() const + { + return numInst - startNumInst; + } + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + // These functions are only used in CPU models that split + // effective address computation from the actual memory access. + void setEA(Addr EA) { panic("SimpleCPU::setEA() not implemented\n"); } + Addr getEA() { panic("SimpleCPU::getEA() not implemented\n"); } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return thread->readIntReg(si->srcRegIdx(idx)); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx, width); + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx, width); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx); + } + + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + thread->setIntReg(si->destRegIdx(idx), val); + result.integer = val; + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val, width); + switch(width) { + case 32: + result.fp = val; + break; + case 64: + result.dbl = val; + break; + }; + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val); + result.fp = val; + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, + int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val, width); + result.integer = val; + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val); + result.integer = val; + } + + uint64_t readPC() { return thread->readPC(); } + + uint64_t readNextPC() { return thread->readNextPC(); } + + void setNextPC(uint64_t val) { + thread->setNextPC(val); + } + + MiscReg readMiscReg(int misc_reg) + { + return thread->readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return thread->readMiscRegWithEffect(misc_reg, fault); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + result.integer = val; + miscRegIdxs.push(misc_reg); + return thread->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + miscRegIdxs.push(misc_reg); + return thread->setMiscRegWithEffect(misc_reg, val); + } + + void recordPCChange(uint64_t val) { changedPC = true; } + void recordNextPCChange(uint64_t val) { changedNextPC = true; } + + bool translateInstReq(Request *req); + void translateDataWriteReq(Request *req); + void translateDataReadReq(Request *req); + +#if FULL_SYSTEM + Fault hwrei() { return thread->hwrei(); } + int readIntrFlag() { return thread->readIntrFlag(); } + void setIntrFlag(int val) { thread->setIntrFlag(val); } + bool inPalMode() { return thread->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(tc); } + bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } +#else + // Assume that the normal CPU's call to syscall was successful. + // The checker's state would have already been updated by the syscall. + void syscall(uint64_t callnum) { } +#endif + + void handleError() + { + if (exitOnError) + panic("Checker found error!"); + } + bool checkFlags(Request *req); + + ThreadContext *tcBase() { return tc; } + SimpleThread *threadBase() { return thread; } + + Result unverifiedResult; + Request *unverifiedReq; + uint8_t *unverifiedMemData; + + bool changedPC; + bool willChangePC; + uint64_t newPC; + bool changedNextPC; + bool exitOnError; + + InstSeqNum youngestSN; +}; + +/** + * Templated Checker class. This Checker class is templated on the + * DynInstPtr of the instruction type that will be verified. Proper + * template instantiations of the Checker must be placed at the bottom + * of checker/cpu.cc. + */ +template <class DynInstPtr> +class Checker : public CheckerCPU +{ + public: + Checker(Params *p) + : CheckerCPU(p) + { } + + void switchOut(Sampler *s); + void takeOverFrom(BaseCPU *oldCPU); + + void tick(DynInstPtr &inst); + + void validateInst(DynInstPtr &inst); + void validateExecution(DynInstPtr &inst); + void validateState(); + + std::list<DynInstPtr> instList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + void dumpInsts(); +}; + +#endif // __CPU_CHECKER_CPU_HH__ diff --git a/src/cpu/checker/cpu_builder.cc b/src/cpu/checker/cpu_builder.cc new file mode 100644 index 000000000..3b7583294 --- /dev/null +++ b/src/cpu/checker/cpu_builder.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "mem/base_mem.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class OzoneChecker : public Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > > +{ + public: + OzoneChecker(Params *p) + : Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<FunctionalMemory *> mem; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + SimObjectParam<BaseMem *> icache; + SimObjectParam<BaseMem *> dcache; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(icache, "L1 instruction cache object"), + INIT_PARAM(dcache, "L1 data cache object"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + +CREATE_SIM_OBJECT(OzoneChecker) +{ + OzoneChecker::Params *params = new OzoneChecker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->exitOnError = exitOnError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + BaseMem *cache = icache; + cache = dcache; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->mem = mem; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + OzoneChecker *cpu = new OzoneChecker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("OzoneChecker", OzoneChecker) diff --git a/src/cpu/checker/o3_cpu_builder.cc b/src/cpu/checker/o3_cpu_builder.cc new file mode 100644 index 000000000..59a6c7158 --- /dev/null +++ b/src/cpu/checker/o3_cpu_builder.cc @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha_impl.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class MemObject; + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class O3Checker : public Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > > +{ + public: + O3Checker(Params *p) + : Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(O3Checker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(O3Checker) + + +CREATE_SIM_OBJECT(O3Checker) +{ + O3Checker::Params *params = new O3Checker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->exitOnError = exitOnError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + O3Checker *cpu = new O3Checker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("O3Checker", O3Checker) diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh new file mode 100644 index 000000000..c0ac8f01d --- /dev/null +++ b/src/cpu/checker/thread_context.hh @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_CHECKER_THREAD_CONTEXT_HH__ +#define __CPU_CHECKER_THREAD_CONTEXT_HH__ + +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +/** + * Derived ThreadContext class for use with the Checker. The template + * parameter is the ThreadContext class used by the specific CPU being + * verified. This CheckerThreadContext is then used by the main CPU + * in place of its usual ThreadContext class. It handles updating the + * checker's state any time state is updated externally through the + * ThreadContext. + */ +template <class TC> +class CheckerThreadContext : public ThreadContext +{ + public: + CheckerThreadContext(TC *actual_tc, + CheckerCPU *checker_cpu) + : actualTC(actual_tc), checkerTC(checker_cpu->thread), + checkerCPU(checker_cpu) + { } + + private: + /** The main CPU's ThreadContext, or class that implements the + * ThreadContext interface. */ + TC *actualTC; + /** The checker's own SimpleThread. Will be updated any time + * anything uses this ThreadContext to externally update a + * thread's state. */ + SimpleThread *checkerTC; + /** Pointer to the checker CPU. */ + CheckerCPU *checkerCPU; + + public: + + BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } + + void setCpuId(int id) + { + actualTC->setCpuId(id); + checkerTC->setCpuId(id); + } + + int readCpuId() { return actualTC->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return actualTC->getSystemPtr(); } + + PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); } + + AlphaITB *getITBPtr() { return actualTC->getITBPtr(); } + + AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); } + + Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } + + FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) + { return actualTC->getVirtPort(); } + + void delVirtPort(VirtualPort *vp) { actualTC->delVirtPort(vp); } +#else + TranslatingPort *getMemPort() { return actualTC->getMemPort(); } + + Process *getProcessPtr() { return actualTC->getProcessPtr(); } +#endif + + Status status() const { return actualTC->status(); } + + void setStatus(Status new_status) + { + actualTC->setStatus(new_status); + checkerTC->setStatus(new_status); + } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1) { actualTC->activate(delay); } + + /// Set the status to Suspended. + void suspend() { actualTC->suspend(); } + + /// Set the status to Unallocated. + void deallocate() { actualTC->deallocate(); } + + /// Set the status to Halted. + void halt() { actualTC->halt(); } + +#if FULL_SYSTEM + void dumpFuncProfile() { actualTC->dumpFuncProfile(); } +#endif + + void takeOverFrom(ThreadContext *oldContext) + { + actualTC->takeOverFrom(oldContext); + checkerTC->takeOverFrom(oldContext); + } + + void regStats(const std::string &name) { actualTC->regStats(name); } + + void serialize(std::ostream &os) { actualTC->serialize(os); } + void unserialize(Checkpoint *cp, const std::string §ion) + { actualTC->unserialize(cp, section); } + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); } + + Tick readLastActivate() { return actualTC->readLastActivate(); } + Tick readLastSuspend() { return actualTC->readLastSuspend(); } + + void profileClear() { return actualTC->profileClear(); } + void profileSample() { return actualTC->profileSample(); } +#endif + + int getThreadNum() { return actualTC->getThreadNum(); } + + // @todo: Do I need this? + MachInst getInst() { return actualTC->getInst(); } + + // @todo: Do I need this? + void copyArchRegs(ThreadContext *tc) + { + actualTC->copyArchRegs(tc); + checkerTC->copyArchRegs(tc); + } + + void clearArchRegs() + { + actualTC->clearArchRegs(); + checkerTC->clearArchRegs(); + } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { return actualTC->readIntReg(reg_idx); } + + FloatReg readFloatReg(int reg_idx, int width) + { return actualTC->readFloatReg(reg_idx, width); } + + FloatReg readFloatReg(int reg_idx) + { return actualTC->readFloatReg(reg_idx); } + + FloatRegBits readFloatRegBits(int reg_idx, int width) + { return actualTC->readFloatRegBits(reg_idx, width); } + + FloatRegBits readFloatRegBits(int reg_idx) + { return actualTC->readFloatRegBits(reg_idx); } + + void setIntReg(int reg_idx, uint64_t val) + { + actualTC->setIntReg(reg_idx, val); + checkerTC->setIntReg(reg_idx, val); + } + + void setFloatReg(int reg_idx, FloatReg val, int width) + { + actualTC->setFloatReg(reg_idx, val, width); + checkerTC->setFloatReg(reg_idx, val, width); + } + + void setFloatReg(int reg_idx, FloatReg val) + { + actualTC->setFloatReg(reg_idx, val); + checkerTC->setFloatReg(reg_idx, val); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width) + { + actualTC->setFloatRegBits(reg_idx, val, width); + checkerTC->setFloatRegBits(reg_idx, val, width); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val) + { + actualTC->setFloatRegBits(reg_idx, val); + checkerTC->setFloatRegBits(reg_idx, val); + } + + uint64_t readPC() { return actualTC->readPC(); } + + void setPC(uint64_t val) + { + actualTC->setPC(val); + checkerTC->setPC(val); + checkerCPU->recordPCChange(val); + } + + uint64_t readNextPC() { return actualTC->readNextPC(); } + + void setNextPC(uint64_t val) + { + actualTC->setNextPC(val); + checkerTC->setNextPC(val); + checkerCPU->recordNextPCChange(val); + } + + uint64_t readNextNPC() { return actualTC->readNextNPC(); } + + void setNextNPC(uint64_t val) + { + actualTC->setNextNPC(val); + checkerTC->setNextNPC(val); + checkerCPU->recordNextPCChange(val); + } + + MiscReg readMiscReg(int misc_reg) + { return actualTC->readMiscReg(misc_reg); } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return actualTC->readMiscRegWithEffect(misc_reg, fault); } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + checkerTC->setMiscReg(misc_reg, val); + return actualTC->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + checkerTC->setMiscRegWithEffect(misc_reg, val); + return actualTC->setMiscRegWithEffect(misc_reg, val); + } + + unsigned readStCondFailures() + { return actualTC->readStCondFailures(); } + + void setStCondFailures(unsigned sc_failures) + { + checkerTC->setStCondFailures(sc_failures); + actualTC->setStCondFailures(sc_failures); + } +#if FULL_SYSTEM + bool inPalMode() { return actualTC->inPalMode(); } +#endif + + // @todo: Fix this! + bool misspeculating() { return actualTC->misspeculating(); } + +#if !FULL_SYSTEM + IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, IntReg val) + { + checkerTC->setSyscallArg(i, val); + actualTC->setSyscallArg(i, val); + } + + void setSyscallReturn(SyscallReturn return_value) + { + checkerTC->setSyscallReturn(return_value); + actualTC->setSyscallReturn(return_value); + } + + Counter readFuncExeInst() { return actualTC->readFuncExeInst(); } +#endif + void changeRegFileContext(RegFile::ContextParam param, + RegFile::ContextVal val) + { + actualTC->changeRegFileContext(param, val); + checkerTC->changeRegFileContext(param, val); + } +}; + +#endif // __CPU_CHECKER_EXEC_CONTEXT_HH__ diff --git a/src/cpu/cpu_models.py b/src/cpu/cpu_models.py index 8d0a15f61..1a9724ca6 100644 --- a/src/cpu/cpu_models.py +++ b/src/cpu/cpu_models.py @@ -23,6 +23,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt ################ # CpuModel class @@ -68,4 +70,13 @@ CpuModel('FullCPU', 'full_cpu_exec.cc', CpuModel('AlphaFullCPU', 'alpha_o3_exec.cc', '#include "cpu/o3/alpha_dyn_inst.hh"', { 'CPU_exec_context': 'AlphaDynInst<AlphaSimpleImpl>' }) +CpuModel('OzoneSimpleCPU', 'ozone_simple_exec.cc', + '#include "cpu/ozone/dyn_inst.hh"', + { 'CPU_exec_context': 'OzoneDynInst<SimpleImpl>' }) +CpuModel('OzoneCPU', 'ozone_exec.cc', + '#include "cpu/ozone/dyn_inst.hh"', + { 'CPU_exec_context': 'OzoneDynInst<OzoneImpl>' }) +CpuModel('CheckerCPU', 'checker_cpu_exec.cc', + '#include "cpu/checker/cpu.hh"', + { 'CPU_exec_context': 'CheckerCPU' }) diff --git a/src/cpu/cpuevent.cc b/src/cpu/cpuevent.cc new file mode 100644 index 000000000..679244a6b --- /dev/null +++ b/src/cpu/cpuevent.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Ali Saidi + */ + +#include "cpu/cpuevent.hh" + +/** Static list of all CpuEvent objects so we can modify their thread + * contexts as needed. */ +CpuEvent::CpuEventList CpuEvent::cpuEventList; + +CpuEvent::~CpuEvent() +{ + CpuEventList::iterator i; + + // delete the event from the global list + for (i = cpuEventList.begin(); i != cpuEventList.end(); ) { + if (*i == this) + i = cpuEventList.erase(i); + else + i++; + } +} + +void +CpuEvent::replaceThreadContext(ThreadContext *oldTc, ThreadContext *newTc) +{ + CpuEventList::iterator i; + + // Update any events that have the old thread context with the new thread + // context + for (i = cpuEventList.begin(); i != cpuEventList.end(); i++) { + if ((*i)->tc == oldTc) + (*i)->tc = newTc; + } +} diff --git a/src/cpu/cpuevent.hh b/src/cpu/cpuevent.hh new file mode 100644 index 000000000..11ac7aafb --- /dev/null +++ b/src/cpu/cpuevent.hh @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Ali Saidi + */ + +#ifndef __CPU_CPUEVENT_HH__ +#define __CPU_CPUEVENT_HH__ + +#include <vector> +#include "sim/eventq.hh" + +class ThreadContext; + +/** This class creates a global list of events than need a pointer to an + * thread context. When a switchover takes place the events can be migrated + * to the new thread context, otherwise you could have a wake timer interrupt + * go off on a switched out cpu or other unfortunate events. This object MUST be + * dynamically allocated to avoid it being deleted after a cpu switch happens. + * */ +class CpuEvent : public Event +{ + private: + /** type of global list of cpu events. */ + typedef std::vector<CpuEvent *> CpuEventList; + + /** Static list of cpu events that is searched every time a cpu switch + * happens. */ + static CpuEventList cpuEventList; + + /** The thread context that is switched to the new cpus. */ + ThreadContext *tc; + + public: + CpuEvent(EventQueue *q, ThreadContext *_tc, Priority p = Default_Pri) + : Event(q, p), tc(_tc) + { cpuEventList.push_back(this); } + + /** delete the cpu event from the global list. */ + ~CpuEvent(); + + /** Update all events switching old tc to new tc. + * @param oldTc the old thread context we are switching from + * @param newTc the new thread context we are switching to. + */ + static void replaceThreadContext(ThreadContext *oldTc, + ThreadContext *newTc); +}; + +template <class T, void (T::* F)(ThreadContext *tc)> +class CpuEventWrapper : public CpuEvent +{ + private: + T *object; + + public: + CpuEventWrapper(T *obj, ThreadContext *_tc, EventQueue *q = &mainEventQueue, + Priority p = Default_Pri) + : CpuEvent(q, _tc, p), object(obj) + { } + void process() { (object->*F)(tc); } +}; + +#endif // __CPU_CPUEVENT_HH__ + diff --git a/src/cpu/exetrace.cc b/src/cpu/exetrace.cc index 0ed3b43c4..7fdad5113 100644 --- a/src/cpu/exetrace.cc +++ b/src/cpu/exetrace.cc @@ -24,6 +24,11 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + * Steve Raasch */ #include <fstream> @@ -82,7 +87,8 @@ Trace::InstRecord::dump(ostream &outs) std::string sym_str; Addr sym_addr; if (debugSymbolTable - && debugSymbolTable->findNearestSymbol(PC, sym_str, sym_addr)) { + && debugSymbolTable->findNearestSymbol(PC, sym_str, sym_addr) + && flags[PC_SYMBOL]) { if (PC != sym_addr) sym_str += csprintf("+%d", PC - sym_addr); outs << "@" << sym_str << " : "; @@ -190,6 +196,8 @@ Param<bool> exe_trace_print_fetchseq(&exeTraceParams, "print_fetchseq", "print fetch sequence number", false); Param<bool> exe_trace_print_cp_seq(&exeTraceParams, "print_cpseq", "print correct-path sequence number", false); +Param<bool> exe_trace_pc_symbol(&exeTraceParams, "pc_symbol", + "Use symbols for the PC if available", true); Param<bool> exe_trace_intel_format(&exeTraceParams, "intel_format", "print trace in intel compatible format", false); Param<string> exe_trace_system(&exeTraceParams, "trace_system", @@ -214,6 +222,7 @@ Trace::InstRecord::setParams() flags[PRINT_INT_REGS] = exe_trace_print_iregs; flags[PRINT_FETCH_SEQ] = exe_trace_print_fetchseq; flags[PRINT_CP_SEQ] = exe_trace_print_cp_seq; + flags[PC_SYMBOL] = exe_trace_pc_symbol; flags[INTEL_FORMAT] = exe_trace_intel_format; trace_system = exe_trace_system; } diff --git a/src/cpu/exetrace.hh b/src/cpu/exetrace.hh index a26cdc517..95f8b449c 100644 --- a/src/cpu/exetrace.hh +++ b/src/cpu/exetrace.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __EXETRACE_HH__ @@ -35,7 +38,7 @@ #include "sim/host.hh" #include "cpu/inst_seq.hh" // for InstSeqNum #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/static_inst.hh" class BaseCPU; @@ -144,6 +147,7 @@ class InstRecord : public Record PRINT_INT_REGS, PRINT_FETCH_SEQ, PRINT_CP_SEQ, + PC_SYMBOL, INTEL_FORMAT, NUM_BITS }; @@ -169,14 +173,14 @@ InstRecord::setRegs(const IntRegFile ®s) inline InstRecord * -getInstRecord(Tick cycle, ExecContext *xc, BaseCPU *cpu, +getInstRecord(Tick cycle, ThreadContext *tc, BaseCPU *cpu, const StaticInstPtr staticInst, Addr pc, int thread = 0) { if (DTRACE(InstExec) && - (InstRecord::traceMisspec() || !xc->misspeculating())) { + (InstRecord::traceMisspec() || !tc->misspeculating())) { return new InstRecord(cycle, cpu, staticInst, pc, - xc->misspeculating(), thread); + tc->misspeculating(), thread); } return NULL; diff --git a/src/cpu/inst_seq.hh b/src/cpu/inst_seq.hh index 8de047af7..e7acd215f 100644 --- a/src/cpu/inst_seq.hh +++ b/src/cpu/inst_seq.hh @@ -24,11 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Nathan Binkert */ #ifndef __STD_TYPES_HH__ #define __STD_TYPES_HH__ +#include <stdint.h> + // inst sequence type, used to order instructions in the ready list, // if this rolls over the ready list order temporarily will get messed // up, but execution will continue and complete correctly diff --git a/src/cpu/intr_control.cc b/src/cpu/intr_control.cc index 43e7f654c..4cbc86891 100644 --- a/src/cpu/intr_control.cc +++ b/src/cpu/intr_control.cc @@ -24,13 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ron Dreslinski */ #include <string> #include <vector> #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/intr_control.hh" #include "sim/builder.hh" #include "sim/sim_object.hh" @@ -48,32 +51,32 @@ IntrControl::IntrControl(const string &name, BaseCPU *c) void IntrControl::post(int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[0]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[0]->getCpuPtr(); temp->post_interrupt(int_num, index); } void IntrControl::post(int cpu_id, int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[cpu_id]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[cpu_id]->getCpuPtr(); temp->post_interrupt(int_num, index); } void IntrControl::clear(int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[0]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[0]->getCpuPtr(); temp->clear_interrupt(int_num, index); } void IntrControl::clear(int cpu_id, int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[cpu_id]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[cpu_id]->getCpuPtr(); temp->clear_interrupt(int_num, index); } diff --git a/src/cpu/intr_control.hh b/src/cpu/intr_control.hh index 5ec4e14cb..2e3f9e038 100644 --- a/src/cpu/intr_control.hh +++ b/src/cpu/intr_control.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ron Dreslinski */ #ifndef __INTR_CONTROL_HH__ diff --git a/src/cpu/memtest/memtest.cc b/src/cpu/memtest/memtest.cc index 54def1012..7ea9eaefc 100644 --- a/src/cpu/memtest/memtest.cc +++ b/src/cpu/memtest/memtest.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt */ // FIX ME: make trackBlkAddr use blocksize from actual cache, not hard coded @@ -35,7 +38,7 @@ #include "base/misc.hh" #include "base/statistics.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/simple_thread.hh" #include "cpu/memtest/memtest.hh" #include "mem/cache/base_cache.hh" #include "sim/builder.hh" @@ -78,7 +81,7 @@ MemTest::MemTest(const string &name, vector<string> cmd; cmd.push_back("/bin/ls"); vector<string> null_vec; - cpuXC = new CPUExecContext(NULL, 0, mainMem, 0); + thread = new SimpleThread(NULL, 0, mainMem, 0); blockSize = cacheInterface->getBlockSize(); blockAddrMask = blockSize - 1; @@ -268,7 +271,7 @@ MemTest::tick() req->data = new uint8_t[req->size]; req->paddr &= ~(req->size - 1); req->time = curTick; - req->xc = cpuXC->getProxy(); + req->xc = thread->getProxy(); if (cmd < percentReads) { // read diff --git a/src/cpu/memtest/memtest.hh b/src/cpu/memtest/memtest.hh index cdb40a26a..42fb235db 100644 --- a/src/cpu/memtest/memtest.hh +++ b/src/cpu/memtest/memtest.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt */ #ifndef __CPU_MEMTEST_MEMTEST_HH__ @@ -39,7 +42,7 @@ #include "sim/sim_object.hh" #include "sim/stats.hh" -class ExecContext; +class ThreadContext; class MemTest : public SimObject { public: @@ -83,7 +86,7 @@ class MemTest : public SimObject MemInterface *cacheInterface; FunctionalMemory *mainMem; FunctionalMemory *checkMem; - CPUExecContext *cpuXC; + SimpleThread *thread; unsigned size; // size of testing memory region diff --git a/src/cpu/o3/2bit_local_pred.cc b/src/cpu/o3/2bit_local_pred.cc index d9744eec7..77a45ea26 100644 --- a/src/cpu/o3/2bit_local_pred.cc +++ b/src/cpu/o3/2bit_local_pred.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,29 +24,41 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ +#include "base/intmath.hh" +#include "base/misc.hh" #include "base/trace.hh" #include "cpu/o3/2bit_local_pred.hh" -DefaultBP::DefaultBP(unsigned _localPredictorSize, - unsigned _localCtrBits, - unsigned _instShiftAmt) +LocalBP::LocalBP(unsigned _localPredictorSize, + unsigned _localCtrBits, + unsigned _instShiftAmt) : localPredictorSize(_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", @@ -58,24 +70,30 @@ DefaultBP::DefaultBP(unsigned _localPredictorSize, instShiftAmt); } +void +LocalBP::reset() +{ + for (int i = 0; i < localPredictorSets; ++i) { + localCtrs[i].reset(); + } +} + bool -DefaultBP::lookup(Addr &branch_addr) +LocalBP::lookup(Addr &branch_addr, void * &bp_history) { bool taken; - uint8_t local_prediction; + uint8_t counter_val; unsigned local_predictor_idx = getLocalIndex(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(); + counter_val = localCtrs[local_predictor_idx].read(); DPRINTF(Fetch, "Branch predictor: prediction is %i.\n", - (int)local_prediction); + (int)counter_val); - taken = getPrediction(local_prediction); + taken = getPrediction(counter_val); #if 0 // Speculative update. @@ -92,8 +110,9 @@ DefaultBP::lookup(Addr &branch_addr) } void -DefaultBP::update(Addr &branch_addr, bool taken) +LocalBP::update(Addr &branch_addr, bool taken, void *bp_history) { + assert(bp_history == NULL); unsigned local_predictor_idx; // Update the local predictor. @@ -102,8 +121,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(); @@ -115,7 +132,7 @@ DefaultBP::update(Addr &branch_addr, bool taken) inline bool -DefaultBP::getPrediction(uint8_t &count) +LocalBP::getPrediction(uint8_t &count) { // Get the MSB of the count return (count >> (localCtrBits - 1)); @@ -123,7 +140,7 @@ DefaultBP::getPrediction(uint8_t &count) inline unsigned -DefaultBP::getLocalIndex(Addr &branch_addr) +LocalBP::getLocalIndex(Addr &branch_addr) { return (branch_addr >> instShiftAmt) & indexMask; } diff --git a/src/cpu/o3/2bit_local_pred.hh b/src/cpu/o3/2bit_local_pred.hh index 97433e542..0a2a71d3e 100644 --- a/src/cpu/o3/2bit_local_pred.hh +++ b/src/cpu/o3/2bit_local_pred.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,43 +24,65 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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" -class DefaultBP +#include <vector> + +/** + * Implements a local predictor that uses the PC to index into a table of + * counters. Note that any time a pointer to the bp_history is given, it + * should be NULL using this predictor because it does not have any branch + * predictor state that needs to be recorded or updated; the update can be + * determined solely by the branch being taken or not taken. + */ +class LocalBP { 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); + LocalBP(unsigned localPredictorSize, unsigned localCtrBits, + unsigned instShiftAmt); /** * Looks up the given address in the branch predictor and returns * a true/false value as to whether it is taken. * @param branch_addr The address of the branch to look up. + * @param bp_history Pointer to any bp history state. * @return Whether or not the branch is taken. */ - bool lookup(Addr &branch_addr); + bool lookup(Addr &branch_addr, void * &bp_history); /** * Updates the branch predictor with the actual result of a branch. * @param branch_addr The address of the branch to update. * @param taken Whether or not the branch was taken. */ - void update(Addr &branch_addr, bool taken); + void update(Addr &branch_addr, bool taken, void *bp_history); - private: + void squash(void *bp_history) + { assert(bp_history == NULL); } - /** Returns the taken/not taken prediction given the value of the + void reset(); + + private: + /** + * 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 +90,14 @@ class DefaultBP inline unsigned getLocalIndex(Addr &PC); /** Array of counters that make up the local predictor. */ - SatCounter *localCtrs; + std::vector<SatCounter> 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 +108,4 @@ class DefaultBP unsigned indexMask; }; -#endif // __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ +#endif // __CPU_O3_2BIT_LOCAL_PRED_HH__ diff --git a/src/cpu/o3/alpha_cpu.cc b/src/cpu/o3/alpha_cpu.cc index 7bc90dae6..39cae696b 100644 --- a/src/cpu/o3/alpha_cpu.cc +++ b/src/cpu/o3/alpha_cpu.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_impl.hh" diff --git a/src/cpu/o3/alpha_cpu.hh b/src/cpu/o3/alpha_cpu.hh index 8e1e0f42a..f81837f3c 100644 --- a/src/cpu/o3/alpha_cpu.hh +++ b/src/cpu/o3/alpha_cpu.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,268 +24,411 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// Todo: Find all the stuff in ExecContext and ev5 that needs to be -// specifically designed for this CPU. - -#ifndef __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ -#define __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ +#ifndef __CPU_O3_ALPHA_FULL_CPU_HH__ +#define __CPU_O3_ALPHA_FULL_CPU_HH__ -#include "cpu/o3/cpu.hh" #include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" +#include "cpu/o3/cpu.hh" #include "sim/byteswap.hh" +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +class TranslatingPort; + +/** + * AlphaFullCPU class. Derives from the FullO3CPU class, and + * implements all ISA and implementation specific functions of the + * CPU. This is the CPU class that is used for the SimObjects, and is + * what is given to the DynInsts. Most of its state exists in the + * FullO3CPU; the state is has is mainly for ISA specific + * functionality. + */ template <class Impl> class AlphaFullCPU : public FullO3CPU<Impl> { protected: typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; typedef TheISA::MiscReg MiscReg; typedef TheISA::RegFile RegFile; typedef TheISA::MiscRegFile MiscRegFile; public: + typedef O3ThreadState<Impl> ImplState; + typedef O3ThreadState<Impl> Thread; typedef typename Impl::Params Params; - public: - AlphaFullCPU(Params ¶ms); + /** Constructs an AlphaFullCPU with the given parameters. */ + AlphaFullCPU(Params *params); + + /** + * Derived ThreadContext class for use with the AlphaFullCPU. It + * provides the interface for any external objects to access a + * single thread's state and some general CPU state. Any time + * external objects try to update state through this interface, + * the CPU will create an event to squash all in-flight + * instructions in order to ensure state is maintained correctly. + * It must be defined specifically for the AlphaFullCPU because + * not all architectural state is located within the O3ThreadState + * (such as the commit PC, and registers), and specific actions + * must be taken when using this interface (such as squashing all + * in-flight instructions when doing a write to this interface). + */ + class AlphaTC : public ThreadContext + { + public: + /** Pointer to the CPU. */ + AlphaFullCPU<Impl> *cpu; + + /** Pointer to the thread state that this TC corrseponds to. */ + O3ThreadState<Impl> *thread; + + /** Returns a pointer to this CPU. */ + virtual BaseCPU *getCpuPtr() { return cpu; } + + /** Sets this CPU's ID. */ + virtual void setCpuId(int id) { cpu->cpu_id = id; } + + /** Reads this CPU's ID. */ + virtual int readCpuId() { return cpu->cpu_id; } + +#if FULL_SYSTEM + /** Returns a pointer to the system. */ + virtual System *getSystemPtr() { return cpu->system; } + + /** Returns a pointer to physical memory. */ + virtual PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + /** Returns a pointer to the ITB. */ + virtual AlphaITB *getITBPtr() { return cpu->itb; } + + /** Returns a pointer to the DTB. */ + virtual AlphaDTB *getDTBPtr() { return cpu->dtb; } + + /** Returns a pointer to this thread's kernel statistics. */ + virtual Kernel::Statistics *getKernelStats() + { return thread->kernelStats; } + + virtual FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + + virtual VirtualPort *getVirtPort(ThreadContext *src_tc = NULL); + + void delVirtPort(VirtualPort *vp); +#else + virtual TranslatingPort *getMemPort() { return thread->getMemPort(); } + + /** Returns a pointer to this thread's process. */ + virtual Process *getProcessPtr() { return thread->getProcessPtr(); } +#endif + /** Returns this thread's status. */ + virtual Status status() const { return thread->status(); } + + /** Sets this thread's 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 + /** Dumps the function profiling information. + * @todo: Implement. + */ + virtual void dumpFuncProfile(); +#endif + /** Takes over execution of a thread from another CPU. */ + virtual void takeOverFrom(ThreadContext *old_context); + + /** Registers statistics associated with this TC. */ + virtual void regStats(const std::string &name); + + /** Serializes state. */ + virtual void serialize(std::ostream &os); + /** Unserializes state. */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + /** Returns pointer to the quiesce event. */ + virtual EndQuiesceEvent *getQuiesceEvent(); + + /** Reads the last tick that this thread was activated on. */ + virtual Tick readLastActivate(); + /** Reads the last tick that this thread was suspended on. */ + virtual Tick readLastSuspend(); + + /** Clears the function profiling information. */ + virtual void profileClear(); + /** Samples the function profiling information. */ + virtual void profileSample(); +#endif + /** Returns this thread's ID number. */ + virtual int getThreadNum() { return thread->readTid(); } + + /** Returns the instruction this thread is currently committing. + * Only used when an instruction faults. + */ + virtual TheISA::MachInst getInst(); + + /** Copies the architectural registers from another TC into this TC. */ + virtual void copyArchRegs(ThreadContext *tc); + + /** Resets all architectural registers to 0. */ + virtual void clearArchRegs(); + + /** Reads an integer register. */ + virtual uint64_t readIntReg(int reg_idx); + + virtual FloatReg readFloatReg(int reg_idx, int width); + + virtual FloatReg readFloatReg(int reg_idx); + + virtual FloatRegBits readFloatRegBits(int reg_idx, int width); + + virtual FloatRegBits readFloatRegBits(int reg_idx); + + /** Sets an integer register to a value. */ + virtual void setIntReg(int reg_idx, uint64_t val); + + virtual void setFloatReg(int reg_idx, FloatReg val, int width); + + virtual void setFloatReg(int reg_idx, FloatReg val); + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val); + + /** Reads this thread's PC. */ + virtual uint64_t readPC() + { return cpu->readPC(thread->readTid()); } + + /** Sets this thread's PC. */ + virtual void setPC(uint64_t val); + + /** Reads this thread's next PC. */ + virtual uint64_t readNextPC() + { return cpu->readNextPC(thread->readTid()); } + + /** Sets this thread's next PC. */ + virtual void setNextPC(uint64_t val); + + virtual uint64_t readNextNPC() + { + panic("Alpha has no NextNPC!"); + return 0; + } + + virtual void setNextNPC(uint64_t val) + { } + + /** Reads a miscellaneous register. */ + virtual MiscReg readMiscReg(int misc_reg) + { return cpu->readMiscReg(misc_reg, thread->readTid()); } + + /** Reads a misc. register, including any side-effects the + * read might have as defined by the architecture. */ + virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return cpu->readMiscRegWithEffect(misc_reg, fault, thread->readTid()); } + + /** Sets a misc. register. */ + virtual Fault setMiscReg(int misc_reg, const MiscReg &val); + + /** Sets a misc. register, including any side-effects the + * write might have as defined by the architecture. */ + virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + /** Returns the number of consecutive store conditional failures. */ + // @todo: Figure out where these store cond failures should go. + virtual unsigned readStCondFailures() + { return thread->storeCondFailures; } + + /** Sets the number of consecutive store conditional failures. */ + virtual void setStCondFailures(unsigned sc_failures) + { thread->storeCondFailures = sc_failures; } + +#if FULL_SYSTEM + /** Returns if the thread is currently in PAL mode, based on + * the PC's value. */ + virtual bool inPalMode() + { return TheISA::PcPAL(cpu->readPC(thread->readTid())); } +#endif + // Only really makes sense for old CPU model. Lots of code + // outside the CPU still checks this function, so it will + // always return false to keep everything working. + /** Checks if the thread is misspeculating. Because it is + * very difficult to determine if the thread is + * misspeculating, this is set as false. */ + virtual bool misspeculating() { return false; } + +#if !FULL_SYSTEM + /** Gets a syscall argument by index. */ + virtual IntReg getSyscallArg(int i); + + /** Sets a syscall argument. */ + virtual void setSyscallArg(int i, IntReg val); + + /** Sets the syscall return value. */ + virtual void setSyscallReturn(SyscallReturn return_value); + + /** Executes a syscall in SE mode. */ + virtual void syscall(int64_t callnum) + { return cpu->syscall(callnum, thread->readTid()); } + + /** Reads the funcExeInst counter. */ + virtual Counter readFuncExeInst() { return thread->funcExeInst; } +#endif + virtual void changeRegFileContext(TheISA::RegFile::ContextParam param, + TheISA::RegFile::ContextVal val) + { panic("Not supported on Alpha!"); } + }; + +#if FULL_SYSTEM + /** ITB pointer. */ AlphaITB *itb; + /** DTB pointer. */ AlphaDTB *dtb; #endif - public: + /** Registers statistics. */ void regStats(); #if FULL_SYSTEM - //Note that the interrupt stuff from the base CPU might be somewhat - //ISA specific (ie NumInterruptLevels). These functions might not - //be needed in FullCPU though. -// void post_interrupt(int int_num, int index); -// void clear_interrupt(int int_num, int index); -// void clear_interrupts(); - - Fault translateInstReq(MemReqPtr &req) + /** Translates instruction requestion. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) { - return itb->translate(req); + return itb->translate(req, thread->getTC()); } - Fault translateDataReadReq(MemReqPtr &req) + /** Translates data read request. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) { - return dtb->translate(req, false); + return dtb->translate(req, thread->getTC(), false); } - Fault translateDataWriteReq(MemReqPtr &req) + /** Translates data write request. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) { - return dtb->translate(req, true); + return dtb->translate(req, thread->getTC(), true); } #else - Fault dummyTranslation(MemReqPtr &req) + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) { -#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; + return thread->getProcessPtr()->pTable->translate(req); } - Fault translateInstReq(MemReqPtr &req) + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) { - return dummyTranslation(req); + return thread->getProcessPtr()->pTable->translate(req); } - Fault translateDataReadReq(MemReqPtr &req) + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) { - return dummyTranslation(req); - } - - Fault translateDataWriteReq(MemReqPtr &req) - { - return dummyTranslation(req); + return thread->getProcessPtr()->pTable->translate(req); } #endif + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg, unsigned tid); - // 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 - // 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; - } + /** Reads a misc. register, including any side effects the read + * might have as defined by the architecture. + */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, unsigned tid); - Fault setMiscReg(int misc_reg, const MiscReg &val) - { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; - } + /** Sets a miscellaneous register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned tid); + + /** Sets a misc. register, including any side effects the write + * might have as defined by the architecture. + */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, unsigned tid); + + /** Initiates a squash of all in-flight instructions for a given + * thread. The source of the squash is an external update of + * state through the TC. + */ + void squashFromTC(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 + /** Posts an interrupt. */ + void post_interrupt(int int_num, int index); + /** Reads the interrupt flag. */ 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); - bool simPalCheck(int palFunc); + /** Traps to handle given fault. */ + void trap(Fault fault, unsigned tid); + bool simPalCheck(int palFunc, unsigned tid); + /** Processes any interrupts. */ void processInterrupts(); -#endif - - -#if !FULL_SYSTEM - // Need to change these into regfile calls that directly set a certain - // 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); - } - - // used to shift args for indirect syscall - void setSyscallArg(int i, IntReg val) - { - this->cpuXC->setIntReg(AlphaISA::ArgumentReg0 + i, val); - } - - 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); - } - } - - void syscall(short thread_num); - void squashStages(); - -#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 - template <class T> - Fault read(MemReqPtr &req, T &data) - { -#if FULL_SYSTEM && THE_ISA == ALPHA_ISA - if (req->flags & LOCKED) { - req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); - req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); - } +#if !FULL_SYSTEM + /** Executes a syscall. + * @todo: Determine if this needs to be virtual. + */ + void syscall(int64_t callnum, int tid); + /** Gets a syscall argument. */ + IntReg getSyscallArg(int i, int tid); + + /** Used to shift args for indirect syscall. */ + void setSyscallArg(int i, IntReg val, int tid); + + /** Sets the return value of a syscall. */ + void setSyscallReturn(SyscallReturn return_value, int tid); #endif - Fault error; - error = this->mem->read(req, data); - data = gtoh(data); - return error; - } - + /** CPU read function, forwards read to LSQ. */ template <class T> - Fault read(MemReqPtr &req, T &data, int load_idx) + Fault read(RequestPtr &req, T &data, int load_idx) { return this->iew.ldstQueue.read(req, data, load_idx); } + /** CPU write function, forwards write to LSQ. */ template <class T> - Fault write(MemReqPtr &req, T &data) - { -#if FULL_SYSTEM && THE_ISA == ALPHA_ISA - ExecContext *xc; - - // If this is a store conditional, act appropriately - if (req->flags & LOCKED) { - xc = req->xc; - - if (req->flags & UNCACHEABLE) { - // Don't update result register (see stq_c in isa_desc) - req->result = 2; - xc->setStCondFailures(0);//Needed? [RGD] - } else { - bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); - Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); - req->result = lock_flag; - if (!lock_flag || - ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { - xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); - xc->setStCondFailures(xc->readStCondFailures() + 1); - if (((xc->readStCondFailures()) % 100000) == 0) { - std::cerr << "Warning: " - << xc->readStCondFailures() - << " consecutive store conditional failures " - << "on cpu " << req->xc->readCpuId() - << std::endl; - } - return NoFault; - } - else xc->setStCondFailures(0); - } - } - - // Need to clear any locked flags on other proccessors for - // this address. Only do this for succsful Store Conditionals - // and all other stores (WH64?). Unsuccessful Store - // Conditionals would have returned above, and wouldn't fall - // through. - for (int i = 0; i < this->system->execContexts.size(); i++){ - xc = this->system->execContexts[i]; - if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == - (req->paddr & ~0xf)) { - xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); - } - } - -#endif - - return this->mem->write(req, (T)htog(data)); - } - - template <class T> - Fault write(MemReqPtr &req, T &data, int store_idx) + Fault write(RequestPtr &req, T &data, int store_idx) { return this->iew.ldstQueue.write(req, data, store_idx); } + Addr lockAddr; + + /** Temporary fix for the lock flag, works in the UP case. */ + bool lockFlag; }; -#endif // __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ +#endif // __CPU_O3_ALPHA_FULL_CPU_HH__ diff --git a/src/cpu/o3/alpha_cpu_builder.cc b/src/cpu/o3/alpha_cpu_builder.cc index 6025b8ef2..828977ccb 100644 --- a/src/cpu/o3/alpha_cpu_builder.cc +++ b/src/cpu/o3/alpha_cpu_builder.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,41 +24,23 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#include "base/inifile.hh" -#include "base/loader/symtab.hh" -#include "base/misc.hh" +#include <string> + #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 "mem/cache/base_cache.hh" -#include "mem/mem_interface.hh" +#include "cpu/o3/alpha_params.hh" +#include "cpu/o3/fu_pool.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<AlphaSimpleImpl> { public: - DerivAlphaFullCPU(AlphaSimpleParams p) + DerivAlphaFullCPU(AlphaSimpleParams *p) : AlphaFullCPU<AlphaSimpleImpl>(p) { } }; @@ -67,6 +49,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) Param<int> clock; Param<int> numThreads; +Param<int> activity; #if FULL_SYSTEM SimObjectParam<System *> system; @@ -76,15 +59,17 @@ SimObjectParam<AlphaDTB *> dtb; #else SimObjectVectorParam<Process *> workload; #endif // FULL_SYSTEM -SimObjectParam<FunctionalMemory *> mem; + +SimObjectParam<MemObject *> mem; + +SimObjectParam<BaseCPU *> checker; Param<Counter> max_insts_any_thread; Param<Counter> max_insts_all_threads; Param<Counter> max_loads_any_thread; Param<Counter> max_loads_all_threads; -SimObjectParam<BaseCache *> icache; -SimObjectParam<BaseCache *> dcache; +Param<unsigned> cachePorts; Param<unsigned> decodeToFetchDelay; Param<unsigned> renameToFetchDelay; @@ -112,25 +97,25 @@ Param<unsigned> executeIntWidth; Param<unsigned> executeFloatWidth; Param<unsigned> executeBranchWidth; Param<unsigned> executeMemoryWidth; +SimObjectParam<FUPool *> fuPool; Param<unsigned> iewToCommitDelay; Param<unsigned> renameToROBDelay; Param<unsigned> commitWidth; Param<unsigned> squashWidth; +Param<Tick> trapLatency; +Param<Tick> fetchTrapLatency; -#if 0 +Param<std::string> predType; Param<unsigned> localPredictorSize; -Param<unsigned> localPredictorCtrBits; -#endif -Param<unsigned> local_predictor_size; -Param<unsigned> local_ctr_bits; -Param<unsigned> local_history_table_size; -Param<unsigned> local_history_bits; -Param<unsigned> global_predictor_size; -Param<unsigned> global_ctr_bits; -Param<unsigned> global_history_bits; -Param<unsigned> choice_predictor_size; -Param<unsigned> choice_ctr_bits; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; Param<unsigned> BTBEntries; Param<unsigned> BTBTagSize; @@ -147,6 +132,16 @@ Param<unsigned> numPhysFloatRegs; Param<unsigned> numIQEntries; Param<unsigned> numROBEntries; +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + Param<unsigned> instShiftAmt; Param<bool> defer_registration; @@ -160,6 +155,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(clock, "clock speed"), INIT_PARAM(numThreads, "number of HW thread contexts"), + INIT_PARAM_DFLT(activity, "Initial activity count", 0), #if FULL_SYSTEM INIT_PARAM(system, "System object"), @@ -170,7 +166,9 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(workload, "Processes to run"), #endif // FULL_SYSTEM - INIT_PARAM_DFLT(mem, "Memory", NULL), + INIT_PARAM(mem, "Memory"), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), INIT_PARAM_DFLT(max_insts_any_thread, "Terminate when any thread reaches this inst count", @@ -187,8 +185,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) "count", 0), - 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"), @@ -196,7 +193,6 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) "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,27 +218,26 @@ 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"), INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), 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_DFLT(trapLatency, "Number of cycles before the trap is handled", 6), + INIT_PARAM_DFLT(fetchTrapLatency, "Number of cycles before the fetch trap is handled", 12), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + 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 +255,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 +292,114 @@ 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; + params->activity = activity; #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; #endif // FULL_SYSTEM - params.mem = mem; + params->mem = mem; + + params->checker = checker; - 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.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.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.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.BTBEntries = BTBEntries; - params.BTBTagSize = BTBTagSize; - - params.RASSize = RASSize; - - params.LQEntries = LQEntries; - params.SQEntries = SQEntries; - params.SSITSize = SSITSize; - params.LFSTSize = LFSTSize; - - params.numPhysIntRegs = numPhysIntRegs; - params.numPhysFloatRegs = numPhysFloatRegs; - params.numIQEntries = numIQEntries; - params.numROBEntries = numROBEntries; - - params.instShiftAmt = 2; - - params.defReg = defer_registration; - - params.functionTrace = function_trace; - params.functionTraceStart = function_trace_start; + params->cachePorts = cachePorts; + + 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->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->fuPool = fuPool; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + params->trapLatency = trapLatency; + params->fetchTrapLatency = fetchTrapLatency; + + params->predType = predType; + 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->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + 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/src/cpu/o3/alpha_cpu_impl.hh b/src/cpu/o3/alpha_cpu_impl.hh index 7c4c2b969..98290e57f 100644 --- a/src/cpu/o3/alpha_cpu_impl.hh +++ b/src/cpu/o3/alpha_cpu_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,33 +24,138 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "arch/alpha/faults.hh" #include "base/cprintf.hh" #include "base/statistics.hh" #include "base/timebuf.hh" -#include "mem/cache/cache.hh" // for dynamic cast -#include "mem/mem_interface.hh" -#include "sim/builder.hh" +#include "cpu/checker/thread_context.hh" #include "sim/sim_events.hh" #include "sim/stats.hh" #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" +#include "cpu/quiesce_event.hh" +#include "kern/kernel_stats.hh" +#include "sim/system.hh" #endif +using namespace TheISA; + template <class Impl> -AlphaFullCPU<Impl>::AlphaFullCPU(Params ¶ms) +AlphaFullCPU<Impl>::AlphaFullCPU(Params *params) +#if FULL_SYSTEM + : FullO3CPU<Impl>(params), itb(params->itb), dtb(params->dtb) +#else : FullO3CPU<Impl>(params) +#endif { DPRINTF(FullCPU, "AlphaFullCPU: Creating AlphaFullCPU object.\n"); + // Setup any thread state. + this->thread.resize(this->numThreads); + + for (int i = 0; i < this->numThreads; ++i) { +#if FULL_SYSTEM + // SMT is not supported in FS mode yet. + assert(this->numThreads == 1); + this->thread[i] = new Thread(this, 0); + this->thread[i]->setStatus(ThreadContext::Suspended); +#else + if (i < params->workload.size()) { + DPRINTF(FullCPU, "FullCPU: Workload[%i] process is %#x", + i, this->thread[i]); + this->thread[i] = new Thread(this, i, params->workload[i], + i, params->mem); + + this->thread[i]->setStatus(ThreadContext::Suspended); + +#if !FULL_SYSTEM + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + TranslatingPort *trans_port; + trans_port = new TranslatingPort(csprintf("%s-%d-funcport", + name(), i), + params->workload[i]->pTable, + false); + mem_port = params->mem->getPort("functional"); + mem_port->setPeer(trans_port); + trans_port->setPeer(mem_port); + this->thread[i]->setMemPort(trans_port); +#endif + //usedTids[i] = true; + //threadMap[i] = i; + } else { + //Allocate Empty thread so M5 can use later + //when scheduling threads to CPU + Process* dummy_proc = NULL; + + this->thread[i] = new Thread(this, i, dummy_proc, i, params->mem); + //usedTids[i] = false; + } +#endif // !FULL_SYSTEM + + ThreadContext *tc; + + // Setup the TC that will serve as the interface to the threads/CPU. + AlphaTC *alpha_tc = new AlphaTC; + + // If we're using a checker, then the TC should be the + // CheckerThreadContext. + if (params->checker) { + tc = new CheckerThreadContext<AlphaTC>( + alpha_tc, this->checker); + } else { + tc = alpha_tc; + } + + alpha_tc->cpu = this; + alpha_tc->thread = this->thread[i]; + +#if FULL_SYSTEM + // Setup quiesce event. + this->thread[i]->quiesceEvent = new EndQuiesceEvent(tc); + + Port *mem_port; + FunctionalPort *phys_port; + VirtualPort *virt_port; + phys_port = new FunctionalPort(csprintf("%s-%d-funcport", + name(), i)); + mem_port = this->system->physmem->getPort("functional"); + mem_port->setPeer(phys_port); + phys_port->setPeer(mem_port); + + virt_port = new VirtualPort(csprintf("%s-%d-vport", + name(), i)); + mem_port = this->system->physmem->getPort("functional"); + mem_port->setPeer(virt_port); + virt_port->setPeer(mem_port); + + this->thread[i]->setPhysPort(phys_port); + this->thread[i]->setVirtPort(virt_port); +#endif + // Give the thread the TC. + this->thread[i]->tc = tc; + + // Add the TC to the CPU's list of TC's. + this->threadContexts.push_back(tc); + } + + for (int i=0; i < this->numThreads; i++) { + this->thread[i]->setFuncExeInst(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 +163,10 @@ AlphaFullCPU<Impl>::AlphaFullCPU(Params ¶ms) this->commit.setCPU(this); this->rob.setCPU(this); + this->regFile.setCPU(this); + + lockAddr = 0; + lockFlag = false; } template <class Impl> @@ -73,179 +182,519 @@ AlphaFullCPU<Impl>::regStats() this->commit.regStats(); } -#if !FULL_SYSTEM +#if FULL_SYSTEM +template <class Impl> +VirtualPort * +AlphaFullCPU<Impl>::AlphaTC::getVirtPort(ThreadContext *src_tc) +{ + if (!src_tc) + return thread->getVirtPort(); + + VirtualPort *vp; + Port *mem_port; + + vp = new VirtualPort("tc-vport", src_tc); + mem_port = cpu->system->physmem->getPort("functional"); + mem_port->setPeer(vp); + vp->setPeer(mem_port); + return vp; +} -// 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. template <class Impl> void -AlphaFullCPU<Impl>::syscall(short thread_num) +AlphaFullCPU<Impl>::AlphaTC::dumpFuncProfile() { - DPRINTF(FullCPU, "AlphaFullCPU: Syscall() called.\n\n"); + // Currently not supported +} +#endif - // Commit stage needs to run as well. - this->commit.tick(); +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::takeOverFrom(ThreadContext *old_context) +{ + // some things should already be set up +#if FULL_SYSTEM + assert(getSystemPtr() == old_context->getSystemPtr()); +#else + assert(getProcessPtr() == old_context->getProcessPtr()); +#endif - squashStages(); + // copy over functional state + setStatus(old_context->status()); + copyArchRegs(old_context); + setCpuId(old_context->readCpuId()); - // Temporarily increase this by one to account for the syscall - // instruction. - ++(this->funcExeInst); +#if !FULL_SYSTEM + thread->funcExeInst = old_context->readFuncExeInst(); +#else + EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); + if (other_quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + other_quiesce->tc = this; + } + if (thread->quiesceEvent) { + thread->quiesceEvent->tc = this; + } - // Copy over all important state to xc once all the unrolling is done. - copyToXC(); + // Transfer kernel stats from one CPU to the other. + thread->kernelStats = old_context->getKernelStats(); +// storeCondFailures = 0; + cpu->lockFlag = false; +#endif - // This is hardcoded to thread 0 while the CPU is only single threaded. - this->thread[0]->syscall(); + old_context->setStatus(ThreadContext::Unallocated); - // Copy over all important state back to CPU. - copyFromXC(); + thread->inSyscall = false; + thread->trapPending = false; +} - // Decrease funcExeInst by one as the normal commit will handle - // incrememnting it. - --(this->funcExeInst); +#if FULL_SYSTEM +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::delVirtPort(VirtualPort *vp) +{ + delete vp->getPeer(); + delete vp; } +#endif -// 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 <class Impl> void -AlphaFullCPU<Impl>::squashStages() +AlphaFullCPU<Impl>::AlphaTC::activate(int delay) { - InstSeqNum rob_head = this->rob.readHeadSeqNum(); + DPRINTF(FullCPU, "Calling activate on AlphaTC\n"); + + if (thread->status() == ThreadContext::Active) + return; - // 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; +#if FULL_SYSTEM + thread->lastActivate = curTick; +#endif + + if (thread->status() == ThreadContext::Unallocated) { + cpu->activateWhenReady(thread->readTid()); + return; } - 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; + thread->setStatus(ThreadContext::Active); + + // status() == Suspended + cpu->activateContext(thread->readTid(), delay); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::suspend() +{ + DPRINTF(FullCPU, "Calling suspend on AlphaTC\n"); + + if (thread->status() == ThreadContext::Suspended) + return; + +#if FULL_SYSTEM + thread->lastActivate = curTick; + thread->lastSuspend = curTick; +#endif +/* +#if FULL_SYSTEM + // Don't change the status from active if there are pending interrupts + if (cpu->check_interrupts()) { + assert(status() == ThreadContext::Active); + return; } +#endif +*/ + thread->setStatus(ThreadContext::Suspended); + cpu->suspendContext(thread->readTid()); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::deallocate() +{ + DPRINTF(FullCPU, "Calling deallocate on AlphaTC\n"); + if (thread->status() == ThreadContext::Unallocated) + return; + + thread->setStatus(ThreadContext::Unallocated); + cpu->deallocateContext(thread->readTid()); } -#endif // FULL_SYSTEM +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::halt() +{ + DPRINTF(FullCPU, "Calling halt on AlphaTC\n"); + + if (thread->status() == ThreadContext::Halted) + return; + + thread->setStatus(ThreadContext::Halted); + cpu->haltContext(thread->readTid()); +} template <class Impl> void -AlphaFullCPU<Impl>::copyToXC() +AlphaFullCPU<Impl>::AlphaTC::regStats(const std::string &name) { - PhysRegIndex renamed_reg; +#if FULL_SYSTEM + thread->kernelStats = new Kernel::Statistics(cpu->system); + thread->kernelStats->regStats(name + ".kern"); +#endif +} - // 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]); - } +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::serialize(std::ostream &os) +{ +#if FULL_SYSTEM + if (thread->kernelStats) + thread->kernelStats->serialize(os); +#endif - // 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->setFloatRegBits(i, - this->regFile.readFloatRegBits(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)); +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::unserialize(Checkpoint *cp, const std::string §ion) +{ +#if FULL_SYSTEM + if (thread->kernelStats) + thread->kernelStats->unserialize(cp, section); +#endif - this->cpuXC->setPC(this->rob.readHeadPC()); - this->cpuXC->setNextPC(this->cpuXC->readPC()+4); +} -#if !FULL_SYSTEM - this->cpuXC->setFuncExeInst(this->funcExeInst); +#if FULL_SYSTEM +template <class Impl> +EndQuiesceEvent * +AlphaFullCPU<Impl>::AlphaTC::getQuiesceEvent() +{ + return thread->quiesceEvent; +} + +template <class Impl> +Tick +AlphaFullCPU<Impl>::AlphaTC::readLastActivate() +{ + return thread->lastActivate; +} + +template <class Impl> +Tick +AlphaFullCPU<Impl>::AlphaTC::readLastSuspend() +{ + return thread->lastSuspend; +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::profileClear() +{} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::profileSample() +{} #endif + +template <class Impl> +TheISA::MachInst +AlphaFullCPU<Impl>::AlphaTC:: getInst() +{ + return thread->getInst(); } -// This function will probably mess things up unless the ROB is empty and -// there are no instructions in the pipeline. template <class Impl> void -AlphaFullCPU<Impl>::copyFromXC() +AlphaFullCPU<Impl>::AlphaTC::copyArchRegs(ThreadContext *tc) { + // This function will mess things up unless the ROB is empty and + // there are no instructions in the pipeline. + unsigned tid = thread->readTid(); PhysRegIndex renamed_reg; // First loop through the integer registers. - for (int i = 0; i < AlphaISA::NumIntRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i); + 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, this->regFile.intRegFile[renamed_reg], - this->cpuXC->readIntReg(i)); + renamed_reg, cpu->readIntReg(renamed_reg), + tc->readIntReg(i)); - this->regFile.setIntReg(renamed_reg, this->cpuXC->readIntReg(i)); + cpu->setIntReg(renamed_reg, tc->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.setFloatRegBits(renamed_reg, - this->cpuXC->readFloatRegBits(i)); + for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i + AlphaISA::FP_Base_DepTag); + cpu->setFloatRegBits(renamed_reg, + tc->readFloatRegBits(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)); + // Copy the misc regs. + copyMiscRegs(tc, this); // Then finally set the PC and the next PC. -// regFile.pc = cpuXC->regs.pc; -// regFile.npc = cpuXC->regs.npc; + cpu->setPC(tc->readPC(), tid); + cpu->setNextPC(tc->readNextPC(), tid); #if !FULL_SYSTEM - this->funcExeInst = this->cpuXC->readFuncExeInst(); + this->thread->funcExeInst = tc->readFuncExeInst(); #endif } +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::clearArchRegs() +{} + +template <class Impl> +uint64_t +AlphaFullCPU<Impl>::AlphaTC::readIntReg(int reg_idx) +{ + return cpu->readArchIntReg(reg_idx, thread->readTid()); +} + +template <class Impl> +FloatReg +AlphaFullCPU<Impl>::AlphaTC::readFloatReg(int reg_idx, int width) +{ + switch(width) { + case 32: + return cpu->readArchFloatRegSingle(reg_idx, thread->readTid()); + case 64: + return cpu->readArchFloatRegDouble(reg_idx, thread->readTid()); + default: + panic("Unsupported width!"); + return 0; + } +} + +template <class Impl> +FloatReg +AlphaFullCPU<Impl>::AlphaTC::readFloatReg(int reg_idx) +{ + return cpu->readArchFloatRegSingle(reg_idx, thread->readTid()); +} + +template <class Impl> +FloatRegBits +AlphaFullCPU<Impl>::AlphaTC::readFloatRegBits(int reg_idx, int width) +{ + DPRINTF(Fault, "Reading floatint register through the TC!\n"); + return cpu->readArchFloatRegInt(reg_idx, thread->readTid()); +} + +template <class Impl> +FloatRegBits +AlphaFullCPU<Impl>::AlphaTC::readFloatRegBits(int reg_idx) +{ + return cpu->readArchFloatRegInt(reg_idx, thread->readTid()); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setIntReg(int reg_idx, uint64_t val) +{ + cpu->setArchIntReg(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setFloatReg(int reg_idx, FloatReg val, int width) +{ + switch(width) { + case 32: + cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid()); + break; + case 64: + cpu->setArchFloatRegDouble(reg_idx, val, thread->readTid()); + break; + } + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setFloatReg(int reg_idx, FloatReg val) +{ + cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid()); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setFloatRegBits(int reg_idx, FloatRegBits val, + int width) +{ + DPRINTF(Fault, "Setting floatint register through the TC!\n"); + cpu->setArchFloatRegInt(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + cpu->setArchFloatRegInt(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setPC(uint64_t val) +{ + cpu->setPC(val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setNextPC(uint64_t val) +{ + cpu->setNextPC(val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +Fault +AlphaFullCPU<Impl>::AlphaTC::setMiscReg(int misc_reg, const MiscReg &val) +{ + Fault ret_fault = cpu->setMiscReg(misc_reg, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } + + return ret_fault; +} + +template <class Impl> +Fault +AlphaFullCPU<Impl>::AlphaTC::setMiscRegWithEffect(int misc_reg, + const MiscReg &val) +{ + Fault ret_fault = cpu->setMiscRegWithEffect(misc_reg, val, + thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } + + return ret_fault; +} + +#if !FULL_SYSTEM + +template <class Impl> +TheISA::IntReg +AlphaFullCPU<Impl>::AlphaTC::getSyscallArg(int i) +{ + return cpu->getSyscallArg(i, thread->readTid()); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setSyscallArg(int i, IntReg val) +{ + cpu->setSyscallArg(i, val, thread->readTid()); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::AlphaTC::setSyscallReturn(SyscallReturn return_value) +{ + cpu->setSyscallReturn(return_value, thread->readTid()); +} + +#endif // FULL_SYSTEM + +template <class Impl> +MiscReg +AlphaFullCPU<Impl>::readMiscReg(int misc_reg, unsigned tid) +{ + return this->regFile.readMiscReg(misc_reg, tid); +} + +template <class Impl> +MiscReg +AlphaFullCPU<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned tid) +{ + return this->regFile.readMiscRegWithEffect(misc_reg, fault, tid); +} + +template <class Impl> +Fault +AlphaFullCPU<Impl>::setMiscReg(int misc_reg, const MiscReg &val, unsigned tid) +{ + return this->regFile.setMiscReg(misc_reg, val, tid); +} + +template <class Impl> +Fault +AlphaFullCPU<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned tid) +{ + return this->regFile.setMiscRegWithEffect(misc_reg, val, tid); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::squashFromTC(unsigned tid) +{ + this->thread[tid]->inSyscall = true; + this->commit.generateTCEvent(tid); +} + #if FULL_SYSTEM template <class Impl> +void +AlphaFullCPU<Impl>::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (this->thread[0]->status() == ThreadContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + this->threadContexts[0]->activate(); + } +} + +template <class Impl> int AlphaFullCPU<Impl>::readIntrFlag() { @@ -259,20 +708,14 @@ AlphaFullCPU<Impl>::setIntrFlag(int val) this->regFile.setIntrFlag(val); } -// Can force commit stage to squash and stuff. template <class Impl> Fault -AlphaFullCPU<Impl>::hwrei() +AlphaFullCPU<Impl>::hwrei(unsigned tid) { - if (!inPalMode()) - return new AlphaISA::UnimplementedOpcodeFault; - - this->setNextPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR)); + // Need to clear the lock flag upon returning from an interrupt. + this->lockFlag = false; -// kernelStats.hwrei(); - - if ((this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR) & 1) == 0) -// AlphaISA::swap_palshadow(®s, false); + this->thread[tid]->kernelStats->hwrei(); this->checkInterrupts = true; @@ -282,15 +725,17 @@ AlphaFullCPU<Impl>::hwrei() template <class Impl> bool -AlphaFullCPU<Impl>::simPalCheck(int palFunc) +AlphaFullCPU<Impl>::simPalCheck(int palFunc, unsigned tid) { -// kernelStats.callpal(palFunc); + if (this->thread[tid]->kernelStats) + this->thread[tid]->kernelStats->callpal(palFunc, + this->threadContexts[tid]); switch (palFunc) { case PAL::halt: halt(); if (--System::numSystemsRunning == 0) - new SimExitEvent("all cpus halted"); + exitSimLoop("all cpus halted"); break; case PAL::bpt: @@ -303,69 +748,125 @@ AlphaFullCPU<Impl>::simPalCheck(int palFunc) return true; } -// Probably shouldn't be able to switch to the trap handler as quickly as -// this. Also needs to get the exception restart address from the commit -// stage. template <class Impl> void -AlphaFullCPU<Impl>::trap(Fault fault) +AlphaFullCPU<Impl>::trap(Fault fault, unsigned tid) +{ + // Pass the thread's TC into the invoke method. + fault->invoke(this->threadContexts[tid]); +} + +template <class Impl> +void +AlphaFullCPU<Impl>::processInterrupts() { -/* // Keep in mind that a trap may be initiated by fetch if there's a TLB - // miss - uint64_t PC = this->commit.readCommitPC(); + // 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. + // @todo: Possibly consolidate the interrupt checking code. + // @todo: Allow other threads to handle interrupts. + + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + this->checkInterrupts = false; + + 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); + } + } + } - DPRINTF(Fault, "Fault %s\n", fault->name()); - this->recordEvent(csprintf("Fault %s", fault->name())); + 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); + } + } + } - //kernelStats.fault(fault); + if (ipl && ipl > this->readMiscReg(IPR_IPLR, 0)) { + this->setMiscReg(IPR_ISR, summary, 0); + this->setMiscReg(IPR_INTID, ipl, 0); + // Checker needs to know these two registers were updated. + if (this->checker) { + this->checker->threadBase()->setMiscReg(IPR_ISR, summary); + this->checker->threadBase()->setMiscReg(IPR_INTID, ipl); + } + this->trap(Fault(new InterruptFault), 0); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + this->readMiscReg(IPR_IPLR, 0), ipl, summary); + } +} - if (fault->isA<ArithmeticFault>()) - panic("Arithmetic traps are unimplemented!"); +#endif // FULL_SYSTEM - // exception restart address - Get the commit PC - if (!fault->isA<InterruptFault>() || !inPalMode(PC)) - this->regFile.miscRegs.setReg(AlphaISA::IPR_EXC_ADDR, PC); +#if !FULL_SYSTEM - if (fault->isA<PalFault>() || fault->isA<ArithmeticFault>()) - // || fault == InterruptFault && !PC_PAL(regs.pc) - { - // traps... skip faulting instruction - AlphaISA::MiscReg ipr_exc_addr = - this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR); - this->regFile.miscRegs.setReg(AlphaISA::IPR_EXC_ADDR, - ipr_exc_addr + 4); - } +template <class Impl> +void +AlphaFullCPU<Impl>::syscall(int64_t callnum, int tid) +{ + DPRINTF(FullCPU, "AlphaFullCPU: [tid:%i] Executing syscall().\n\n", tid); + + DPRINTF(Activity,"Activity: syscall() called.\n"); - if (!inPalMode(PC)) - swapPALShadow(true); + // Temporarily increase this by one to account for the syscall + // instruction. + ++(this->thread[tid]->funcExeInst); - this->regFile.setPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_PAL_BASE) + - (dynamic_cast<AlphaFault *>(fault.get()))->vect()); - this->regFile.setNextPC(PC + sizeof(MachInst));*/ + // Execute the actual syscall. + this->thread[tid]->syscall(callnum); + + // Decrease funcExeInst by one as the normal commit will handle + // incrementing it. + --(this->thread[tid]->funcExeInst); } template <class Impl> -void -AlphaFullCPU<Impl>::processInterrupts() +TheISA::IntReg +AlphaFullCPU<Impl>::getSyscallArg(int i, int tid) { - // Check for interrupts here. For now can copy the code that exists - // within isa_fullsys_traits.hh. + return this->readArchIntReg(AlphaISA::ArgumentReg0 + i, tid); } -// 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 <class Impl> void -AlphaFullCPU<Impl>::swapPALShadow(bool use_shadow) +AlphaFullCPU<Impl>::setSyscallArg(int i, IntReg val, int tid) { - if (palShadowEnabled == use_shadow) - panic("swap_palshadow: wrong PAL shadow state"); - - palShadowEnabled = use_shadow; - - // Will have to lookup in rename map to get physical registers, then - // swap. + this->setArchIntReg(AlphaISA::ArgumentReg0 + i, val, tid); } -#endif // FULL_SYSTEM +template <class Impl> +void +AlphaFullCPU<Impl>::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/src/cpu/o3/alpha_dyn_inst.cc b/src/cpu/o3/alpha_dyn_inst.cc index 72ac77d95..0c1723eec 100644 --- a/src/cpu/o3/alpha_dyn_inst.cc +++ b/src/cpu/o3/alpha_dyn_inst.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst_impl.hh" diff --git a/src/cpu/o3/alpha_dyn_inst.hh b/src/cpu/o3/alpha_dyn_inst.hh index 5b8a05e5c..36a08c4a7 100644 --- a/src/cpu/o3/alpha_dyn_inst.hh +++ b/src/cpu/o3/alpha_dyn_inst.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,23 +24,28 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 "arch/isa_traits.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" + +class Packet; /** - * 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 + * for instructions that are executing. */ - template <class Impl> class AlphaDynInst : public BaseDynInst<Impl> { @@ -50,10 +55,14 @@ class AlphaDynInst : public BaseDynInst<Impl> /** 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. */ typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; /** Misc register index type. */ typedef TheISA::MiscReg MiscReg; @@ -64,60 +73,74 @@ class AlphaDynInst : public BaseDynInst<Impl> 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(); + + /** Initiates the access. Only valid for memory operations. */ + Fault initiateAcc(); + + /** Completes the access. Only valid for memory operations. */ + Fault completeAcc(Packet *pkt); + + private: + /** Initializes variables. */ + void initVars(); public: + /** Reads a miscellaneous register. */ 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); } + /** Reads a misc. register, including any side-effects the read + * might have as defined by the architecture. + */ 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); } + /** Sets a misc. register. */ Fault setMiscReg(int misc_reg, const MiscReg &val) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; + this->instResult.integer = val; + return this->cpu->setMiscReg(misc_reg, val, this->threadNumber); } + /** Sets a misc. register, including any side-effects the write + * might have as defined by the architecture. + */ 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 - void syscall(); + /** Calls a syscall. */ + void syscall(int64_t callnum); #endif - - private: /** Physical register index of the destination registers of this * instruction. @@ -178,32 +201,32 @@ class AlphaDynInst : public BaseDynInst<Impl> void setIntReg(const StaticInst *si, int idx, uint64_t val) { this->cpu->setIntReg(_destRegIdx[idx], val); - this->instResult.integer = val; + BaseDynInst<Impl>::setIntReg(si, idx, val); } void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) { this->cpu->setFloatReg(_destRegIdx[idx], val, width); - this->instResult.fp = val; + BaseDynInst<Impl>::setFloatReg(si, idx, val, width); } void setFloatReg(const StaticInst *si, int idx, FloatReg val) { this->cpu->setFloatReg(_destRegIdx[idx], val); - this->instResult.dbl = val; + BaseDynInst<Impl>::setFloatReg(si, idx, val); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, int width) { this->cpu->setFloatRegBits(_destRegIdx[idx], val, width); - this->instResult.integer = val; + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) { this->cpu->setFloatRegBits(_destRegIdx[idx], val); - this->instResult.integer = val; + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); } /** Returns the physical register index of the i'th destination @@ -249,16 +272,24 @@ class AlphaDynInst : public BaseDynInst<Impl> } public: + /** Calculates EA part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ 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 if we want to split + * memory operations into EA calculation and memory access parts. + */ 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/src/cpu/o3/alpha_dyn_inst_impl.hh b/src/cpu/o3/alpha_dyn_inst_impl.hh index 96b7d3430..a73cf4a7d 100644 --- a/src/cpu/o3/alpha_dyn_inst_impl.hh +++ b/src/cpu/o3/alpha_dyn_inst_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,47 +24,95 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" template <class Impl> -AlphaDynInst<Impl>::AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC, +AlphaDynInst<Impl>::AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, FullCPU *cpu) : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) { + initVars(); +} + +template <class Impl> +AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initVars(); +} + +template <class Impl> +void +AlphaDynInst<Impl>::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 < this->staticInst->numDestRegs(); i++) - { + for (int i = 0; i < this->staticInst->numDestRegs(); i++) { _destRegIdx[i] = this->staticInst->destRegIdx(i); } - for (int i = 0; i < this->staticInst->numSrcRegs(); i++) - { + for (int i = 0; i < this->staticInst->numSrcRegs(); i++) { _srcRegIdx[i] = this->staticInst->srcRegIdx(i); this->_readySrcRegIdx[i] = 0; } +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). 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 <class Impl> -AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst) - : BaseDynInst<Impl>(_staticInst) +Fault +AlphaDynInst<Impl>::initiateAcc() { - // 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); - } + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). 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; - for (int i = 0; i < _staticInst->numSrcRegs(); i++) - { - _srcRegIdx[i] = _staticInst->srcRegIdx(i); + return this->fault; +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::completeAcc(Packet *pkt) +{ + if (this->isLoad()) { + this->fault = this->staticInst->completeAcc(pkt, this, + this->traceData); + } else if (this->isStore()) { + this->fault = this->staticInst->completeAcc(pkt, this, + this->traceData); + } else { + panic("Unknown type!"); } + + return this->fault; } #if FULL_SYSTEM @@ -72,14 +120,26 @@ template <class Impl> Fault AlphaDynInst<Impl>::hwrei() { - return this->cpu->hwrei(); + // Can only do a hwrei when in pal mode. + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + // Set the next PC based on the value of the EXC_ADDR IPR. + this->setNextPC(this->cpu->readMiscReg(AlphaISA::IPR_EXC_ADDR, + this->threadNumber)); + + // Tell CPU to clear any state it needs to if a hwrei is taken. + this->cpu->hwrei(this->threadNumber); + + // FIXME: XXX check for interrupts? XXX + return NoFault; } template <class Impl> int AlphaDynInst<Impl>::readIntrFlag() { -return this->cpu->readIntrFlag(); + return this->cpu->readIntrFlag(); } template <class Impl> @@ -93,28 +153,28 @@ template <class Impl> bool AlphaDynInst<Impl>::inPalMode() { - return this->cpu->inPalMode(); + return this->cpu->inPalMode(this->PC); } template <class Impl> void AlphaDynInst<Impl>::trap(Fault fault) { - this->cpu->trap(fault); + this->cpu->trap(fault, this->threadNumber); } template <class Impl> bool AlphaDynInst<Impl>::simPalCheck(int palFunc) { - return this->cpu->simPalCheck(palFunc); + return this->cpu->simPalCheck(palFunc, this->threadNumber); } #else template <class Impl> void -AlphaDynInst<Impl>::syscall() +AlphaDynInst<Impl>::syscall(int64_t callnum) { - this->cpu->syscall(this->threadNumber); + this->cpu->syscall(callnum, this->threadNumber); } #endif diff --git a/src/cpu/o3/alpha_impl.hh b/src/cpu/o3/alpha_impl.hh index 5e39fcb37..52f7c2394 100644 --- a/src/cpu/o3/alpha_impl.hh +++ b/src/cpu/o3/alpha_impl.hh @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 +43,7 @@ class AlphaDynInst; template <class Impl> 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 +56,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<AlphaSimpleImpl> CPUPol; - /** The DynInst to be used. */ + /** The DynInst type to be used. */ typedef AlphaDynInst<AlphaSimpleImpl> DynInst; /** The refcounted DynInst pointer to be used. In most cases this is @@ -65,15 +67,16 @@ struct AlphaSimpleImpl */ typedef RefCountingPtr<DynInst> DynInstPtr; - /** The FullCPU to be used. */ + /** The FullCPU type to be used. */ typedef AlphaFullCPU<AlphaSimpleImpl> 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/src/cpu/o3/alpha_params.hh b/src/cpu/o3/alpha_params.hh index 79b0937e3..f3cf36887 100644 --- a/src/cpu/o3/alpha_params.hh +++ b/src/cpu/o3/alpha_params.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,20 +24,22 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 FunctionalMemory; +class AlphaITB; +class FUPool; +class MemObject; class Process; -class MemInterface; +class System; /** * This file defines the parameters that will be used for the AlphaFullCPU. @@ -56,13 +58,19 @@ class AlphaSimpleParams : public BaseFullCPU::Params Process *process; #endif // FULL_SYSTEM - FunctionalMemory *mem; + MemObject *mem; + + BaseCPU *checker; + + unsigned activity; // // Caches // - MemInterface *icacheInterface; - MemInterface *dcacheInterface; +// MemInterface *icacheInterface; +// MemInterface *dcacheInterface; + + unsigned cachePorts; // // Fetch @@ -102,6 +110,7 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned executeFloatWidth; unsigned executeBranchWidth; unsigned executeMemoryWidth; + FUPool *fuPool; // // Commit @@ -110,24 +119,22 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned renameToROBDelay; unsigned commitWidth; unsigned squashWidth; + Tick trapLatency; + Tick fetchTrapLatency; // - // Branch predictor (BP & BTB) + // Branch predictor (BP, BTB, RAS) // -/* + std::string predType; 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 +161,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/src/cpu/o3/bpred_unit.cc b/src/cpu/o3/bpred_unit.cc index 85bd6f0a6..b33543bdc 100644 --- a/src/cpu/o3/bpred_unit.cc +++ b/src/cpu/o3/bpred_unit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,10 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #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<AlphaSimpleImpl>; +template class BPredUnit<AlphaSimpleImpl>; +template class BPredUnit<OzoneImpl>; +//template class BPredUnit<SimpleImpl>; diff --git a/src/cpu/o3/bpred_unit.hh b/src/cpu/o3/bpred_unit.hh index 2725684f7..2c0a39565 100644 --- a/src/cpu/o3/bpred_unit.hh +++ b/src/cpu/o3/bpred_unit.hh @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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,99 +37,220 @@ #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 <list> /** * Basically a wrapper class to hold both the branch predictor - * and the BTB. Right now I'm unsure of the implementation; it would - * be nicer to have something closer to the CPUPolicy or the Impl where - * this is just typedefs, but it forces the upper level stages to be - * aware of the constructors of the BP and the BTB. The nicer thing - * to do is have this templated on the Impl, accept the usual Params - * object, and be able to call the constructors on the BP and BTB. + * and the BTB. */ template<class Impl> -class TwobitBPredUnit +class BPredUnit { - public: + private: typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; - TwobitBPredUnit(Params ¶ms); + enum PredType { + Local, + Tournament + }; + + PredType predictor; + public: + + /** + * @param params The params object, that has the size of the BP and BTB. + */ + BPredUnit(Params *params); + + /** + * Registers statistics. + */ void regStats(); - bool predict(DynInstPtr &inst, Addr &PC); + void switchOut(); + + void takeOverFrom(); + + /** + * 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); + + // @todo: Rename this function. + void BPUncond(void * &bp_history); - 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); - bool BPLookup(Addr &inst_PC) - { return BP.lookup(inst_PC); } + /** + * @param bp_history Pointer to the history object. The predictor + * will need to update any state and delete the object. + */ + void BPSquash(void *bp_history); + /** + * 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. + * @param bp_history Pointer that will be set to an object that + * has the branch predictor state associated with the lookup. + * @return Whether the branch is taken or not taken. + */ + bool BPLookup(Addr &inst_PC, void * &bp_history); + + /** + * 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. - void BPUpdate(Addr &inst_PC, bool taken) - { BP.update(inst_PC, taken); } + /** + * 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. + * @param bp_history Pointer to the branch predictor state that is + * associated with the branch lookup that is being updated. + * @todo Make this update flexible enough to handle a global predictor. + */ + void BPUpdate(Addr &inst_PC, bool taken, void *bp_history); + /** + * 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); } + + void dump(); private: struct PredictorHistory { + /** + * Makes a predictor history struct that contains any + * information needed to update the predictor, BTB, and RAS. + */ 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, void *bp_history, + const unsigned _tid) + : seqNum(seq_num), PC(inst_PC), RASTarget(0), + RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0), + wasCall(0), bpHistory(bp_history) { } + /** 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; - unsigned globalHistory; + /** The RAS index of the instruction (only valid if a call). */ + unsigned RASIndex; + + /** 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; - unsigned RASIndex; - - Addr RASTarget; + /** Pointer to the history object passed back from the branch + * predictor. It is used to update or restore state of the + * branch predictor. + */ + void *bpHistory; }; - std::list<PredictorHistory> predHist; + typedef std::list<PredictorHistory> 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 local branch predictor. */ + LocalBP *localBP; - DefaultBP BP; + /** The tournament branch predictor. */ + TournamentBP *tournamentBP; + /** 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/src/cpu/o3/bpred_unit_impl.hh b/src/cpu/o3/bpred_unit_impl.hh index 8d16a0cdf..0da02145b 100644 --- a/src/cpu/o3/bpred_unit_impl.hh +++ b/src/cpu/o3/bpred_unit_impl.hh @@ -24,27 +24,54 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ +#include <list> +#include <vector> + #include "base/trace.hh" #include "base/traceflags.hh" #include "cpu/o3/bpred_unit.hh" +using namespace std; + template<class Impl> -TwobitBPredUnit<Impl>::TwobitBPredUnit(Params ¶ms) - : BP(params.local_predictor_size, - params.local_ctr_bits, - params.instShiftAmt), - BTB(params.BTBEntries, - params.BTBTagSize, - params.instShiftAmt), - RAS(params.RASSize) +BPredUnit<Impl>::BPredUnit(Params *params) + : BTB(params->BTBEntries, + params->BTBTagSize, + params->instShiftAmt) { + // Setup the selected predictor. + if (params->predType == "local") { + localBP = new LocalBP(params->localPredictorSize, + params->localCtrBits, + params->instShiftAmt); + predictor = Local; + } else if (params->predType == "tournament") { + tournamentBP = new TournamentBP(params->localPredictorSize, + params->localCtrBits, + params->localHistoryTableSize, + params->localHistoryBits, + params->globalPredictorSize, + params->globalHistoryBits, + params->globalCtrBits, + params->choicePredictorSize, + params->choiceCtrBits, + params->instShiftAmt); + predictor = Tournament; + } else { + fatal("Invalid BP selected!"); + } + + for (int i=0; i < Impl::MaxThreads; i++) + RAS[i].init(params->RASSize); } template <class Impl> void -TwobitBPredUnit<Impl>::regStats() +BPredUnit<Impl>::regStats() { lookups .name(name() + ".BPredUnit.lookups") @@ -79,7 +106,7 @@ TwobitBPredUnit<Impl>::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 @@ -89,12 +116,36 @@ TwobitBPredUnit<Impl>::regStats() } template <class Impl> +void +BPredUnit<Impl>::switchOut() +{ + // Clear any state upon switch out. + for (int i = 0; i < Impl::MaxThreads; ++i) { + squash(0, i); + } +} + +template <class Impl> +void +BPredUnit<Impl>::takeOverFrom() +{ + // Can reset all predictor state, but it's not necessarily better + // than leaving it be. +/* + for (int i = 0; i < Impl::MaxThreads; ++i) + RAS[i].reset(); + + BP.reset(); + BTB.reset(); +*/ +} + +template <class Impl> bool -TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC) +BPredUnit<Impl>::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. - // Once that's done, speculatively update the predictor? // Save off record of branch stuff so the RAS can be fixed // up once it's done. @@ -105,19 +156,25 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC) ++lookups; + void *bp_history = NULL; + if (inst->isUncondCtrl()) { - DPRINTF(Fetch, "BranchPred: Unconditional control.\n"); + DPRINTF(Fetch, "BranchPred: [tid:%i] Unconditional control.\n", tid); pred_taken = true; + // Tell the BP there was an unconditional branch. + BPUncond(bp_history); } else { ++condPredicted; - pred_taken = BPLookup(PC); + pred_taken = BPLookup(PC, bp_history); - 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, + bp_history, tid); // Now lookup in the BTB or RAS. if (pred_taken) { @@ -126,45 +183,48 @@ TwobitBPredUnit<Impl>::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); + // If it's not a return, use the BTB to get the target addr. + 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 +240,173 @@ TwobitBPredUnit<Impl>::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 <class Impl> void -TwobitBPredUnit<Impl>::update(const InstSeqNum &done_sn) +BPredUnit<Impl>::update(const InstSeqNum &done_sn, unsigned tid) { - DPRINTF(Fetch, "BranchPred: Commiting branches until sequence number " - "%i.\n", done_sn); - - while (!predHist.empty() && predHist.back().seqNum <= done_sn) { - assert(!predHist.empty()); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Commiting branches until sequence" + "number %lli.\n", tid, done_sn); - // Update the branch predictor with the correct results of branches. - BP.update(predHist.back().PC, predHist.back().predTaken); + while (!predHist[tid].empty() && + predHist[tid].back().seqNum <= done_sn) { + // Update the branch predictor with the correct results. + BPUpdate(predHist[tid].back().PC, + predHist[tid].back().predTaken, + predHist[tid].back().bpHistory); - predHist.pop_back(); + predHist[tid].pop_back(); } } template <class Impl> void -TwobitBPredUnit<Impl>::squash(const InstSeqNum &squashed_sn) +BPredUnit<Impl>::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); - - 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.pop(); + History &pred_hist = predHist[tid]; + + 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[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(); + // This call should delete the bpHistory. + BPSquash(pred_hist.front().bpHistory); + + pred_hist.pop_front(); } + } template <class Impl> void -TwobitBPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, - const Addr &corr_target, - const bool actually_taken) +BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, + const Addr &corr_target, + 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) { + squash(squashed_sn, tid); - if (predHist.front().usedRAS) { - DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, " - "target: %#x.\n", - predHist.front().RASIndex, - predHist.front().RASTarget); + // 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()) { + assert(pred_hist.front().seqNum == squashed_sn); + if (pred_hist.front().usedRAS) { + ++RASIncorrect; + } - RAS.restore(predHist.front().RASIndex, - predHist.front().RASTarget); - } else if (predHist.front().wasCall) { - DPRINTF(Fetch, "BranchPred: Removing speculative entry added " - "to the RAS.\n"); + BPUpdate(pred_hist.front().PC, actually_taken, + pred_hist.front().bpHistory); - RAS.pop(); - } + BTB.update(pred_hist.front().PC, corr_target, tid); + pred_hist.pop_front(); + } +} + +template <class Impl> +void +BPredUnit<Impl>::BPUncond(void * &bp_history) +{ + // Only the tournament predictor cares about unconditional branches. + if (predictor == Tournament) { + tournamentBP->uncondBr(bp_history); + } +} - predHist.pop_front(); +template <class Impl> +void +BPredUnit<Impl>::BPSquash(void *bp_history) +{ + if (predictor == Local) { + localBP->squash(bp_history); + } else if (predictor == Tournament) { + tournamentBP->squash(bp_history); + } else { + panic("Predictor type is unexpected value!"); } +} - predHist.front().predTaken = actually_taken; +template <class Impl> +bool +BPredUnit<Impl>::BPLookup(Addr &inst_PC, void * &bp_history) +{ + if (predictor == Local) { + return localBP->lookup(inst_PC, bp_history); + } else if (predictor == Tournament) { + return tournamentBP->lookup(inst_PC, bp_history); + } else { + panic("Predictor type is unexpected value!"); + } +} - if (predHist.front().usedRAS) { - ++RASIncorrect; +template <class Impl> +void +BPredUnit<Impl>::BPUpdate(Addr &inst_PC, bool taken, void *bp_history) +{ + if (predictor == Local) { + localBP->update(inst_PC, taken, bp_history); + } else if (predictor == Tournament) { + tournamentBP->update(inst_PC, taken, bp_history); + } else { + panic("Predictor type is unexpected value!"); } +} + +template <class Impl> +void +BPredUnit<Impl>::dump() +{ + typename History::iterator pred_hist_it; + + for (int i = 0; i < Impl::MaxThreads; ++i) { + if (!predHist[i].empty()) { + pred_hist_it = predHist[i].begin(); - BP.update(predHist.front().PC, actually_taken); + cprintf("predHist[%i].size(): %i\n", i, predHist[i].size()); - BTB.update(predHist.front().PC, corr_target); + while (pred_hist_it != predHist[i].end()) { + cprintf("[sn:%lli], PC:%#x, tid:%i, predTaken:%i, " + "bpHistory:%#x\n", + (*pred_hist_it).seqNum, (*pred_hist_it).PC, + (*pred_hist_it).tid, (*pred_hist_it).predTaken, + (*pred_hist_it).bpHistory); + pred_hist_it++; + } + + cprintf("\n"); + } + } } diff --git a/src/cpu/o3/btb.cc b/src/cpu/o3/btb.cc index 2d39c3856..01640f4d1 100644 --- a/src/cpu/o3/btb.cc +++ b/src/cpu/o3/btb.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "base/intmath.hh" @@ -39,14 +41,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; } @@ -57,6 +60,14 @@ DefaultBTB::DefaultBTB(unsigned _numEntries, tagShiftAmt = instShiftAmt + floorLog2(numEntries); } +void +DefaultBTB::reset() +{ + for (int i = 0; i < numEntries; ++i) { + btb[i].valid = false; + } +} + inline unsigned DefaultBTB::getIndex(const Addr &inst_PC) @@ -73,7 +84,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 +92,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 +105,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 +113,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 +123,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/src/cpu/o3/btb.hh b/src/cpu/o3/btb.hh index 77bdc32ea..dfa3b7b06 100644 --- a/src/cpu/o3/btb.hh +++ b/src/cpu/o3/btb.hh @@ -24,13 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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" +#include "base/misc.hh" class DefaultBTB { @@ -42,39 +45,86 @@ 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); - - bool valid(const Addr &inst_PC); - - void update(const Addr &inst_PC, const Addr &target_PC); + void reset(); + + /** 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); + + /** 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); + + /** 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<BTBEntry> 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/src/cpu/o3/comm.hh b/src/cpu/o3/comm.hh index c74c77ddf..bf1bd08e8 100644 --- a/src/cpu/o3/comm.hh +++ b/src/cpu/o3/comm.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,32 +24,41 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_COMM_HH__ -#define __CPU_O3_CPU_COMM_HH__ +#ifndef __CPU_O3_COMM_HH__ +#define __CPU_O3_COMM_HH__ #include <vector> +#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; +/** Struct that defines the information passed from fetch to decode. */ template<class Impl> -struct SimpleFetchSimpleDecode { +struct DefaultFetchDefaultDecode { typedef typename Impl::DynInstPtr DynInstPtr; int size; DynInstPtr insts[Impl::MaxWidth]; + Fault fetchFault; + InstSeqNum fetchFaultSN; + bool clearFetchFault; }; +/** Struct that defines the information passed from decode to rename. */ template<class Impl> -struct SimpleDecodeSimpleRename { +struct DefaultDecodeDefaultRename { typedef typename Impl::DynInstPtr DynInstPtr; int size; @@ -57,8 +66,9 @@ struct SimpleDecodeSimpleRename { DynInstPtr insts[Impl::MaxWidth]; }; +/** Struct that defines the information passed from rename to IEW. */ template<class Impl> -struct SimpleRenameSimpleIEW { +struct DefaultRenameDefaultIEW { typedef typename Impl::DynInstPtr DynInstPtr; int size; @@ -66,20 +76,23 @@ struct SimpleRenameSimpleIEW { DynInstPtr insts[Impl::MaxWidth]; }; +/** Struct that defines the information passed from IEW to commit. */ template<class Impl> -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<class Impl> @@ -91,73 +104,95 @@ struct IssueStruct { DynInstPtr insts[Impl::MaxWidth]; }; +/** Struct that defines all backwards communication. */ +template<class Impl> 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; - // Extra bit of information so that the LDSTQ only updates when it - // needs to. - bool commitIsLoad; + //Just in case we want to do a commit/squash on a cycle + //(necessary for multiple ROBs?) + bool commitInsts; + InstSeqNum squashSeqNum; // 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/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc index cf33d7f8b..770008a33 100644 --- a/src/cpu/o3/commit.cc +++ b/src/cpu/o3/commit.cc @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/commit_impl.hh" -template class SimpleCommit<AlphaSimpleImpl>; +template class DefaultCommit<AlphaSimpleImpl>; diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh index 580c1a316..b7404c488 100644 --- a/src/cpu/o3/commit.hh +++ b/src/cpu/o3/commit.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,31 +24,46 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// 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 "base/statistics.hh" #include "base/timebuf.hh" -#include "mem/memory_interface.hh" - +#include "cpu/exetrace.hh" +#include "cpu/inst_seq.hh" + +template <class> +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 misprediction 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. + */ template<class Impl> -class SimpleCommit +class DefaultCommit { public: // Typedefs from the Impl. @@ -57,62 +72,219 @@ 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::Fetch Fetch; + typedef typename CPUPol::IEW IEW; + + typedef O3ThreadState<Impl> Thread; + + /** Event class used to schedule a squash due to a trap (fault or + * interrupt) to happen on a specific cycle. + */ + class TrapEvent : public Event { + private: + DefaultCommit<Impl> *commit; + unsigned tid; + + public: + TrapEvent(DefaultCommit<Impl> *_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<Thread *> &threads); + + /** Sets the main time buffer pointer, used for backwards communication. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + + /** Sets the pointer to the queue coming from rename. */ void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + /** Sets the pointer to the queue coming from IEW. */ void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr); + void setFetchStage(Fetch *fetch_stage); + + Fetch *fetchStage; + + /** Sets the pointer to the IEW stage. */ + void setIEWStage(IEW *iew_stage); + + /** The pointer to the IEW stage. Used solely to ensure that + * various events (traps, interrupts, syscalls) do not occur until + * all stores have written back. + */ + IEW *iewStage; + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *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(); + + /** Initializes the switching out of commit. */ + void switchOut(); + + /** Completes the switch out of commit. */ + void doSwitchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** 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(); + /** Returns the number of free ROB entries for a specific thread. */ + unsigned numROBFreeEntries(unsigned tid); + + /** Generates an event to schedule a squash due to a trap. */ + void generateTrapEvent(unsigned tid); + + /** Records that commit needs to initiate a squash due to an + * external state update through the TC. + */ + void generateTCEvent(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(); + + /** Squashes all in flight instructions. */ + void squashAll(unsigned tid); + + /** Handles squashing due to a trap. */ + void squashFromTrap(unsigned tid); + + /** Handles squashing due to an TC write. */ + void squashFromTC(unsigned tid); + /** 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); + /** Gets instructions from rename and inserts them into the ROB. */ void getInsts(); + /** Marks completed instructions using information sent from IEW. */ void markCompletedInsts(); + /** Gets the thread to commit, based on the SMT policy. */ + int getCommittingThread(); + + /** 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: - uint64_t readCommitPC(); + /** Returns the PC of the head instruction of the ROB. + * @todo: Probably remove this function as it returns only thread 0. + */ + uint64_t readPC() { return PC[0]; } + + /** Returns the PC of a specific thread. */ + uint64_t readPC(unsigned tid) { return PC[tid]; } - void setSquashing() { _status = ROBSquashing; } + /** Sets the PC of a specific thread. */ + void setPC(uint64_t val, unsigned tid) { PC[tid] = val; } + + /** Reads the PC of a specific thread. */ + uint64_t readNextPC(unsigned tid) { return nextPC[tid]; } + + /** Sets the next PC of a specific thread. */ + void setNextPC(uint64_t val, unsigned tid) { nextPC[tid] = val; } private: /** Time buffer interface. */ @@ -124,6 +296,10 @@ class SimpleCommit /** Wire to read information from IEW (for ROB). */ typename TimeBuffer<TimeStruct>::wire robInfoFromIEW; + TimeBuffer<FetchStruct> *fetchQueue; + + typename TimeBuffer<FetchStruct>::wire fromFetch; + /** IEW instruction queue interface. */ TimeBuffer<IEWStruct> *iewQueue; @@ -136,22 +312,54 @@ class SimpleCommit /** Wire to read information from rename queue. */ typename TimeBuffer<RenameStruct>::wire fromRename; + public: /** ROB interface. */ ROB *rob; + private: /** Pointer to FullCPU. */ FullCPU *cpu; - /** Memory interface. Used for d-cache accesses. */ - MemInterface *dcacheInterface; + /** Vector of all of the threads. */ + std::vector<Thread *> thread; + + Fault fetchFault; + + 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 tcSquash[Impl::MaxThreads]; + + /** Priority List used for Commit Policy */ + std::list<unsigned> priority_list; - private: /** 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 +373,82 @@ class SimpleCommit /** Commit width, in instructions. */ unsigned commitWidth; + /** Number of Reorder Buffers */ + unsigned numRobs; + + /** Number of Active Threads */ + unsigned numThreads; + + /** Is a switch out pending. */ + bool switchPending; + + /** Is commit switched out. */ + bool switchedOut; + + /** The latency to handle a trap. Used when scheduling trap + * squash event. + */ + Tick trapLatency; + + Tick fetchTrapLatency; + + Tick fetchFaultTick; + + /** The commit PC of each thread. Refers to the instruction that + * is currently being processed/committed. + */ + Addr PC[Impl::MaxThreads]; + + /** The next PC of each thread. */ + 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<unsigned> *activeThreads; + + /** Rename map interface. */ + RenameMap *renameMap[Impl::MaxThreads]; + + /** Updates commit stats based on this instruction. */ + void updateComInstStats(DynInstPtr &inst); + + /** 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; - Stats::Scalar<> commitCommittedBranches; - Stats::Scalar<> commitCommittedLoads; - Stats::Scalar<> commitCommittedMemRefs; + /** Stat for the total number of branch mispredicts that caused a squash. */ Stats::Scalar<> branchMispredicts; - - Stats::Distribution<> n_committed_dist; + /** Distribution of the number of committed instructions each cycle. */ + Stats::Distribution<> numCommittedDist; + + /** Total number of instructions committed. */ + Stats::Vector<> statComInst; + /** Total number of software prefetches committed. */ + Stats::Vector<> statComSwp; + /** Stat for the total number of committed memory references. */ + Stats::Vector<> statComRefs; + /** Stat for the total number of committed loads. */ + Stats::Vector<> statComLoads; + /** Total number of committed memory barriers. */ + Stats::Vector<> statComMembars; + /** Total number of committed branches. */ + Stats::Vector<> statComBranches; + + /** Number of cycles where the commit bandwidth limit is reached. */ + Stats::Scalar<> commitEligibleSamples; + /** Number of instructions not committed due to bandwidth limits. */ + Stats::Vector<> commitEligible; }; -#endif // __CPU_O3_CPU_SIMPLE_COMMIT_HH__ +#endif // __CPU_O3_COMMIT_HH__ diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh index e289bc0c0..ceb2918e0 100644 --- a/src/cpu/o3/commit_impl.hh +++ b/src/cpu/o3/commit_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,28 +24,117 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ +#include <algorithm> +#include <string> + +#include "base/loader/symtab.hh" #include "base/timebuf.hh" -#include "cpu/o3/commit.hh" +#include "cpu/checker/cpu.hh" #include "cpu/exetrace.hh" +#include "cpu/o3/commit.hh" +#include "cpu/o3/thread_state.hh" + +using namespace std; template <class Impl> -SimpleCommit<Impl>::SimpleCommit(Params ¶ms) - : dcacheInterface(params.dcacheInterface), - iewToCommitDelay(params.iewToCommitDelay), - renameToROBDelay(params.renameToROBDelay), - renameWidth(params.renameWidth), - iewWidth(params.executeWidth), - commitWidth(params.commitWidth) +DefaultCommit<Impl>::TrapEvent::TrapEvent(DefaultCommit<Impl> *_commit, + unsigned _tid) + : Event(&mainEventQueue, CPU_Tick_Pri), commit(_commit), tid(_tid) { - _status = Idle; + this->setFlags(Event::AutoDelete); } template <class Impl> void -SimpleCommit<Impl>::regStats() +DefaultCommit<Impl>::TrapEvent::process() +{ + // This will get reset by commit if it was switched out at the + // time of this event processing. + commit->trapSquash[tid] = true; +} + +template <class Impl> +const char * +DefaultCommit<Impl>::TrapEvent::description() +{ + return "Trap event"; +} + +template <class Impl> +DefaultCommit<Impl>::DefaultCommit(Params *params) + : 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), + switchPending(false), + switchedOut(false), + trapLatency(params->trapLatency), + fetchTrapLatency(params->fetchTrapLatency) { + _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; + tcSquash[i] = false; + PC[i] = nextPC[i] = 0; + } + + fetchFaultTick = 0; + fetchTrapWait = 0; +} + +template <class Impl> +std::string +DefaultCommit<Impl>::name() const +{ + return cpu->name() + ".commit"; +} + +template <class Impl> +void +DefaultCommit<Impl>::regStats() +{ + using namespace Stats; commitCommittedInsts .name(name() + ".commitCommittedInsts") .desc("The number of committed instructions") @@ -63,41 +152,110 @@ SimpleCommit<Impl>::regStats() .desc("The number of times commit has been forced to stall to " "communicate backwards") .prereq(commitNonSpecStalls); - commitCommittedBranches - .name(name() + ".commitCommittedBranches") - .desc("The number of committed branches") - .prereq(commitCommittedBranches); - commitCommittedLoads - .name(name() + ".commitCommittedLoads") - .desc("The number of committed loads") - .prereq(commitCommittedLoads); - commitCommittedMemRefs - .name(name() + ".commitCommittedMemRefs") - .desc("The number of committed memory references") - .prereq(commitCommittedMemRefs); branchMispredicts .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") .flags(Stats::pdf) ; + + statComInst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + statComSwp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + statComRefs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + statComLoads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + statComMembars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + statComBranches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commitEligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commitEligibleSamples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; } template <class Impl> void -SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultCommit<Impl>::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); + + trapLatency = cpu->cycles(trapLatency); + fetchTrapLatency = cpu->cycles(fetchTrapLatency); +} + +template <class Impl> +void +DefaultCommit<Impl>::setThreads(vector<Thread *> &threads) +{ + thread = threads; } template <class Impl> void -SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); timeBuffer = tb_ptr; @@ -111,7 +269,18 @@ SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) template <class Impl> void -SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +DefaultCommit<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *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 <class Impl> +void +DefaultCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) { DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); renameQueue = rq_ptr; @@ -122,7 +291,7 @@ SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) template <class Impl> void -SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) +DefaultCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) { DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); iewQueue = iq_ptr; @@ -133,7 +302,40 @@ SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) template <class Impl> void -SimpleCommit<Impl>::setROB(ROB *rob_ptr) +DefaultCommit<Impl>::setFetchStage(Fetch *fetch_stage) +{ + fetchStage = fetch_stage; +} + +template <class Impl> +void +DefaultCommit<Impl>::setIEWStage(IEW *iew_stage) +{ + iewStage = iew_stage; +} + +template<class Impl> +void +DefaultCommit<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Commit, "Commit: Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +DefaultCommit<Impl>::setRenameMap(RenameMap rm_ptr[]) +{ + DPRINTF(Commit, "Setting rename map pointers.\n"); + + for (int i=0; i < numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template <class Impl> +void +DefaultCommit<Impl>::setROB(ROB *rob_ptr) { DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); rob = rob_ptr; @@ -141,107 +343,451 @@ SimpleCommit<Impl>::setROB(ROB *rob_ptr) template <class Impl> void -SimpleCommit<Impl>::tick() -{ - // 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(); - - // 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; +DefaultCommit<Impl>::initStage() +{ + rob->setActiveThreads(activeThreads); + rob->resetEntries(); + + // 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); + } + + cpu->activityThisCycle(); +} + +template <class Impl> +void +DefaultCommit<Impl>::switchOut() +{ + switchPending = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::doSwitchOut() +{ + switchedOut = true; + switchPending = false; + rob->switchOut(); +} + +template <class Impl> +void +DefaultCommit<Impl>::takeOverFrom() +{ + switchedOut = false; + _status = Active; + _nextStatus = Inactive; + for (int i=0; i < numThreads; i++) { + commitStatus[i] = Idle; + changedROBNumEntries[i] = false; + trapSquash[i] = false; + tcSquash[i] = false; + } + squashCounter = 0; + rob->takeOverFrom(); +} + +template <class Impl> +void +DefaultCommit<Impl>::updateStatus() +{ + // reset ROB changed variable + list<unsigned>::iterator threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + changedROBNumEntries[tid] = false; + + // Also check if any of the threads has a trap pending + if (commitStatus[tid] == TrapPending || + commitStatus[tid] == FetchTrapPending) { + _nextStatus = Active; } - } else { - commit(); } + 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; +} + +template <class Impl> +void +DefaultCommit<Impl>::setNextStatus() +{ + int squashes = 0; + + list<unsigned>::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 <class Impl> +bool +DefaultCommit<Impl>::changedROBEntries() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + return true; + } + } + + return false; +} + +template <class Impl> +unsigned +DefaultCommit<Impl>::numROBFreeEntries(unsigned tid) +{ + return rob->numFreeEntries(tid); +} + +template <class Impl> +void +DefaultCommit<Impl>::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 <class Impl> +void +DefaultCommit<Impl>::generateTCEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating TC squash event for [tid:%i]\n", tid); + + tcSquash[tid] = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashAll(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].nextPC = PC[tid]; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashFromTrap(unsigned tid) +{ + squashAll(tid); + + DPRINTF(Commit, "Squashing from trap, restarting at PC %#x\n", PC[tid]); + + thread[tid]->trapPending = false; + thread[tid]->inSyscall = false; + + trapSquash[tid] = false; + + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); + + ++squashCounter; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashFromTC(unsigned tid) +{ + squashAll(tid); + + DPRINTF(Commit, "Squashing from TC, restarting at PC %#x\n", PC[tid]); + + thread[tid]->inSyscall = false; + assert(!thread[tid]->trapPending); + + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); + + tcSquash[tid] = false; + + ++squashCounter; +} + +template <class Impl> +void +DefaultCommit<Impl>::tick() +{ + wroteToTimeBuffer = false; + _nextStatus = Inactive; + + if (switchPending && rob->isEmpty() && !iewStage->hasStoresToWB()) { + cpu->signalSwitched(); + return; + } + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + // Check if any of the threads are done squashing. Change the + // status if they are done. + 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(); - // Writeback number of free ROB entries here. - DPRINTF(Commit, "Commit: ROB has %d free entries.\n", - rob->numFreeEntries()); - toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); + 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 <class Impl> void -SimpleCommit<Impl>::commit() +DefaultCommit<Impl>::commit() { + ////////////////////////////////////// // Check for interrupts ////////////////////////////////////// - // Process interrupts if interrupts are enabled and not in PAL mode. - // Take the PC from commit and write it to the IPR, then squash. The - // interrupt completing will take care of restoring the PC from that value - // in the IPR. Look at IPR[EXC_ADDR]; - // hwrei() is what resets the PC to the place where instruction execution - // beings again. #if FULL_SYSTEM - if (//checkInterrupts && + // Process interrupts if interrupts are enabled, not in PAL mode, + // and no other traps or external squashes are currently pending. + // @todo: Allow other threads to handle interrupts. + 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] && + !tcSquash[0]) { + // Tell fetch that there is an interrupt pending. This will + // make fetch wait until it sees a non PAL-mode PC, at which + // point it stops fetching instructions. + toIEW->commitInfo[0].interruptPending = true; + + // Wait until the ROB is empty and all stores have drained in + // order to enter the interrupt. + if (rob->isEmpty() && !iewStage->hasStoresToWB()) { + // 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); - // CPU will handle implementation of the interrupt. - cpu->processInterrupts(); + toIEW->commitInfo[0].clearInterrupt = true; + + DPRINTF(Commit, "Interrupt detected.\n"); + } else { + DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n"); + } } #endif // FULL_SYSTEM //////////////////////////////////// - // Check for squash signal, handle that first + // Check for any possible squashes, handle them 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"); + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; +/* + if (fromFetch->fetchFault && commitStatus[0] != TrapPending) { + // Record the fault. Wait until it's empty in the ROB. + // Then handle the trap. Ignore it if there's already a + // trap pending as fetch will be redirected. + fetchFault = fromFetch->fetchFault; + 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; + } + + // Fetch may tell commit to clear the trap if it's been squashed. + 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(!tcSquash[tid]); + squashFromTrap(tid); + } else if (tcSquash[tid] == true) { + squashFromTC(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]) { - _status = ROBSquashing; + DPRINTF(Commit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n", + tid, + fromIEW->mispredPC[tid], + fromIEW->squashedSeqNum[tid]); - InstSeqNum squashed_inst = fromIEW->squashedSeqNum; + DPRINTF(Commit, "[tid:%i]: Redirecting to PC %#x\n", + tid, + fromIEW->nextPC[tid]); - rob->squash(squashed_inst); + commitStatus[tid] = ROBSquashing; - // Send back the sequence number of the squashed instruction. - toIEW->commitInfo.doneSeqNum = squashed_inst; + ++squashCounter; - // Send back the squash signal to tell stages that they should squash. - toIEW->commitInfo.squash = true; + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid]; - // Send back the rob squashing signal so other stages know that the - // ROB is in the process of squashing. - toIEW->commitInfo.robSquashing = true; + if (fromIEW->includeSquashInst[tid] == true) + squashed_inst--; - toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict; + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB. + youngestSeqNum[tid] = squashed_inst; - toIEW->commitInfo.branchTaken = fromIEW->branchTaken; + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; - toIEW->commitInfo.nextPC = fromIEW->nextPC; + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; - toIEW->commitInfo.mispredPC = fromIEW->mispredPC; + toIEW->commitInfo[tid].squash = true; - if (toIEW->commitInfo.branchMispredict) { - ++branchMispredicts; + // 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]; + + 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,121 +795,186 @@ SimpleCommit<Impl>::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 <class Impl> void -SimpleCommit<Impl>::commitInsts() +DefaultCommit<Impl>::commitInsts() { //////////////////////////////////// // Handle commit - // Note that commit will be handled prior to the ROB so that the ROB - // only tries to commit instructions it has in this current cycle, and - // not instructions it is writing in during this cycle. - // Can't commit and squash things at the same time... + // Note that commit will be handled prior to putting new + // instructions in the ROB so that the ROB only tries to commit + // instructions it has in this current cycle, and not instructions + // it is writing in during this cycle. 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; + // 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 the head instruction is squashed, it is ready to retire + // (be removed from the ROB) at any time. 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; + 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]; + nextPC[tid] = nextPC[tid] + sizeof(TheISA::MachInst); +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + // Debug statement. Checks to make sure we're not + // currently updating state while handling PC events. + if (count == 0) + assert(!thread[tid]->inSyscall && + !thread[tid]->trapPending); + oldpc = PC[tid]; + cpu->system->pcEventQueue.service( + thread[tid]->getTC()); + 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); + + if (num_committed == commitWidth) { + commitEligibleSamples++; + } } template <class Impl> bool -SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) +DefaultCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) { - // Make sure instruction is valid assert(head_inst); - // If the instruction is not executed yet, then it is a non-speculative - // or store inst. Signal backwards that it should be executed. + int tid = head_inst->threadNumber; + + // If the instruction is not executed yet, then it will need extra + // handling. 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--; - - 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()); + thread[tid]->funcExeInst--; + + head_inst->reachedCommit = true; + + if (head_inst->isNonSpeculative() || + head_inst->isStoreConditional() || + head_inst->isMemBarrier() || + head_inst->isWriteBarrier()) { + + 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()); + +#if !FULL_SYSTEM + // Hack to make sure syscalls/memory barriers/quiesces + // 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; + } - toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; // Change the instruction so it won't try to commit again until // it is executed. @@ -372,74 +983,111 @@ SimpleCommit<Impl>::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. 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() ) - { - // 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"); + if (head_inst->isThreadSync()) { + // Not handled for now. + panic("Thread sync instructions are not handled yet.\n"); + } + + // Stores mark themselves as completed. + if (!head_inst->isStore()) { + head_inst->setCompleted(); + } + + // Use checker prior to updating anything due to traps or PC + // based events. + if (cpu->checker) { + cpu->checker->tick(head_inst); } // Check if the instruction caused a fault. If so, trap. Fault inst_fault = head_inst->getFault(); if (inst_fault != NoFault) { - if (!head_inst->isNop()) { + head_inst->setCompleted(); #if FULL_SYSTEM - cpu->trap(inst_fault); -#else // !FULL_SYSTEM - panic("fault (%d) detected @ PC %08p", inst_fault, - head_inst->PC); -#endif // FULL_SYSTEM + DPRINTF(Commit, "Inst [sn:%lli] PC %#x has a fault\n", + head_inst->seqNum, head_inst->readPC()); + + if (iewStage->hasStoresToWB() || inst_num > 0) { + DPRINTF(Commit, "Stores outstanding, fault must wait.\n"); + return false; } - } - // 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"); - return false; - } + if (cpu->checker && head_inst->isStore()) { + cpu->checker->tick(head_inst); + } - // 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... + assert(!thread[tid]->inSyscall); - if (head_inst->isControl()) { + // Mark that we're in state update mode so that the trap's + // execution doesn't generate extra squashes. + thread[tid]->inSyscall = true; -#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 + // 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 its fault. + thread[tid]->setInst( + static_cast<TheISA::MachInst>(head_inst->staticInst->machInst)); + + // Execute the trap. Although it's slightly unrealistic in + // terms of timing (as it doesn't wait for the full timing of + // the trap event to complete before updating state), it's + // needed to update the state as soon as possible. This + // prevents external agents from changing any specific state + // that the trap need. + cpu->trap(inst_fault, tid); + + // Exit state update mode to avoid accidental updating. + thread[tid]->inSyscall = false; + + commitStatus[tid] = TrapPending; - ++commitCommittedBranches; + // Generate trap squash event. + generateTrapEvent(tid); + + return false; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + head_inst->PC); +#endif // FULL_SYSTEM } - // Now that the instruction is going to be committed, finalize its - // trace data. + updateComInstStats(head_inst); + 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; + } + + // 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(); + // Finally clear the head ROB entry. + rob->retireHead(tid); // Return true to indicate that we have committed an instruction. return true; @@ -447,37 +1095,39 @@ SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) template <class Impl> void -SimpleCommit<Impl>::getInsts() +DefaultCommit<Impl>::getInsts() { - ////////////////////////////////////// - // Handle ROB functions - ////////////////////////////////////// - - // Read any issued 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. + // Read any renamed instructions and place them into the ROB. 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 <class Impl> void -SimpleCommit<Impl>::markCompletedInsts() +DefaultCommit<Impl>::markCompletedInsts() { // Grab completed insts out of the IEW instruction queue, and mark // instructions completed within the ROB. @@ -485,18 +1135,177 @@ SimpleCommit<Impl>::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:%lli] 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(); + } + } +} + +template <class Impl> +bool +DefaultCommit<Impl>::robDoneSquashing() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); - // Mark the instruction as ready to commit. - fromIEW->insts[inst_num]->setCanCommit(); + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isDoneSquashing(tid)) + return false; } + + return true; } template <class Impl> -uint64_t -SimpleCommit<Impl>::readCommitPC() +void +DefaultCommit<Impl>::updateComInstStats(DynInstPtr &inst) { - return rob->readHeadPC(); + unsigned thread = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + statComSwp[thread]++; + } else { + statComInst[thread]++; + } +#else + statComInst[thread]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + statComBranches[thread]++; + + // + // Memory references + // + if (inst->isMemRef()) { + statComRefs[thread]++; + + if (inst->isLoad()) { + statComLoads[thread]++; + } + } + + if (inst->isMemBarrier()) { + statComMembars[thread]++; + } +} + +//////////////////////////////////////// +// // +// SMT COMMIT POLICY MAINTAINED HERE // +// // +//////////////////////////////////////// +template <class Impl> +int +DefaultCommit<Impl>::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<class Impl> +int +DefaultCommit<Impl>::roundRobin() +{ + list<unsigned>::iterator pri_iter = priority_list.begin(); + list<unsigned>::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<class Impl> +int +DefaultCommit<Impl>::oldestReady() +{ + unsigned oldest = 0; + bool first = true; + + list<unsigned>::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/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index a268dbc23..788c6b164 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "config/full_system.hh" @@ -33,19 +35,30 @@ #else #include "sim/process.hh" #endif -#include "sim/root.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" +#include "cpu/activity.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" #include "cpu/o3/alpha_dyn_inst.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/cpu.hh" +#include "sim/root.hh" +#include "sim/stat_control.hh" + using namespace std; +using namespace TheISA; + +BaseFullCPU::BaseFullCPU(Params *params) + : BaseCPU(params), cpu_id(0) +{ +} -BaseFullCPU::BaseFullCPU(Params ¶ms) - : BaseCPU(¶ms), cpu_id(0) +void +BaseFullCPU::regStats() { + BaseCPU::regStats(); } template <class Impl> @@ -68,97 +81,82 @@ FullO3CPU<Impl>::TickEvent::description() return "FullO3CPU tick event"; } -//Call constructor to all the pipeline stages here template <class Impl> -FullO3CPU<Impl>::FullO3CPU(Params ¶ms) -#if FULL_SYSTEM +FullO3CPU<Impl>::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 or derived + // from latencies timeBuffer(5, 5), fetchQueue(5, 5), decodeQueue(5, 5), renameQueue(5, 5), iewQueue(5, 5), - - cpuXC(NULL), + activityRec(NumStages, 10, params->activity), globalSeqNum(1), #if FULL_SYSTEM - system(params.system), - memCtrl(system->memctrl), + system(params->system), physmem(system->physmem), - itb(params.itb), - dtb(params.dtb), - mem(params.mem), -#else - // Hardcoded for a single thread!! - mem(params.workload[0]->getMemory()), #endif // FULL_SYSTEM - - icacheInterface(params.icacheInterface), - dcacheInterface(params.dcacheInterface), - deferRegistration(params.defReg), - numInsts(0), - funcExeInst(0) + mem(params->mem), + switchCount(0), + deferRegistration(params->deferRegistration), + numThreads(number_of_threads) { _status = Idle; -#if !FULL_SYSTEM - thread.resize(this->number_of_threads); -#endif - - for (int i = 0; i < this->number_of_threads; ++i) { + if (params->checker) { + BaseCPU *temp_checker = params->checker; + checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker->setMemory(mem); #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 + checker->setSystem(params->system); +#endif + } else { + checker = NULL; } - // 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]; +#if !FULL_SYSTEM + thread.resize(number_of_threads); + tids.resize(number_of_threads); +#endif + + // 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. - // 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); @@ -170,6 +168,7 @@ FullO3CPU<Impl>::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 +177,79 @@ FullO3CPU<Impl>::FullO3CPU(Params ¶ms) commit.setIEWQueue(&iewQueue); commit.setRenameQueue(&renameQueue); + commit.setFetchStage(&fetch); + commit.setIEWStage(&iew); + rename.setIEWStage(&iew); + rename.setCommitStage(&commit); + +#if !FULL_SYSTEM + int active_threads = params->workload.size(); +#else + int active_threads = 1; +#endif + + //Make Sure That this a Valid Architeture + 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. - rename.setRenameMap(&renameMap); - iew.setRenameMap(&renameMap); + 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 <= active_threads - 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); - // Setup the free list for whichever stages need it. + 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 ROB for whichever stages need it. commit.setROB(&rob); + + lastRunningCycle = curTick; + + contextSwitch = false; } template <class Impl> @@ -199,7 +261,58 @@ template <class Impl> void FullO3CPU<Impl>::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 <class Impl> @@ -208,9 +321,11 @@ FullO3CPU<Impl>::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 +336,11 @@ FullO3CPU<Impl>::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,114 +348,405 @@ FullO3CPU<Impl>::tick() renameQueue.advance(); iewQueue.advance(); - if (_status == Running && !tickEvent.scheduled()) - tickEvent.schedule(curTick + 1); + activityRec.advance(); + + if (removeInstsThisCycle) { + cleanUpRemovedInsts(); + } + + if (!tickEvent.scheduled()) { + if (_status == SwitchedOut) { + // increment stat + lastRunningCycle = curTick; + } else if (!activityRec.active()) { + lastRunningCycle = curTick; + timesIdled++; + } else { + tickEvent.schedule(curTick + cycles(1)); + } + } + +#if !FULL_SYSTEM + updateThreadPriority(); +#endif + } template <class Impl> void FullO3CPU<Impl>::init() { - if(!deferRegistration) - { - this->registerExecContexts(); + if (!deferRegistration) { + registerThreadContexts(); + } - // Need to do a copy of the xc->regs into the CPU's regfile so - // that it can start properly. + // 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; + + for (int tid=0; tid < number_of_threads; tid++) { #if FULL_SYSTEM - ExecContext *src_xc = system->execContexts[0]; - TheISA::initCPU(src_xc, src_xc->readCpuId()); + ThreadContext *src_tc = threadContexts[tid]; #else - ExecContext *src_xc = thread[0]->getProxy(); + ThreadContext *src_tc = thread[tid]->getTC(); #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_tc->status() != ThreadContext::Suspended) { + continue; } - // Then loop through the floating point registers. - for (int i = 0; i < TheISA::NumFloatRegs; ++i) - { - regFile.floatRegFile.setRegBits(i, src_xc->readRegBits(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_tc, src_tc->readCpuId()); +#endif + } + + // Clear inSyscall. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = false; + + // Initialize stages. + fetch.initStage(); + iew.initStage(); + rename.initStage(); + commit.initStage(); + + commit.setThreads(thread); +} + +template <class Impl> +void +FullO3CPU<Impl>::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 ThreadContext. +#if 0 +#if FULL_SYSTEM + ThreadContext *src_tc = system->threadContexts[tid]; +#else + ThreadContext *src_tc = 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->copyFromTC(tid); + + //Set PC/NPC + regFile.pc[tid] = src_tc->readPC(); + regFile.npc[tid] = src_tc->readNextPC(); + + src_tc->setStatus(ThreadContext::Active); + + activateContext(tid,1); + + //Reset ROB/IQ/LSQ Entries + commit.rob->resetEntries(); + iew.resetEntries(); +#endif +} + +template <class Impl> +void +FullO3CPU<Impl>::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->copyToTC(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 <class Impl> void -FullO3CPU<Impl>::activateContext(int thread_num, int delay) +FullO3CPU<Impl>::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 <class Impl> +void +FullO3CPU<Impl>::activateContext(int tid, int delay) { // Needs to set each stage to running as well. + list<unsigned>::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 || _status == SwitchedOut); scheduleTickEvent(delay); + // Be sure to signal that there's some activity so the CPU doesn't + // deschedule itself. + activityRec.activity(); + fetch.wakeFromQuiesce(); + _status = Running; } template <class Impl> void -FullO3CPU<Impl>::suspendContext(int thread_num) +FullO3CPU<Impl>::suspendContext(int tid) { - panic("suspendContext unimplemented!"); + DPRINTF(FullCPU,"[tid: %i]: Suspended ...\n", tid); + unscheduleTickEvent(); + _status = Idle; +/* + //Remove From Active List, if Active + list<unsigned>::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 <class Impl> void -FullO3CPU<Impl>::deallocateContext(int thread_num) +FullO3CPU<Impl>::deallocateContext(int tid) { - panic("deallocateContext unimplemented!"); + DPRINTF(FullCPU,"[tid:%i]: Deallocating ...", tid); +/* + //Remove From Active List, if Active + list<unsigned>::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 <class Impl> +void +FullO3CPU<Impl>::haltContext(int tid) +{ + DPRINTF(FullCPU,"[tid:%i]: Halted ...", tid); +/* + //Remove From Active List, if Active + list<unsigned>::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 <class Impl> void -FullO3CPU<Impl>::haltContext(int thread_num) +FullO3CPU<Impl>::switchOut(Sampler *_sampler) { - panic("haltContext unimplemented!"); + sampler = _sampler; + switchCount = 0; + fetch.switchOut(); + decode.switchOut(); + rename.switchOut(); + iew.switchOut(); + commit.switchOut(); + + // Wake the CPU and record activity so everything can drain out if + // the CPU is currently idle. + wakeCPU(); + activityRec.activity(); } template <class Impl> void -FullO3CPU<Impl>::switchOut() +FullO3CPU<Impl>::signalSwitched() { - panic("FullO3CPU does not have a switch out function.\n"); + if (++switchCount == NumStages) { + fetch.doSwitchOut(); + rename.doSwitchOut(); + commit.doSwitchOut(); + instList.clear(); + while (!removeList.empty()) { + removeList.pop(); + } + + if (checker) + checker->switchOut(sampler); + + if (tickEvent.scheduled()) + tickEvent.squash(); + sampler->signalSwitched(); + _status = SwitchedOut; + } + assert(switchCount <= 5); } template <class Impl> void FullO3CPU<Impl>::takeOverFrom(BaseCPU *oldCPU) { + // Flush out any old data from the time buffers. + for (int i = 0; i < 10; ++i) { + timeBuffer.advance(); + fetchQueue.advance(); + decodeQueue.advance(); + renameQueue.advance(); + iewQueue.advance(); + } + + activityRec.reset(); + BaseCPU::takeOverFrom(oldCPU); + fetch.takeOverFrom(); + decode.takeOverFrom(); + rename.takeOverFrom(); + iew.takeOverFrom(); + commit.takeOverFrom(); + assert(!tickEvent.scheduled()); - // Set all status's to active, schedule the - // CPU's tick event. - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; - if (xc->status() == ExecContext::Active && _status != Running) { + // @todo: Figure out how to properly select the tid to put onto + // the active threads list. + int tid = 0; + + list<unsigned>::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); + } + + // Set all statuses to active, schedule the CPU's tick event. + // @todo: Fix up statuses so this is handled properly + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { _status = Running; tickEvent.schedule(curTick); } } -} - -template <class Impl> -InstSeqNum -FullO3CPU<Impl>::getAndIncrementInstSeq() -{ - // Hopefully this works right. - return globalSeqNum++; + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick); } template <class Impl> @@ -411,156 +821,368 @@ FullO3CPU<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val) template <class Impl> uint64_t -FullO3CPU<Impl>::readPC() +FullO3CPU<Impl>::readArchIntReg(int reg_idx, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readIntReg(phys_reg); +} + +template <class Impl> +float +FullO3CPU<Impl>::readArchFloatRegSingle(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatReg(phys_reg); +} + +template <class Impl> +double +FullO3CPU<Impl>::readArchFloatRegDouble(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatReg(phys_reg, 64); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readArchFloatRegInt(int reg_idx, unsigned tid) { - return regFile.readPC(); + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatRegBits(phys_reg); } template <class Impl> void -FullO3CPU<Impl>::setNextPC(uint64_t val) +FullO3CPU<Impl>::setArchIntReg(int reg_idx, uint64_t val, unsigned tid) { - regFile.setNextPC(val); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setIntReg(phys_reg, val); } template <class Impl> void -FullO3CPU<Impl>::setPC(Addr new_PC) +FullO3CPU<Impl>::setArchFloatRegSingle(int reg_idx, float val, unsigned tid) { - regFile.setPC(new_PC); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatReg(phys_reg, val); } template <class Impl> void -FullO3CPU<Impl>::addInst(DynInstPtr &inst) +FullO3CPU<Impl>::setArchFloatRegDouble(int reg_idx, double val, unsigned tid) { - instList.push_back(inst); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatReg(phys_reg, val, 64); } template <class Impl> void -FullO3CPU<Impl>::instDone() +FullO3CPU<Impl>::setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid) { - // Keep an instruction count. - numInsts++; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); - // Check for instruction-count-based events. - comInstEventQueue[0]->serviceEvents(numInsts); + regFile.setFloatRegBits(phys_reg, val); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readPC(unsigned tid) +{ + return commit.readPC(tid); } template <class Impl> void -FullO3CPU<Impl>::removeBackInst(DynInstPtr &inst) +FullO3CPU<Impl>::setPC(Addr new_PC,unsigned tid) { - DynInstPtr inst_to_delete; + commit.setPC(new_PC, tid); +} - // Walk through the instruction list, removing any instructions - // that were inserted after the given instruction, inst. - while (instList.back() != inst) - { - assert(!instList.empty()); +template <class Impl> +uint64_t +FullO3CPU<Impl>::readNextPC(unsigned tid) +{ + return commit.readNextPC(tid); +} - // Obtain the pointer to the instruction. - inst_to_delete = instList.back(); +template <class Impl> +void +FullO3CPU<Impl>::setNextPC(uint64_t val,unsigned tid) +{ + commit.setNextPC(val, tid); +} - DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n", - inst_to_delete->seqNum, inst_to_delete->readPC()); +template <class Impl> +typename FullO3CPU<Impl>::ListIt +FullO3CPU<Impl>::addInst(DynInstPtr &inst) +{ + instList.push_back(inst); - // Remove the instruction from the list. - instList.pop_back(); + return --(instList.end()); +} - // Mark it as squashed. - inst_to_delete->setSquashed(); - } +template <class Impl> +void +FullO3CPU<Impl>::instDone(unsigned tid) +{ + // Keep an instruction count. + thread[tid]->numInst++; + thread[tid]->numInsts++; + committedInsts[tid]++; + totalCommittedInsts++; + + // Check for instruction-count-based events. + comInstEventQueue[tid]->serviceEvents(thread[tid]->numInst); +} + +template <class Impl> +void +FullO3CPU<Impl>::addToRemoveList(DynInstPtr &inst) +{ + removeInstsThisCycle = true; + + removeList.push(inst->getInstListIt()); } template <class Impl> void FullO3CPU<Impl>::removeFrontInst(DynInstPtr &inst) { - DynInstPtr inst_to_remove; + DPRINTF(FullCPU, "FullCPU: Removing committed instruction [tid:%i] PC %#x " + "[sn:%lli]\n", + inst->threadNumber, inst->readPC(), inst->seqNum); - // The front instruction should be the same one being asked to be removed. - assert(instList.front() == inst); + 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 <class Impl> void -FullO3CPU<Impl>::removeInstsNotInROB() +FullO3CPU<Impl>::removeInstsNotInROB(unsigned tid) { - DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction " - "list.\n"); + DPRINTF(FullCPU, "FullCPU: Thread %i: Deleting instructions from instruction" + " list.\n", tid); + + ListIt end_it; + + bool rob_empty = false; + + 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"); + } - DynInstPtr rob_tail = rob.readTailInst(); + removeInstsThisCycle = true; - removeBackInst(rob_tail); + 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()); + + squashInstIt(inst_it, tid); + + inst_it--; + } + + // If the ROB was empty, then we actually need to remove the first + // instruction as well. + if (rob_empty) { + squashInstIt(inst_it, tid); + } } template <class Impl> void -FullO3CPU<Impl>::removeInstsUntil(const InstSeqNum &seq_num) +FullO3CPU<Impl>::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.\n"); + "list that are from [tid:%i] and above [sn:%lli] (end=%lli).\n", + tid, seq_num, (*inst_iter)->seqNum); - DynInstPtr inst_to_delete; + while ((*inst_iter)->seqNum > seq_num) { - while (instList.back()->seqNum > seq_num) { - assert(!instList.empty()); + bool break_loop = (inst_iter == instList.begin()); - // Obtain the pointer to the instruction. - inst_to_delete = instList.back(); + squashInstIt(inst_iter, tid); - DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n", - inst_to_delete->seqNum, inst_to_delete->readPC()); + inst_iter--; - // Remove the instruction from the list. - instList.back() = NULL; - instList.pop_back(); + if (break_loop) + break; + } +} + +template <class Impl> +inline void +FullO3CPU<Impl>::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. - inst_to_delete->setSquashed(); - } + (*instIt)->setSquashed(); + // @todo: Formulate a consistent method for deleting + // instructions from the instruction list + // Remove the instruction from the list. + removeList.push(instIt); + } } template <class Impl> void +FullO3CPU<Impl>::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 <class Impl> +void FullO3CPU<Impl>::removeAllInsts() { instList.clear(); } - +*/ template <class Impl> void FullO3CPU<Impl>::dumpInsts() { int num = 0; - typename list<DynInstPtr>::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 <class Impl> void FullO3CPU<Impl>::wakeDependents(DynInstPtr &inst) { iew.wakeDependents(inst); } +*/ +template <class Impl> +void +FullO3CPU<Impl>::wakeCPU() +{ + if (activityRec.active() || tickEvent.scheduled()) { + DPRINTF(Activity, "CPU already running.\n"); + return; + } + + DPRINTF(Activity, "Waking up CPU\n"); + + idleCycles += (curTick - 1) - lastRunningCycle; + + tickEvent.schedule(curTick); +} + +template <class Impl> +int +FullO3CPU<Impl>::getFreeTid() +{ + for (int i=0; i < numThreads; i++) { + if (!tids[i]) { + tids[i] = true; + return i; + } + } + + return -1; +} + +template <class Impl> +void +FullO3CPU<Impl>::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 <class Impl> +void +FullO3CPU<Impl>::updateThreadPriority() +{ + if (activeThreads.size() > 1) + { + //DEFAULT TO ROUND ROBIN SCHEME + //e.g. Move highest priority to end of thread list + list<unsigned>::iterator list_begin = activeThreads.begin(); + list<unsigned>::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<AlphaSimpleImpl>; diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index f7c80e8a1..ff41a3306 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -24,33 +24,36 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -//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_CPU_HH__ +#define __CPU_O3_CPU_HH__ #include <iostream> #include <list> +#include <queue> +#include <set> #include <vector> +#include "arch/isa_traits.hh" #include "base/statistics.hh" #include "base/timebuf.hh" #include "config/full_system.hh" +#include "cpu/activity.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/simple_thread.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; +template <class> +class Checker; +class ThreadContext; +class MemObject; class Process; class BaseFullCPU : public BaseCPU @@ -59,59 +62,79 @@ 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(); + + int readCpuId() { return cpu_id; } protected: int cpu_id; }; +/** + * FullO3CPU class, has each of the stages (fetch through commit) + * within it, as well as all of the time buffers between stages. The + * tick() function for the CPU is defined here. + */ template <class Impl> class FullO3CPU : public BaseFullCPU { public: - //Put typedefs from the Impl here. + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + + // Typedefs from the Impl here. typedef typename Impl::CPUPol CPUPolicy; typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; + typedef O3ThreadState<Impl> Thread; + + typedef typename std::list<DynInstPtr>::iterator ListIt; + public: enum Status { Running, Idle, Halted, - Blocked // ? + Blocked, + SwitchedOut }; + /** Overall CPU status. */ Status _status; private: class TickEvent : public Event { private: + /** Pointer to the CPU. */ FullO3CPU<Impl> *cpu; public: + /** Constructs a tick event. */ TickEvent(FullO3CPU<Impl> *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()) - tickEvent.reschedule(curTick + delay); + tickEvent.reschedule(curTick + cycles(delay)); else if (!tickEvent.scheduled()) - tickEvent.schedule(curTick + delay); + tickEvent.schedule(curTick + cycles(delay)); } - /// Unschedule tick event, regardless of its current state. + /** Unschedule tick event, regardless of its current state. */ void unscheduleTickEvent() { if (tickEvent.scheduled()) @@ -119,25 +142,83 @@ 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); - void switchOut(); + /** 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) { panic("Unimplemented!"); } + + /** Switches out this CPU. */ + void switchOut(Sampler *sampler); + + /** Signals to this CPU that a stage has completed switching out. */ + void signalSwitched(); + + /** Takes over from another CPU. */ void takeOverFrom(BaseCPU *oldCPU); /** Get the current instruction sequence number, and increment it. */ - InstSeqNum getAndIncrementInstSeq(); + InstSeqNum getAndIncrementInstSeq() + { return globalSeqNum++; } #if FULL_SYSTEM /** Check if this address is a valid instruction address. */ @@ -147,27 +228,24 @@ 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); } - - bool validDataAddr(Addr addr) - { return thread[0]->validDataAddr(addr); } + /** Get instruction asid. */ + int getInstAsid(unsigned tid) + { return thread[tid]->getInstAsid(); } - int getInstAsid() { return thread[0]->getInstAsid(); } - int getDataAsid() { return thread[0]->getDataAsid(); } + /** Get data asid. */ + int getDataAsid(unsigned tid) + { return thread[tid]->getDataAsid(); } #endif - // - // New accessors for new decoder. - // + /** Register accessors. Index refers to the physical register index. */ uint64_t readIntReg(int reg_idx); FloatReg readFloatReg(int reg_idx); @@ -180,103 +258,115 @@ class FullO3CPU : public BaseFullCPU void setIntReg(int reg_idx, uint64_t val); - void setFloatReg(int reg_idx, FloatReg val, int width); + void setFloatReg(int reg_idx, FloatReg val); void setFloatReg(int reg_idx, FloatReg val, int width); void setFloatRegBits(int reg_idx, FloatRegBits val); - void setFloatRegBits(int reg_idx, FloatRegBits val); + void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + uint64_t readArchIntReg(int reg_idx, unsigned tid); + + float readArchFloatRegSingle(int reg_idx, unsigned tid); + + double readArchFloatRegDouble(int reg_idx, unsigned tid); + + uint64_t readArchFloatRegInt(int reg_idx, unsigned tid); + + /** Architectural register accessors. Looks up in the commit + * rename table to obtain the true physical index of the + * architected register first, then accesses that physical + * register. + */ + void setArchIntReg(int reg_idx, uint64_t val, unsigned tid); + + void setArchFloatRegSingle(int reg_idx, float val, unsigned tid); - uint64_t readPC(); + void setArchFloatRegDouble(int reg_idx, double val, unsigned tid); - void setNextPC(uint64_t val); + void setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid); - void setPC(Addr new_PC); + /** Reads the commit PC of a specific thread. */ + uint64_t readPC(unsigned tid); + + /** Sets the commit PC of a specific thread. */ + void setPC(Addr new_PC, unsigned tid); + + /** Reads the next PC of a specific thread. */ + uint64_t readNextPC(unsigned tid); + + /** Sets the next PC of a specific thread. */ + 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(); - - /** 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); - - /** 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 - * than the instruction being removed). Used when retiring instructions. - * @todo: Remove the argument to this function, and just have it remove - * last instruction once it's verified that commit has the same ordering - * as the instruction list. + void instDone(unsigned tid); + + /** Add Instructions to the CPU Remove List*/ + void addToRemoveList(DynInstPtr &inst); + + /** Remove an instruction from the front end of the list. There's + * no restriction on location of the instruction. */ 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); - /** Remove all instructions from the list. */ - void removeAllInsts(); + /** Removes the instruction pointed to by the iterator. */ + inline void squashInstIt(const ListIt &instIt, const unsigned &tid); - void dumpInsts(); + /** Cleans up all instructions on the remove list. */ + void cleanUpRemovedInsts(); - /** Basically a wrapper function so that instructions executed at - * commit can tell the instruction queue that they have completed. - * Eventually this hack should be removed. - */ - void wakeDependents(DynInstPtr &inst); + /** Debug function to print all instructions on the list. */ + void dumpInsts(); public: /** List of all the instructions in flight. */ - list<DynInstPtr> instList; + std::list<DynInstPtr> instList; + + /** List of all the instructions that will be removed at the end of this + * cycle. + */ + std::queue<ListIt> removeList; + +#ifdef DEBUG + /** Debug structure to keep track of the sequence numbers still in + * flight. + */ + std::set<InstSeqNum> 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; @@ -284,12 +374,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<unsigned> 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. */ @@ -318,46 +429,109 @@ class FullO3CPU : public BaseFullCPU /** The IEW stage's instruction queue. */ TimeBuffer<IEWStruct> iewQueue; + private: + /** The activity recorder; used to tell if the CPU has any + * activity remaining or if it can go to idle and deschedule + * itself. + */ + ActivityRecorder activityRec; + public: - /** The temporary exec context to support older accessors. */ - CPUExecContext *cpuXC; + /** Records that there was time buffer activity this cycle. */ + void activityThisCycle() { activityRec.activity(); } - /** Temporary function to get pointer to exec context. */ - ExecContext *xcBase() - { - return thread[0]->getProxy(); - } + /** Changes a stage's status to active within the activity recorder. */ + void activateStage(const StageIdx idx) + { activityRec.activateStage(idx); } + + /** Changes a stage's status to inactive within the activity recorder. */ + void deactivateStage(const StageIdx idx) + { activityRec.deactivateStage(idx); } - CPUExecContext *cpuXCBase() + /** Wakes the CPU, rescheduling the CPU if it's not already active. */ + void wakeCPU(); + + /** Gets a free thread id. Use if thread ids change across system. */ + int getFreeTid(); + + public: + /** Returns a pointer to a thread context. */ + ThreadContext *tcBase(unsigned tid) { - return thread[0]; + return thread[tid]->getTC(); } + /** The global sequence number counter. */ InstSeqNum globalSeqNum; + /** Pointer to the checker, which can dynamically verify + * instruction results at run time. This can be set to NULL if it + * is not being used. + */ + Checker<DynInstPtr> *checker; + #if FULL_SYSTEM + /** Pointer to the system. */ System *system; - MemoryController *memCtrl; + /** Pointer to physical memory. */ PhysicalMemory *physmem; +#endif - AlphaITB *itb; - AlphaDTB *dtb; + /** Pointer to memory. */ + MemObject *mem; -// SWContext *swCtx; -#endif - std::vector<CPUExecContext *> thread; + /** Pointer to the sampler */ + Sampler *sampler; + + /** Counter of how many stages have completed switching out. */ + int switchCount; - FunctionalMemory *mem; + /** Pointers to all of the threads in the CPU. */ + std::vector<Thread *> thread; + /** 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; - - Counter funcExeInst; + /** Is there a context switch pending? */ + bool contextSwitch; + + /** Threads Scheduled to Enter CPU */ + std::list<int> 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<unsigned,unsigned> threadMap; + + /** Available thread ids in the cpu*/ + std::vector<unsigned> 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 +#endif // __CPU_O3_CPU_HH__ diff --git a/src/cpu/o3/cpu_policy.hh b/src/cpu/o3/cpu_policy.hh index 41f06f81b..32a0adcf1 100644 --- a/src/cpu/o3/cpu_policy.hh +++ b/src/cpu/o3/cpu_policy.hh @@ -24,15 +24,18 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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" @@ -47,42 +50,70 @@ #include "cpu/o3/comm.hh" +/** + * Struct that defines the key classes to be used by the CPU. All + * classes use the typedefs defined here to determine what are the + * classes of the other stages and communication buffers. In order to + * change a structure such as the IQ, simply change the typedef here + * to use the desired class instead, and recompile. In order to + * create a different CPU to be used simultaneously with this one, see + * the alpha_impl.hh file for instructions. + */ template<class Impl> struct SimpleCPUPolicy { - typedef TwobitBPredUnit<Impl> BPredUnit; + /** Typedef for the branch prediction unit (which includes the BP, + * RAS, and BTB). + */ + typedef BPredUnit<Impl> BPredUnit; + /** Typedef for the register file. Most classes assume a unified + * physical register file. + */ typedef PhysRegFile<Impl> RegFile; + /** Typedef for the freelist of registers. */ typedef SimpleFreeList FreeList; + /** Typedef for the rename map. */ typedef SimpleRenameMap RenameMap; + /** Typedef for the ROB. */ typedef ROB<Impl> ROB; + /** Typedef for the instruction queue/scheduler. */ typedef InstructionQueue<Impl> IQ; + /** Typedef for the memory dependence unit. */ typedef MemDepUnit<StoreSet, Impl> MemDepUnit; - typedef LDSTQ<Impl> LDSTQ; + /** Typedef for the LSQ. */ + typedef LSQ<Impl> LSQ; + /** Typedef for the thread-specific LSQ units. */ + typedef LSQUnit<Impl> LSQUnit; - typedef SimpleFetch<Impl> Fetch; - typedef SimpleDecode<Impl> Decode; - typedef SimpleRename<Impl> Rename; - typedef SimpleIEW<Impl> IEW; - typedef SimpleCommit<Impl> Commit; + /** Typedef for fetch. */ + typedef DefaultFetch<Impl> Fetch; + /** Typedef for decode. */ + typedef DefaultDecode<Impl> Decode; + /** Typedef for rename. */ + typedef DefaultRename<Impl> Rename; + /** Typedef for Issue/Execute/Writeback. */ + typedef DefaultIEW<Impl> IEW; + /** Typedef for commit. */ + typedef DefaultCommit<Impl> Commit; /** The struct for communication between fetch and decode. */ - typedef SimpleFetchSimpleDecode<Impl> FetchStruct; + typedef DefaultFetchDefaultDecode<Impl> FetchStruct; /** The struct for communication between decode and rename. */ - typedef SimpleDecodeSimpleRename<Impl> DecodeStruct; + typedef DefaultDecodeDefaultRename<Impl> DecodeStruct; /** The struct for communication between rename and IEW. */ - typedef SimpleRenameSimpleIEW<Impl> RenameStruct; + typedef DefaultRenameDefaultIEW<Impl> RenameStruct; /** The struct for communication between IEW and commit. */ - typedef SimpleIEWSimpleCommit<Impl> IEWStruct; + typedef DefaultIEWDefaultCommit<Impl> IEWStruct; /** The struct for communication within the IEW stage. */ typedef IssueStruct<Impl> IssueStruct; /** The struct for all backwards communication. */ - typedef TimeBufStruct TimeStruct; + typedef TimeBufStruct<Impl> TimeStruct; }; -#endif //__CPU_O3_CPU_CPU_POLICY_HH__ +#endif //__CPU_O3_CPU_POLICY_HH__ diff --git a/src/cpu/o3/decode.cc b/src/cpu/o3/decode.cc index 290648318..4924f018a 100644 --- a/src/cpu/o3/decode.cc +++ b/src/cpu/o3/decode.cc @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/decode_impl.hh" -template class SimpleDecode<AlphaSimpleImpl>; +template class DefaultDecode<AlphaSimpleImpl>; diff --git a/src/cpu/o3/decode.hh b/src/cpu/o3/decode.hh index 5b9a0f822..ff88358d6 100644 --- a/src/cpu/o3/decode.hh +++ b/src/cpu/o3/decode.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,18 +24,27 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <queue> #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 Impl> -class SimpleDecode +class DefaultDecode { private: // Typedefs from the Impl. @@ -50,49 +59,132 @@ 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<TimeStruct> *tb_ptr); + /** Sets pointer to time buffer used to communicate to the next stage. */ void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr); + /** Sets pointer to time buffer coming from fetch. */ void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Switches out the decode stage. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** 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 +219,27 @@ class SimpleDecode /** Wire to get fetch's output from fetch queue. */ typename TimeBuffer<FetchStruct>::wire fromFetch; + /** Queue of all instructions coming from fetch this cycle. */ + std::queue<DynInstPtr> insts[Impl::MaxThreads]; + /** Skid buffer between fetch and decode. */ - std::queue<FetchStruct> skidBuffer; + std::queue<DynInstPtr> 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 +255,43 @@ 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<unsigned> *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 is resolved at decode. */ + Stats::Scalar<> decodeBranchResolved; + /** 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/src/cpu/o3/decode_impl.hh b/src/cpu/o3/decode_impl.hh index 463f0ddac..8a6ea6626 100644 --- a/src/cpu/o3/decode_impl.hh +++ b/src/cpu/o3/decode_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,75 +24,105 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/decode.hh" +using namespace std; + template<class Impl> -SimpleDecode<Impl>::SimpleDecode(Params ¶ms) - : renameToDecodeDelay(params.renameToDecodeDelay), - iewToDecodeDelay(params.iewToDecodeDelay), - commitToDecodeDelay(params.commitToDecodeDelay), - fetchToDecodeDelay(params.fetchToDecodeDelay), - decodeWidth(params.decodeWidth), - numInst(0) +DefaultDecode<Impl>::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; + _status = Inactive; + + // Setup status, make sure stall signals are clear. + 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 <class Impl> +std::string +DefaultDecode<Impl>::name() const +{ + return cpu->name() + ".decode"; } template <class Impl> void -SimpleDecode<Impl>::regStats() +DefaultDecode<Impl>::regStats() { decodeIdleCycles - .name(name() + ".decodeIdleCycles") + .name(name() + ".DECODE:IdleCycles") .desc("Number of cycles decode is idle") .prereq(decodeIdleCycles); decodeBlockedCycles - .name(name() + ".decodeBlockedCycles") + .name(name() + ".DECODE:BlockedCycles") .desc("Number of cycles decode is blocked") .prereq(decodeBlockedCycles); + decodeRunCycles + .name(name() + ".DECODE:RunCycles") + .desc("Number of cycles decode is running") + .prereq(decodeRunCycles); decodeUnblockCycles - .name(name() + ".decodeUnblockCycles") + .name(name() + ".DECODE:UnblockCycles") .desc("Number of cycles decode is unblocking") .prereq(decodeUnblockCycles); decodeSquashCycles - .name(name() + ".decodeSquashCycles") + .name(name() + ".DECODE:SquashCycles") .desc("Number of cycles decode is squashing") .prereq(decodeSquashCycles); + decodeBranchResolved + .name(name() + ".DECODE:BranchResolved") + .desc("Number of times decode resolved a branch") + .prereq(decodeBranchResolved); decodeBranchMispred - .name(name() + ".decodeBranchMispred") + .name(name() + ".DECODE:BranchMispred") .desc("Number of times decode detected a branch misprediction") .prereq(decodeBranchMispred); decodeControlMispred - .name(name() + ".decodeControlMispred") + .name(name() + ".DECODE:ControlMispred") .desc("Number of times decode detected an instruction incorrectly" " predicted as a control") .prereq(decodeControlMispred); decodeDecodedInsts - .name(name() + ".decodeDecodedInsts") + .name(name() + ".DECODE:DecodedInsts") .desc("Number of instructions handled by decode") .prereq(decodeDecodedInsts); decodeSquashedInsts - .name(name() + ".decodeSquashedInsts") + .name(name() + ".DECODE:SquashedInsts") .desc("Number of squashed instructions handled by decode") .prereq(decodeSquashedInsts); } template<class Impl> void -SimpleDecode<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultDecode<Impl>::setCPU(FullCPU *cpu_ptr) { - DPRINTF(Decode, "Decode: Setting CPU pointer.\n"); + DPRINTF(Decode, "Setting CPU pointer.\n"); cpu = cpu_ptr; } template<class Impl> void -SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *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 +136,9 @@ SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) template<class Impl> void -SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) +DefaultDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *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,9 +147,9 @@ SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) template<class Impl> void -SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +DefaultDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *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. @@ -127,250 +157,541 @@ SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) } template<class Impl> +void +DefaultDecode<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Decode, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +DefaultDecode<Impl>::switchOut() +{ + // Decode can immediately switch out. + cpu->signalSwitched(); +} + +template <class Impl> +void +DefaultDecode<Impl>::takeOverFrom() +{ + _status = Inactive; + + // Be sure to reset state and clear out any old instructions. + for (int i = 0; i < numThreads; ++i) { + decodeStatus[i] = Idle; + + stalls[i].rename = false; + stalls[i].iew = false; + stalls[i].commit = false; + while (!insts[i].empty()) + insts[i].pop(); + while (!skidBuffer[i].empty()) + skidBuffer[i].pop(); + branchCount[i] = 0; + } + wroteToTimeBuffer = false; +} + +template<class Impl> +bool +DefaultDecode<Impl>::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<class Impl> inline bool -SimpleDecode<Impl>::fetchInstsValid() +DefaultDecode<Impl>::fetchInstsValid() { return fromFetch->size > 0; } template<class Impl> -void -SimpleDecode<Impl>::block() +bool +DefaultDecode<Impl>::block(unsigned tid) { - DPRINTF(Decode, "Decode: Blocking.\n"); - - // Set the status to Blocked. - _status = Blocked; + DPRINTF(Decode, "[tid:%u]: Blocking.\n", tid); // Add the current inputs to the skid buffer so they can be // reprocessed when this stage unblocks. - skidBuffer.push(*fromFetch); + skidInsert(tid); + + // 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) { + // Set the status to Blocked. + decodeStatus[tid] = Blocked; + + if (decodeStatus[tid] != Unblocking) { + toFetch->decodeBlock[tid] = true; + wroteToTimeBuffer = true; + } - // 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. + return true; + } + + return false; } template<class Impl> -inline void -SimpleDecode<Impl>::unblock() +bool +DefaultDecode<Impl>::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(); - - // 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; } + + DPRINTF(Decode, "[tid:%u]: Currently unblocking.\n", tid); + + return false; } -// This squash is specifically for when Decode detects a PC-relative branch -// was predicted incorrectly. template<class Impl> void -SimpleDecode<Impl>::squash(DynInstPtr &inst) +DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) { - DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction " - "detected at decode.\n"); - Addr new_PC = inst->readNextPC(); - - 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; + DPRINTF(Decode, "[tid:%i]: Squashing due to incorrect branch prediction " + "detected at decode.\n", tid); + + // Send back mispredict information. + 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->branchTarget(); + toFetch->decodeInfo[tid].branchTaken = + inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); + + // Might have to tell fetch to unblock. + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { + toFetch->decodeUnblock[tid] = 1; + } // Set status to squashing. - _status = Squashing; + decodeStatus[tid] = Squashing; + + for (int i=0; i<fromFetch->size; i++) { + if (fromFetch->insts[i]->threadNumber == tid && + fromFetch->insts[i]->seqNum > inst->seqNum) { + fromFetch->insts[i]->squashed = true; + } + } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. + 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<class Impl> -void -SimpleDecode<Impl>::squash() +unsigned +DefaultDecode<Impl>::squash(unsigned tid) { - DPRINTF(Decode, "Decode: Squashing.\n"); + 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. + // @todo: Determine if this still happens. + if (toFetch->decodeBlock[tid]) { + toFetch->decodeBlock[tid] = 0; + } else { + toFetch->decodeUnblock[tid] = 1; + } +#else + toFetch->decodeUnblock[tid] = 1; +#endif + } + // Set status to squashing. - _status = Squashing; + decodeStatus[tid] = Squashing; - // Maybe advance the time buffer? Not sure what to do in the normal - // case. + // Go through incoming instructions from fetch and squash them. + unsigned squash_count = 0; - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) - { - skidBuffer.pop(); + for (int i=0; i<fromFetch->size; i++) { + if (fromFetch->insts[i]->threadNumber == tid) { + fromFetch->insts[i]->squashed = true; + squash_count++; + } } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. + while (!insts[tid].empty()) { + insts[tid].pop(); + } + + while (!skidBuffer[tid].empty()) { + skidBuffer[tid].pop(); + } + + return squash_count; } template<class Impl> void -SimpleDecode<Impl>::tick() +DefaultDecode<Impl>::skidInsert(unsigned tid) { - // Decode should try to execute 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"); - // Make sure that the skid buffer has something in it if the - // status is unblocking. - assert(_status == Unblocking ? !skidBuffer.empty() : 1); + DynInstPtr inst = NULL; - decode(); + while (!insts[tid].empty()) { + inst = insts[tid].front(); - // 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; + insts[tid].pop(); - if (fetchInstsValid()) { - // Add the current inputs to the skid buffer so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromFetch); - } + assert(tid == inst->threadNumber); - unblock(); - } - } else if (_status == Blocked) { - ++decodeBlockedCycles; + DPRINTF(Decode,"Inserting [sn:%lli] PC:%#x into decode skidBuffer %i\n", + inst->seqNum, inst->readPC(), inst->threadNumber); - if (fetchInstsValid()) { - block(); - } + skidBuffer[tid].push(inst); + } - if (!fromRename->renameInfo.stall && - !fromIEW->iewInfo.stall && - !fromCommit->commitInfo.stall) { - DPRINTF(Decode, "Decode: Stall signals cleared, going to " - "unblock.\n"); - _status = Unblocking; + // @todo: Eventually need to enforce this by not letting a thread + // fetch past its skidbuffer + assert(skidBuffer[tid].size() <= skidBufferMax); +} - // 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; +template<class Impl> +bool +DefaultDecode<Impl>::skidsEmpty() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template<class Impl> +void +DefaultDecode<Impl>::updateStatus() +{ + bool any_unblocking = false; + + list<unsigned>::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"); - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); + cpu->activateStage(FullCPU::DecodeIdx); } - } else if (_status == Squashing) { - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - _status = Running; - } else if (fromCommit->commitInfo.squash) { - ++decodeSquashCycles; - - squash(); + } 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 <class Impl> +void +DefaultDecode<Impl>::sortInsts() +{ + int insts_from_fetch = fromFetch->size; +#ifdef DEBUG + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_fetch; ++i) { + insts[fromFetch->insts[i]->threadNumber].push(fromFetch->insts[i]); + } +} + template<class Impl> void -SimpleDecode<Impl>::decode() +DefaultDecode<Impl>::readStallSignals(unsigned tid) { - // Check time buffer if being told to squash. - if (fromCommit->commitInfo.squash) { - squash(); - return; + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; } - // Check time buffer if being told to stall. - if (fromRename->renameInfo.stall || - fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) { - block(); - return; + 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 <class Impl> +bool +DefaultDecode<Impl>::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); } - // 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 (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<class Impl> +void +DefaultDecode<Impl>::tick() +{ + wroteToTimeBuffer = false; + + bool status_change = false; + + toRenameIndex = 0; + + list<unsigned>::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<class Impl> +void +DefaultDecode<Impl>::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 (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(!skidsEmpty()); + + // If the status was unblocking, then instructions from the skid + // buffer were used. Remove those instructions and handle + // the rest of unblocking. + decodeInsts(tid); + + if (fetchInstsValid()) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + status_change = unblock(tid) || status_change; + } +} + +template <class Impl> +void +DefaultDecode<Impl>::decodeInsts(unsigned tid) +{ + // 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(); + + 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<DynInstPtr> + &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 +699,12 @@ SimpleDecode<Impl>::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 +712,40 @@ SimpleDecode<Impl>::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()) { + ++decodeBranchResolved; - inst->setNextPC(inst->branchTarget()); - - if (inst->mispredicted()) { + if (inst->branchTarget() != inst->readPredTarg()) { ++decodeBranchMispred; + // Might want to set some sort of boolean and just do // a check at the end - squash(inst); + squash(inst, inst->threadNumber); + inst->setPredTarg(inst->branchTarget()); + 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; + // 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); } - numInst = 0; + // Record that decode has written to the time buffer for activity + // tracking. + if (toRenameIndex) { + wroteToTimeBuffer = true; + } } diff --git a/src/cpu/o3/dep_graph.hh b/src/cpu/o3/dep_graph.hh new file mode 100644 index 000000000..3659b1a37 --- /dev/null +++ b/src/cpu/o3/dep_graph.hh @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_DEP_GRAPH_HH__ +#define __CPU_O3_DEP_GRAPH_HH__ + +#include "cpu/o3/comm.hh" + +/** Node in a linked list. */ +template <class DynInstPtr> +class DependencyEntry +{ + public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + + DynInstPtr inst; + //Might want to include data about what arch. register the + //dependence is waiting on. + DependencyEntry<DynInstPtr> *next; +}; + +/** Array of linked list that maintains the dependencies between + * producing instructions and consuming instructions. Each linked + * list represents a single physical register, having the future + * producer of the register's value, and all consumers waiting on that + * value on the list. The head node of each linked list represents + * the producing instruction of that register. Instructions are put + * on the list upon reaching the IQ, and are removed from the list + * either when the producer completes, or the instruction is squashed. +*/ +template <class DynInstPtr> +class DependencyGraph +{ + public: + typedef DependencyEntry<DynInstPtr> DepEntry; + + /** Default construction. Must call resize() prior to use. */ + DependencyGraph() + : numEntries(0), memAllocCounter(0), nodesTraversed(0), nodesRemoved(0) + { } + + /** Resize the dependency graph to have num_entries registers. */ + void resize(int num_entries); + + /** Clears all of the linked lists. */ + void reset(); + + /** Inserts an instruction to be dependent on the given index. */ + void insert(PhysRegIndex idx, DynInstPtr &new_inst); + + /** Sets the producing instruction of a given register. */ + void setInst(PhysRegIndex idx, DynInstPtr &new_inst) + { dependGraph[idx].inst = new_inst; } + + /** Clears the producing instruction. */ + void clearInst(PhysRegIndex idx) + { dependGraph[idx].inst = NULL; } + + /** Removes an instruction from a single linked list. */ + void remove(PhysRegIndex idx, DynInstPtr &inst_to_remove); + + /** Removes and returns the newest dependent of a specific register. */ + DynInstPtr pop(PhysRegIndex idx); + + /** Checks if there are any dependents on a specific register. */ + bool empty(PhysRegIndex idx) { return !dependGraph[idx].next; } + + /** Debugging function to dump out the dependency graph. + */ + void dump(); + + private: + /** Array of linked lists. Each linked list is a list of all the + * instructions that depend upon a given register. The actual + * register's index is used to index into the graph; ie all + * instructions in flight that are dependent upon r34 will be + * in the linked list of dependGraph[34]. + */ + DepEntry *dependGraph; + + /** Number of linked lists; identical to the number of registers. */ + int numEntries; + + // Debug variable, remove when done testing. + unsigned memAllocCounter; + + public: + // Debug variable, remove when done testing. + uint64_t nodesTraversed; + // Debug variable, remove when done testing. + uint64_t nodesRemoved; +}; + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::resize(int num_entries) +{ + numEntries = num_entries; + dependGraph = new DepEntry[numEntries]; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::reset() +{ + // Clear the dependency graph + DepEntry *curr; + DepEntry *prev; + + for (int i = 0; i < numEntries; ++i) { + curr = dependGraph[i].next; + + while (curr) { + memAllocCounter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + if (dependGraph[i].inst) { + dependGraph[i].inst = NULL; + } + + dependGraph[i].next = NULL; + } +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::insert(PhysRegIndex idx, DynInstPtr &new_inst) +{ + //Add this new, dependent instruction at the head of the dependency + //chain. + + // First create the entry that will be added to the head of the + // dependency chain. + DepEntry *new_entry = new DepEntry; + new_entry->next = dependGraph[idx].next; + new_entry->inst = new_inst; + + // Then actually add it to the chain. + dependGraph[idx].next = new_entry; + + ++memAllocCounter; +} + + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::remove(PhysRegIndex idx, + DynInstPtr &inst_to_remove) +{ + DepEntry *prev = &dependGraph[idx]; + DepEntry *curr = dependGraph[idx].next; + + // Make sure curr isn't NULL. Because this instruction is being + // removed from a dependency list, it must have been placed there at + // an earlier time. The dependency chain should not be empty, + // unless the instruction dependent upon it is already ready. + if (curr == NULL) { + return; + } + + nodesRemoved++; + + // Find the instruction to remove within the dependency linked list. + while (curr->inst != inst_to_remove) { + prev = curr; + curr = curr->next; + nodesTraversed++; + + assert(curr != NULL); + } + + // Now remove this instruction from the list. + prev->next = curr->next; + + --memAllocCounter; + + // Could push this off to the destructor of DependencyEntry + curr->inst = NULL; + + delete curr; +} + +template <class DynInstPtr> +DynInstPtr +DependencyGraph<DynInstPtr>::pop(PhysRegIndex idx) +{ + DepEntry *node; + node = dependGraph[idx].next; + DynInstPtr inst = NULL; + if (node) { + inst = node->inst; + dependGraph[idx].next = node->next; + node->inst = NULL; + memAllocCounter--; + delete node; + } + return inst; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::dump() +{ + DepEntry *curr; + + for (int i = 0; i < numEntries; ++i) + { + curr = &dependGraph[i]; + + if (curr->inst) { + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); + } else { + cprintf("dependGraph[%i]: No producer. consumer: ", i); + } + + while (curr->next != NULL) { + curr = curr->next; + + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); + } + + cprintf("\n"); + } + cprintf("memAllocCounter: %i\n", memAllocCounter); +} + +#endif // __CPU_O3_DEP_GRAPH_HH__ diff --git a/src/cpu/o3/fetch.cc b/src/cpu/o3/fetch.cc index 8ad5e6565..5f52d0fca 100644 --- a/src/cpu/o3/fetch.cc +++ b/src/cpu/o3/fetch.cc @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/fetch_impl.hh" -template class SimpleFetch<AlphaSimpleImpl>; +template class DefaultFetch<AlphaSimpleImpl>; diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh index cc64800d9..76b32de68 100644 --- a/src/cpu/o3/fetch.hh +++ b/src/cpu/o3/fetch.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,28 +24,33 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// 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 "arch/utility.hh" #include "base/statistics.hh" #include "base/timebuf.hh" #include "cpu/pc_event.hh" -#include "mem/mem_interface.hh" +#include "mem/packet.hh" +#include "mem/port.hh" #include "sim/eventq.hh" +class Sampler; + /** - * 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 functionality of the CPU by indicating to + * the CPU when it is active and inactive. */ - template <class Impl> -class SimpleFetch +class DefaultFetch { public: /** Typedefs from Impl. */ @@ -55,56 +60,153 @@ 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; + + /** IcachePort class for DefaultFetch. Handles doing the + * communication with the cache/memory. + */ + class IcachePort : public Port + { + protected: + /** Pointer to fetch. */ + DefaultFetch<Impl> *fetch; + + public: + /** Default constructor. */ + IcachePort(DefaultFetch<Impl> *_fetch) + : Port(_fetch->name() + "-iport"), fetch(_fetch) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles setting fetch to the + * proper status to start fetching. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of a failed fetch. */ + virtual void recvRetry(); + }; 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, - IcacheMissStall, - IcacheMissComplete + Fetching, + TrapPending, + QuiescePending, + SwitchOut, + IcacheWaitResponse, + IcacheWaitRetry, + IcacheAccessComplete }; - // 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; - public: - class CacheCompletionEvent : public Event - { - private: - SimpleFetch *fetch; + /** Per-thread status. */ + ThreadStatus fetchStatus[Impl::MaxThreads]; - public: - CacheCompletionEvent(SimpleFetch *_fetch); + /** Fetch policy. */ + FetchPriority fetchPolicy; - virtual void process(); - virtual const char *description(); - }; + /** List that has the threads organized by priority. */ + std::list<unsigned> priorityList; 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<TimeStruct> *time_buffer); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to time buffer used to communicate to the next stage. */ void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); - void processCacheCompletion(); + /** Initialize stage. */ + void initStage(); + + /** Processes cache completion event. */ + void processCacheCompletion(PacketPtr pkt); + + /** Begins the switch out of the fetch stage. */ + void switchOut(); + + /** Completes the switch out of the fetch stage. */ + void doSwitchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Checks if the fetch stage is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Tells fetch to wake up from a quiesce instruction. */ + 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,24 +222,54 @@ 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); + + /** 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 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. + /** Align a PC to the start of an I-cache block. */ Addr icacheBlockAlignPC(Addr addr) { addr = TheISA::realPCToFetchPC(addr); @@ -145,6 +277,25 @@ class SimpleFetch } private: + /** Handles retrying the fetch access. */ + void recvRetry(); + + /** 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; @@ -170,14 +321,41 @@ class SimpleFetch /** Wire used to write any information heading to decode. */ typename TimeBuffer<FetchStruct>::wire toDecode; + MemObject *mem; + /** Icache interface. */ - MemInterface *icacheInterface; + IcachePort *icachePort; /** BPredUnit. */ BPredUnit branchPred; + /** Per-thread fetch PC. */ + Addr PC[Impl::MaxThreads]; + + /** Per-thread next PC. */ + Addr nextPC[Impl::MaxThreads]; + /** Memory request used to access cache. */ - MemReqPtr memReq; + RequestPtr 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; @@ -194,6 +372,15 @@ class SimpleFetch /** The width of fetch in instructions. */ unsigned fetchWidth; + /** Is the cache blocked? If so no threads can access it. */ + bool cacheBlocked; + + /** The packet that is waiting to be retried. */ + PacketPtr retryPkt; + + /** The thread that is waiting on the cache to tell fetch to retry. */ + int retryTid; + /** Cache block size. */ int cacheBlkSize; @@ -201,23 +388,68 @@ 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<unsigned> *activeThreads; + /** Number of threads. */ + unsigned numThreads; + + /** Number of threads that are actively fetching. */ + unsigned numFetchingThreads; + + /** Thread ID being fetched. */ + int threadFetched; + + /** Checks if there is an interrupt pending. If there is, fetch + * must stop once it is not fetching PAL instructions. + */ + bool interruptPending; + + /** Records if fetch is switched out. */ + bool switchedOut; + + // @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; + Stats::Scalar<> fetchedBranches; + /** 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; + /** Total number of cycles spent blocked. */ Stats::Scalar<> fetchBlockedCycles; + /** Total number of cycles spent in any other state. */ + Stats::Scalar<> fetchMiscStallCycles; + /** Stat for total number of fetched cache lines. */ Stats::Scalar<> fetchedCacheLines; - - Stats::Distribution<> fetch_nisn_dist; + /** Total number of outstanding icache accesses that were dropped + * due to a squash. + */ + Stats::Scalar<> fetchIcacheSquashes; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; + /** Rate of how often fetch was idle. */ + Stats::Formula idleRate; + /** Number of branch fetches per cycle. */ + Stats::Formula branchRate; + /** Number of instruction fetched per cycle. */ + Stats::Formula fetchRate; }; -#endif //__CPU_O3_CPU_SIMPLE_FETCH_HH__ +#endif //__CPU_O3_FETCH_HH__ diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh index 8029fc732..c0a2a5d09 100644 --- a/src/cpu/o3/fetch_impl.hh +++ b/src/cpu/o3/fetch_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,85 +24,161 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// Remove this later; used only for debugging. -#define OPCODE(X) (X >> 26) & 0x3f - #include "arch/isa_traits.hh" -#include "sim/byteswap.hh" +#include "arch/utility.hh" +#include "cpu/checker/cpu.hh" #include "cpu/exetrace.hh" -#include "mem/base_mem.hh" -#include "mem/mem_interface.hh" -#include "mem/mem_req.hh" #include "cpu/o3/fetch.hh" - +#include "mem/packet.hh" +#include "mem/request.hh" +#include "sim/byteswap.hh" +#include "sim/host.hh" #include "sim/root.hh" +#if FULL_SYSTEM +#include "arch/tlb.hh" +#include "arch/vtophys.hh" +#include "base/remote_gdb.hh" +#include "sim/system.hh" +#endif // FULL_SYSTEM + +#include <algorithm> + +using namespace std; +using namespace TheISA; + template<class Impl> -SimpleFetch<Impl>::CacheCompletionEvent -::CacheCompletionEvent(SimpleFetch *_fetch) - : Event(&mainEventQueue), - fetch(_fetch) +Tick +DefaultFetch<Impl>::IcachePort::recvAtomic(PacketPtr pkt) { + panic("DefaultFetch doesn't expect recvAtomic callback!"); + return curTick; } template<class Impl> void -SimpleFetch<Impl>::CacheCompletionEvent::process() +DefaultFetch<Impl>::IcachePort::recvFunctional(PacketPtr pkt) { - fetch->processCacheCompletion(); + panic("DefaultFetch doesn't expect recvFunctional callback!"); } template<class Impl> -const char * -SimpleFetch<Impl>::CacheCompletionEvent::description() +void +DefaultFetch<Impl>::IcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("DefaultFetch doesn't expect recvStatusChange callback!"); +} + +template<class Impl> +bool +DefaultFetch<Impl>::IcachePort::recvTiming(Packet *pkt) { - return "SimpleFetch cache completion event"; + fetch->processCacheCompletion(pkt); + return true; } template<class Impl> -SimpleFetch<Impl>::SimpleFetch(Params ¶ms) - : icacheInterface(params.icacheInterface), +void +DefaultFetch<Impl>::IcachePort::recvRetry() +{ + fetch->recvRetry(); +} + +template<class Impl> +DefaultFetch<Impl>::DefaultFetch(Params *params) + : mem(params->mem), branchPred(params), - decodeToFetchDelay(params.decodeToFetchDelay), - renameToFetchDelay(params.renameToFetchDelay), - iewToFetchDelay(params.iewToFetchDelay), - commitToFetchDelay(params.commitToFetchDelay), - fetchWidth(params.fetchWidth) -{ - DPRINTF(Fetch, "Fetch: Fetch constructor called\n"); - - // Set status to idle. - _status = Idle; - - // 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]; + decodeToFetchDelay(params->decodeToFetchDelay), + renameToFetchDelay(params->renameToFetchDelay), + iewToFetchDelay(params->iewToFetchDelay), + commitToFetchDelay(params->commitToFetchDelay), + fetchWidth(params->fetchWidth), + cacheBlocked(false), + retryPkt(NULL), + retryTid(-1), + numThreads(params->numberOfThreads), + numFetchingThreads(params->smtNumFetchingThreads), + interruptPending(false), + switchedOut(false) +{ + if (numThreads > Impl::MaxThreads) + fatal("numThreads is not a valid value\n"); + + DPRINTF(Fetch, "Fetch constructor called\n"); + + // 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; + cacheBlkSize = 64; // 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); + + memReq[tid] = NULL; + + // 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 <class Impl> +std::string +DefaultFetch<Impl>::name() const +{ + return cpu->name() + ".fetch"; } template <class Impl> void -SimpleFetch<Impl>::regStats() +DefaultFetch<Impl>::regStats() { icacheStallCycles .name(name() + ".icacheStallCycles") @@ -110,58 +186,114 @@ SimpleFetch<Impl>::regStats() .prereq(icacheStallCycles); fetchedInsts - .name(name() + ".fetchedInsts") + .name(name() + ".Insts") .desc("Number of instructions fetch has processed") .prereq(fetchedInsts); + + fetchedBranches + .name(name() + ".Branches") + .desc("Number of branches that fetch encountered") + .prereq(fetchedBranches); + predictedBranches .name(name() + ".predictedBranches") .desc("Number of branches that fetch has predicted taken") .prereq(predictedBranches); + fetchCycles - .name(name() + ".fetchCycles") + .name(name() + ".Cycles") .desc("Number of cycles fetch has run and was not squashing or" " blocked") .prereq(fetchCycles); + fetchSquashCycles - .name(name() + ".fetchSquashCycles") + .name(name() + ".SquashCycles") .desc("Number of cycles fetch has spent squashing") .prereq(fetchSquashCycles); + + fetchIdleCycles + .name(name() + ".IdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + fetchBlockedCycles - .name(name() + ".fetchBlockedCycles") + .name(name() + ".BlockedCycles") .desc("Number of cycles fetch has spent blocked") .prereq(fetchBlockedCycles); + fetchedCacheLines - .name(name() + ".fetchedCacheLines") + .name(name() + ".CacheLines") .desc("Number of cache lines fetched") .prereq(fetchedCacheLines); - fetch_nisn_dist + fetchMiscStallCycles + .name(name() + ".MiscStallCycles") + .desc("Number of cycles fetch has spent waiting on interrupts, or " + "bad addresses, or out of MSHRs") + .prereq(fetchMiscStallCycles); + + fetchIcacheSquashes + .name(name() + ".IcacheSquashes") + .desc("Number of outstanding Icache misses that were squashed") + .prereq(fetchIcacheSquashes); + + 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 = fetchedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; branchPred.regStats(); } template<class Impl> void -SimpleFetch<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultFetch<Impl>::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(); + + // Name is finally available, so create the port. + icachePort = new IcachePort(this); + + Port *mem_dport = mem->getPort(""); + icachePort->setPeer(mem_dport); + mem_dport->setPeer(icachePort); + + if (cpu->checker) { + cpu->checker->setIcachePort(icachePort); + } + + // Fetch needs to start fetching instructions at the very beginning, + // so it must start up in active state. + switchToActive(); } template<class Impl> void -SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) +DefaultFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *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,9 +305,17 @@ SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) template<class Impl> void -SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +DefaultFetch<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Fetch, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template<class Impl> +void +DefaultFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) { - DPRINTF(Fetch, "Fetch: Setting the fetch queue pointer.\n"); + DPRINTF(Fetch, "Setting the fetch queue pointer.\n"); fetchQueue = fq_ptr; // Create wire to write information to proper place in fetch queue. @@ -184,21 +324,134 @@ SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) template<class Impl> void -SimpleFetch<Impl>::processCacheCompletion() +DefaultFetch<Impl>::initStage() { - DPRINTF(Fetch, "Fetch: Waking up from cache miss.\n"); + // Setup PC and nextPC with initial state. + for (int tid = 0; tid < numThreads; tid++) { + PC[tid] = cpu->readPC(tid); + nextPC[tid] = cpu->readNextPC(tid); + } +} + +template<class Impl> +void +DefaultFetch<Impl>::processCacheCompletion(PacketPtr pkt) +{ + unsigned tid = pkt->req->getThreadNum(); + + 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] != IcacheWaitResponse || + pkt->req != memReq[tid] || + isSwitchedOut()) { + ++fetchIcacheSquashes; + delete pkt->req; + delete pkt; + memReq[tid] = NULL; + 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 IcacheAccessComplete if we're not stalled as well. + if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheAccessComplete; + } + + // Reset the mem req to NULL. + delete pkt->req; + delete pkt; + memReq[tid] = NULL; +} + +template <class Impl> +void +DefaultFetch<Impl>::switchOut() +{ + // Fetch is ready to switch out at any time. + switchedOut = true; + cpu->signalSwitched(); +} + +template <class Impl> +void +DefaultFetch<Impl>::doSwitchOut() +{ + // Branch predictor needs to have its state cleared. + branchPred.switchOut(); +} + +template <class Impl> +void +DefaultFetch<Impl>::takeOverFrom() +{ + // Reset all state + for (int i = 0; i < Impl::MaxThreads; ++i) { + stalls[i].decode = 0; + stalls[i].rename = 0; + stalls[i].iew = 0; + stalls[i].commit = 0; + PC[i] = cpu->readPC(i); + nextPC[i] = cpu->readNextPC(i); + fetchStatus[i] = Running; + } + numInst = 0; + wroteToTimeBuffer = false; + _status = Inactive; + switchedOut = false; + branchPred.takeOverFrom(); +} + +template <class Impl> +void +DefaultFetch<Impl>::wakeFromQuiesce() +{ + DPRINTF(Fetch, "Waking up from quiesce\n"); + // Hopefully this is safe + // @todo: Allow other threads to wake from quiesce. + fetchStatus[0] = Running; +} + +template <class Impl> +inline void +DefaultFetch<Impl>::switchToActive() +{ + if (_status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(FullCPU::FetchIdx); + + _status = Active; + } +} + +template <class Impl> +inline void +DefaultFetch<Impl>::switchToInactive() +{ + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(FullCPU::FetchIdx); + + _status = Inactive; + } } template <class Impl> bool -SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) +DefaultFetch<Impl>::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 +464,9 @@ SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) return false; } - predict_taken = branchPred.predict(inst, next_PC); + predict_taken = branchPred.predict(inst, next_PC, inst->threadNumber); + + ++fetchedBranches; if (predict_taken) { ++predictedBranches; @@ -221,251 +476,425 @@ SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) } template <class Impl> -Fault -SimpleFetch<Impl>::fetchCacheLine(Addr fetch_PC) +bool +DefaultFetch<Impl>::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 (cacheBlocked || (interruptPending && flags == 0) || switchedOut) { + // Hold off fetch from getting new instructions when: + // Cache is blocked, or + // while an interrupt is pending and we're not in PAL mode, or + // fetch is switched out. + 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); + // Build request here. + RequestPtr mem_req = new Request(tid, fetch_PC, cacheBlkSize, flags, + fetch_PC, cpu->readCpuId(), tid); - // Translate the instruction request. - // Should this function be - // in the CPU class ? Probably...ITB/DTB should exist within the - // CPU. + memReq[tid] = mem_req; - fault = cpu->translateInstReq(memReq); + // Translate the instruction request. + fault = cpu->translateInstReq(mem_req, cpu->thread[tid]); // In the case of faults, the fetch stage may need to stall and wait - // on what caused the fetch (ITB or Icache miss). + // for the ITB miss to be handled. // If translation was successful, attempt to read the first // instruction. if (fault == NoFault) { +#if 0 + if (cpu->system->memctrl->badaddr(memReq[tid]->paddr) || + memReq[tid]->flags & UNCACHEABLE) { + DPRINTF(Fetch, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path)!", + memReq[tid]->paddr); + ret_fault = TheISA::genMachineCheckFault(); + return false; + } +#endif + + // Build packet here. + PacketPtr data_pkt = new Packet(mem_req, + Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(cacheData[tid]); + DPRINTF(Fetch, "Fetch: Doing instruction read.\n"); - fault = cpu->mem->read(memReq, cacheData); - // 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 && fault == NoFault) { - DPRINTF(Fetch, "Fetch: Doing timing memory access.\n"); - memReq->completionEvent = NULL; - - memReq->time = curTick; + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (!icachePort->sendTiming(data_pkt)) { + assert(retryPkt == NULL); + assert(retryTid == -1); + DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid); + fetchStatus[tid] = IcacheWaitRetry; + retryPkt = data_pkt; + retryTid = tid; + cacheBlocked = true; + return false; + } - MemAccessResult result = icacheInterface->access(memReq); + DPRINTF(Fetch, "Doing cache access.\n"); - // 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); + lastIcacheStall[tid] = curTick; - // 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. + DPRINTF(Activity, "[tid:%i]: Activity: Waiting on I-cache " + "response.\n", tid); - DPRINTF(Fetch, "Fetch: Stalling due to icache miss.\n"); - _status = IcacheMissStall; - } + fetchStatus[tid] = IcacheWaitResponse; + } else { + delete mem_req; + memReq[tid] = NULL; } - return fault; + ret_fault = fault; + return true; } template <class Impl> inline void -SimpleFetch<Impl>::doSquash(const Addr &new_PC) +DefaultFetch<Impl>::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] == IcacheWaitResponse) { + DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n", + tid); + memReq[tid] = NULL; } - _status = Squashing; + // Get rid of the retrying packet if it was from this thread. + if (retryTid == tid) { + assert(cacheBlocked); + cacheBlocked = false; + retryTid = -1; + retryPkt = NULL; + delete retryPkt->req; + delete retryPkt; + } + + fetchStatus[tid] = Squashing; ++fetchSquashCycles; } template<class Impl> void -SimpleFetch<Impl>::squashFromDecode(const Addr &new_PC, - const InstSeqNum &seq_num) +DefaultFetch<Impl>::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<class Impl> +bool +DefaultFetch<Impl>::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<class Impl> +typename DefaultFetch<Impl>::FetchStatus +DefaultFetch<Impl>::updateFetchStatus() +{ + //Check Running + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + + unsigned tid = *threads++; + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == Squashing || + fetchStatus[tid] == IcacheAccessComplete) { + + if (_status == Inactive) { + DPRINTF(Activity, "[tid:%i]: Activating stage.\n",tid); + + if (fetchStatus[tid] == IcacheAccessComplete) { + 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 <class Impl> void -SimpleFetch<Impl>::squash(const Addr &new_PC) +DefaultFetch<Impl>::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<class Impl> +template <class Impl> void -SimpleFetch<Impl>::tick() +DefaultFetch<Impl>::tick() { + list<unsigned>::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 <class Impl> +bool +DefaultFetch<Impl>::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. - - 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); - - _status = Blocked; + if (checkStall(tid) && fetchStatus[tid] != IcacheWaitResponse) { + DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid); - ++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; + fetchStatus[tid] = Blocked; - ++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"); - - // Switch status to running - _status = Running; - - ++fetchCycles; - - fetch(); - } else if (_status != IcacheMissStall) { - DPRINTF(Fetch, "Fetch: Running stage.\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); - ++fetchCycles; + fetchStatus[tid] = Running; - 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<class Impl> void -SimpleFetch<Impl>::fetch() +DefaultFetch<Impl>::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,60 +902,71 @@ SimpleFetch<Impl>::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"); - - // Reset the completion event to NULL. - memReq->completionEvent = NULL; - - _status = Running; + if (fetchStatus[tid] == IcacheAccessComplete) { + DPRINTF(Fetch, "[tid:%i]: Icache miss is complete.\n", + tid); + + 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); + + bool fetch_success = fetchCacheLine(fetch_PC, fault, tid); + if (!fetch_success) { + ++fetchMiscStallCycles; + return; + } } else { - DPRINTF(Fetch, "Fetch: Attempting to translate and read " - "instruction, starting at PC %08p.\n", - fetch_PC); + if (fetchStatus[tid] == Idle) { + ++fetchIdleCycles; + } else if (fetchStatus[tid] == Blocked) { + ++fetchBlockedCycles; + } else if (fetchStatus[tid] == Squashing) { + ++fetchSquashCycles; + } else if (fetchStatus[tid] == IcacheWaitResponse) { + ++icacheStallCycles; + } - fault = fetchCacheLine(fetch_PC); + // Status is Idle, Squashing, Blocked, or IcacheWaitResponse, so + // fetch should do nothing. + return; } - // 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) { + ++fetchCycles; + + // If we had a stall due to an icache miss, then return. + if (fetchStatus[tid] == IcacheWaitResponse) { + ++icacheStallCycles; + status_change = true; 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. - 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"); - - ////////////////////////// - // Fetch first instruction - ////////////////////////// + DPRINTF(Fetch, "[tid:%i]: Adding instructions to queue to " + "decode.\n",tid); // Need to keep track of whether or not a predicted branch // 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 +976,40 @@ SimpleFetch<Impl>::fetch() // Get the instruction from the array of the cache line. inst = gtoh(*reinterpret_cast<MachInst *> - (&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); + + instruction->setASID(tid); + + instruction->setState(cpu->thread[tid]); - DPRINTF(Fetch, "Fetch: Instruction %i created, with PC %#x\n", - inst_seq, instruction->readPC()); + DPRINTF(Fetch, "[tid:%i]: Instruction PC %#x created " + "[sn:%lli]\n", + tid, instruction->readPC(), inst_seq); - DPRINTF(Fetch, "Fetch: Instruction opcode is: %03p\n", - OPCODE(inst)); + 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->tcBase(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,48 +1019,243 @@ SimpleFetch<Impl>::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; + // We shouldn't be in an icache miss and also have a fault (an ITB + // miss) + if (fetchStatus[tid] == IcacheWaitResponse) { + panic("Fetch should have exited prior to this!"); } - // Handle the fault. - // This stage will not be able to continue until all the ROB - // slots are empty, at which point the fault can be handled. - // The only other way it can wake up is if a squash comes along - // and changes the PC. Not sure how to handle that case...perhaps - // 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. + // Send the fault to commit. This thread will not do anything + // until commit handles the fault. The only other way it can + // wake up is if a squash comes along and changes the PC. +#if FULL_SYSTEM + assert(numInst != fetchWidth); + // Get a sequence number. + inst_seq = cpu->getAndIncrementInstSeq(); + // We will use a nop in order to carry the fault. + ext_inst = TheISA::NoopMachInst; - DPRINTF(Fetch, "Fetch: Blocked, need to handle the trap.\n"); + // Create a new DynInst from the dummy nop. + DynInstPtr instruction = new DynInst(ext_inst, fetch_PC, + next_PC, + inst_seq, cpu); + instruction->setPredTarg(next_PC + instSize); + instruction->setThread(tid); - _status = Blocked; -#if FULL_SYSTEM -// 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. + instruction->setASID(tid); + + instruction->setState(cpu->thread[tid]); + + instruction->traceData = NULL; + + instruction->setInstListIt(cpu->addInst(instruction)); + + instruction->fault = fault; + + toDecode->insts[numInst] = instruction; + toDecode->size++; + + 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]); #else // !FULL_SYSTEM - fatal("fault (%d) detected @ PC %08p", fault, cpu->readPC()); + warn("%lli fault (%d) detected @ PC %08p", curTick, fault, PC[tid]); #endif // FULL_SYSTEM } } + +template<class Impl> +void +DefaultFetch<Impl>::recvRetry() +{ + assert(cacheBlocked); + if (retryPkt != NULL) { + assert(retryTid != -1); + assert(fetchStatus[retryTid] == IcacheWaitRetry); + + if (icachePort->sendTiming(retryPkt)) { + fetchStatus[retryTid] = IcacheWaitResponse; + retryPkt = NULL; + retryTid = -1; + cacheBlocked = false; + } + } else { + assert(retryTid == -1); + // Access has been squashed since it was sent out. Just clear + // the cache being blocked. + cacheBlocked = false; + } +} + +/////////////////////////////////////// +// // +// SMT FETCH POLICY MAINTAINED HERE // +// // +/////////////////////////////////////// +template<class Impl> +int +DefaultFetch<Impl>::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] == IcacheAccessComplete || + fetchStatus[tid] == Idle) { + return tid; + } else { + return -1; + } + } + +} + + +template<class Impl> +int +DefaultFetch<Impl>::roundRobin() +{ + list<unsigned>::iterator pri_iter = priorityList.begin(); + list<unsigned>::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] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) { + + priorityList.erase(pri_iter); + priorityList.push_back(high_pri); + + return high_pri; + } + + pri_iter++; + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::iqCount() +{ + priority_queue<unsigned> PQ; + + list<unsigned>::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] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::lsqCount() +{ + priority_queue<unsigned> PQ; + + + list<unsigned>::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] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::branchCount() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + return *threads; +} diff --git a/src/cpu/o3/free_list.cc b/src/cpu/o3/free_list.cc index 6f0b4be1e..ae651398b 100644 --- a/src/cpu/o3/free_list.cc +++ b/src/cpu/o3/free_list.cc @@ -24,13 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "base/trace.hh" #include "cpu/o3/free_list.hh" -SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs, +SimpleFreeList::SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, unsigned _numPhysicalIntRegs, unsigned _numLogicalFloatRegs, unsigned _numPhysicalFloatRegs) @@ -40,43 +43,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/src/cpu/o3/free_list.hh b/src/cpu/o3/free_list.hh index 0b85dba1e..c669b0b34 100644 --- a/src/cpu/o3/free_list.hh +++ b/src/cpu/o3/free_list.hh @@ -24,15 +24,18 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <iostream> #include <queue> #include "arch/isa_traits.hh" +#include "base/misc.hh" #include "base/trace.hh" #include "base/traceflags.hh" #include "cpu/o3/comm.hh" @@ -45,10 +48,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 +77,51 @@ class SimpleFreeList /** Total number of physical registers. */ int numPhysicalRegs; - /** DEBUG stuff below. */ - std::vector<int> freeIntRegsScoreboard; - - std::vector<bool> 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 +129,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 +139,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 +155,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 +184,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/src/cpu/o3/fu_pool.cc b/src/cpu/o3/fu_pool.cc new file mode 100644 index 000000000..545deea9b --- /dev/null +++ b/src/cpu/o3/fu_pool.cc @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include <sstream> + +#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<FUDesc *> 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; + } + } + + assert(fu_idx < numFU); + + unitBusy[fu_idx] = true; + + return fu_idx; +} + +void +FUPool::freeUnitNextCycle(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"; + } +} + +void +FUPool::switchOut() +{ +} + +void +FUPool::takeOverFrom() +{ + for (int i = 0; i < numFU; i++) { + unitBusy[i] = false; + } + unitsToBeFreed.clear(); +} + +// + +//////////////////////////////////////////////////////////////////////////// +// +// 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<FUDesc *> 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/src/cpu/o3/fu_pool.hh b/src/cpu/o3/fu_pool.hh new file mode 100644 index 000000000..52d83f056 --- /dev/null +++ b/src/cpu/o3/fu_pool.hh @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_FU_POOL_HH__ +#define __CPU_O3_FU_POOL_HH__ + +#include <bitset> +#include <list> +#include <string> +#include <vector> + +#include "base/sched_list.hh" +#include "cpu/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<Num_OpClasses> capabilityList; + + /** Bitvector listing which FUs are busy. */ + std::vector<bool> unitBusy; + + /** List of units to be freed at the end of this cycle. */ + std::vector<int> 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<int> funcUnitsIdx; + }; + + /** Per op class queues of FUs that provide that capability. */ + FUIdxQueue fuPerCapList[Num_OpClasses]; + + /** Number of FUs. */ + int numFU; + + /** Functional units. */ + std::vector<FuncUnit *> funcUnits; + + typedef std::vector<FuncUnit *>::iterator fuListIterator; + + public: + + /** Constructs a FU pool. */ + FUPool(std::string name, std::vector<FUDesc *> 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 freeUnitNextCycle(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]; + } + + /** Switches out functional unit pool. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); +}; + +#endif // __CPU_O3_FU_POOL_HH__ diff --git a/src/cpu/o3/iew.cc b/src/cpu/o3/iew.cc index 45b5610e7..8145f4cc7 100644 --- a/src/cpu/o3/iew.cc +++ b/src/cpu/o3/iew.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" @@ -31,4 +33,4 @@ #include "cpu/o3/iew_impl.hh" #include "cpu/o3/inst_queue.hh" -template class SimpleIEW<AlphaSimpleImpl>; +template class DefaultIEW<AlphaSimpleImpl>; diff --git a/src/cpu/o3/iew.hh b/src/cpu/o3/iew.hh index 1e370d4e6..2e61af5fc 100644 --- a/src/cpu/o3/iew.hh +++ b/src/cpu/o3/iew.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,24 +24,45 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -//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 <queue> -#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 Impl> -class SimpleIEW +class DefaultIEW { private: //Typedefs from Impl @@ -52,7 +73,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 +81,205 @@ 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 { - private: - DynInstPtr inst; - SimpleIEW<Impl> *iewStage; + /** Constructs a DefaultIEW with the given parameters. */ + DefaultIEW(Params *params); - public: - WritebackEvent(DynInstPtr &_inst, SimpleIEW<Impl> *_iew); - - virtual void process(); - virtual const char *description(); - }; - - public: - SimpleIEW(Params ¶ms); + /** 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<TimeStruct> *tb_ptr); + /** Sets time buffer for getting instructions coming from rename. */ void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + /** Sets time buffer to pass on instructions to commit. */ void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr); - void setRenameMap(RenameMap *rm_ptr); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); - void squash(); + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *sb_ptr); - void squashDueToBranch(DynInstPtr &inst); + /** Starts switch out of IEW stage. */ + void switchOut(); - void squashDueToMem(DynInstPtr &inst); + /** Completes switch out of IEW stage. */ + void doSwitchOut(); - void block(); + /** Takes over from another CPU's thread. */ + void takeOverFrom(); - inline void unblock(); + /** Returns if IEW is switched out. */ + bool isSwitchedOut() { return switchedOut; } + /** 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); + /** 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(); + + /** Returns if the LSQ has any stores to writeback. */ + bool hasStoresToWB() { return ldstQueue.hasStoresToWB(); } + private: - void dispatchInsts(); + /** 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(); + private: + /** Updates execution stats based on the instruction. */ + void updateExeInstStats(DynInstPtr &inst); - //Interfaces to objects inside and outside of IEW. - /** Time buffer interface. */ + /** Pointer to main time buffer used for backwards communication. */ TimeBuffer<TimeStruct> *timeBuffer; + /** Wire to write information heading to previous stages. */ + typename TimeBuffer<TimeStruct>::wire toFetch; + /** Wire to get commit's output from backwards time buffer. */ typename TimeBuffer<TimeStruct>::wire fromCommit; @@ -158,32 +307,67 @@ class SimpleIEW /** Wire to write infromation heading to commit. */ typename TimeBuffer<IEWStruct>::wire toCommit; - //Will need internal queue to hold onto instructions coming from - //the rename stage in case of a stall. + /** Queue of all instructions coming from rename this cycle. */ + std::queue<DynInstPtr> insts[Impl::MaxThreads]; + /** Skid buffer between rename and IEW. */ - std::queue<RenameStruct> skidBuffer; + std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads]; - protected: + /** 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... + /** 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. */ - RenameMap *renameMap; + bool wroteToTimeBuffer; - /** CPU interface. */ - FullCPU *cpu; + /** 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 +395,108 @@ 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. + /** 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 cyclesSquashing; + unsigned wbCycle; + + /** Number of active threads. */ + unsigned numThreads; + + /** Pointer to list of active threads. */ + std::list<unsigned> *activeThreads; + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** Is this stage switched out. */ + bool switchedOut; + + /** 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; - Stats::Scalar<> iewExecLoadInsts; - Stats::Scalar<> iewExecStoreInsts; + /** Stat for total number of executed load instructions. */ + Stats::Vector<> 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; + + /** Number of executed software prefetches. */ + Stats::Vector<> exeSwp; + /** Number of executed nops. */ + Stats::Vector<> exeNop; + /** Number of executed meomory references. */ + Stats::Vector<> exeRefs; + /** Number of executed branches. */ + Stats::Vector<> exeBranches; + +// Stats::Vector<> issued_ops; +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; +*/ + /** Number of instructions issued per cycle. */ + Stats::Formula issueRate; + /** Number of executed store instructions. */ + Stats::Formula iewExecStoreInsts; +// Stats::Formula issue_op_rate; +// Stats::Formula fu_busy_rate; + /** Number of instructions sent to commit. */ + Stats::Vector<> iewInstsToCommit; + /** Number of instructions that writeback. */ + Stats::Vector<> writebackCount; + /** Number of instructions that wake consumers. */ + Stats::Vector<> producerInst; + /** Number of instructions that wake up from producers. */ + Stats::Vector<> consumerInst; + /** Number of instructions that were delayed in writing back due + * to resource contention. + */ + Stats::Vector<> wbPenalized; + + /** Number of instructions per cycle written back. */ + Stats::Formula wbRate; + /** Average number of woken instructions per writeback. */ + Stats::Formula wbFanout; + /** Number of instructions per cycle delayed in writing back . */ + Stats::Formula wbPenalizedRate; }; -#endif // __CPU_O3_CPU_IEW_HH__ +#endif // __CPU_O3_IEW_HH__ diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh index 85217dd10..3929f2e19 100644 --- a/src/cpu/o3/iew_impl.hh +++ b/src/cpu/o3/iew_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,64 +24,41 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ // @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 <queue> #include "base/timebuf.hh" +#include "cpu/o3/fu_pool.hh" #include "cpu/o3/iew.hh" -template<class Impl> -SimpleIEW<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, - SimpleIEW<Impl> *_iew) - : Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew) -{ - this->setFlags(Event::AutoDelete); -} - -template<class Impl> -void -SimpleIEW<Impl>::WritebackEvent::process() -{ - DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n"); - - // 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(); -} - -template<class Impl> -const char * -SimpleIEW<Impl>::WritebackEvent::description() -{ - return "LSQ writeback event"; -} +using namespace std; template<class Impl> -SimpleIEW<Impl>::SimpleIEW(Params ¶ms) - : // Just make this time buffer really big for now +DefaultIEW<Impl>::DefaultIEW(Params *params) + : // @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) -{ - DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth); - _status = Idle; - _issueStatus = Idle; - _exeStatus = Idle; - _wbStatus = Idle; + fuPool(params->fuPool), + commitToIEWDelay(params->commitToIEWDelay), + renameToIEWDelay(params->renameToIEWDelay), + issueToExecuteDelay(params->issueToExecuteDelay), + issueReadWidth(params->issueWidth), + issueWidth(params->issueWidth), + executeWidth(params->executeWidth), + numThreads(params->numberOfThreads), + switchedOut(false) +{ + _status = Active; + exeStatus = Running; + wbStatus = Idle; // Setup wire to read instructions coming from issue. fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay); @@ -89,13 +66,33 @@ SimpleIEW<Impl>::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; + + skidBufferMax = (3 * (renameToIEWDelay * params->renameWidth)) + issueWidth; +} + +template <class Impl> +std::string +DefaultIEW<Impl>::name() const +{ + return cpu->name() + ".iew"; } template <class Impl> void -SimpleIEW<Impl>::regStats() +DefaultIEW<Impl>::regStats() { + using namespace Stats; + instQueue.regStats(); iewIdleCycles @@ -114,8 +111,6 @@ SimpleIEW<Impl>::regStats() .name(name() + ".iewUnblockCycles") .desc("Number of cycles IEW is unblocking"); -// iewWBInsts; - iewDispatchedInsts .name(name() + ".iewDispatchedInsts") .desc("Number of instructions dispatched to IQ"); @@ -140,17 +135,19 @@ SimpleIEW<Impl>::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"); iewExecLoadInsts + .init(cpu->number_of_threads) .name(name() + ".iewExecLoadInsts") - .desc("Number of load instructions executed"); - - iewExecStoreInsts - .name(name() + ".iewExecStoreInsts") - .desc("Number of store instructions executed"); + .desc("Number of load instructions executed") + .flags(total); iewExecSquashedInsts .name(name() + ".iewExecSquashedInsts") @@ -163,24 +160,161 @@ SimpleIEW<Impl>::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; + + exeSwp + .init(cpu->number_of_threads) + .name(name() + ".EXEC:swp") + .desc("number of swp insts executed") + .flags(total) + ; + + exeNop + .init(cpu->number_of_threads) + .name(name() + ".EXEC:nop") + .desc("number of nop insts executed") + .flags(total) + ; + + exeRefs + .init(cpu->number_of_threads) + .name(name() + ".EXEC:refs") + .desc("number of memory reference insts executed") + .flags(total) + ; + + exeBranches + .init(cpu->number_of_threads) + .name(name() + ".EXEC:branches") + .desc("Number of branches executed") + .flags(total) + ; + + issueRate + .name(name() + ".EXEC:rate") + .desc("Inst execution rate") + .flags(total) + ; + issueRate = iewExecutedInsts / cpu->numCycles; + + iewExecStoreInsts + .name(name() + ".EXEC:stores") + .desc("Number of stores executed") + .flags(total) + ; + iewExecStoreInsts = exeRefs - iewExecLoadInsts; +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + + iewInstsToCommit + .init(cpu->number_of_threads) + .name(name() + ".WB:sent") + .desc("cumulative count of insts sent to commit") + .flags(total) + ; + + writebackCount + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wbPenalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + wbPenalizedRate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wbPenalizedRate = wbPenalized / writebackCount; + + wbFanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wbFanout = producerInst / consumerInst; + + wbRate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wbRate = writebackCount / cpu->numCycles; +} + +template<class Impl> +void +DefaultIEW<Impl>::initStage() +{ + 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<class Impl> void -SimpleIEW<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultIEW<Impl>::setCPU(FullCPU *cpu_ptr) { - DPRINTF(IEW, "IEW: Setting CPU pointer.\n"); + DPRINTF(IEW, "Setting CPU pointer.\n"); cpu = cpu_ptr; instQueue.setCPU(cpu_ptr); ldstQueue.setCPU(cpu_ptr); + + cpu->activateStage(FullCPU::IEWIdx); } template<class Impl> void -SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *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 +323,17 @@ SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *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<class Impl> void -SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +DefaultIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *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 +342,9 @@ SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) template<class Impl> void -SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) +DefaultIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *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 +353,938 @@ SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) template<class Impl> void -SimpleIEW<Impl>::setRenameMap(RenameMap *rm_ptr) +DefaultIEW<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(IEW, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; + + ldstQueue.setActiveThreads(at_ptr); + instQueue.setActiveThreads(at_ptr); +} + +template<class Impl> +void +DefaultIEW<Impl>::setScoreboard(Scoreboard *sb_ptr) +{ + DPRINTF(IEW, "Setting scoreboard pointer.\n"); + scoreboard = sb_ptr; +} + +template <class Impl> +void +DefaultIEW<Impl>::switchOut() { - DPRINTF(IEW, "IEW: Setting rename map pointer.\n"); - renameMap = rm_ptr; + // IEW is ready to switch out at any time. + cpu->signalSwitched(); +} + +template <class Impl> +void +DefaultIEW<Impl>::doSwitchOut() +{ + // Clear any state. + switchedOut = true; + + instQueue.switchOut(); + ldstQueue.switchOut(); + fuPool->switchOut(); + + for (int i = 0; i < numThreads; i++) { + while (!insts[i].empty()) + insts[i].pop(); + while (!skidBuffer[i].empty()) + skidBuffer[i].pop(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::takeOverFrom() +{ + // Reset all state. + _status = Active; + exeStatus = Running; + wbStatus = Idle; + switchedOut = false; + + instQueue.takeOverFrom(); + ldstQueue.takeOverFrom(); + fuPool->takeOverFrom(); + + initStage(); + cpu->activityThisCycle(); + + for (int i=0; i < numThreads; i++) { + dispatchStatus[i] = Running; + stalls[i].commit = false; + fetchRedirect[i] = false; + } + + updateLSQNextCycle = false; + + // @todo: Fix hardcoded number + for (int i = 0; i < 6; ++i) { + issueToExecQueue.advance(); + } } template<class Impl> void -SimpleIEW<Impl>::squash() +DefaultIEW<Impl>::squash(unsigned tid) { - DPRINTF(IEW, "IEW: Squashing all instructions.\n"); - _status = Squashing; + 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); + + updatedQueues = true; + + // Clear the skid buffer in case it has any data in it. + while (!skidBuffer[tid].empty()) { + + if (skidBuffer[tid].front()->isLoad() || + skidBuffer[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + skidBuffer[tid].pop(); + } + + while (!insts[tid].empty()) { + if (insts[tid].front()->isLoad() || + insts[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + insts[tid].pop(); + } } template<class Impl> void -SimpleIEW<Impl>::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; - - // 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() != +DefaultIEW<Impl>::squashDueToBranch(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, PC: %#x " + "[sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->mispredPC[tid] = inst->readPC(); + toCommit->nextPC[tid] = inst->readNextPC(); + toCommit->branchMispredict[tid] = true; + toCommit->branchTaken[tid] = inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); + + toCommit->includeSquashInst[tid] = false; + + wroteToTimeBuffer = true; } template<class Impl> void -SimpleIEW<Impl>::squashDueToMem(DynInstPtr &inst) +DefaultIEW<Impl>::squashDueToMemOrder(DynInstPtr &inst, unsigned tid) { - 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; + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readNextPC(); + + toCommit->includeSquashInst[tid] = false; - // 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(); + wroteToTimeBuffer = true; } template<class Impl> void -SimpleIEW<Impl>::block() +DefaultIEW<Impl>::squashDueToMemBlocked(DynInstPtr &inst, unsigned tid) { - DPRINTF(IEW, "IEW: Blocking.\n"); - // Set the status to Blocked. - _status = Blocked; + 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(); + + // Must include the broadcasted SN in the squash. + toCommit->includeSquashInst[tid] = true; + + ldstQueue.setLoadBlockedHandled(tid); + + wroteToTimeBuffer = true; +} + +template<class Impl> +void +DefaultIEW<Impl>::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. - skidBuffer.push(*fromRename); + 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. + dispatchStatus[tid] = Blocked; } template<class Impl> -inline void -SimpleIEW<Impl>::unblock() +void +DefaultIEW<Impl>::unblock(unsigned tid) { - // 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; + 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<class Impl> void -SimpleIEW<Impl>::wakeDependents(DynInstPtr &inst) +DefaultIEW<Impl>::wakeDependents(DynInstPtr &inst) { instQueue.wakeDependents(inst); } +template<class Impl> +void +DefaultIEW<Impl>::rescheduleMemInst(DynInstPtr &inst) +{ + instQueue.rescheduleMemInst(inst); +} + +template<class Impl> +void +DefaultIEW<Impl>::replayMemInst(DynInstPtr &inst) +{ + instQueue.replayMemInst(inst); +} template<class Impl> void -SimpleIEW<Impl>::instToCommit(DynInstPtr &inst) +DefaultIEW<Impl>::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. + 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 <class Impl> +unsigned +DefaultIEW<Impl>::validInstsFromRename() +{ + unsigned inst_count = 0; + + for (int i=0; i<fromRename->size; i++) { + if (!fromRename->insts[i]->squashed) + inst_count++; + } + + return inst_count; +} + +template<class Impl> void -SimpleIEW<Impl>::dispatchInsts() -{ - //////////////////////////////////////// - // DISPATCH/ISSUE stage - //////////////////////////////////////// - - //Put into its own function? - //Add instructions to IQ if there are any instructions there - - // Check if there are any instructions coming from rename, and we're. - // not squashing. - if (fromRename->size > 0) { - int insts_to_add = fromRename->size; - - // 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]; - - // 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); +DefaultIEW<Impl>::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; - ++iewDispStoreInsts; - } else if (inst->isNonSpeculative()) { - DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction " - "encountered, skipping.\n"); + while (!insts[tid].empty()) { + inst = insts[tid].front(); - // Same hack as with stores. - inst->setCanCommit(); + 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<class Impl> +int +DefaultIEW<Impl>::skidCount() +{ + int max=0; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_count = skidBuffer[*threads++].size(); + if (max < thread_count) + max = thread_count; + } + + return max; +} + +template<class Impl> +bool +DefaultIEW<Impl>::skidsEmpty() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template <class Impl> +void +DefaultIEW<Impl>::updateStatus() +{ + bool any_unblocking = false; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + 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 <class Impl> +void +DefaultIEW<Impl>::resetEntries() +{ + instQueue.resetEntries(); + ldstQueue.resetEntries(); +} + +template <class Impl> +void +DefaultIEW<Impl>::readStallSignals(unsigned tid) +{ + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template <class Impl> +bool +DefaultIEW<Impl>::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 <class Impl> +void +DefaultIEW<Impl>::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); - // Specificall insert it as nonspeculative. + 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 <class Impl> +void +DefaultIEW<Impl>::sortInsts() +{ + int insts_from_rename = fromRename->size; +#ifdef DEBUG + for (int i = 0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_rename; ++i) { + insts[fromRename->insts[i]->threadNumber].push(fromRename->insts[i]); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::wakeCPU() +{ + cpu->wakeCPU(); +} + +template <class Impl> +void +DefaultIEW<Impl>::activityThisCycle() +{ + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); +} + +template <class Impl> +inline void +DefaultIEW<Impl>::activateStage() +{ + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(FullCPU::IEWIdx); +} + +template <class Impl> +inline void +DefaultIEW<Impl>::deactivateStage() +{ + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(FullCPU::IEWIdx); +} + +template<class Impl> +void +DefaultIEW<Impl>::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 <class Impl> +void +DefaultIEW<Impl>::dispatchInsts(unsigned tid) +{ + dispatchedAllInsts = true; + + // Obtain instructions from skid buffer if unblocking, or queue from rename + // otherwise. + std::queue<DynInstPtr> &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. + + // 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->isStoreConditional()) { + // Store conditionals need to be set as "canCommit()" + // so that commit can process them when they reach the + // head of commit. + // @todo: This is somewhat specific to Alpha. + inst->setCanCommit(); instQueue.insertNonSpec(inst); + add_to_iq = false; ++iewDispNonSpecInsts; + } else { + add_to_iq = true; + } - continue; - } else if (inst->isNop()) { - DPRINTF(IEW, "IEW: Issue: Nop instruction encountered " - ", skipping.\n"); + toRename->iewInfo[tid].dispatchedToLSQ++; +#if FULL_SYSTEM + } else if (inst->isMemBarrier() || inst->isWriteBarrier()) { + // Same as non-speculative stores. + 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); - inst->setIssued(); - inst->setExecuted(); - inst->setCanCommit(); + // Same as non-speculative stores. + inst->setCanCommit(); - instQueue.advanceTail(inst); + // Specifically insert it as nonspeculative. + instQueue.insertNonSpec(inst); - continue; - } else if (inst->isExecuted()) { - assert(0 && "Instruction shouldn't be executed.\n"); - DPRINTF(IEW, "IEW: Issue: Executed branch encountered, " - "skipping.\n"); + ++iewDispNonSpecInsts; - inst->setIssued(); - inst->setCanCommit(); + add_to_iq = false; + } else if (inst->isNop()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nop instruction encountered, " + "skipping.\n", tid); - instQueue.advanceTail(inst); + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); - continue; - } + instQueue.recordProducer(inst); + + exeNop[tid]++; + + 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.recordProducer(inst); - // If the instruction queue is not full, then add the - // instruction. - instQueue.insert(fromRename->insts[inst_num]); + add_to_iq = false; + } else { + add_to_iq = true; + } - ++iewDispatchedInsts; + // 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 <class Impl> +void +DefaultIEW<Impl>::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 <class Impl> void -SimpleIEW<Impl>::executeInsts() +DefaultIEW<Impl>::executeInsts() { - //////////////////////////////////////// - //EXECUTE/WRITEBACK stage - //////////////////////////////////////// + wbNumInst = 0; + wbCycle = 0; + + list<unsigned>::iterator threads = (*activeThreads).begin(); - //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. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + fetchRedirect[tid] = false; + } - int fu_usage = 0; - bool fetch_redirect = false; - int inst_slot = 0; - int time_slot = 0; + // Uncomment this if you want to see all available instructions. +// printAvailableInsts(); // 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]; - ++inst_num) { + int insts_to_execute = fromIssue->size; + int inst_num = 0; + for (; inst_num < insts_to_execute; + ++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]; + DynInstPtr inst = instQueue.getInstToExecute(); - 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); - - ++iewExecLoadInsts; + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = ldstQueue.executeLoad(inst); } 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->getFlags() & 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; - } + updateExeInstStats(inst); - assert(time_slot < 5); - } + // Check if branch prediction was correct, if not then we need + // to tell commit to squash in flight instructions. Only + // handle this if there hasn't already been something that + // redirects fetch in this group of instructions. - // May actually have to work this out, especially with loads and stores + // 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; - // Add finished instruction to queue to commit. - (*iewQueue)[time_slot].insts[inst_slot] = inst; - (*iewQueue)[time_slot].size++; + if (!fetchRedirect[tid]) { - // 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; + fetchRedirect[tid] = true; - DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n"); - DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n", + 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(); + // If there was an ordering violation, then 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,164 +1292,236 @@ SimpleIEW<Impl>::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); + } + } + } + + // Update and record activity if we processed any instructions. + 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 <class Impl> +void +DefaultIEW<Impl>::writebackInsts() +{ + // Loop through the head of the time buffer and wake any + // dependents. These instructions are about to write back. Also + // mark scoreboard that this instruction is finally complete. + // Either have IEW have direct access to scoreboard, or have this + // as part of backwards communication. + for (int inst_num = 0; inst_num < issueWidth && + toCommit->insts[inst_num]; inst_num++) { + DynInstPtr inst = toCommit->insts[inst_num]; + int tid = inst->threadNumber; + + DPRINTF(IEW, "Sending instructions to commit, [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + iewInstsToCommit[tid]++; + + // 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()) { + int dependents = instQueue.wakeDependents(inst); + + 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)); + } + + if (dependents) { + producerInst[tid]++; + consumerInst[tid]+= dependents; } + writebackCount[tid]++; } } } template<class Impl> void -SimpleIEW<Impl>::tick() +DefaultIEW<Impl>::tick() { - // Considering putting all the state-determining stuff in this section. + wbNumInst = 0; + wbCycle = 0; - // 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. + wroteToTimeBuffer = false; + updatedQueues = false; - // 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(); + sortInsts(); - // If it's currently unblocking, check to see if it should switch - // to running. - if (_status == Unblocking) { - unblock(); + // Free function units marked as being freed this cycle. + fuPool->processFreeUnits(); - ++iewUnblockCycles; - } - } else if (_status == Squashing) { + list<unsigned>::iterator threads = (*activeThreads).begin(); - DPRINTF(IEW, "IEW: Still squashing.\n"); + // Check stall and squash signals, dispatch any instructions. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; - // 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"); + DPRINTF(IEW,"Issue: Processing [tid:%i]\n",tid); - _status = Running; - instQueue.stopSquash(); - } else { - instQueue.doSquash(); - } + checkSignalsAndUpdate(tid); + dispatch(tid); + } - ++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 (exeStatus != Squashing) { + executeInsts(); - // If there's still instructions coming from rename, continue to - // put them on the skid buffer. - if (fromRename->size == 0) { - block(); - } + writebackInsts(); - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - } + // 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(); - ++iewBlockCycles; + // Also should advance its own time buffers if the stage ran. + // Not the best place for it, but this works (hopefully). + issueToExecQueue.advance(); } - // @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(); + 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(); // 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); - } + threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = (*threads++); - DPRINTF(IEW, "IEW: IQ has %i free entries.\n", - instQueue.numFreeEntries()); -} + DPRINTF(IEW,"Processing [tid:%i]\n",tid); -template<class Impl> -void -SimpleIEW<Impl>::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; - } + // Update structures based on instructions committed. + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + !fromCommit->commitInfo[tid].robSquashing) { - dispatchInsts(); + ldstQueue.commitStores(fromCommit->commitInfo[tid].doneSeqNum,tid); - // Have the instruction queue try to schedule any ready instructions. - instQueue.scheduleReadyInsts(); + ldstQueue.commitLoads(fromCommit->commitInfo[tid].doneSeqNum,tid); - executeInsts(); + updateLSQNextCycle = true; + instQueue.commit(fromCommit->commitInfo[tid].doneSeqNum,tid); + } - // 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 - // instructions have actual latencies, this loop must be separate. - // Also mark scoreboard that this instruction is finally complete. - // 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++) - { - DynInstPtr inst = toCommit->insts[inst_num]; + if (fromCommit->commitInfo[tid].nonSpecSeqNum != 0) { - DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n", - inst->readPC()); + //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(!inst->isSquashed()) { - instQueue.wakeDependents(inst); + if (broadcast_free_entries) { + toFetch->iewInfo[tid].iqCount = + instQueue.getCount(tid); + toFetch->iewInfo[tid].ldstqCount = + ldstQueue.getCount(tid); - for (int i = 0; i < inst->numDestRegs(); i++) - { - renameMap->markAsReady(inst->renamedDestRegIdx(i)); - } + 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); } - // Also should advance its own time buffers if the stage ran. - // Not the best place for it, but this works (hopefully). - issueToExecQueue.advance(); + 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(); + } } -#if !FULL_SYSTEM -template<class Impl> +template <class Impl> void -SimpleIEW<Impl>::lsqWriteback() +DefaultIEW<Impl>::updateExeInstStats(DynInstPtr &inst) { - ldstQueue.writebackAllInsts(); -} + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exeSwp[thread_number]++; + else + iewExecutedInsts++; +#else + iewExecutedInsts++; #endif + + // + // Control operations + // + if (inst->isControl()) + exeBranches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exeRefs[thread_number]++; + + if (inst->isLoad()) { + iewExecLoadInsts[thread_number]++; + } + } +} diff --git a/src/cpu/o3/inst_queue.cc b/src/cpu/o3/inst_queue.cc index 2ff2282b4..f2c6b8213 100644 --- a/src/cpu/o3/inst_queue.cc +++ b/src/cpu/o3/inst_queue.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" @@ -32,7 +34,3 @@ // Force instantiation of InstructionQueue. template class InstructionQueue<AlphaSimpleImpl>; - -template<> -unsigned -InstructionQueue<AlphaSimpleImpl>::DependencyEntry::mem_alloc_counter = 0; diff --git a/src/cpu/o3/inst_queue.hh b/src/cpu/o3/inst_queue.hh index 43fe96c49..60a713020 100644 --- a/src/cpu/o3/inst_queue.hh +++ b/src/cpu/o3/inst_queue.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <list> #include <map> @@ -37,8 +39,13 @@ #include "base/statistics.hh" #include "base/timebuf.hh" #include "cpu/inst_seq.hh" +#include "cpu/o3/dep_graph.hh" +#include "cpu/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 +54,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 Impl> class InstructionQueue @@ -58,87 +72,198 @@ 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<DynInstPtr>::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; - /** - * 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; - } + /** 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<Impl> *iqPtr; + + /** Should the FU be added to the list to be freed upon + * completing this event. + */ + bool freeFU; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstructionQueue<Impl> *iq_ptr); + + virtual void process(); + virtual const char *description(); + void setFreeFU() { freeFU = true; } }; - typedef std::priority_queue<DynInstPtr, vector<DynInstPtr>, pqCompare> - ReadyInstQueue; + /** Constructs an IQ. */ + InstructionQueue(Params *params); - InstructionQueue(Params ¶ms); + /** Destructs the IQ. */ + ~InstructionQueue(); + /** Returns the name of the IQ. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu); + /** Resets all instruction queue state. */ + void resetState(); + + /** Sets CPU pointer. */ + void setCPU(FullCPU *_cpu) { cpu = _cpu; } + + /** Sets active threads list. */ + void setActiveThreads(std::list<unsigned> *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<IssueStruct> *i2eQueue); + /** Sets the global time buffer. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + /** Switches out the instruction queue. */ + void switchOut(); + + /** Takes over execution from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if the IQ is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** 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); - void advanceTail(DynInstPtr &inst); + /** Inserts a memory or write barrier into the IQ to make sure + * loads and stores are ordered properly. + */ + void insertBarrier(DynInstPtr &barr_inst); + + /** Returns the oldest scheduled instruction, and removes it from + * the list of instructions waiting to execute. + */ + DynInstPtr getInstToExecute(); + + /** + * Records the instruction as the producer of a register without + * adding it to the rest of the IQ. + */ + void recordProducer(DynInstPtr &inst) + { addToProducers(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); - void wakeDependents(DynInstPtr &completed_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. */ + int 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(); + /** 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 +276,40 @@ class InstructionQueue /** Wire to read information from timebuffer. */ typename TimeBuffer<TimeStruct>::wire fromCommit; - enum InstList { - Int, - Float, - Branch, - Memory, - Misc, - Squashed, - None - }; + /** Function unit pool. */ + FUPool *fuPool; - /** List of ready int instructions. Used to keep track of the order in - * which instructions should issue. - */ - ReadyInstQueue readyIntInsts; + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; - /** List of ready floating point instructions. */ - ReadyInstQueue readyFloatInsts; + /** List of instructions that are ready to be executed. */ + std::list<DynInstPtr> instsToExecute; - /** List of ready branch instructions. */ - ReadyInstQueue readyBranchInsts; + /** + * 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 miscellaneous instructions. */ - ReadyInstQueue readyMiscInsts; + typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> + ReadyInstQueue; - /** 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 @@ -191,34 +320,80 @@ class InstructionQueue */ std::map<InstSeqNum, DynInstPtr> nonSpecInsts; - typedef typename std::map<InstSeqNum, DynInstPtr>::iterator non_spec_it_t; + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt; - /** Number of free IQ entries left. */ - unsigned freeEntries; + /** Entry for the list age ordering by op class. */ + struct ListOrderEntry { + OpClass queueType; + InstSeqNum oldestInst; + }; - /** The number of entries in the instruction queue. */ - unsigned numEntries; + /** List that contains the age order of the oldest instruction of each + * ready queue. Used to select the oldest instruction available + * among op classes. + * @todo: Might be better to just move these entries around instead + * of creating new ones every time the position changes due to an + * instruction issuing. Not sure std::list supports this. + */ + std::list<ListOrderEntry> listOrder; - /** The number of integer instructions that can be issued in one - * cycle. + typedef typename std::list<ListOrderEntry>::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. */ - unsigned intWidth; + ListOrderIt readyIt[Num_OpClasses]; + + /** Add an op class to the age order list. */ + void addToOrderList(OpClass op_class); - /** The number of floating point instructions that can be issued - * in one cycle. + /** + * 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. */ - unsigned floatWidth; + void moveToYoungerInst(ListOrderIt age_order_it); + + DependencyGraph<DynInstPtr> dependGraph; + + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// + + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; + + /** Number of Total Threads*/ + unsigned numThreads; - /** The number of branches that can be issued in one cycle. */ - unsigned branchWidth; + /** Pointer to list of active threads. */ + std::list<unsigned> *activeThreads; - /** The number of memory instructions that can be issued in one cycle. */ - unsigned memoryWidth; + /** 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; + + /** The number of entries in the instruction queue. */ + unsigned numEntries; /** 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. */ @@ -232,55 +407,11 @@ class InstructionQueue */ unsigned commitToIEWDelay; - ////////////////////////////////// - // Variables needed for squashing - ////////////////////////////////// + /** Is the IQ switched out. */ + bool switchedOut; /** The sequence number of the squashed instruction. */ - InstSeqNum squashedSeqNum; - - /** Iterator that points to the youngest instruction in the IQ. */ - ListIt tail; - - /** 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; - - /////////////////////////////////// - // Dependency graph stuff - /////////////////////////////////// - - class DependencyEntry - { - public: - DynInstPtr inst; - //Might want to include data about what arch. register the - //dependence is waiting on. - DependencyEntry *next; - - //This function, and perhaps this whole class, stand out a little - //bit as they don't fit a classification well. I want access - //to the underlying structure of the linked list, yet at - //the same time it feels like this should be something abstracted - //away. So for now it will sit here, within the IQ, until - //a better implementation is decided upon. - // This function probably shouldn't be within the entry... - void insert(DynInstPtr &new_inst); - - void remove(DynInstPtr &inst_to_remove); - - // Debug variable, remove when done testing. - static unsigned mem_alloc_counter; - }; - - /** Array of linked lists. Each linked list is a list of all the - * instructions that depend upon a given register. The actual - * register's index is used to index into the graph; ie all - * instructions in flight that are dependent upon r34 will be - * in the linked list of dependGraph[34]. - */ - DependencyEntry *dependGraph; + InstSeqNum squashedSeqNum[Impl::MaxThreads]; /** A cache of the recently woken registers. It is 1 if the register * has been woken up recently, and 0 if the register has been added @@ -288,49 +419,85 @@ class InstructionQueue * is basically a secondary scoreboard, and should pretty much mirror * the scoreboard that exists in the rename map. */ - vector<bool> regScoreboard; + std::vector<bool> regScoreboard; + /** Adds an instruction to the dependency graph, as a consumer. */ bool addToDependents(DynInstPtr &new_inst); - void insertDependency(DynInstPtr &new_inst); - void createDependency(DynInstPtr &new_inst); + /** Adds an instruction to the dependency graph, as a producer. */ + void addToProducers(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. */ int countInsts(); - /** Debugging function to dump out the dependency graph. - */ - void dumpDependGraph(); - /** Debugging function to dump all the list sizes, as well as print * out the list of nonspeculative instructions. Should not be used * in any other capacity, but it has no harmful sideaffects. */ 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; + + Stats::Scalar<> iqInstsIssued; + /** 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; + /** Distribution of number of instructions in the queue. */ + Stats::VectorDistribution<> queueResDist; + /** Distribution of the number of instructions issued. */ + Stats::Distribution<> numIssuedDist; + /** Distribution of the cycles it takes to issue an instruction. */ + Stats::VectorDistribution<> issueDelayDist; + + /** Number of times an instruction could not be issued because a + * FU was busy. + */ + Stats::Vector<> statFuBusy; +// Stats::Vector<> dist_unissued; + /** Stat for total number issued for each instruction type. */ + Stats::Vector2d<> statIssuedInstType; + + /** Number of instructions issued per cycle. */ + Stats::Formula issueRate; +// Stats::Formula issue_stores; +// Stats::Formula issue_op_rate; + /** Number of times the FU was busy. */ + Stats::Vector<> fuBusy; + /** Number of times the FU was busy per instruction issued. */ + Stats::Formula fuBusyRate; }; -#endif //__CPU_O3_CPU_INST_QUEUE_HH__ +#endif //__CPU_O3_INST_QUEUE_HH__ diff --git a/src/cpu/o3/inst_queue_impl.hh b/src/cpu/o3/inst_queue_impl.hh index 048dc7c00..06a052c6f 100644 --- a/src/cpu/o3/inst_queue_impl.hh +++ b/src/cpu/o3/inst_queue_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,75 +24,152 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// Todo: -// Current ordering allows for 0 cycle added-to-scheduled. Could maybe fake -// it; either do in reverse order, or have added instructions put into a -// different ready queue that, in scheduleRreadyInsts(), gets put onto the -// normal ready queue. This would however give only a one cycle delay, -// but probably is more flexible to actually add in a delay parameter than -// just running it backwards. - #include <limits> #include <vector> #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<InstSeqNum>::max(); +using namespace std; template <class Impl> -InstructionQueue<Impl>::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<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstructionQueue<Impl> *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr), freeFU(false) { - // Initialize the number of free IQ entries. - freeEntries = numEntries; + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InstructionQueue<Impl>::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, freeFU ? fuIdx : -1); + inst = NULL; +} + + +template <class Impl> +const char * +InstructionQueue<Impl>::FUCompletion::description() +{ + return "Functional unit completion event"; +} + +template <class Impl> +InstructionQueue<Impl>::InstructionQueue(Params *params) + : fuPool(params->fuPool), + numEntries(params->numIQEntries), + totalWidth(params->issueWidth), + numPhysIntRegs(params->numPhysIntRegs), + numPhysFloatRegs(params->numPhysFloatRegs), + commitToIEWDelay(params->commitToIEWDelay) +{ + assert(fuPool); + + switchedOut = false; + + numThreads = params->numberOfThreads; // Set the number of physical registers as the number of int + float numPhysRegs = numPhysIntRegs + numPhysFloatRegs; - DPRINTF(IQ, "IQ: There are %i physical registers.\n", numPhysRegs); + DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs); //Create an entry for each physical register within the //dependency graph. - dependGraph = new DependencyEntry[numPhysRegs]; + dependGraph.resize(numPhysRegs); // Resize the register scoreboard. regScoreboard.resize(numPhysRegs); - // 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 - // registers start off as ready. However this doesn't matter for the - // 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) - { - dependGraph[i].next = NULL; - dependGraph[i].inst = NULL; - regScoreboard[i] = false; + //Initialize Mem Dependence Units + for (int i = 0; i < numThreads; i++) { + memDepUnit[i].init(params,i); + memDepUnit[i].setIQ(this); } + resetState(); + + 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 <class Impl> +InstructionQueue<Impl>::~InstructionQueue() +{ + dependGraph.reset(); +#ifdef DEBUG + cprintf("Nodes traversed: %i, removed: %i\n", + dependGraph.nodesTraversed, dependGraph.nodesRemoved); +#endif +} + +template <class Impl> +std::string +InstructionQueue<Impl>::name() const +{ + return cpu->name() + ".iq"; } template <class Impl> void InstructionQueue<Impl>::regStats() { + using namespace Stats; iqInstsAdded .name(name() + ".iqInstsAdded") .desc("Number of instructions added to the IQ (excludes non-spec)") @@ -103,36 +180,31 @@ InstructionQueue<Impl>::regStats() .desc("Number of non-speculative instructions added to the IQ") .prereq(iqNonSpecInstsAdded); -// iqIntInstsAdded; + iqInstsIssued + .name(name() + ".iqInstsIssued") + .desc("Number of instructions issued") + .prereq(iqInstsIssued); iqIntInstsIssued .name(name() + ".iqIntInstsIssued") .desc("Number of integer instructions issued") .prereq(iqIntInstsIssued); -// iqFloatInstsAdded; - iqFloatInstsIssued .name(name() + ".iqFloatInstsIssued") .desc("Number of float instructions issued") .prereq(iqFloatInstsIssued); -// iqBranchInstsAdded; - iqBranchInstsIssued .name(name() + ".iqBranchInstsIssued") .desc("Number of branch instructions issued") .prereq(iqBranchInstsIssued); -// iqMemInstsAdded; - iqMemInstsIssued .name(name() + ".iqMemInstsIssued") .desc("Number of memory instructions issued") .prereq(iqMemInstsIssued); -// iqMiscInstsAdded; - iqMiscInstsIssued .name(name() + ".iqMiscInstsIssued") .desc("Number of miscellaneous instructions issued") @@ -143,12 +215,6 @@ InstructionQueue<Impl>::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 +232,158 @@ InstructionQueue<Impl>::regStats() .desc("Number of squashed non-spec instructions that were removed") .prereq(iqSquashedNonSpecRemoved); - // Tell mem dependence unit to reg stats as well. - memDepUnit.regStats(); + queueResDist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queueResDist.subname(i, opClassStrings[i]); + } + numIssuedDist + .init(0,totalWidth,1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(pdf) + ; +/* + dist_unissued + .init(Num_OpClasses+2) + .name(name() + ".ISSUE:unissued_cause") + .desc("Reason ready instruction not issued") + .flags(pdf | dist) + ; + for (int i=0; i < (Num_OpClasses + 2); ++i) { + dist_unissued.subname(i, unissued_names[i]); + } +*/ + statIssuedInstType + .init(numThreads,Num_OpClasses) + .name(name() + ".ISSUE:FU_type") + .desc("Type of FU issued") + .flags(total | pdf | dist) + ; + statIssuedInstType.ysubnames(opClassStrings); + + // + // How long did instructions for a particular FU type wait prior to issue + // + + issueDelayDist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issueDelayDist.subname(i, subname.str()); + } + + issueRate + .name(name() + ".ISSUE:rate") + .desc("Inst issue rate") + .flags(total) + ; + issueRate = iqInstsIssued / cpu->numCycles; +/* + issue_stores + .name(name() + ".ISSUE:stores") + .desc("Number of stores issued") + .flags(total) + ; + issue_stores = exe_refs - exe_loads; +*/ +/* + issue_op_rate + .name(name() + ".ISSUE:op_rate") + .desc("Operation issue rate") + .flags(total) + ; + issue_op_rate = issued_ops / numCycles; +*/ + statFuBusy + .init(Num_OpClasses) + .name(name() + ".ISSUE:fu_full") + .desc("attempts to use FU when none available") + .flags(pdf | dist) + ; + for (int i=0; i < Num_OpClasses; ++i) { + statFuBusy.subname(i, opClassStrings[i]); + } + + fuBusy + .init(numThreads) + .name(name() + ".ISSUE:fu_busy_cnt") + .desc("FU busy when requested") + .flags(total) + ; + + fuBusyRate + .name(name() + ".ISSUE:fu_busy_rate") + .desc("FU busy rate (busy events/executed inst)") + .flags(total) + ; + fuBusyRate = fuBusy / iqInstsIssued; + + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } } template <class Impl> void -InstructionQueue<Impl>::setCPU(FullCPU *cpu_ptr) +InstructionQueue<Impl>::resetState() { - cpu = cpu_ptr; + //Initialize thread IQ counts + for (int i = 0; i <numThreads; i++) { + count[i] = 0; + instList[i].clear(); + } + + // Initialize the number of free IQ entries. + freeEntries = numEntries; - tail = cpu->instList.begin(); + // Note that in actuality, the registers corresponding to the logical + // registers start off as ready. However this doesn't matter for the + // 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) { + regScoreboard[i] = false; + } + + for (int i = 0; i < numThreads; ++i) { + squashedSeqNum[i] = 0; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + while (!readyInsts[i].empty()) + readyInsts[i].pop(); + queueOnList[i] = false; + readyIt[i] = listOrder.end(); + } + nonSpecInsts.clear(); + listOrder.clear(); +} + +template <class Impl> +void +InstructionQueue<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; } template <class Impl> void -InstructionQueue<Impl>::setIssueToExecuteQueue( - TimeBuffer<IssueStruct> *i2e_ptr) +InstructionQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *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,19 +391,77 @@ template <class Impl> void InstructionQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *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 <class Impl> +void +InstructionQueue<Impl>::switchOut() +{ + resetState(); + dependGraph.reset(); + switchedOut = true; + for (int i = 0; i < numThreads; ++i) { + memDepUnit[i].switchOut(); + } +} + +template <class Impl> +void +InstructionQueue<Impl>::takeOverFrom() +{ + switchedOut = false; +} + +template <class Impl> +int +InstructionQueue<Impl>::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template <class Impl> +void +InstructionQueue<Impl>::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::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 <class Impl> unsigned InstructionQueue<Impl>::numFreeEntries() { return freeEntries; } +template <class Impl> +unsigned +InstructionQueue<Impl>::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 <class Impl> @@ -219,447 +476,412 @@ InstructionQueue<Impl>::isFull() } template <class Impl> +bool +InstructionQueue<Impl>::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstructionQueue<Impl>::hasReadyInsts() +{ + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +} + +template <class Impl> void InstructionQueue<Impl>::insert(DynInstPtr &new_inst) { // Make sure the instruction is valid assert(new_inst); - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - new_inst->readPC()); + DPRINTF(IQ, "Adding instruction [sn:%lli] PC %#x to the IQ.\n", + new_inst->seqNum, 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) != 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; + new_inst->setInIQ(); + // Look through its source registers (physical regs), and mark any // dependencies. addToDependents(new_inst); // Have this instruction set itself as the producer of its destination // register(s). - createDependency(new_inst); + addToProducers(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); } ++iqInstsAdded; + count[new_inst->threadNumber]++; + assert(freeEntries == (numEntries - countInsts())); } template <class Impl> void -InstructionQueue<Impl>::insertNonSpec(DynInstPtr &inst) +InstructionQueue<Impl>::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); - - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - inst->readPC()); + assert(new_inst); - // 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); + nonSpecInsts[new_inst->seqNum] = new_inst; - // 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(); - } + DPRINTF(IQ, "Adding non-speculative instruction [sn:%lli] PC %#x " + "to the IQ.\n", + new_inst->seqNum, new_inst->readPC()); - // 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++; + assert(freeEntries != 0); - // 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; + new_inst->setInIQ(); + // Have this instruction set itself as the producer of its destination // register(s). - createDependency(inst); + addToProducers(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; + + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); } -// 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 <class Impl> void -InstructionQueue<Impl>::advanceTail(DynInstPtr &inst) +InstructionQueue<Impl>::insertBarrier(DynInstPtr &barr_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++; + memDepUnit[barr_inst->threadNumber].insertBarrier(barr_inst); - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } - - assert(freeEntries <= numEntries); + insertNonSpec(barr_inst); +} - // Have this instruction set itself as the producer of its destination - // register(s). - createDependency(inst); +template <class Impl> +typename Impl::DynInstPtr +InstructionQueue<Impl>::getInstToExecute() +{ + assert(!instsToExecute.empty()); + DynInstPtr inst = instsToExecute.front(); + instsToExecute.pop_front(); + return inst; } -// Need to make sure the number of float and integer instructions -// issued does not exceed the total issue bandwidth. -// @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. template <class Impl> void -InstructionQueue<Impl>::scheduleReadyInsts() +InstructionQueue<Impl>::addToOrderList(OpClass op_class) { - 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; - - 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; + assert(!readyInsts[op_class].empty()); - oldest_inst = MaxInstSeqNum; + ListOrderEntry queue_entry; - list_with_oldest = None; + queue_entry.queueType = op_class; - if (!readyIntInsts.empty() && - int_issued < intWidth) { + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; - insts_available = true; + ListOrderIt list_it = listOrder.begin(); + ListOrderIt list_end_it = listOrder.end(); - int_head_inst = readyIntInsts.top(); - - if (int_head_inst->isSquashed()) { - readyIntInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } - - oldest_inst = int_head_inst->seqNum; - - list_with_oldest = Int; + while (list_it != list_end_it) { + if ((*list_it).oldestInst > queue_entry.oldestInst) { + break; } - 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_it++; + } - list_with_oldest = Float; - } - } + readyIt[op_class] = listOrder.insert(list_it, queue_entry); + queueOnList[op_class] = true; +} - if (!readyBranchInsts.empty() && - branch_issued < branchWidth) { +template <class Impl> +void +InstructionQueue<Impl>::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; + } - insts_available = true; + readyIt[op_class] = listOrder.insert(next_it, queue_entry); +} - branch_head_inst = readyBranchInsts.top(); +template <class Impl> +void +InstructionQueue<Impl>::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. + if (isSwitchedOut()) { + return; + } - if (branch_head_inst->isSquashed()) { - readyBranchInsts.pop(); + iewStage->wakeCPU(); - ++iqLoopSquashStalls; + if (fu_idx > -1) + fuPool->freeUnitNextCycle(fu_idx); - continue; - } else if (branch_head_inst->seqNum < oldest_inst) { - oldest_inst = branch_head_inst->seqNum; + // @todo: Ensure that these FU Completions happen at the beginning + // of a cycle, otherwise they could add too many instructions to + // the queue. + issueToExecuteQueue->access(0)->size++; + instsToExecute.push_back(inst); +} - list_with_oldest = Branch; - } +// @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. +template <class Impl> +void +InstructionQueue<Impl>::scheduleReadyInsts() +{ + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); - } + IssueStruct *i2e_info = issueToExecuteQueue->access(0); - if (!memDepUnit.empty() && - memory_issued < memoryWidth) { + // 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; - insts_available = true; + while (total_issued < totalWidth && + order_it != order_end_it) { + OpClass op_class = (*order_it).queueType; - mem_head_inst = memDepUnit.top(); + assert(!readyInsts[op_class].empty()); - if (mem_head_inst->isSquashed()) { - memDepUnit.pop(); + DynInstPtr issuing_inst = readyInsts[op_class].top(); - ++iqLoopSquashStalls; + assert(issuing_inst->seqNum == (*order_it).oldestInst); - continue; - } else if (mem_head_inst->seqNum < oldest_inst) { - oldest_inst = mem_head_inst->seqNum; + if (issuing_inst->isSquashed()) { + readyInsts[op_class].pop(); - list_with_oldest = Memory; + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; } - } - - if (!readyMiscInsts.empty()) { - - insts_available = true; - - misc_head_inst = readyMiscInsts.top(); - if (misc_head_inst->isSquashed()) { - readyMiscInsts.pop(); + listOrder.erase(order_it++); - ++iqLoopSquashStalls; + ++iqSquashedInstsIssued; - continue; - } else if (misc_head_inst->seqNum < oldest_inst) { - oldest_inst = misc_head_inst->seqNum; - - list_with_oldest = Misc; - } + continue; } - if (!squashedInsts.empty()) { - - insts_available = true; + int idx = -2; + int op_latency = 1; + int tid = issuing_inst->threadNumber; - squashed_head_inst = squashedInsts.top(); + if (op_class != No_OpClass) { + idx = fuPool->getUnit(op_class); - if (squashed_head_inst->seqNum < oldest_inst) { - list_with_oldest = Squashed; + if (idx > -1) { + op_latency = fuPool->getOpLatency(op_class); } - } - 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; + // If we have an instruction that doesn't require a FU, or a + // valid FU, then schedule for execution. + if (idx == -2 || idx != -1) { + if (op_latency == 1) { + i2e_info->size++; + instsToExecute.push_back(issuing_inst); + + // Add the FU onto the list of FU's to be freed next + // cycle if we used one. + if (idx >= 0) + fuPool->freeUnitNextCycle(idx); + } else { + int issue_latency = fuPool->getIssueLatency(op_class); + // Generate completion event for the FU + FUCompletion *execution = new FUCompletion(issuing_inst, + idx, this); + + execution->schedule(curTick + cpu->cycles(issue_latency - 1)); + + // @todo: Enforce that issue_latency == 1 or op_latency + if (issue_latency > 1) { + // If FU isn't pipelined, then it must be freed + // upon the execution completing. + execution->setFreeFU(); + } else { + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnitNextCycle(idx); + } + } - memDepUnit.pop(); - ++memory_issued; - DPRINTF(IQ, "IQ: Issuing memory instruction PC %#x.\n", - issuing_inst->readPC()); - break; + DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x " + "[sn:%lli]\n", + tid, issuing_inst->readPC(), + issuing_inst->seqNum); - case Misc: - issuing_inst = misc_head_inst; - readyMiscInsts.pop(); + readyInsts[op_class].pop(); - ++iqMiscInstsIssued; + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } - DPRINTF(IQ, "IQ: Issuing a miscellaneous instruction PC %#x.\n", - issuing_inst->readPC()); - break; + issuing_inst->setIssued(); + ++total_issued; - 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; + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[tid]--; + issuing_inst->removeInIQ(); + } else { + memDepUnit[tid].issue(issuing_inst); + } - DPRINTF(IQ, "IQ: Issuing squashed instruction PC %#x.\n", - squashed_head_inst->readPC()); - break; + listOrder.erase(order_it++); + statIssuedInstType[tid][op_class]++; + } else { + statFuBusy[op_class]++; + fuBusy[tid]++; + ++order_it; } + } - 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; - } + numIssuedDist.sample(total_issued); + iqInstsIssued+= total_issued; - assert(freeEntries == (numEntries - countInsts())); + // If we issued any instructions, tell the CPU we had activity. + if (total_issued) { + cpu->activityThisCycle(); + } else { + DPRINTF(IQ, "Not able to schedule any instructions.\n"); } - - iqIntInstsIssued += int_issued; - iqFloatInstsIssued += float_issued; - iqBranchInstsIssued += branch_issued; - iqMemInstsIssued += memory_issued; - iqSquashedInstsIssued += squashed_issued; } template <class Impl> void InstructionQueue<Impl>::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()); - // Mark this instruction as ready to issue. + unsigned tid = (*inst_it).second->threadNumber; + (*inst_it).second->setCanIssue(); - // Now schedule the instruction. 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 <class Impl> void +InstructionQueue<Impl>::commit(const InstSeqNum &inst, unsigned tid) +{ + 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 <class Impl> +int InstructionQueue<Impl>::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. - DependencyEntry *curr; + int dependents = 0; - // Tell the memory dependence unit to wake any dependents on this - // instruction if it is a memory instruction. + DPRINTF(IQ, "Waking dependents of completed instruction.\n"); + + assert(!completed_inst->isSquashed()); + // Tell the memory dependence unit to wake any dependents on this + // instruction if it is a memory instruction. Also complete the memory + // instruction at this point since we know it executed without issues. + // @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,44 +898,94 @@ InstructionQueue<Impl>::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) { + //Go through the dependency chain, marking the registers as + //ready within the waiting instructions. + DynInstPtr dep_inst = dependGraph.pop(dest_reg); - curr = dependGraph[dest_reg].next; - - DPRINTF(IQ, "IQ: Waking up a dependent instruction, PC%#x.\n", - curr->inst->readPC()); + while (dep_inst) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", + dep_inst->readPC()); // Might want to give more information to the instruction - // so that it knows which of its source registers is ready. - // However that would mean that the dependency graph entries - // would need to hold the src_reg_idx. - curr->inst->markSrcRegReady(); - - addIfReady(curr->inst); - - dependGraph[dest_reg].next = curr->next; + // so that it knows which of its source registers is + // ready. However that would mean that the dependency + // graph entries would need to hold the src_reg_idx. + dep_inst->markSrcRegReady(); - DependencyEntry::mem_alloc_counter--; + addIfReady(dep_inst); - curr->inst = NULL; + dep_inst = dependGraph.pop(dest_reg); - delete curr; + ++dependents; } - // Reset the head node now that all of its dependents have been woken - // up. - dependGraph[dest_reg].next = NULL; - dependGraph[dest_reg].inst = NULL; + // Reset the head node now that all of its dependents have + // been woken up. + assert(dependGraph.empty(dest_reg)); + dependGraph.clearInst(dest_reg); // Mark the scoreboard as having that register ready. regScoreboard[dest_reg] = true; } + return dependents; +} + +template <class Impl> +void +InstructionQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst) +{ + OpClass op_class = ready_inst->opClass(); + + readyInsts[op_class].push(ready_inst); + + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + if (!queueOnList[op_class]) { + addToOrderList(op_class); + } else if (readyInsts[op_class].top()->seqNum < + (*readyIt[op_class]).oldestInst) { + listOrder.erase(readyIt[op_class]); + addToOrderList(op_class); + } + + 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 <class Impl> +void +InstructionQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template <class Impl> +void +InstructionQueue<Impl>::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} + +template <class Impl> +void +InstructionQueue<Impl>::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 <class Impl> @@ -721,58 +993,64 @@ void InstructionQueue<Impl>::violation(DynInstPtr &store, DynInstPtr &faulting_load) { - memDepUnit.violation(store, faulting_load); + memDepUnit[store->threadNumber].violation(store, faulting_load); } template <class Impl> void -InstructionQueue<Impl>::squash() +InstructionQueue<Impl>::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; - - // Setup the squash iterator to point to the tail. - squashIt = tail; + squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; // 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 <class Impl> void -InstructionQueue<Impl>::doSquash() +InstructionQueue<Impl>::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); + // Start at the tail. + ListIt squash_it = instList[tid].end(); + --squash_it; - 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 (squash_it != instList[tid].end() && + (*squash_it)->seqNum > squashedSeqNum[tid]) { + + DynInstPtr squashed_inst = (*squash_it); // 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()) { + --squash_it; + 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->isStoreConditional() && + !squashed_inst->isMemBarrier() && + !squashed_inst->isWriteBarrier()) { for (int src_reg_idx = 0; src_reg_idx < squashed_inst->numSrcRegs(); @@ -781,25 +1059,31 @@ InstructionQueue<Impl>::doSquash() PhysRegIndex src_reg = squashed_inst->renamedSrcRegIdx(src_reg_idx); - // Only remove it from the dependency graph if it was - // placed there in the first place. - // HACK: This assumes that instructions woken up from the - // dependency chain aren't informed that a specific src - // register has become ready. This may not always be true - // in the future. + // Only remove it from the dependency graph if it + // was placed there in the first place. + + // 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); + dependGraph.remove(src_reg, 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,91 +1093,25 @@ InstructionQueue<Impl>::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()); + DPRINTF(IQ, "[tid:%i]: Instruction [sn:%lli] PC %#x " + "squashed.\n", + tid, squashed_inst->seqNum, squashed_inst->readPC()); } - --squashIt; + instList[tid].erase(squash_it--); ++iqSquashedInstsExamined; } - - assert(freeEntries <= numEntries); - - if (freeEntries == numEntries) { - tail = cpu->instList.end(); - } - -} - -template <class Impl> -void -InstructionQueue<Impl>::stopSquash() -{ - // Clear up the squash variables to ensure that squashing doesn't - // get called improperly. - squashedSeqNum = 0; - - squashIt = cpu->instList.end(); -} - -template <class Impl> -void -InstructionQueue<Impl>::DependencyEntry::insert(DynInstPtr &new_inst) -{ - //Add this new, dependent instruction at the head of the dependency - //chain. - - // First create the entry that will be added to the head of the - // dependency chain. - DependencyEntry *new_entry = new DependencyEntry; - new_entry->next = this->next; - new_entry->inst = new_inst; - - // Then actually add it to the chain. - this->next = new_entry; - - ++mem_alloc_counter; -} - -template <class Impl> -void -InstructionQueue<Impl>::DependencyEntry::remove(DynInstPtr &inst_to_remove) -{ - DependencyEntry *prev = this; - DependencyEntry *curr = this->next; - - // Make sure curr isn't NULL. Because this instruction is being - // removed from a dependency list, it must have been placed there at - // an earlier time. The dependency chain should not be empty, - // unless the instruction dependent upon it is already ready. - if (curr == NULL) { - return; - } - - // Find the instruction to remove within the dependency linked list. - while(curr->inst != inst_to_remove) - { - prev = curr; - curr = curr->next; - - assert(curr != NULL); - } - - // Now remove this instruction from the list. - prev->next = curr->next; - - --mem_alloc_counter; - - // Could push this off to the destructor of DependencyEntry - curr->inst = NULL; - - delete curr; } template <class Impl> @@ -920,21 +1138,21 @@ InstructionQueue<Impl>::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); - dependGraph[src_reg].insert(new_inst); + dependGraph.insert(src_reg, new_inst); // Change the return value to indicate that something // 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. - new_inst->markSrcRegReady(); + new_inst->markSrcRegReady(src_reg_idx); } } } @@ -944,12 +1162,12 @@ InstructionQueue<Impl>::addToDependents(DynInstPtr &new_inst) template <class Impl> void -InstructionQueue<Impl>::createDependency(DynInstPtr &new_inst) +InstructionQueue<Impl>::addToProducers(DynInstPtr &new_inst) { - //Actually nothing really needs to be marked when an - //instruction becomes the producer of a register's value, - //but for convenience a ptr to the producing instruction will - //be placed in the head node of the dependency links. + // Nothing really needs to be marked when an instruction becomes + // the producer of a register's value, but for convenience a ptr + // to the producing instruction will be placed in the head node of + // the dependency links. int8_t total_dest_regs = new_inst->numDestRegs(); for (int dest_reg_idx = 0; @@ -966,13 +1184,13 @@ InstructionQueue<Impl>::createDependency(DynInstPtr &new_inst) continue; } - dependGraph[dest_reg].inst = new_inst; - - if (dependGraph[dest_reg].next) { - dumpDependGraph(); - panic("IQ: Dependency graph not empty!"); + if (!dependGraph.empty(dest_reg)) { + dependGraph.dump(); + panic("Dependency graph %i not empty!", dest_reg); } + dependGraph.setInst(dest_reg, new_inst); + // Mark the scoreboard to say it's not yet ready. regScoreboard[dest_reg] = false; } @@ -982,155 +1200,204 @@ template <class Impl> void InstructionQueue<Impl>::addIfReady(DynInstPtr &inst) { - //If the instruction now has all of its source registers + // If the instruction now has all of its source registers // available, then add it to the list of ready instructions. 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[inst->threadNumber].regsReady(inst); - memDepUnit.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); + return; + } - } else if (inst->isFloating()) { + OpClass op_class = inst->opClass(); - DPRINTF(IQ, "IQ: Floating instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyFloatInsts.push(inst); + 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); - } else { - DPRINTF(IQ, "IQ: Miscellaneous instruction is ready to issue, " - "putting it onto the ready list, PC %#x..\n", - inst->readPC()); + readyInsts[op_class].push(inst); - readyMiscInsts.push(inst); + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + if (!queueOnList[op_class]) { + addToOrderList(op_class); + } else if (readyInsts[op_class].top()->seqNum < + (*readyIt[op_class]).oldestInst) { + listOrder.erase(readyIt[op_class]); + addToOrderList(op_class); } } } -/* - * 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 <class Impl> int InstructionQueue<Impl>::countInsts() { - ListIt count_it = cpu->instList.begin(); +#if 0 + //ksewell:This works but definitely could use a cleaner write + //with a more intuitive way of counting. Right now it's + //just brute force .... + // Change the #if if you want to use this method. 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 != 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; + } + } - while (count_it != tail) { - if (!(*count_it)->isIssued()) { - ++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 <class Impl> void -InstructionQueue<Impl>::dumpDependGraph() +InstructionQueue<Impl>::dumpLists() { - DependencyEntry *curr; + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts[i].size()); - for (int i = 0; i < numPhysRegs; ++i) - { - curr = &dependGraph[i]; + cprintf("\n"); + } - if (curr->inst) { - cprintf("dependGraph[%i]: producer: %#x consumer: ", i, - curr->inst->readPC()); - } else { - cprintf("dependGraph[%i]: No producer. consumer: ", i); - } + cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); - while (curr->next != NULL) { - curr = curr->next; + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); - cprintf("%#x ", curr->inst->readPC()); - } + cprintf("Non speculative list: "); - cprintf("\n"); + 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 <class Impl> void -InstructionQueue<Impl>::dumpLists() +InstructionQueue<Impl>::dumpInsts() { - cprintf("Ready integer list size: %i\n", readyIntInsts.size()); + 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()); - cprintf("Ready float list size: %i\n", readyFloatInsts.size()); + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } - cprintf("Ready branch list size: %i\n", readyBranchInsts.size()); + cprintf("\n"); - cprintf("Ready misc list size: %i\n", readyMiscInsts.size()); + inst_list_it++; + ++num; + } + } - cprintf("Squashed list size: %i\n", squashedInsts.size()); + cprintf("Insts to Execute list:\n"); - cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); + int num = 0; + int valid_num = 0; + ListIt inst_list_it = instsToExecute.begin(); - non_spec_it_t non_spec_it = nonSpecInsts.begin(); + while (inst_list_it != instsToExecute.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("Non speculative list: "); + 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()); - while (non_spec_it != nonSpecInsts.end()) { - cprintf("%#x ", (*non_spec_it).second->readPC()); - ++non_spec_it; - } + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } - cprintf("\n"); + cprintf("\n"); + inst_list_it++; + ++num; + } } diff --git a/src/cpu/o3/lsq.cc b/src/cpu/o3/lsq.cc new file mode 100644 index 000000000..de0325920 --- /dev/null +++ b/src/cpu/o3/lsq.cc @@ -0,0 +1,38 @@ +/* + * 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. + * + * Authors: Korey Sewell + */ + +#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<AlphaSimpleImpl>; + diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh new file mode 100644 index 000000000..bc4154c85 --- /dev/null +++ b/src/cpu/o3/lsq.hh @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Korey Sewell + */ + +#ifndef __CPU_O3_LSQ_HH__ +#define __CPU_O3_LSQ_HH__ + +#include <map> +#include <queue> + +#include "config/full_system.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/lsq_unit.hh" +#include "mem/port.hh" +#include "sim/sim_object.hh" + +template <class Impl> +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; + + /** SMT policy. */ + 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<unsigned> *at_ptr); + /** Sets the CPU pointer. */ + void setCPU(FullCPU *cpu_ptr); + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr); + /** Switches out the LSQ. */ + void switchOut(); + /** Takes over execution from another CPU's thread. */ + void takeOverFrom(); + + /** 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) + { thread[tid].tick(); } + + /** 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); + + /** 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) + { thread[tid].commitLoads(youngest_inst); } + + /** + * Commits stores up until the given sequence number for a specific thread. + */ + void commitStores(InstSeqNum &youngest_inst, unsigned tid) + { thread[tid].commitStores(youngest_inst); } + + /** + * 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) + { thread[tid].squash(squashed_num); } + + /** 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) + { return thread[tid].violation(); } + + /** Returns if a load is blocked due to the memory system for a specific + * thread. + */ + bool loadBlocked(unsigned tid) + { return thread[tid].loadBlocked(); } + + 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) + { return thread[tid].getMemDepViolator(); } + + /** Returns the head index of the load queue for a specific thread. */ + int getLoadHead(unsigned tid) + { return thread[tid].getLoadHead(); } + + /** 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) + { return thread[tid].getStoreHead(); } + + /** 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) + { return thread[tid].getCount(); } + + /** 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) + { return thread[tid].numLoads(); } + + /** 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) + { return thread[tid].numStores(); } + + /** 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) + { return thread[tid].numLoadsReady(); } + + /** 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) + { return thread[tid].hasStoresToWB(); } + + /** Returns the number of stores a specific thread has to write back. */ + int numStoresToWB(unsigned tid) + { return thread[tid].numStoresToWB(); } + + /** 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) + { return thread[tid].willWB(); } + + /** Debugging function to print out all instructions. */ + void dumpInsts(); + /** Debugging function to print out instructions from a specific thread. */ + void dumpInsts(unsigned tid) + { thread[tid].dumpInsts(); } + + /** Executes a read operation, using the load specified at the load index. */ + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + /** Executes a store operation, using the store specified at the store + * index. + */ + template <class T> + Fault write(RequestPtr 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; + + /** List of Active Threads in System. */ + std::list<unsigned> *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; + + /** Number of Threads. */ + unsigned numThreads; +}; + +template <class Impl> +template <class T> +Fault +LSQ<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + unsigned tid = req->getThreadNum(); + + return thread[tid].read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +LSQ<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + unsigned tid = req->getThreadNum(); + + return thread[tid].write(req, data, store_idx); +} + +#endif // __CPU_O3_LSQ_HH__ diff --git a/src/cpu/o3/lsq_impl.hh b/src/cpu/o3/lsq_impl.hh new file mode 100644 index 000000000..27aa0dc3c --- /dev/null +++ b/src/cpu/o3/lsq_impl.hh @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Korey Sewell + */ + +#include <algorithm> +#include <string> + +#include "cpu/o3/lsq.hh" + +using namespace std; + +template <class Impl> +LSQ<Impl>::LSQ(Params *params) + : LQEntries(params->LQEntries), SQEntries(params->SQEntries), + 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, maxSQEntries, tid); + } +} + + +template<class Impl> +std::string +LSQ<Impl>::name() const +{ + return iewStage->name() + ".lsq"; +} + +template<class Impl> +void +LSQ<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + activeThreads = at_ptr; + assert(activeThreads != 0); +} + +template<class Impl> +void +LSQ<Impl>::setCPU(FullCPU *cpu_ptr) +{ + cpu = cpu_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setCPU(cpu_ptr); + } +} + +template<class Impl> +void +LSQ<Impl>::setIEW(IEW *iew_ptr) +{ + iewStage = iew_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setIEW(iew_ptr); + } +} + +template <class Impl> +void +LSQ<Impl>::switchOut() +{ + for (int tid = 0; tid < numThreads; tid++) { + thread[tid].switchOut(); + } +} + +template <class Impl> +void +LSQ<Impl>::takeOverFrom() +{ + for (int tid = 0; tid < numThreads; tid++) { + thread[tid].takeOverFrom(); + } +} + +template <class Impl> +int +LSQ<Impl>::entryAmount(int num_threads) +{ + if (lsqPolicy == Partitioned) { + return LQEntries / num_threads; + } else { + return 0; + } +} + +template <class Impl> +void +LSQ<Impl>::resetEntries() +{ + if (lsqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::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<class Impl> +void +LSQ<Impl>::removeEntries(unsigned tid) +{ + thread[tid].clearLQ(); + thread[tid].clearSQ(); +} + +template<class Impl> +void +LSQ<Impl>::resizeEntries(unsigned size,unsigned tid) +{ + thread[tid].resizeLQ(size); + thread[tid].resizeSQ(size); +} + +template<class Impl> +void +LSQ<Impl>::tick() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + thread[tid].tick(); + } +} + +template<class Impl> +void +LSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + unsigned tid = load_inst->threadNumber; + + thread[tid].insertLoad(load_inst); +} + +template<class Impl> +void +LSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + unsigned tid = store_inst->threadNumber; + + thread[tid].insertStore(store_inst); +} + +template<class Impl> +Fault +LSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeLoad(inst); +} + +template<class Impl> +Fault +LSQ<Impl>::executeStore(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeStore(inst); +} + +template<class Impl> +void +LSQ<Impl>::writebackStores() +{ + list<unsigned>::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<class Impl> +bool +LSQ<Impl>::violation() +{ + /* Answers: Does Anybody Have a Violation?*/ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (thread[tid].violation()) + return true; + } + + return false; +} + +template<class Impl> +int +LSQ<Impl>::getCount() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += getCount(tid); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numLoads() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += numLoads(tid); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numStores() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numStores(); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numLoadsReady() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numLoadsReady(); + } + + return total; +} + +template<class Impl> +unsigned +LSQ<Impl>::numFreeEntries() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numFreeEntries(); + } + + return total; +} + +template<class Impl> +unsigned +LSQ<Impl>::numFreeEntries(unsigned tid) +{ + //if( lsqPolicy == Dynamic ) + //return numFreeEntries(); + //else + return thread[tid].numFreeEntries(); +} + +template<class Impl> +bool +LSQ<Impl>::isFull() +{ + list<unsigned>::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<class Impl> +bool +LSQ<Impl>::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<class Impl> +bool +LSQ<Impl>::lqFull() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].lqFull()) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::lqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return lqFull(); + else + return thread[tid].lqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::sqFull() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!sqFull(tid)) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::sqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return sqFull(); + else + return thread[tid].sqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::isStalled() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].isStalled()) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::isStalled(unsigned tid) +{ + if( lsqPolicy == Dynamic ) + return isStalled(); + else + return thread[tid].isStalled(); +} + +template<class Impl> +bool +LSQ<Impl>::hasStoresToWB() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!hasStoresToWB(tid)) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::willWB() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!willWB(tid)) + return false; + } + + return true; +} + +template<class Impl> +void +LSQ<Impl>::dumpInsts() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + thread[tid].dumpInsts(); + } +} diff --git a/src/cpu/o3/lsq_unit.cc b/src/cpu/o3/lsq_unit.cc new file mode 100644 index 000000000..e935ffa5c --- /dev/null +++ b/src/cpu/o3/lsq_unit.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#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<AlphaSimpleImpl>; + diff --git a/src/cpu/o3/lsq_unit.hh b/src/cpu/o3/lsq_unit.hh new file mode 100644 index 000000000..ce0cdd36f --- /dev/null +++ b/src/cpu/o3/lsq_unit.hh @@ -0,0 +1,711 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_LSQ_UNIT_HH__ +#define __CPU_O3_LSQ_UNIT_HH__ + +#include <algorithm> +#include <map> +#include <queue> + +#include "arch/faults.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/packet.hh" +#include "mem/port.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 Impl> +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; + + 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); + + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr) + { iewStage = iew_ptr; } + + /** Switches out LSQ unit. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if the LSQ is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** 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) { panic("Not implemented"); return NoFault; } + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** 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(); + + void completeDataAccess(PacketPtr pkt); + + // @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. */ + bool loadBlocked() + { return isLoadBlocked; } + + /** Clears the signal that a load became blocked. */ + void clearLoadBlocked() + { isLoadBlocked = false; } + + /** Returns if the blocked load was handled. */ + bool isLoadBlockedHandled() + { return loadBlockedHandled; } + + /** Records the blocked load as being handled. */ + 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); } + + /** 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 && + !isStoreBlocked; } + + private: + /** Writes back the instruction, sending it to IEW. */ + void writeback(DynInstPtr &inst, PacketPtr pkt); + + /** Handles completing the send of a store to memory. */ + void storePostSend(Packet *pkt); + + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Handles doing the retry. */ + void recvRetry(); + + /** 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); + + public: + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + private: + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Pointer to the IEW stage. */ + IEW *iewStage; + + /** Pointer to memory object. */ + MemObject *mem; + + /** DcachePort class for this LSQ Unit. Handles doing the + * communication with the cache/memory. + * @todo: Needs to be moved to the LSQ level and have some sort + * of arbitration. + */ + class DcachePort : public Port + { + protected: + /** Pointer to CPU. */ + FullCPU *cpu; + /** Pointer to LSQ. */ + LSQUnit *lsq; + + public: + /** Default constructor. */ + DcachePort(FullCPU *_cpu, LSQUnit *_lsq) + : Port(_lsq->name() + "-dport"), cpu(_cpu), lsq(_lsq) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles writing back and + * completing the load or store that has returned from + * memory. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of the previous send. */ + virtual void recvRetry(); + }; + + /** Pointer to the D-cache. */ + DcachePort *dcachePort; + + /** Derived class to hold any sender state the LSQ needs. */ + class LSQSenderState : public Packet::SenderState + { + public: + /** Default constructor. */ + LSQSenderState() + : noWB(false) + { } + + /** Instruction who initiated the access to memory. */ + DynInstPtr inst; + /** Whether or not it is a load. */ + bool isLoad; + /** The LQ/SQ index of the instruction. */ + int idx; + /** Whether or not the instruction will need to writeback. */ + bool noWB; + }; + + /** Writeback event, specifically for when stores forward data to loads. */ + class WritebackEvent : public Event { + public: + /** Constructs a writeback event. */ + WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, LSQUnit *lsq_ptr); + + /** Processes the writeback event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** Instruction whose results are being written back. */ + DynInstPtr inst; + + /** The packet that would have been sent to memory. */ + PacketPtr pkt; + + /** The pointer to the LSQ unit that issued the store. */ + LSQUnit<Impl> *lsqPtr; + }; + + 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 request for the store. */ + RequestPtr 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; + }; + + private: + /** The LSQUnit thread id. */ + unsigned lsqID; + + /** The store queue. */ + std::vector<SQEntry> storeQueue; + + /** The load queue. */ + std::vector<DynInstPtr> loadQueue; + + /** The number of LQ entries, plus a sentinel entry (circular queue). + * @todo: Consider having var that records the true number of LQ entries. + */ + unsigned LQEntries; + /** The number of SQ entries, plus a sentinel entry (circular queue). + * @todo: Consider having var that records the true number of SQ entries. + */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ. */ + 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 may be 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; + + /** Is the LSQ switched out. */ + bool switchedOut; + + //list<InstSeqNum> mshrSeqNums; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::wire fromIssue; + + /** 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; + + /** The packet that needs to be retried. */ + PacketPtr retryPkt; + + /** Whehter or not a store is blocked due to the memory system. */ + bool isStoreBlocked; + + /** Whether or not a load is blocked due to the memory system. */ + bool isLoadBlocked; + + /** Has the blocked load been handled. */ + bool loadBlockedHandled; + + /** The sequence number of the blocked load. */ + InstSeqNum blockedLoadSeqNum; + + /** 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. +/* + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsq_forw_loads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> inv_addr_loads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> inv_addr_swpfs; + + // total non-speculative bogus addresses seen (debug var) + Counter sim_invalid_addrs; + Stats::Vector<> fu_busy; //cumulative fu busy + + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsq_blocked_loads; + + Stats::Scalar<> lsqInversion; +*/ + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(Request *req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(Request *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 <class Impl> +template <class T> +Fault +LSQUnit<Impl>::read(Request *req, T &data, int load_idx) +{ + DynInstPtr load_inst = loadQueue[load_idx]; + + assert(load_inst); + + assert(!load_inst->isExecuted()); + + // 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). + if (req->getFlags() & UNCACHEABLE && + (load_idx != loadHead || !load_inst->reachedCommit)) { + iewStage->rescheduleMemInst(load_inst); + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + int store_idx = load_inst->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->getPaddr()); + +#if FULL_SYSTEM + if (req->getFlags() & LOCKED) { + cpu->lockAddr = req->getPaddr(); + 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->getVaddr() >= storeQueue[store_idx].inst->effAddr; + bool store_has_upper_limit = + (req->getVaddr() + req->getSize()) <= + (storeQueue[store_idx].inst->effAddr + store_size); + bool lower_load_has_store_part = + req->getVaddr() < (storeQueue[store_idx].inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->getVaddr() + req->getSize()) > + 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) { + // Get shift amount for offset into the store's data. + int shift_amt = req->getVaddr() & (store_size - 1); + // @todo: Magic number, assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = storeQueue[store_idx].data >> shift_amt; + + assert(!load_inst->memData); + load_inst->memData = new uint8_t[64]; + + memcpy(load_inst->memData, &data, req->getSize()); + + DPRINTF(LSQUnit, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->getVaddr(), data); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(load_inst->memData); + + WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, this); + + // 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 && + load_inst->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(load_inst); + + // 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->getVaddr()); + + return NoFault; + } + } + + // If there's no forwarding case, then go access memory + DPRINTF(LSQUnit, "Doing functional access for inst [sn:%lli] PC %#x\n", + load_inst->seqNum, load_inst->readPC()); + + assert(!load_inst->memData); + load_inst->memData = new uint8_t[64]; + + ++usedPorts; + + DPRINTF(LSQUnit, "Doing timing access for inst PC %#x\n", + load_inst->readPC()); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(load_inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = true; + state->idx = load_idx; + state->inst = load_inst; + data_pkt->senderState = state; + + // if we have a cache, do cache access too + if (!dcachePort->sendTiming(data_pkt)) { + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < load_inst->seqNum) + return NoFault; + + // Record that the load was blocked due to memory. This + // load will squash all instructions after it, be + // refetched, and re-executed. + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = load_inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + if (data_pkt->result != Packet::Success) { + DPRINTF(LSQUnit, "LSQUnit: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + load_inst->seqNum); + } else { + DPRINTF(LSQUnit, "LSQUnit: D-cache hit!\n"); + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + load_inst->seqNum); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +LSQUnit<Impl>::write(Request *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->getPaddr(), data, storeHead, + storeQueue[store_idx].inst->seqNum); + + 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/src/cpu/o3/lsq_unit_impl.hh b/src/cpu/o3/lsq_unit_impl.hh new file mode 100644 index 000000000..6f32ec304 --- /dev/null +++ b/src/cpu/o3/lsq_unit_impl.hh @@ -0,0 +1,929 @@ +/* + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/checker/cpu.hh" +#include "cpu/o3/lsq_unit.hh" +#include "base/str.hh" +#include "mem/request.hh" + +template<class Impl> +LSQUnit<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt, + LSQUnit *lsq_ptr) + : Event(&mainEventQueue), inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +LSQUnit<Impl>::WritebackEvent::process() +{ + if (!lsqPtr->isSwitchedOut()) { + lsqPtr->writeback(inst, pkt); + } + delete pkt; +} + +template<class Impl> +const char * +LSQUnit<Impl>::WritebackEvent::description() +{ + return "Store writeback event"; +} + +template<class Impl> +void +LSQUnit<Impl>::completeDataAccess(PacketPtr pkt) +{ + LSQSenderState *state = dynamic_cast<LSQSenderState *>(pkt->senderState); + DynInstPtr inst = state->inst; + DPRINTF(IEW, "Writeback event [sn:%lli]\n", inst->seqNum); + DPRINTF(Activity, "Activity: Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + + if (isSwitchedOut() || inst->isSquashed()) { + delete state; + delete pkt; + return; + } else { + if (!state->noWB) { + writeback(inst, pkt); + } + + if (inst->isStore()) { + completeStore(state->idx); + } + } + + delete state; + delete pkt; +} + +template <class Impl> +Tick +LSQUnit<Impl>::DcachePort::recvAtomic(PacketPtr pkt) +{ + panic("O3CPU model does not work with atomic mode!"); + return curTick; +} + +template <class Impl> +void +LSQUnit<Impl>::DcachePort::recvFunctional(PacketPtr pkt) +{ + panic("O3CPU doesn't expect recvFunctional callback!"); +} + +template <class Impl> +void +LSQUnit<Impl>::DcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("O3CPU doesn't expect recvStatusChange callback!"); +} + +template <class Impl> +bool +LSQUnit<Impl>::DcachePort::recvTiming(PacketPtr pkt) +{ + lsq->completeDataAccess(pkt); + return true; +} + +template <class Impl> +void +LSQUnit<Impl>::DcachePort::recvRetry() +{ + lsq->recvRetry(); +} + +template <class Impl> +LSQUnit<Impl>::LSQUnit() + : loads(0), stores(0), storesToWB(0), stalled(false), + isStoreBlocked(false), isLoadBlocked(false), + loadBlockedHandled(false) +{ +} + +template<class Impl> +void +LSQUnit<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) +{ + DPRINTF(LSQUnit, "Creating LSQUnit%i object.\n",id); + + switchedOut = false; + + lsqID = id; + + // Add 1 for the sentinel entry (they are circular queues). + LQEntries = maxLQEntries + 1; + SQEntries = maxSQEntries + 1; + + loadQueue.resize(LQEntries); + storeQueue.resize(SQEntries); + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + cachePorts = params->cachePorts; + + mem = params->mem; + + memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template<class Impl> +void +LSQUnit<Impl>::setCPU(FullCPU *cpu_ptr) +{ + cpu = cpu_ptr; + dcachePort = new DcachePort(cpu, this); + + Port *mem_dport = mem->getPort(""); + dcachePort->setPeer(mem_dport); + mem_dport->setPeer(dcachePort); + + if (cpu->checker) { + cpu->checker->setDcachePort(dcachePort); + } +} + +template<class Impl> +std::string +LSQUnit<Impl>::name() const +{ + if (Impl::MaxThreads == 1) { + return iewStage->name() + ".lsq"; + } else { + return iewStage->name() + ".lsq.thread." + to_string(lsqID); + } +} + +template<class Impl> +void +LSQUnit<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +LSQUnit<Impl>::clearSQ() +{ + storeQueue.clear(); +} + +template<class Impl> +void +LSQUnit<Impl>::switchOut() +{ + switchedOut = true; + for (int i = 0; i < loadQueue.size(); ++i) + loadQueue[i] = NULL; + + assert(storesToWB == 0); +} + +template<class Impl> +void +LSQUnit<Impl>::takeOverFrom() +{ + switchedOut = false; + loads = stores = storesToWB = 0; + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + + memDepViolator = NULL; + + blockedLoadSeqNum = 0; + + stalled = false; + isLoadBlocked = false; + loadBlockedHandled = false; +} + +template<class Impl> +void +LSQUnit<Impl>::resizeLQ(unsigned size) +{ + unsigned size_plus_sentinel = size + 1; + assert(size_plus_sentinel >= LQEntries); + + if (size_plus_sentinel > LQEntries) { + while (size_plus_sentinel > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size_plus_sentinel; + } + +} + +template<class Impl> +void +LSQUnit<Impl>::resizeSQ(unsigned size) +{ + unsigned size_plus_sentinel = size + 1; + if (size_plus_sentinel > SQEntries) { + while (size_plus_sentinel > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size_plus_sentinel; + } +} + +template <class Impl> +void +LSQUnit<Impl>::insert(DynInstPtr &inst) +{ + assert(inst->isMemRef()); + + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } + + inst->setInLSQ(); +} + +template <class Impl> +void +LSQUnit<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead); + assert(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 <class Impl> +void +LSQUnit<Impl>::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 <class Impl> +typename Impl::DynInstPtr +LSQUnit<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +LSQUnit<Impl>::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 <class Impl> +int +LSQUnit<Impl>::numLoadsReady() +{ + int load_idx = loadHead; + int retval = 0; + + while (load_idx != loadTail) { + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +template <class Impl> +Fault +LSQUnit<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(LSQUnit, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + load_fault = inst->initiateAcc(); + + // If the instruction faulted, then we need to send it along to commit + // without the instruction completing. + if (load_fault != NoFault) { + // Send this instruction to commit, also make sure iew stage + // realizes there is activity. + iewStage->instToCommit(inst); + iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +LSQUnit<Impl>::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(); + + if (storeQueue[store_idx].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 (store_inst->isStoreConditional()) { + // 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) { + // Really only need to check loads that have actually executed + // It's safe to check all loads because effAddr is set to + // InvalAddr when the dyn inst is created. + + // @todo: For now this is extra conservative, detecting a + // violation if the addresses match assuming all accesses + // are quad word accesses. + + // @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 <class Impl> +void +LSQUnit<Impl>::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(LSQUnit, "Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->readPC()); + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template <class Impl> +void +LSQUnit<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +LSQUnit<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || storeQueue[storeHead].inst); + + int store_idx = storeHead; + + while (store_idx != storeTail) { + assert(storeQueue[store_idx].inst); + // Mark any stores that are now committed and have not yet + // been marked as able to write back. + 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; + + ++storesToWB; + } + + incrStIdx(store_idx); + } +} + +template <class Impl> +void +LSQUnit<Impl>::writebackStores() +{ + while (storesToWB > 0 && + storeWBIdx != storeTail && + storeQueue[storeWBIdx].inst && + storeQueue[storeWBIdx].canWB && + usedPorts < cachePorts) { + + if (isStoreBlocked) { + DPRINTF(LSQUnit, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + // Store didn't write any data so no need to write it back to + // memory. + if (storeQueue[storeWBIdx].size == 0) { + completeStore(storeWBIdx); + + incrStIdx(storeWBIdx); + + continue; + } + + ++usedPorts; + + if (storeQueue[storeWBIdx].inst->isDataPrefetch()) { + incrStIdx(storeWBIdx); + + continue; + } + + assert(storeQueue[storeWBIdx].req); + assert(!storeQueue[storeWBIdx].committed); + + DynInstPtr inst = storeQueue[storeWBIdx].inst; + + Request *req = storeQueue[storeWBIdx].req; + storeQueue[storeWBIdx].committed = true; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + memcpy(inst->memData, (uint8_t *)&storeQueue[storeWBIdx].data, + req->getSize()); + + PacketPtr data_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = false; + state->idx = storeWBIdx; + state->inst = inst; + data_pkt->senderState = state; + + 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->getPaddr(), *(inst->memData), + storeQueue[storeWBIdx].inst->seqNum); + + // @todo: Remove this SC hack once the memory system handles it. + if (req->getFlags() & LOCKED) { + if (req->getFlags() & UNCACHEABLE) { + req->setScResult(2); + } else { + if (cpu->lockFlag) { + req->setScResult(1); + } else { + req->setScResult(0); + // Hack: Instantly complete this store. + completeDataAccess(data_pkt); + incrStIdx(storeWBIdx); + continue; + } + } + } else { + // Non-store conditionals do not need a writeback. + state->noWB = true; + } + + if (!dcachePort->sendTiming(data_pkt)) { + // Need to handle becoming blocked on a store. + isStoreBlocked = true; + + assert(retryPkt == NULL); + retryPkt = data_pkt; + } else { + storePostSend(data_pkt); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +/*template <class Impl> +void +LSQUnit<Impl>::removeMSHR(InstSeqNum seqNum) +{ + list<InstSeqNum>::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 <class Impl> +void +LSQUnit<Impl>::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) { + 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; + } + + // Clear the smart pointer to make sure it is decremented. + 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) { + // Instructions marked as can WB are already committed. + if (storeQueue[store_idx].canWB) { + break; + } + + 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; + } + + // Clear the smart pointer to make sure it is decremented. + storeQueue[store_idx].inst->squashed = true; + storeQueue[store_idx].inst = NULL; + storeQueue[store_idx].canWB = 0; + + storeQueue[store_idx].req = NULL; + --stores; + + // Inefficient! + storeTail = store_idx; + + decrStIdx(store_idx); + } +} + +template <class Impl> +void +LSQUnit<Impl>::storePostSend(Packet *pkt) +{ + 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 (!storeQueue[storeWBIdx].inst->isStoreConditional()) { + // The store is basically completed at this time. This + // only works so long as the checker doesn't try to + // verify the value in memory for stores. + storeQueue[storeWBIdx].inst->setCompleted(); + if (cpu->checker) { + cpu->checker->tick(storeQueue[storeWBIdx].inst); + } + } + + if (pkt->result != Packet::Success) { + DPRINTF(LSQUnit,"D-Cache Write Miss on idx:%i!\n", + storeWBIdx); + + DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(LSQUnit, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // @todo: Increment stat here. + } 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); + } + + incrStIdx(storeWBIdx); +} + +template <class Impl> +void +LSQUnit<Impl>::writeback(DynInstPtr &inst, PacketPtr pkt) +{ + iewStage->wakeCPU(); + + // Squashed instructions do not need to complete their access. + if (inst->isSquashed()) { + assert(!inst->isStore()); + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Complete access to copy data to proper place. + inst->completeAcc(pkt); + } + + // Need to insert instruction into queue to commit + iewStage->instToCommit(inst); + + iewStage->activityThisCycle(); +} + +template <class Impl> +void +LSQUnit<Impl>::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, "Completing store [sn:%lli], idx:%i, store head " + "idx:%i\n", + storeQueue[store_idx].inst->seqNum, store_idx, 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]); + } + + storeQueue[store_idx].inst->setCompleted(); + + // Tell the checker we've completed this instruction. Some stores + // may get reported twice to the checker, but the checker can + // handle that case. + if (cpu->checker) { + cpu->checker->tick(storeQueue[store_idx].inst); + } +} + +template <class Impl> +void +LSQUnit<Impl>::recvRetry() +{ + if (isStoreBlocked) { + assert(retryPkt != NULL); + + if (dcachePort->sendTiming(retryPkt)) { + storePostSend(retryPkt); + retryPkt = NULL; + isStoreBlocked = false; + } else { + // Still blocked! + } + } else if (isLoadBlocked) { + DPRINTF(LSQUnit, "Loads squash themselves and all younger insts, " + "no need to resend packet.\n"); + } else { + DPRINTF(LSQUnit, "Retry received but LSQ is no longer blocked.\n"); + } +} + +template <class Impl> +inline void +LSQUnit<Impl>::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template <class Impl> +inline void +LSQUnit<Impl>::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template <class Impl> +inline void +LSQUnit<Impl>::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template <class Impl> +inline void +LSQUnit<Impl>::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} + +template <class Impl> +void +LSQUnit<Impl>::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"); +} diff --git a/src/cpu/o3/mem_dep_unit.cc b/src/cpu/o3/mem_dep_unit.cc index 9c1e7f9d8..a95103266 100644 --- a/src/cpu/o3/mem_dep_unit.cc +++ b/src/cpu/o3/mem_dep_unit.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" @@ -34,3 +36,15 @@ // Force instantation of memory dependency unit using store sets and // AlphaSimpleImpl. template class MemDepUnit<StoreSet, AlphaSimpleImpl>; + +#ifdef DEBUG +template <> +int +MemDepUnit<StoreSet, AlphaSimpleImpl>::MemDepEntry::memdep_count = 0; +template <> +int +MemDepUnit<StoreSet, AlphaSimpleImpl>::MemDepEntry::memdep_insert = 0; +template <> +int +MemDepUnit<StoreSet, AlphaSimpleImpl>::MemDepEntry::memdep_erase = 0; +#endif diff --git a/src/cpu/o3/mem_dep_unit.hh b/src/cpu/o3/mem_dep_unit.hh index ca63577a1..e399f0133 100644 --- a/src/cpu/o3/mem_dep_unit.hh +++ b/src/cpu/o3/mem_dep_unit.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,17 +24,33 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <map> +#include <list> #include <set> +#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 Impl> +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 +68,166 @@ 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(); + /** Switches out the memory dependence predictor. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Sets the pointer to the IQ. */ + void setIQ(InstructionQueue<Impl> *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<InstSeqNum>::iterator sn_it_t; - typedef typename std::map<InstSeqNum, DynInstPtr>::iterator dyn_it_t; - - // Forward declarations so that the following two typedefs work. - class Dependency; - class ltDependency; - - typedef typename std::set<Dependency, ltDependency>::iterator dep_it_t; - typedef typename std::map<InstSeqNum, vector<dep_it_t> >::iterator - sd_it_t; - - struct Dependency { - Dependency(const InstSeqNum &_seqNum) - : seqNum(_seqNum), regsReady(0), memDepReady(0) - { } - - Dependency(const InstSeqNum &_seqNum, bool _regsReady, - bool _memDepReady) - : seqNum(_seqNum), regsReady(_regsReady), - memDepReady(_memDepReady) - { } - - InstSeqNum seqNum; - mutable bool regsReady; - mutable bool memDepReady; - mutable sd_it_t storeDep; - }; + typedef typename std::list<DynInstPtr>::iterator ListIt; + + class MemDepEntry; - struct ltDependency { - bool operator() (const Dependency &lhs, const Dependency &rhs) + typedef RefCountingPtr<MemDepEntry> MemDepEntryPtr; + + /** 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) { - return lhs.seqNum < rhs.seqNum; +#ifdef DEBUG + ++memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry created. " + "memdep_count=%i\n", memdep_count); +#endif } + + /** Frees any pointers. */ + ~MemDepEntry() + { + for (int i = 0; i < dependInsts.size(); ++i) { + dependInsts[i] = NULL; + } +#ifdef DEBUG + --memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry deleted. " + "memdep_count=%i\n", memdep_count); +#endif + } + + /** 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<MemDepEntryPtr> 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. */ +#ifdef DEBUG + static int memdep_count; + static int memdep_insert; + static int memdep_erase; +#endif }; - 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<Dependency, ltDependency> 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<InstSeqNum> readyInsts; + typedef m5::hash_map<InstSeqNum, MemDepEntryPtr, SNHash> 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<InstSeqNum, vector<dep_it_t> > 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<InstSeqNum, DynInstPtr> 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<DynInstPtr> instList[Impl::MaxThreads]; + + /** A list of all instructions that are going to be replayed. */ + std::list<DynInstPtr> instsToReplay; /** The memory dependence predictor. It is accessed upon new * instructions being added to the IQ, and responds by telling @@ -155,10 +236,29 @@ class MemDepUnit { */ MemDepPred depPred; + /** Is there an outstanding load barrier that loads must wait on. */ + bool loadBarrier; + /** The sequence number of the load barrier. */ + InstSeqNum loadBarrierSN; + /** Is there an outstanding store barrier that loads must wait on. */ + bool storeBarrier; + /** The sequence number of the store barrier. */ + InstSeqNum storeBarrierSN; + + /** Pointer to the IQ. */ + InstructionQueue<Impl> *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/src/cpu/o3/mem_dep_unit_impl.hh b/src/cpu/o3/mem_dep_unit_impl.hh index 296db4c4e..16f67a4e0 100644 --- a/src/cpu/o3/mem_dep_unit_impl.hh +++ b/src/cpu/o3/mem_dep_unit_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,17 +24,64 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include <map> +#include "cpu/o3/inst_queue.hh" #include "cpu/o3/mem_dep_unit.hh" template <class MemDepPred, class Impl> -MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params ¶ms) - : depPred(params.SSITSize, params.LFSTSize) +MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params *params) + : depPred(params->SSITSize, params->LFSTSize), loadBarrier(false), + loadBarrierSN(0), storeBarrier(false), storeBarrierSN(0), iqPtr(NULL) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit object.\n"); +} + +template <class MemDepPred, class Impl> +MemDepUnit<MemDepPred, Impl>::~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++); + } + } + +#ifdef DEBUG + assert(MemDepEntry::memdep_count == 0); +#endif +} + +template <class MemDepPred, class Impl> +std::string +MemDepUnit<MemDepPred, Impl>::name() const { - DPRINTF(MemDepUnit, "MemDepUnit: Creating MemDepUnit object.\n"); + return "memdepunit"; +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::init(Params *params, int tid) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid); + + id = tid; + + depPred.init(params->SSITSize, params->LFSTSize); } template <class MemDepPred, class Impl> @@ -60,56 +107,99 @@ MemDepUnit<MemDepPred, Impl>::regStats() template <class MemDepPred, class Impl> void +MemDepUnit<MemDepPred, Impl>::switchOut() +{ + // Clear any state. + for (int i = 0; i < Impl::MaxThreads; ++i) { + instList[i].clear(); + } + instsToReplay.clear(); + memDepHash.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::takeOverFrom() +{ + // Be sure to reset all state. + loadBarrier = storeBarrier = false; + loadBarrierSN = storeBarrierSN = 0; + depPred.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::setIQ(InstructionQueue<Impl> *iq_ptr) +{ + iqPtr = iq_ptr; +} + +template <class MemDepPred, class Impl> +void MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst) { - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(inst); + + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif - Dependency unresolved_dependencies(inst_seq_num); + instList[tid].push_back(inst); - InstSeqNum producing_store = depPred.checkInst(inst->readPC()); + inst_entry->listIt = --(instList[tid].end()); - if (producing_store == 0 || - storeDependents.find(producing_store) == storeDependents.end()) { + // Check any barriers and the dependence predictor for any + // producing memrefs/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()); + } - DPRINTF(MemDepUnit, "MemDepUnit: No dependency for inst PC " - "%#x.\n", inst->readPC()); + MemDepEntryPtr store_entry = NULL; - unresolved_dependencies.storeDep = storeDependents.end(); + // 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/barrier. + 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); - - assert(!(*store_loc).second.empty()); + store_entry->dependInsts.push_back(inst_entry); if (inst->isLoad()) { ++conflictingLoads; @@ -119,277 +209,288 @@ MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst) } if (inst->isStore()) { - DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n", - inst->readPC()); - - 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]; + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - 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 <class MemDepPred, class Impl> void MemDepUnit<MemDepPred, Impl>::insertNonSpec(DynInstPtr &inst) { - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(inst); - Dependency non_spec_inst(inst_seq_num); + // Insert the MemDepEntry into the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif - non_spec_inst.storeDep = storeDependents.end(); + // Add the instruction to the list. + instList[tid].push_back(inst); - waitingInsts.insert(non_spec_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()); - - 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]; + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - 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 <class MemDepPred, class Impl> -typename Impl::DynInstPtr & -MemDepUnit<MemDepPred, Impl>::top() +void +MemDepUnit<MemDepPred, Impl>::insertBarrier(DynInstPtr &barr_inst) { - topInst = memInsts.find( (*readyInsts.begin()) ); + InstSeqNum barr_sn = barr_inst->seqNum; + // Memory barriers block loads and stores, write barriers only stores. + 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"); + } + + unsigned tid = barr_inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(barr_inst); + + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(barr_sn, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif - DPRINTF(MemDepUnit, "MemDepUnit: Top instruction is PC %#x.\n", - (*topInst).second->readPC()); + // Add the instruction to the instruction list. + instList[tid].push_back(barr_inst); - return (*topInst).second; + inst_entry->listIt = --(instList[tid].end()); } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::pop() +MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Removing instruction PC %#x.\n", - (*topInst).second->readPC()); + DPRINTF(MemDepUnit, "Marking registers as ready for " + "instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - wakeDependents((*topInst).second); + MemDepEntryPtr inst_entry = findInHash(inst); - issue((*topInst).second); + inst_entry->regsReady = true; - memInsts.erase(topInst); + if (inst_entry->memDepReady) { + DPRINTF(MemDepUnit, "Instruction has its memory " + "dependencies resolved, adding it to the ready list.\n"); - topInst = memInsts.end(); + moveToReady(inst_entry); + } else { + DPRINTF(MemDepUnit, "Instruction still waiting on " + "memory dependency.\n"); + } } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst) +MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Marking registers as ready for " - "instruction PC %#x.\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); + + moveToReady(inst_entry); +} - Dependency inst_to_find(inst_seq_num); +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::reschedule(DynInstPtr &inst) +{ + instsToReplay.push_back(inst); +} - dep_it_t waiting_inst = waitingInsts.find(inst_to_find); +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::replay(DynInstPtr &inst) +{ + DynInstPtr temp_inst; + bool found_inst = false; - assert(waiting_inst != waitingInsts.end()); + // For now this replay function replays all waiting memory ops. + while (!instsToReplay.empty()) { + temp_inst = instsToReplay.front(); - if ((*waiting_inst).memDepReady) { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction has its memory " - "dependencies resolved, adding it to the ready list.\n"); + MemDepEntryPtr inst_entry = findInHash(temp_inst); - moveToReady(waiting_inst); - } else { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction still waiting on " - "memory dependency.\n"); + DPRINTF(MemDepUnit, "Replaying mem instruction PC %#x " + "[sn:%lli].\n", + temp_inst->readPC(), temp_inst->seqNum); - (*waiting_inst).regsReady = true; + moveToReady(inst_entry); + + if (temp_inst == inst) { + found_inst = true; + } + + instsToReplay.pop_front(); } + + assert(found_inst); } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst) +MemDepUnit<MemDepPred, Impl>::completed(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Marking non speculative " - "instruction PC %#x as ready.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Completed mem instruction PC %#x " + "[sn:%lli].\n", + inst->readPC(), inst->seqNum); - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; - Dependency inst_to_find(inst_seq_num); + // Remove the instruction from the hash and the list. + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); - dep_it_t waiting_inst = waitingInsts.find(inst_to_find); + assert(hash_it != memDepHash.end()); - assert(waiting_inst != waitingInsts.end()); + instList[tid].erase((*hash_it).second->listIt); - moveToReady(waiting_inst); + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); +#ifdef DEBUG + MemDepEntry::memdep_erase++; +#endif } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst) +MemDepUnit<MemDepPred, Impl>::completeBarrier(DynInstPtr &inst) { - assert(readyInsts.find(inst->seqNum) != readyInsts.end()); - - DPRINTF(MemDepUnit, "MemDepUnit: Issuing instruction PC %#x.\n", - inst->readPC()); - - // Remove the instruction from the ready list. - readyInsts.erase(inst->seqNum); - - depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); + 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 <class MemDepPred, class Impl> void MemDepUnit<MemDepPred, Impl>::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 + 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; } } - storeDependents.erase(sd_it); + inst_entry->dependInsts.clear(); } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num) +MemDepUnit<MemDepPred, Impl>::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) { - if (!storeDependents.empty()) { - sd_it_t dep_it = storeDependents.end(); + DPRINTF(MemDepUnit, "Squashing inst [sn:%lli]\n", + (*squash_it)->seqNum); - --dep_it; + hash_it = memDepHash.find((*squash_it)->seqNum); - // 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()); + assert(hash_it != memDepHash.end()); - storeDependents.erase(dep_it--); - } + (*hash_it).second->squashed = true; + + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); +#ifdef DEBUG + MemDepEntry::memdep_erase++; +#endif + + instList[tid].erase(squash_it--); } // Tell the dependency predictor to squash as well. - depPred.squash(squashed_num); + depPred.squash(squashed_num, tid); } template <class MemDepPred, class Impl> @@ -397,7 +498,7 @@ void MemDepUnit<MemDepPred, Impl>::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 +506,66 @@ MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst, } template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Issuing instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); +} + +template <class MemDepPred, class Impl> +inline typename MemDepUnit<MemDepPred,Impl>::MemDepEntryPtr & +MemDepUnit<MemDepPred, Impl>::findInHash(const DynInstPtr &inst) +{ + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); + + assert(hash_it != memDepHash.end()); + + return (*hash_it).second; +} + +template <class MemDepPred, class Impl> inline void -MemDepUnit<MemDepPred, Impl>::moveToReady(dep_it_t &woken_inst) +MemDepUnit<MemDepPred, Impl>::moveToReady(MemDepEntryPtr &woken_inst_entry) { - DPRINTF(MemDepUnit, "MemDepUnit: Adding instruction sequence number %i " - "to the ready list.\n", (*woken_inst).seqNum); + DPRINTF(MemDepUnit, "Adding instruction [sn:%lli] " + "to the ready list.\n", woken_inst_entry->inst->seqNum); - // Add it to the ready list. - readyInsts.insert((*woken_inst).seqNum); + assert(!woken_inst_entry->squashed); + + iqPtr->addReadyMemInst(woken_inst_entry->inst); +} - // Remove it from the waiting instructions. - waitingInsts.erase(woken_inst); + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::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()); + +#ifdef DEBUG + cprintf("Memory dependence entries: %i\n", MemDepEntry::memdep_count); +#endif } diff --git a/src/cpu/o3/ras.cc b/src/cpu/o3/ras.cc index 0a7d6ca63..f9939259a 100644 --- a/src/cpu/o3/ras.cc +++ b/src/cpu/o3/ras.cc @@ -24,16 +24,30 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #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; + + addrStack.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) + addrStack[i] = 0; +} +void +ReturnAddrStack::reset() +{ + usedEntries = 0; + tos = 0; for (int i = 0; i < numEntries; ++i) addrStack[i] = 0; } @@ -53,9 +67,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/src/cpu/o3/ras.hh b/src/cpu/o3/ras.hh index 46d98181e..5c8a93285 100644 --- a/src/cpu/o3/ras.hh +++ b/src/cpu/o3/ras.hh @@ -24,45 +24,74 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <vector> +/** 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); + + void reset(); + /** 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<Addr> 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/src/cpu/o3/regfile.hh b/src/cpu/o3/regfile.hh index a5cfa8f3c..ade5e4e56 100644 --- a/src/cpu/o3/regfile.hh +++ b/src/cpu/o3/regfile.hh @@ -24,15 +24,17 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Gabe Black */ -#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" +#include "arch/types.hh" #include "base/trace.hh" #include "config/full_system.hh" #include "cpu/o3/comm.hh" @@ -42,32 +44,40 @@ #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 <vector> +/** + * Simple physical register file class. + * Right now this is specific to Alpha until we decide if/how to make things + * generic enough to support other ISAs. + */ template <class Impl> class PhysRegFile { protected: typedef TheISA::IntReg IntReg; typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; 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. + typedef union { + FloatReg d; + FloatRegBits q; + } PhysFloatReg; - //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. + // Note that most of the definitions of the IntReg, FloatReg, etc. exist + // within the Impl/ISA class and not within this PhysRegFile class. - //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,12 +90,13 @@ 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); DPRINTF(IEW, "RegFile: Access to int register %i, has data " - "%i\n", int(reg_idx), intRegFile[reg_idx]); + "%#x\n", int(reg_idx), intRegFile[reg_idx]); return intRegFile[reg_idx]; } @@ -96,14 +107,15 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatReg floatReg = floatRegFile.readReg(reg_idx, width); + FloatReg floatReg = floatRegFile[reg_idx].d; DPRINTF(IEW, "RegFile: Access to %d byte float register %i, has " - "data %8.8d\n", int(reg_idx), (double)floatReg); + "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q); return floatReg; } + /** Reads a floating point register (double precision). */ FloatReg readFloatReg(PhysRegIndex reg_idx) { // Remove the base Float reg dependency. @@ -111,14 +123,15 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatReg floatReg = floatRegFile.readReg(reg_idx); + FloatReg floatReg = floatRegFile[reg_idx].d; DPRINTF(IEW, "RegFile: Access to float register %i, has " - "data %8.8d\n", int(reg_idx), (double)floatReg); + "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q); return floatReg; } + /** Reads a floating point register as an integer. */ FloatRegBits readFloatRegBits(PhysRegIndex reg_idx, int width) { // Remove the base Float reg dependency. @@ -126,10 +139,10 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatRegBits floatRegBits = floatRegFile.readRegBits(reg_idx, width); + FloatRegBits floatRegBits = floatRegFile[reg_idx].q; - DPRINTF(IEW, "RegFile: Access to %d byte float register %i as int, " - "has data %lli\n", int(reg_idx), (uint64_t)floatRegBits); + DPRINTF(IEW, "RegFile: Access to float register %i as int, " + "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits); return floatRegBits; } @@ -141,24 +154,27 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatRegBits floatRegBits = floatRegFile.readRegBits(reg_idx); + FloatRegBits floatRegBits = floatRegFile[reg_idx].q; DPRINTF(IEW, "RegFile: Access to float register %i as int, " - "has data %lli\n", int(reg_idx), (uint64_t)floatRegBits); + "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits); return floatRegBits; } + /** Sets an integer register to the given value. */ void setIntReg(PhysRegIndex reg_idx, uint64_t val) { assert(reg_idx < numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting int register %i to %lli\n", + DPRINTF(IEW, "RegFile: Setting int register %i to %#x\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 setFloatReg(PhysRegIndex reg_idx, FloatReg val, int width) { // Remove the base Float reg dependency. @@ -166,12 +182,14 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %8.8d\n", - int(reg_idx), (double)val); + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); - floatRegFile.setReg(reg_idx, val, width); + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; } + /** Sets a double precision floating point register to the given value. */ void setFloatReg(PhysRegIndex reg_idx, FloatReg val) { // Remove the base Float reg dependency. @@ -179,12 +197,14 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %8.8d\n", - int(reg_idx), (double)val); + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); - floatRegFile.setReg(reg_idx, val); + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; } + /** Sets a floating point register to the given integer value. */ void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val, int width) { // Remove the base Float reg dependency. @@ -192,10 +212,10 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n", + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", int(reg_idx), (uint64_t)val); - floatRegFile.setRegBits(reg_idx, val, width); + floatRegFile[reg_idx].q = val; } void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val) @@ -205,81 +225,68 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n", + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", int(reg_idx), (uint64_t)val); - floatRegFile.setRegBits(reg_idx, val); - } - - uint64_t readPC() - { - return pc; + floatRegFile[reg_idx].q = val; } - void setPC(uint64_t val) + MiscReg readMiscReg(int misc_reg, unsigned thread_id) { - pc = val; + return miscRegs[thread_id].readReg(misc_reg); } - void setNextPC(uint64_t val) + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned thread_id) { - npc = val; + return miscRegs[thread_id].readRegWithEffect(misc_reg, fault, + cpu->tcBase(thread_id)); } - //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) + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned thread_id) { - // Dummy function for now. - // @todo: Fix this once proxy XC is used. - return 0; + return miscRegs[thread_id].setReg(misc_reg, val); } - Fault setMiscReg(int misc_reg, const MiscReg &val) + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned thread_id) { - // Dummy function for now. - // @todo: Fix this once proxy XC is used. - return NoFault; + return miscRegs[thread_id].setRegWithEffect(misc_reg, val, + cpu->tcBase(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; /** Floating point register file. */ - FloatReg *floatRegFile; + PhysFloatReg *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; }; @@ -290,10 +297,14 @@ PhysRegFile<Impl>::PhysRegFile(unsigned _numPhysicalIntRegs, numPhysicalFloatRegs(_numPhysicalFloatRegs) { intRegFile = new IntReg[numPhysicalIntRegs]; - floatRegFile = new FloatReg[numPhysicalFloatRegs]; + floatRegFile = new PhysFloatReg[numPhysicalFloatRegs]; - memset(intRegFile, 0, sizeof(*intRegFile)); - memset(floatRegFile, 0, sizeof(*floatRegFile)); + for (int i = 0; i < Impl::MaxThreads; ++i) { + miscRegs[i].clear(); + } + + memset(intRegFile, 0, sizeof(IntReg) * numPhysicalIntRegs); + memset(floatRegFile, 0, sizeof(PhysFloatReg) * numPhysicalFloatRegs); } -#endif // __CPU_O3_CPU_REGFILE_HH__ +#endif diff --git a/src/cpu/o3/rename.cc b/src/cpu/o3/rename.cc index 6e9ee23da..9ca8e82c6 100644 --- a/src/cpu/o3/rename.cc +++ b/src/cpu/o3/rename.cc @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "cpu/o3/alpha_dyn_inst.hh" #include "cpu/o3/alpha_impl.hh" #include "cpu/o3/rename_impl.hh" -template class SimpleRename<AlphaSimpleImpl>; +template class DefaultRename<AlphaSimpleImpl>; diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh index 07b442964..42fdf6bf5 100644 --- a/src/cpu/o3/rename.hh +++ b/src/cpu/o3/rename.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +24,32 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// 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 <list> #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 Impl> -class SimpleRename +class DefaultRename { public: // Typedefs from the Impl. @@ -51,112 +58,242 @@ 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 list is used to queue the instructions. Barrier insts must + // be added to the front of the list, which is the only reason for + // using a list instead of a queue. (Most other stages use a + // queue) + typedef std::list<DynInstPtr> 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, - BarrierStall + SerializeStall }; 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<TimeStruct> *tb_ptr); + /** Sets pointer to time buffer used to communicate to the next stage. */ void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + /** Sets pointer to time buffer coming from decode. */ void setDecodeQueue(TimeBuffer<DecodeStruct> *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; } + + /** Sets pointer to commit stage. Used only for initialization. */ + void setCommitStage(Commit *commit_stage) + { commit_ptr = commit_stage; } + + private: + /** Pointer to IEW stage. Used only for initialization. */ + IEW *iew_ptr; + + /** Pointer to commit stage. Used only for initialization. */ + Commit *commit_ptr; + + public: + /** Initializes variables for the stage. */ + void initStage(); + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to rename maps (per-thread structures). */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); + /** Sets pointer to the free list. */ void setFreeList(FreeList *fl_ptr); - void dumpHistory(); + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *_scoreboard); - void tick(); + /** Switches out the rename stage. */ + void switchOut(); + + /** Completes the switch out. */ + void doSwitchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Squashes all instructions in a thread. */ + void squash(unsigned tid); - void rename(); + /** Ticks rename, which processes all input signals and attempts to rename + * as many instructions as possible. + */ + void tick(); - void squash(); + /** Debugging function used to dump history buffer of renamings. */ + void dumpHistory(); private: - void block(); + /** 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); - inline void unblock(); + /** Calculates the number of free ROB entries for a specific thread. */ + inline int calcFreeROBEntries(unsigned tid); - void doSquash(); + /** Calculates the number of free IQ entries for a specific thread. */ + inline int calcFreeIQEntries(unsigned tid); - void removeFromHistory(InstSeqNum inst_seq_num); + /** Calculates the number of free LSQ entries for a specific thread. */ + inline int calcFreeLSQEntries(unsigned tid); - inline void renameSrcRegs(DynInstPtr &inst); + /** Returns the number of valid instructions coming from decode. */ + unsigned validInsts(); - inline void renameDestRegs(DynInstPtr &inst); + /** Reads signals telling rename to block/unblock. */ + void readStallSignals(unsigned tid); - inline int calcFreeROBEntries(); + /** Checks if any stages are telling rename to block. */ + bool checkStall(unsigned tid); - inline int calcFreeIQEntries(); + /** Gets the number of free entries for a specific thread. */ + void readFreeEntries(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. + /** Checks the signals and updates the status. */ + 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<RenameHistory> historyBuffer; + /** A per-thread list of all destination register renames, used to either + * undo rename mappings or free old physical registers. + */ + std::list<RenameHistory> 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<TimeStruct> *timeBuffer; /** Wire to get IEW's output from backwards time buffer. */ @@ -166,7 +303,6 @@ class SimpleRename typename TimeBuffer<TimeStruct>::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<TimeStruct>::wire toDecode; /** Rename instruction queue. */ @@ -181,15 +317,71 @@ class SimpleRename /** Wire to get decode's output from decode queue. */ typename TimeBuffer<DecodeStruct>::wire fromDecode; + /** Queue of all instructions coming from decode this cycle. */ + InstQueue insts[Impl::MaxThreads]; + /** Skid buffer between rename and decode. */ - std::queue<DecodeStruct> 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<unsigned> *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 serialize instruction that rename has stalled on. */ + DynInstPtr serializeInst[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 +399,74 @@ 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 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. */ - unsigned numInst; + 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 serializing inst. */ + Stats::Scalar<> renameSerializeStallCycles; + /** 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; + /** Number of serialize instructions handled. */ + Stats::Scalar<> renamedSerializing; + /** Number of instructions marked as temporarily serializing. */ + Stats::Scalar<> renamedTempSerializing; + /** Number of instructions inserted into skid buffers. */ + Stats::Scalar<> renameSkidInsts; }; -#endif // __CPU_O3_CPU_SIMPLE_RENAME_HH__ +#endif // __CPU_O3_RENAME_HH__ diff --git a/src/cpu/o3/rename_impl.hh b/src/cpu/o3/rename_impl.hh index 2068b36ab..df33b98ee 100644 --- a/src/cpu/o3/rename_impl.hh +++ b/src/cpu/o3/rename_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include <list> @@ -31,97 +33,146 @@ #include "config/full_system.hh" #include "cpu/o3/rename.hh" +using namespace std; + +template <class Impl> +DefaultRename<Impl>::DefaultRename(Params *params) + : iewToRenameDelay(params->iewToRenameDelay), + decodeToRenameDelay(params->decodeToRenameDelay), + commitToRenameDelay(params->commitToRenameDelay), + renameWidth(params->renameWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads) +{ + _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; + serializeInst[i] = NULL; + + instsInProgress[i] = 0; + + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } + + // @todo: Make into a parameter. + skidBufferMax = (2 * (iewToRenameDelay * params->decodeWidth)) + renameWidth; +} + template <class Impl> -SimpleRename<Impl>::SimpleRename(Params ¶ms) - : iewToRenameDelay(params.iewToRenameDelay), - decodeToRenameDelay(params.decodeToRenameDelay), - commitToRenameDelay(params.commitToRenameDelay), - renameWidth(params.renameWidth), - commitWidth(params.commitWidth), - numInst(0) +std::string +DefaultRename<Impl>::name() const { - _status = Idle; + return cpu->name() + ".rename"; } template <class Impl> void -SimpleRename<Impl>::regStats() +DefaultRename<Impl>::regStats() { renameSquashCycles - .name(name() + ".renameSquashCycles") + .name(name() + ".RENAME:SquashCycles") .desc("Number of cycles rename is squashing") .prereq(renameSquashCycles); renameIdleCycles - .name(name() + ".renameIdleCycles") + .name(name() + ".RENAME:IdleCycles") .desc("Number of cycles rename is idle") .prereq(renameIdleCycles); renameBlockCycles - .name(name() + ".renameBlockCycles") + .name(name() + ".RENAME:BlockCycles") .desc("Number of cycles rename is blocking") .prereq(renameBlockCycles); + renameSerializeStallCycles + .name(name() + ".RENAME:serializeStallCycles") + .desc("count of cycles rename stalled for serializing inst") + .flags(Stats::total); + renameRunCycles + .name(name() + ".RENAME:RunCycles") + .desc("Number of cycles rename is running") + .prereq(renameIdleCycles); renameUnblockCycles - .name(name() + ".renameUnblockCycles") + .name(name() + ".RENAME:UnblockCycles") .desc("Number of cycles rename is unblocking") .prereq(renameUnblockCycles); renameRenamedInsts - .name(name() + ".renameRenamedInsts") + .name(name() + ".RENAME:RenamedInsts") .desc("Number of instructions processed by rename") .prereq(renameRenamedInsts); renameSquashedInsts - .name(name() + ".renameSquashedInsts") + .name(name() + ".RENAME:SquashedInsts") .desc("Number of squashed instructions processed by rename") .prereq(renameSquashedInsts); renameROBFullEvents - .name(name() + ".renameROBFullEvents") - .desc("Number of times rename has considered the ROB 'full'") + .name(name() + ".RENAME:ROBFullEvents") + .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'") + .name(name() + ".RENAME:IQFullEvents") + .desc("Number of times rename has blocked due to IQ full") .prereq(renameIQFullEvents); + renameLSQFullEvents + .name(name() + ".RENAME:LSQFullEvents") + .desc("Number of times rename has blocked due to LSQ full") + .prereq(renameLSQFullEvents); renameFullRegistersEvents - .name(name() + ".renameFullRegisterEvents") + .name(name() + ".RENAME:FullRegisterEvents") .desc("Number of times there has been no free registers") .prereq(renameFullRegistersEvents); renameRenamedOperands - .name(name() + ".renameRenamedOperands") + .name(name() + ".RENAME:RenamedOperands") .desc("Number of destination operands rename has renamed") .prereq(renameRenamedOperands); renameRenameLookups - .name(name() + ".renameRenameLookups") + .name(name() + ".RENAME:RenameLookups") .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") + .name(name() + ".RENAME:CommittedMaps") .desc("Number of HB maps that are committed") .prereq(renameCommittedMaps); renameUndoneMaps - .name(name() + ".renameUndoneMaps") + .name(name() + ".RENAME:UndoneMaps") .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); + renamedSerializing + .name(name() + ".RENAME:serializingInsts") + .desc("count of serializing insts renamed") + .flags(Stats::total) + ; + renamedTempSerializing + .name(name() + ".RENAME:tempSerializingInsts") + .desc("count of temporary serializing insts renamed") + .flags(Stats::total) + ; + renameSkidInsts + .name(name() + ".RENAME:skidInsts") + .desc("count of insts added to the skid buffer") + .flags(Stats::total) + ; } template <class Impl> void -SimpleRename<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultRename<Impl>::setCPU(FullCPU *cpu_ptr) { - DPRINTF(Rename, "Rename: Setting CPU pointer.\n"); + DPRINTF(Rename, "Setting CPU pointer.\n"); cpu = cpu_ptr; } template <class Impl> void -SimpleRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *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 +187,9 @@ SimpleRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) template <class Impl> void -SimpleRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +DefaultRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *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 +198,9 @@ SimpleRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) template <class Impl> void -SimpleRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) +DefaultRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *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 +209,721 @@ SimpleRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) template <class Impl> void -SimpleRename<Impl>::setRenameMap(RenameMap *rm_ptr) +DefaultRename<Impl>::initStage() { - DPRINTF(Rename, "Rename: Setting rename map pointer.\n"); - renameMap = rm_ptr; + // Grab the number of free entries directly from the stages. + 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; + } } +template<class Impl> +void +DefaultRename<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Rename, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + + template <class Impl> void -SimpleRename<Impl>::setFreeList(FreeList *fl_ptr) +DefaultRename<Impl>::setRenameMap(RenameMap rm_ptr[]) { - DPRINTF(Rename, "Rename: Setting free list pointer.\n"); + DPRINTF(Rename, "Setting rename map pointers.\n"); + + for (int i=0; i<numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template <class Impl> +void +DefaultRename<Impl>::setFreeList(FreeList *fl_ptr) +{ + DPRINTF(Rename, "Setting free list pointer.\n"); freeList = fl_ptr; } +template<class Impl> +void +DefaultRename<Impl>::setScoreboard(Scoreboard *_scoreboard) +{ + DPRINTF(Rename, "Setting scoreboard pointer.\n"); + scoreboard = _scoreboard; +} + +template <class Impl> +void +DefaultRename<Impl>::switchOut() +{ + // Rename is ready to switch out at any time. + cpu->signalSwitched(); +} + template <class Impl> void -SimpleRename<Impl>::dumpHistory() +DefaultRename<Impl>::doSwitchOut() { - typename list<RenameHistory>::iterator buf_it = historyBuffer.begin(); + // Clear any state, fix up the rename map. + for (int i = 0; i < numThreads; i++) { + typename list<RenameHistory>::iterator hb_it = historyBuffer[i].begin(); - 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); + while (!historyBuffer[i].empty()) { + assert(hb_it != historyBuffer[i].end()); - buf_it++; + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", i, (*hb_it).instSeqNum); + + // Tell the rename map to set the architected register to the + // previous physical register that it was renamed to. + renameMap[i]->setEntry(hb_it->archReg, hb_it->prevPhysReg); + + // Put the renamed physical register back on the free list. + freeList->addReg(hb_it->newPhysReg); + + historyBuffer[i].erase(hb_it++); + } + insts[i].clear(); + skidBuffer[i].clear(); } } template <class Impl> void -SimpleRename<Impl>::block() +DefaultRename<Impl>::takeOverFrom() { - DPRINTF(Rename, "Rename: Blocking.\n"); - // Set status to Blocked. - _status = Blocked; + _status = Inactive; + initStage(); - // Add the current inputs onto the skid buffer, so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromDecode); + // Reset all state prior to taking over from the other CPU. + for (int i=0; i< numThreads; i++) { + renameStatus[i] = Idle; + + stalls[i].iew = false; + stalls[i].commit = false; + serializeInst[i] = NULL; + + instsInProgress[i] = 0; - // 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. + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } } template <class Impl> -inline void -SimpleRename<Impl>::unblock() -{ - DPRINTF(Rename, "Rename: Read instructions out of skid buffer this " - "cycle.\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()) { - toDecode->renameInfo.stall = true; - } else { - DPRINTF(Rename, "Rename: Done unblocking.\n"); - _status = Running; +void +DefaultRename<Impl>::squash(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Squashing instructions.\n",tid); + + // 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] == SerializeStall) { + + toDecode->renameUnblock[tid] = 1; + + serializeInst[tid] = NULL; } + + // Set the status to Squashing. + renameStatus[tid] = Squashing; + + // Squash any instructions from decode. + unsigned squashCount = 0; + + for (int i=0; i<fromDecode->size; 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 <class Impl> void -SimpleRename<Impl>::doSquash() +DefaultRename<Impl>::tick() { - typename list<RenameHistory>::iterator hb_it = historyBuffer.begin(); + wroteToTimeBuffer = false; - InstSeqNum squashed_seq_num = fromCommit->commitInfo.doneSeqNum; + blockThisCycle = false; -#if FULL_SYSTEM - assert(!historyBuffer.empty()); -#else - // After a syscall squashes everything, the history buffer may be empty - // but the ROB may still be squashing instructions. - if (historyBuffer.empty()) { + bool status_change = false; + + toIEWIndex = 0; + + sortInsts(); + + list<unsigned>::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<class Impl> +void +DefaultRename<Impl>::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] == SerializeStall) { + ++renameSerializeStallCycles; + } + + 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); + + 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 <class Impl> +void +DefaultRename<Impl>::renameInsts(unsigned tid) +{ + // 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; } -#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()); + DynInstPtr inst; - DPRINTF(Rename, "Rename: Removing history entry with sequence " - "number %i.\n", (*hb_it).instSeqNum); + // 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; - // 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); + FullSource source = ROB; - // Put the renamed physical register back on the free list. - freeList->addReg(hb_it->newPhysReg); + if (free_iq_entries < min_free_entries) { + min_free_entries = free_iq_entries; + source = IQ; + } - ++renameValidUndoneMaps; + 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(); } + } - historyBuffer.erase(hb_it++); + int renamed_insts = 0; - ++renameUndoneMaps; + 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(); + + 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. + + // In this model, IPR accesses are serialize before + // instructions, and store conditionals are serialize after + // instructions. This is mainly due to lack of support for + // out-of-order operations of either of those classes of + // instructions. + if ((inst->isIprAccess() || inst->isSerializeBefore()) && + !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) { + renamedSerializing++; + inst->setSerializeHandled(); + } else { + renamedTempSerializing++; + } + + // Change status over to SerializeStall so that other stages know + // what this is blocked on. + renameStatus[tid] = SerializeStall; + + serializeInst[tid] = inst; + + blockThisCycle = true; + + break; + } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) && + !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize after instruction encountered.\n"); + + renamedSerializing++; + + 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; + + // Decrement how many instructions are available. + --insts_available; + } + + instsInProgress[tid] += renamed_insts; + renameRenamedInsts += 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<class Impl> +void +DefaultRename<Impl>::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()); + + ++renameSkidInsts; + + skidBuffer[tid].push_back(inst); + } + + if (skidBuffer[tid].size() > skidBufferMax) + panic("Skidbuffer Exceeded Max Size"); +} + template <class Impl> void -SimpleRename<Impl>::squash() +DefaultRename<Impl>::sortInsts() { - DPRINTF(Rename, "Rename: Squashing instructions.\n"); - // Set the status to Squashing. - _status = Squashing; + int insts_from_decode = fromDecode->size; +#ifdef DEBUG + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_decode; ++i) { + DynInstPtr inst = fromDecode->insts[i]; + insts[inst->threadNumber].push_back(inst); + } +} - numInst = 0; +template<class Impl> +bool +DefaultRename<Impl>::skidsEmpty() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) - { - skidBuffer.pop(); + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; } - doSquash(); + return true; } template<class Impl> void -SimpleRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num) +DefaultRename<Impl>::updateStatus() { - DPRINTF(Rename, "Rename: Removing a committed instruction from the " - "history buffer, until sequence number %lli.\n", inst_seq_num); - typename list<RenameHistory>::iterator hb_it = historyBuffer.end(); + bool any_unblocking = false; - --hb_it; + list<unsigned>::iterator threads = (*activeThreads).begin(); - if (hb_it->instSeqNum > inst_seq_num) { - DPRINTF(Rename, "Rename: Old sequence number encountered. Ensure " - "that a syscall happened recently.\n"); - return; + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (renameStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } } - while ((*hb_it).instSeqNum != inst_seq_num) - { - // Make sure we haven't gone off the end of the list. - assert(hb_it != historyBuffer.end()); - - // 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" - " number %i.\n", - (*hb_it).prevPhysReg, (*hb_it).instSeqNum); - - if (!(*hb_it).placeHolder) { - freeList->addReg((*hb_it).prevPhysReg); - ++renameCommittedMaps; + // 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"); - historyBuffer.erase(hb_it--); + cpu->deactivateStage(FullCPU::RenameIdx); + } } +} - // Finally free up the previous register of the finished instruction - // itself. - if (!(*hb_it).placeHolder) { - freeList->addReg(hb_it->prevPhysReg); - ++renameCommittedMaps; +template <class Impl> +bool +DefaultRename<Impl>::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. + skidInsert(tid); + + // 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 SerializeStall to Blocked, otherwise + // it would not know to complete the serialize stall. + if (renameStatus[tid] != SerializeStall) { + // Set status to Blocked. + renameStatus[tid] = Blocked; + return true; + } + } + + return false; +} + +template <class Impl> +bool +DefaultRename<Impl>::unblock(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Trying to unblock.\n", tid); + + // Rename is done unblocking if the skid buffer is empty. + if (skidBuffer[tid].empty() && renameStatus[tid] != SerializeStall) { + + DPRINTF(Rename, "[tid:%u]: Done unblocking.\n", tid); + + toDecode->renameUnblock[tid] = true; + wroteToTimeBuffer = true; + + renameStatus[tid] = Running; + return true; } - historyBuffer.erase(hb_it); + return false; +} + +template <class Impl> +void +DefaultRename<Impl>::doSquash(unsigned tid) +{ + typename list<RenameHistory>::iterator hb_it = historyBuffer[tid].begin(); + + InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum; + + // After a syscall squashes everything, the history buffer may be empty + // but the ROB may still be squashing instructions. + if (historyBuffer[tid].empty()) { + return; + } + + // Go through the most recent instructions, undoing the mappings + // they did and freeing up the registers. + while (!historyBuffer[tid].empty() && + (*hb_it).instSeqNum > squashed_seq_num) { + assert(hb_it != historyBuffer[tid].end()); + + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", tid, (*hb_it).instSeqNum); + + // 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); + + historyBuffer[tid].erase(hb_it++); + + ++renameUndoneMaps; + } +} + +template<class Impl> +void +DefaultRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num, unsigned tid) +{ + 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<RenameHistory>::iterator hb_it = historyBuffer[tid].end(); + + --hb_it; + + 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; + } + + // 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) { + + DPRINTF(Rename, "[tid:%u]: Freeing up older rename of reg %i, " + "[sn:%lli].\n", + tid, (*hb_it).prevPhysReg, (*hb_it).instSeqNum); + + freeList->addReg((*hb_it).prevPhysReg); + ++renameCommittedMaps; + + historyBuffer[tid].erase(hb_it--); + } } template <class Impl> inline void -SimpleRename<Impl>::renameSrcRegs(DynInstPtr &inst) +DefaultRename<Impl>::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 +934,341 @@ SimpleRename<Impl>::renameSrcRegs(DynInstPtr &inst) template <class Impl> inline void -SimpleRename<Impl>::renameDestRegs(DynInstPtr &inst) +DefaultRename<Impl>::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); + ++renameRenamedOperands; + } +} - DPRINTF(Rename, "Rename: Adding instruction to history buffer, " - "sequence number %lli.\n", - (*historyBuffer.begin()).instSeqNum); +template <class Impl> +inline int +DefaultRename<Impl>::calcFreeROBEntries(unsigned tid) +{ + int num_free = freeEntries[tid].robEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); - // 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); + //DPRINTF(Rename,"[tid:%i]: %i rob free\n",tid,num_free); - ++renameRenamedOperands; - } - } + return num_free; } template <class Impl> inline int -SimpleRename<Impl>::calcFreeROBEntries() +DefaultRename<Impl>::calcFreeIQEntries(unsigned tid) { - return fromCommit->commitInfo.freeROBEntries - - 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 <class Impl> inline int -SimpleRename<Impl>::calcFreeIQEntries() +DefaultRename<Impl>::calcFreeLSQEntries(unsigned tid) { - return fromIEW->iewInfo.freeIQEntries - renameWidth * iewToRenameDelay; -} + int num_free = freeEntries[tid].lsqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatchedToLSQ); -template<class Impl> -void -SimpleRename<Impl>::tick() -{ - // 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); - } + //DPRINTF(Rename,"[tid:%i]: %i lsq free\n",tid,num_free); - unblock(); - } - } else if (_status == Blocked) { - ++renameBlockCycles; + return num_free; +} - // If stage is blocked and still receiving valid instructions, - // make sure to store them in the skid buffer. - if (fromDecode->size > 0) { +template <class Impl> +unsigned +DefaultRename<Impl>::validInsts() +{ + unsigned inst_count = 0; - block(); + for (int i=0; i<fromDecode->size; i++) { + if (!fromDecode->insts[i]->squashed) + inst_count++; + } - // Continue to tell previous stage to stall. - toDecode->renameInfo.stall = true; - } + return inst_count; +} - 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; - } +template <class Impl> +void +DefaultRename<Impl>::readStallSignals(unsigned tid) +{ + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - return; - } - } else if (_status == Squashing) { - ++renameSquashCycles; + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } - if (fromCommit->commitInfo.squash) { - squash(); - } else if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } - DPRINTF(Rename, "Rename: Done squashing, going to running.\n"); - _status = Running; - rename(); - } else { - doSquash(); - } + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; } +} - // 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 +template <class Impl> +bool +DefaultRename<Impl>::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] == SerializeStall && + (!emptyROB[tid] || instsInProgress[tid])) { + DPRINTF(Rename,"[tid:%i]: Stall: Serialize stall and ROB is not " + "empty.\n", + tid); + ret_val = true; } + return ret_val; } -template<class Impl> +template <class Impl> void -SimpleRename<Impl>::rename() -{ - // 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; +DefaultRename<Impl>::readFreeEntries(unsigned tid) +{ + bool updated = false; + if (fromIEW->iewInfo[tid].usedIQ) { + freeEntries[tid].iqEntries = + fromIEW->iewInfo[tid].freeIQEntries; + updated = true; } - // 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; + if (fromIEW->iewInfo[tid].usedLSQ) { + freeEntries[tid].lsqEntries = + fromIEW->iewInfo[tid].freeLSQEntries; + updated = true; } - // 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; + if (fromCommit->commitInfo[tid].usedROB) { + freeEntries[tid].robEntries = + fromCommit->commitInfo[tid].freeROBEntries; + emptyROB[tid] = fromCommit->commitInfo[tid].emptyROB; + updated = true; } - // 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; - } - - //////////////////////////////////// - // Actual rename part. - //////////////////////////////////// - - DynInstPtr inst; + DPRINTF(Rename, "[tid:%i]: Free IQ: %i, Free ROB: %i, Free LSQ: %i\n", + tid, + freeEntries[tid].iqEntries, + freeEntries[tid].robEntries, + freeEntries[tid].lsqEntries); - // 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; + DPRINTF(Rename, "[tid:%i]: %i instructions not yet in ROB\n", + tid, instsInProgress[tid]); +} - bool block_this_cycle = false; +template <class Impl> +bool +DefaultRename<Impl>::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 serialize 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; + } - // 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); + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Rename, "[tid:%u]: ROB is still squashing.\n", tid); - unsigned to_iew_index = 0; + renameStatus[tid] = Squashing; - // 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; + return true; + } - if (free_rob_entries <= 0) { - ++renameROBFullEvents; - } else { - ++renameIQFullEvents; - } + if (checkStall(tid)) { + return block(tid); + } - 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); + if (renameStatus[tid] == Blocked) { + DPRINTF(Rename, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); - insts_available = min_iq_rob; + renameStatus[tid] = Unblocking; - block_this_cycle = true; + unblock(tid); - if (free_rob_entries < free_iq_entries) { - ++renameROBFullEvents; - } else { - ++renameIQFullEvents; - } + return true; } - while (insts_available > 0) { - DPRINTF(Rename, "Rename: Sending instructions to iew.\n"); + 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); - // Get the next instruction either from the skid buffer or the - // decode queue. - inst = _status == Unblocking ? skidBuffer.front().insts[numInst] : - fromDecode->insts[numInst]; + renameStatus[tid] = Running; - if (inst->isSquashed()) { - DPRINTF(Rename, "Rename: instruction %i with PC %#x is " - "squashed, skipping.\n", - inst->seqNum, inst->readPC()); + return false; + } - // Go to the next instruction. - ++numInst; + if (renameStatus[tid] == SerializeStall) { + // Stall ends once the ROB is free. + DPRINTF(Rename, "[tid:%u]: Done with serialize stall, switching to " + "unblocking.\n", tid); - ++renameSquashedInsts; + DynInstPtr serial_inst = serializeInst[tid]; - // Decrement how many instructions are available. - --insts_available; + renameStatus[tid] = Unblocking; - continue; - } + unblock(tid); - 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; + DPRINTF(Rename, "[tid:%u]: Processing instruction [%lli] with " + "PC %#x.\n", + tid, serial_inst->seqNum, serial_inst->readPC()); - block_this_cycle = true; + // Put instruction into queue here. + serial_inst->clearSerializeBefore(); - break; + if (!skidBuffer[tid].empty()) { + skidBuffer[tid].push_front(serial_inst); + } else { + insts[tid].push_front(serial_inst); } - // 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; + DPRINTF(Rename, "[tid:%u]: Instruction must be processed by rename." + " Adding to front of list.", tid); - ++renameFullRegistersEvents; + serializeInst[tid] = NULL; - break; - } + return true; + } - renameSrcRegs(inst); + // 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; +} - renameDestRegs(inst); +template<class Impl> +void +DefaultRename<Impl>::serializeAfter(InstQueue &inst_list, + unsigned tid) +{ + if (inst_list.empty()) { + // Mark a bit to say that I must serialize on the next instruction. + serializeOnNextInst[tid] = true; + return; + } - // Put instruction in rename queue. - toIEW->insts[to_iew_index] = inst; - ++(toIEW->size); + // Set the next instruction as serializing. + inst_list.front()->setSerializeBefore(); +} - // Decrease the number of free ROB and IQ entries. - --free_rob_entries; - --free_iq_entries; +template <class Impl> +inline void +DefaultRename<Impl>::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; + } +} - // Increment which instruction we're on. - ++to_iew_index; - ++numInst; +template <class Impl> +void +DefaultRename<Impl>::dumpHistory() +{ + typename list<RenameHistory>::iterator buf_it; - ++renameRenamedInsts; + for (int i = 0; i < numThreads; i++) { - // Decrement how many instructions are available. - --insts_available; - } + buf_it = historyBuffer[i].begin(); - // Check if there's any instructions left that haven't yet been renamed. - // If so then block. - if (block_this_cycle) { - block(); + 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); - 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; + buf_it++; + } } } diff --git a/src/cpu/o3/rename_map.cc b/src/cpu/o3/rename_map.cc index 10963f7de..befbc3e8a 100644 --- a/src/cpu/o3/rename_map.cc +++ b/src/cpu/o3/rename_map.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include <vector> @@ -32,118 +34,105 @@ using namespace std; -// Todo: Consider making functions inline. Avoid having things that are -// using the zero register or misc registers from adding on the registers -// to the free list. Possibly remove the direct communication between -// this and the freelist. Considering making inline bool functions that -// 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) +// @todo: Consider making inline bool functions that determine if the +// register is a logical int, logical fp, physical int, physical fp, +// etc. + +SimpleRenameMap::~SimpleRenameMap() +{ +} + +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) { - DPRINTF(Rename, "Rename: Creating rename map. Phys: %i / %i, Float: " - "%i / %i.\n", numLogicalIntRegs, numPhysicalIntRegs, + 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]; - - // Should combine this into one scoreboard. - intScoreboard.resize(numPhysicalIntRegs); - floatScoreboard.resize(numPhysicalRegs); - miscScoreboard.resize(numPhysicalRegs + numMiscRegs); - - // 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; - } + //Create the rename maps + intRenameMap.resize(numLogicalIntRegs); + floatRenameMap.resize(numLogicalRegs); - // 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; - } + if (bindRegs) { + DPRINTF(Rename, "Binding registers into rename map %i",id); - 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++; - } + // 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++; + } - for (PhysRegIndex index = numPhysicalIntRegs; - index < numPhysicalIntRegs + numLogicalFloatRegs; ++index) - { - floatScoreboard[index] = 1; - } + // 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); - // 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; - } + PhysRegIndex temp_ireg = ireg_idx; - // Initialize the entries in the misc register scoreboard to be ready. - for (PhysRegIndex index = numPhysicalRegs; - index < numPhysicalRegs + numMiscRegs; ++index) - { - miscScoreboard[index] = 1; - } -} + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = temp_ireg++; + } -SimpleRenameMap::~SimpleRenameMap() -{ - // Delete the rename maps as they were allocated with new. - delete [] intRenameMap; - delete [] floatRenameMap; + PhysRegIndex temp_freg = freg_idx; + + for (PhysRegIndex index = numLogicalIntRegs; + index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = temp_freg++; + } + } } void SimpleRenameMap::setFreeList(SimpleFreeList *fl_ptr) { - //Setup the interface to the freelist. freeList = fl_ptr; } -// Don't allow this stage to fault; force that check to the rename stage. -// Simply ask to rename a logical register and get back a new physical -// register index. SimpleRenameMap::RenameInfo SimpleRenameMap::rename(RegIndex arch_reg) { @@ -156,45 +145,33 @@ SimpleRenameMap::rename(RegIndex arch_reg) // requested architected register. prev_reg = intRenameMap[arch_reg].physical_reg; - // If it's not referencing the zero register, then mark the register - // as not ready. + // If it's not referencing the zero register, then rename the + // register. if (arch_reg != intZeroReg) { - // Get a free physical register to rename to. renamed_reg = freeList->getIntReg(); - // Update the integer rename map. intRenameMap[arch_reg].physical_reg = renamed_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; } } else if (arch_reg < numLogicalRegs) { - // Subtract off the base offset for floating point registers. -// arch_reg = arch_reg - numLogicalIntRegs; - // Record the current physical register that is renamed to the // requested architected register. prev_reg = floatRenameMap[arch_reg].physical_reg; - // If it's not referencing the zero register, then mark the register - // as not ready. + // If it's not referencing the zero register, then rename the + // register. if (arch_reg != floatZeroReg) { - // Get a free floating point register to rename to. renamed_reg = freeList->getFloatReg(); - // Update the floating point rename map. floatRenameMap[arch_reg].physical_reg = renamed_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; @@ -203,10 +180,10 @@ SimpleRenameMap::rename(RegIndex arch_reg) // Subtract off the base offset for miscellaneous registers. arch_reg = arch_reg - numLogicalRegs; - // No renaming happens to the misc. registers. They are simply the - // registers that come after all the physical registers; thus - // take the base architected register and add the physical registers - // to it. + // No renaming happens to the misc. registers. They are + // simply the registers that come after all the physical + // registers; thus take the base architected register and add + // the physical registers to it. renamed_reg = arch_reg + numPhysicalRegs; // Set the previous register to the same register; mainly it must be @@ -215,24 +192,17 @@ SimpleRenameMap::rename(RegIndex arch_reg) prev_reg = renamed_reg; assert(renamed_reg < numPhysicalRegs + numMiscRegs); - - miscScoreboard[renamed_reg] = false; } return RenameInfo(renamed_reg, prev_reg); } -//Perhaps give this a pair as a return value, of the physical register -//and whether or not it's ready. PhysRegIndex SimpleRenameMap::lookup(RegIndex arch_reg) { if (arch_reg < numLogicalIntRegs) { return intRenameMap[arch_reg].physical_reg; } else if (arch_reg < numLogicalRegs) { - // Subtract off the base FP offset. -// arch_reg = arch_reg - numLogicalIntRegs; - return floatRenameMap[arch_reg].physical_reg; } else { // Subtract off the misc registers offset. @@ -244,38 +214,18 @@ 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 SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg) { + // In this implementation the miscellaneous registers do not + // actually rename, so this function does not allow you to try to + // change their mappings. if (arch_reg < numLogicalIntRegs) { DPRINTF(Rename, "Rename Map: Integer register %i being set to %i.\n", (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); @@ -283,55 +233,6 @@ SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg) } } -void -SimpleRenameMap::squash(vector<RegIndex> freed_regs, - vector<UnmapInfo> unmaps) -{ - panic("Not sure this function should be called."); - - // Not sure the rename map should be able to access the free list - // like this. - while (!freed_regs.empty()) { - RegIndex free_register = freed_regs.back(); - - if (free_register < numPhysicalIntRegs) { - freeList->addIntReg(free_register); - } else { - // Subtract off the base FP dependence tag. - free_register = free_register - numPhysicalIntRegs; - freeList->addFloatReg(free_register); - } - - freed_regs.pop_back(); - } - - // 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/src/cpu/o3/rename_map.hh b/src/cpu/o3/rename_map.hh index 57be4a64a..c4c90c99a 100644 --- a/src/cpu/o3/rename_map.hh +++ b/src/cpu/o3/rename_map.hh @@ -24,14 +24,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ // Todo: Create destructor. // 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 <iostream> #include <utility> @@ -62,18 +64,30 @@ class SimpleRenameMap typedef std::pair<PhysRegIndex, PhysRegIndex> RenameInfo; public: - //Constructor - SimpleRenameMap(unsigned _numLogicalIntRegs, - unsigned _numPhysicalIntRegs, - unsigned _numLogicalFloatRegs, - unsigned _numPhysicalFloatRegs, - unsigned _numMiscRegs, - RegIndex _intZeroReg, - RegIndex _floatZeroReg); + /** Default constructor. init() must be called prior to use. */ + SimpleRenameMap() {}; /** Destructor. */ ~SimpleRenameMap(); + /** Initializes rename map with given parameters. */ + 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); + + /** Sets the free list used with this rename map. */ void setFreeList(SimpleFreeList *fl_ptr); //Tell rename map to get a free physical register for a given @@ -84,23 +98,19 @@ 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<RegIndex> freed_regs, - std::vector<UnmapInfo> unmaps); - int numFreeEntries(); private: + /** Rename Map ID */ + int id; + /** Number of logical integer registers. */ int numLogicalIntRegs; @@ -143,31 +153,16 @@ class SimpleRenameMap { } }; + private: /** Integer rename map. */ - RenameEntry *intRenameMap; + std::vector<RenameEntry> intRenameMap; /** Floating point rename map. */ - RenameEntry *floatRenameMap; + std::vector<RenameEntry> 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<bool> intScoreboard; - - /** Scoreboard of physical floating registers, saying whether or not they - * are ready. - */ - std::vector<bool> floatScoreboard; - - /** Scoreboard of miscellaneous registers, saying whether or not they - * are ready. - */ - std::vector<bool> miscScoreboard; }; -#endif //__CPU_O3_CPU_RENAME_MAP_HH__ +#endif //__CPU_O3_RENAME_MAP_HH__ diff --git a/src/cpu/o3/rob.cc b/src/cpu/o3/rob.cc index c10f782fd..f99e5ccfd 100644 --- a/src/cpu/o3/rob.cc +++ b/src/cpu/o3/rob.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Nathan Binkert */ #include "cpu/o3/alpha_dyn_inst.hh" diff --git a/src/cpu/o3/rob.hh b/src/cpu/o3/rob.hh index 1185564ad..6d1402531 100644 --- a/src/cpu/o3/rob.hh +++ b/src/cpu/o3/rob.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +24,19 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -// 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 <string> #include <utility> #include <vector> /** - * 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 Impl> class ROB @@ -54,16 +48,43 @@ class ROB typedef typename Impl::FullCPU FullCPU; typedef typename Impl::DynInstPtr DynInstPtr; - typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo_t; - typedef typename list<DynInstPtr>::iterator InstIt_t; + typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo; + typedef typename std::list<DynInstPtr>::iterator InstIt; + + /** Possible ROB statuses. */ + enum Status { + Running, + Idle, + ROBSquashing + }; + + /** 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,21 @@ 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<unsigned>* at_ptr); + + /** Switches out the ROB. */ + void switchOut(); + + /** Takes over another CPU's thread. */ + void takeOverFrom(); + + /** 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 +114,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(); + + /** 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); - DynInstPtr readTailInst() { return (*tail); } + /** 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); - void retireHead(); + /** Is the oldest instruction across all threads ready. */ +// bool isHeadReady(); - 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); - uint64_t readHeadPC(); + /** 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); - uint64_t readHeadNextPC(); + /** Reads the sequence number of the oldest head instruction. */ +// InstSeqNum readHeadSeqNum(); - 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(); - uint64_t readTailPC(); + /** Reads the PC of the tail instruction of a specific thread. */ +// uint64_t readTailPC(unsigned tid); - InstSeqNum readTailSeqNum(); + /** 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 +249,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<unsigned>* 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<DynInstPtr> 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 +296,24 @@ 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; + /** Dummy instruction returned if there are no insts left. */ + 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/src/cpu/o3/rob_impl.hh b/src/cpu/o3/rob_impl.hh index e7a5671d9..97694e371 100644 --- a/src/cpu/o3/rob_impl.hh +++ b/src/cpu/o3/rob_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,22 +24,78 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <class Impl> -ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth) +ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth, + string _smtROBPolicy, unsigned _smtROBThreshold, + unsigned _numThreads) : numEntries(_numEntries), squashWidth(_squashWidth), numInstsInROB(0), - squashedSeqNum(0) + squashedSeqNum(0), + numThreads(_numThreads) +{ + 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 <class Impl> +std::string +ROB<Impl>::name() const { - doneSquashing = true; + return cpu->name() + ".rob"; } template <class Impl> @@ -48,124 +104,227 @@ ROB<Impl>::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 <class Impl> -int -ROB<Impl>::countInsts() +void +ROB<Impl>::setActiveThreads(list<unsigned> *at_ptr) { - // Start at 1; if the tail matches cpu->instList.begin(), then there is - // one inst in the ROB. - int return_val = 1; + DPRINTF(ROB, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} - // 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; +template <class Impl> +void +ROB<Impl>::switchOut() +{ + for (int tid = 0; tid < numThreads; tid++) { + instList[tid].clear(); } +} - // 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; +template <class Impl> +void +ROB<Impl>::takeOverFrom() +{ + for (int tid=0; tid < numThreads; tid++) { + doneSquashing[tid] = true; + threadEntries[tid] = 0; + squashIt[tid] = instList[tid].end(); } + numInstsInROB = 0; - return return_val; + // Initialize the "universal" ROB head & tail point to invalid + // pointers + head = instList[0].end(); + tail = instList[0].end(); +} - // 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 <class Impl> +void +ROB<Impl>::resetEntries() +{ + if (robPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::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 <class Impl> +int +ROB<Impl>::entryAmount(int num_threads) +{ + if (robPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + +template <class Impl> +int +ROB<Impl>::countInsts() +{ + int total=0; + + for (int i=0;i < numThreads;i++) + total += countInsts(i); + + return total; +} + +template <class Impl> +int +ROB<Impl>::countInsts(unsigned tid) +{ + return instList[tid].size(); } template <class Impl> void ROB<Impl>::insertInst(DynInstPtr &inst) { - // Make sure we have the right number of instructions. - assert(numInstsInROB == countInsts()); - // Make sure the instruction is valid. + //assert(numInstsInROB == countInsts()); 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(); + 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. - assert((*tail) == inst); + //Must Decrement for iterator to actually be valid since __.end() + //actually points to 1 after the last inst + tail = instList[tid].end(); + tail--; + + inst->setInROB(); - DPRINTF(ROB, "ROB: Now has %d instructions.\n", numInstsInROB); + ++numInstsInROB; + ++threadEntries[tid]; + + assert((*tail) == inst); + DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]); } // Whatever calls this function needs to ensure that it properly frees up // registers prior to this function. +/* template <class Impl> void ROB<Impl>::retireHead() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); + assert(numInstsInROB > 0); + + int tid = (*head)->threadNumber; + + retireHead(tid); + + if (numInstsInROB == 0) { + tail = instList[tid].end(); + } +} +*/ + +template <class Impl> +void +ROB<Impl>::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]; + + head_inst->removeInROB(); + head_inst->setCommitted(); - // 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. + instList[tid].erase(head_it); + + //Update "Global" Head of ROB + updateHead(); + + // @todo: 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); +} +/* +template <class Impl> +bool +ROB<Impl>::isHeadReady() +{ + if (numInstsInROB != 0) { + return (*head)->readyToCommit(); + } - if (numInstsInROB == 0) { - tail = cpu->instList.end(); + return false; +} +*/ +template <class Impl> +bool +ROB<Impl>::isHeadReady(unsigned tid) +{ + if (threadEntries[tid] != 0) { + return instList[tid].front()->readyToCommit(); } + + return false; } template <class Impl> bool -ROB<Impl>::isHeadReady() +ROB<Impl>::canCommit() { - if (numInstsInROB != 0) { - return cpu->instList.front()->readyToCommit(); + //@todo: set ActiveThreads through ROB or CPU + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (isHeadReady(tid)) { + return true; + } } return false; @@ -175,131 +334,339 @@ template <class Impl> unsigned ROB<Impl>::numFreeEntries() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); return numEntries - numInstsInROB; } template <class Impl> +unsigned +ROB<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - threadEntries[tid]; +} + +template <class Impl> void -ROB<Impl>::doSquash() +ROB<Impl>::doSquash(unsigned tid) { - DPRINTF(ROB, "ROB: Squashing instructions.\n"); + DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n", + tid, squashedSeqNum); + + assert(squashIt[tid] != instList[tid].end()); - assert(squashIt != cpu->instList.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)->seqNum != squashedSeqNum; + numSquashed < squashWidth && + squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->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); + 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)->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 " + (*squashIt[tid])->setSquashed(); + + (*squashIt[tid])->setCanCommit(); + + + if (squashIt[tid] == instList[tid].begin()) { + DPRINTF(ROB, "Reached head of instruction list while " "squashing.\n"); - squashIt = cpu->instList.end(); + squashIt[tid] = instList[tid].end(); - doneSquashing = true; + doneSquashing[tid] = true; return; } -#endif - // Move the tail iterator to the next instruction. - squashIt--; + 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)->seqNum == squashedSeqNum) { - DPRINTF(ROB, "ROB: Done squashing instructions.\n"); + if ((*squashIt[tid])->seqNum <= squashedSeqNum) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); - squashIt = cpu->instList.end(); + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + } - doneSquashing = true; + if (robTailUpdate) { + updateTail(); } } + template <class Impl> void -ROB<Impl>::squash(InstSeqNum squash_num) +ROB<Impl>::updateHead() { - DPRINTF(ROB, "ROB: Starting to squash within the ROB.\n"); - doneSquashing = false; + DynInstPtr head_inst; + InstSeqNum lowest_num = 0; + bool first_valid = true; + + // @todo: set ActiveThreads through ROB or CPU + list<unsigned>::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 <class Impl> +void +ROB<Impl>::updateTail() +{ + tail = instList[0].end(); + bool first_valid = true; + + list<unsigned>::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 <class Impl> +void +ROB<Impl>::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 <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readHeadInst() +{ + if (numInstsInROB != 0) { + assert((*head)->isInROB()==true); + return *head; + } else { + return dummyInst; + } +} +*/ +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::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 <class Impl> uint64_t ROB<Impl>::readHeadPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - DynInstPtr head_inst = cpu->instList.front(); + DynInstPtr head_inst = *head; return head_inst->readPC(); } template <class Impl> uint64_t +ROB<Impl>::readHeadPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readPC(); +} + + +template <class Impl> +uint64_t ROB<Impl>::readHeadNextPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - DynInstPtr head_inst = cpu->instList.front(); + DynInstPtr head_inst = *head; return head_inst->readNextPC(); } template <class Impl> +uint64_t +ROB<Impl>::readHeadNextPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readNextPC(); +} + +template <class Impl> InstSeqNum ROB<Impl>::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 <class Impl> +InstSeqNum +ROB<Impl>::readHeadSeqNum(unsigned tid) +{ + InstIt head_thread = instList[tid].begin(); + + return ((*head_thread)->seqNum); +} + +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readTailInst() +{ + //assert(numInstsInROB == countInsts()); + //assert(tail != instList[0].end()); + + return (*tail); +} +*/ +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readTailInst(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return *tail_thread; +} + +/* +template <class Impl> uint64_t ROB<Impl>::readTailPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - assert(tail != cpu->instList.end()); + //assert(tail != instList[0].end()); return (*tail)->readPC(); } template <class Impl> +uint64_t +ROB<Impl>::readTailPC(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->readPC(); +} + +template <class Impl> InstSeqNum ROB<Impl>::readTailSeqNum() { @@ -309,4 +676,18 @@ ROB<Impl>::readTailSeqNum() return (*tail)->seqNum; } -#endif // __CPU_O3_CPU_ROB_IMPL_HH__ +template <class Impl> +InstSeqNum +ROB<Impl>::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/src/cpu/o3/sat_counter.cc b/src/cpu/o3/sat_counter.cc index d20fff650..68d3ef627 100644 --- a/src/cpu/o3/sat_counter.cc +++ b/src/cpu/o3/sat_counter.cc @@ -24,27 +24,29 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ #include "base/misc.hh" #include "cpu/o3/sat_counter.hh" SatCounter::SatCounter() - : maxVal(0), counter(0) + : initialVal(0), counter(0) { } SatCounter::SatCounter(unsigned bits) - : maxVal((1 << bits) - 1), counter(0) + : initialVal(0), maxVal((1 << bits) - 1), counter(0) { } -SatCounter::SatCounter(unsigned bits, unsigned initial_val) - : maxVal((1 << bits) - 1), counter(initial_val) +SatCounter::SatCounter(unsigned bits, uint8_t initial_val) + : initialVal(initialVal), maxVal((1 << bits) - 1), counter(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."); } } @@ -53,19 +55,3 @@ SatCounter::setBits(unsigned bits) { maxVal = (1 << bits) - 1; } - -void -SatCounter::increment() -{ - if(counter < maxVal) { - ++counter; - } -} - -void -SatCounter::decrement() -{ - if(counter > 0) { - --counter; - } -} diff --git a/src/cpu/o3/sat_counter.hh b/src/cpu/o3/sat_counter.hh index b7cfe6423..7e15119b0 100644 --- a/src/cpu/o3/sat_counter.hh +++ b/src/cpu/o3/sat_counter.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 The Regents of The University of Michigan + * Copyright (c) 2005-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,11 +24,14 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 "base/misc.hh" #include "sim/host.hh" /** @@ -44,47 +47,70 @@ class SatCounter /** * Constructor for the counter. */ - SatCounter(); + SatCounter() + : initialVal(0), counter(0) + { } /** * Constructor for the counter. * @param bits How many bits the counter will have. */ - SatCounter(unsigned bits); + SatCounter(unsigned bits) + : initialVal(0), maxVal((1 << bits) - 1), counter(0) + { } /** * Constructor for the counter. * @param bits How many bits the counter will have. * @param initial_val Starting value for each counter. */ - SatCounter(unsigned bits, unsigned initial_val); + SatCounter(unsigned bits, uint8_t initial_val) + : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val) + { + // Check to make sure initial value doesn't exceed the max + // counter value. + if (initial_val > maxVal) { + fatal("BP: Initial counter value exceeds max size."); + } + } /** * Sets the number of bits. */ - void setBits(unsigned bits); + void setBits(unsigned bits) { maxVal = (1 << bits) - 1; } + + void reset() { counter = initialVal; } /** * Increments the counter's current value. */ - void increment(); + void increment() + { + if (counter < maxVal) { + ++counter; + } + } /** * Decrements the counter's current value. */ - void decrement(); + void decrement() + { + if (counter > 0) { + --counter; + } + } /** * Read the counter's value. */ const uint8_t read() const - { - return counter; - } + { return counter; } private: + uint8_t initialVal; uint8_t maxVal; uint8_t counter; }; -#endif // __CPU_O3_CPU_SAT_COUNTER_HH__ +#endif // __CPU_O3_SAT_COUNTER_HH__ diff --git a/src/cpu/o3/scoreboard.cc b/src/cpu/o3/scoreboard.cc new file mode 100644 index 000000000..1859b35a4 --- /dev/null +++ b/src/cpu/o3/scoreboard.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005-2006 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. + * + * Authors: Korey Sewell + * Kevin Lim + */ + +#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. + return; + } + + regScoreBoard[ready_reg] = 0; +} diff --git a/src/cpu/o3/scoreboard.hh b/src/cpu/o3/scoreboard.hh new file mode 100644 index 000000000..f8e4df3b7 --- /dev/null +++ b/src/cpu/o3/scoreboard.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2005-2006 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. + * + * Authors: Korey Sewell + * Kevin Lim + */ + +#ifndef __CPU_O3_SCOREBOARD_HH__ +#define __CPU_O3_SCOREBOARD_HH__ + +#include <iostream> +#include <utility> +#include <vector> +#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<bool> 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/src/cpu/o3/store_set.cc b/src/cpu/o3/store_set.cc index 11023f4a8..2d28b617f 100644 --- a/src/cpu/o3/store_set.cc +++ b/src/cpu/o3/store_set.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,49 +24,94 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ +#include "base/intmath.hh" +#include "base/misc.hh" #include "base/trace.hh" #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); + + if (!isPowerOf2(SSITSize)) { + fatal("Invalid SSIT size!\n"); + } - 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]; + if (!isPowerOf2(LFSTSize)) { + fatal("Invalid LFST size!\n"); + } + + LFST.resize(LFSTSize); + + validLFST.resize(LFSTSize); + + for (int i = 0; i < LFSTSize; ++i) { + validLFST[i] = false; + LFST[i] = 0; + } + + indexMask = SSITSize - 1; + + offsetBits = 2; +} + +StoreSet::~StoreSet() +{ +} - validLFST.resize(LFST_size); +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); - SSCounters = new int[LFST_size]; + SSIT.resize(SSITSize); - for (int i = 0; i < LFST_size; ++i) - { + 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; - SSCounters[i] = 0; + LFST[i] = 0; } - index_mask = SSIT_size - 1; + indexMask = SSITSize - 1; - offset_bits = 2; + 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 +128,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 +140,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 +152,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 +159,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 +186,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 +201,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 +222,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 +233,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 +262,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 +277,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 +288,31 @@ 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. - 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 +320,29 @@ 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(); } +void +StoreSet::dump() +{ + cprintf("storeList.size(): %i\n", storeList.size()); + SeqNumMapIt store_list_it = storeList.begin(); + + int num = 0; + + while (store_list_it != storeList.end()) { + cprintf("%i: [sn:%lli] SSID:%i\n", + num, (*store_list_it).first, (*store_list_it).second); + num++; + store_list_it++; + } +} diff --git a/src/cpu/o3/store_set.hh b/src/cpu/o3/store_set.hh index 5a885d838..f5a44a1ac 100644 --- a/src/cpu/o3/store_set.hh +++ b/src/cpu/o3/store_set.hh @@ -24,63 +24,124 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <list> +#include <map> +#include <utility> #include <vector> #include "arch/isa_traits.hh" #include "cpu/inst_seq.hh" +struct ltseqnum { + bool operator()(const InstSeqNum &lhs, const InstSeqNum &rhs) const + { + return lhs > rhs; + } +}; + +/** + * Implements a store set predictor for determining if memory + * instructions are dependent upon each other. See paper "Memory + * Dependence Prediction using Store Sets" by Chrysos and Emer. SSID + * stands for Store Set ID, SSIT stands for Store Set ID Table, and + * LFST is Last Fetched Store Table. + */ class StoreSet { public: typedef unsigned SSID; public: + /** Default constructor. init() must be called prior to use. */ + StoreSet() { }; + + /** Creates store set predictor with given table sizes. */ StoreSet(int SSIT_size, int LFST_size); + /** Default destructor. */ + ~StoreSet(); + + /** Initializes the store set predictor with the given table sizes. */ + void init(int SSIT_size, int LFST_size); + + /** Records a memory ordering violation between the younger load + * and the older store. */ void violation(Addr store_PC, Addr load_PC); + /** Inserts a load into the store set predictor. This does nothing but + * is included in case other predictors require a similar function. + */ void insertLoad(Addr load_PC, InstSeqNum load_seq_num); - void insertStore(Addr store_PC, InstSeqNum store_seq_num); + /** Inserts a store into the store set predictor. Updates the + * LFST if the store has a valid SSID. */ + void insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid); + /** Checks if the instruction with the given PC is dependent upon + * any store. @return Returns the sequence number of the store + * instruction this PC is dependent upon. Returns 0 if none. + */ InstSeqNum checkInst(Addr PC); + /** Records this PC/sequence number as issued. */ void issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store); - void squash(InstSeqNum squashed_num); + /** Squashes for a specific thread until the given sequence number. */ + void squash(InstSeqNum squashed_num, unsigned tid); + /** Resets all tables. */ void clear(); + /** Debug function to dump the contents of the store list. */ + void dump(); + private: + /** Calculates the index into the SSIT based on the PC. */ inline int calcIndex(Addr PC) - { return (PC >> offset_bits) & index_mask; } + { return (PC >> offsetBits) & indexMask; } + /** Calculates a Store Set ID based on the PC. */ inline SSID calcSSID(Addr PC) - { return ((PC ^ (PC >> 10)) % LFST_size); } + { return ((PC ^ (PC >> 10)) % LFSTSize); } - SSID *SSIT; + /** The Store Set ID Table. */ + std::vector<SSID> SSIT; + /** Bit vector to tell if the SSIT has a valid entry. */ std::vector<bool> validSSIT; - InstSeqNum *LFST; + /** Last Fetched Store Table. */ + std::vector<InstSeqNum> LFST; + /** Bit vector to tell if the LFST has a valid entry. */ std::vector<bool> validLFST; - int *SSCounters; + /** Map of stores that have been inserted into the store set, but + * not yet issued or squashed. + */ + std::map<InstSeqNum, int, ltseqnum> storeList; + + typedef std::map<InstSeqNum, int, ltseqnum>::iterator SeqNumMapIt; - int SSIT_size; + /** Store Set ID Table size, in entries. */ + int SSITSize; - int LFST_size; + /** Last Fetched Store Table size, in entries. */ + int LFSTSize; - int index_mask; + /** Mask to obtain the index. */ + 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/src/cpu/o3/thread_state.hh b/src/cpu/o3/thread_state.hh new file mode 100644 index 000000000..b6535baa1 --- /dev/null +++ b/src/cpu/o3/thread_state.hh @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_THREAD_STATE_HH__ +#define __CPU_O3_THREAD_STATE_HH__ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" + +class Event; +class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class FunctionalMemory; +class Process; +#endif + +/** + * Class that has various thread state, such as the status, the + * current instruction being processed, whether or not the thread has + * a trap pending or is being externally updated, the ThreadContext + * pointer, etc. It also handles anything related to a specific + * thread's process, such as syscalls and checking valid addresses. + */ +template <class Impl> +struct O3ThreadState : public ThreadState { + typedef ThreadContext::Status Status; + typedef typename Impl::FullCPU FullCPU; + + private: + /** Pointer to the CPU. */ + FullCPU *cpu; + public: + /** Whether or not the thread is currently in syscall mode, and + * thus able to be externally updated without squashing. + */ + bool inSyscall; + + /** Whether or not the thread is currently waiting on a trap, and + * thus able to be externally updated without squashing. + */ + bool trapPending; + +#if FULL_SYSTEM + O3ThreadState(FullCPU *_cpu, int _thread_num) + : ThreadState(-1, _thread_num), + inSyscall(0), trapPending(0) + { } +#else + O3ThreadState(FullCPU *_cpu, int _thread_num, Process *_process, int _asid, + MemObject *mem) + : ThreadState(-1, _thread_num, mem, _process, _asid), + cpu(_cpu), inSyscall(0), trapPending(0) + { } +#endif + + /** Pointer to the ThreadContext of this thread. */ + ThreadContext *tc; + + /** Returns a pointer to the TC of this thread. */ + ThreadContext *getTC() { return tc; } + +#if !FULL_SYSTEM + /** Handles the syscall. */ + void syscall(int64_t callnum) { process->syscall(callnum, tc); } +#endif +}; + +#endif // __CPU_O3_THREAD_STATE_HH__ diff --git a/src/cpu/o3/tournament_pred.cc b/src/cpu/o3/tournament_pred.cc index 3fb580510..7cf78dcb1 100644 --- a/src/cpu/o3/tournament_pred.cc +++ b/src/cpu/o3/tournament_pred.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,41 +24,50 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ +#include "base/intmath.hh" #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) + if (!isPowerOf2(localPredictorSize)) { + fatal("Invalid local predictor size!\n"); + } //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); + if (!isPowerOf2(localHistoryTableSize)) { + fatal("Invalid local history table size!\n"); + } + //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; @@ -66,8 +75,12 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size, // Setup the local history mask localHistoryMask = (1 << localHistoryBits) - 1; + if (!isPowerOf2(globalPredictorSize)) { + fatal("Invalid global predictor size!\n"); + } + //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); @@ -77,12 +90,17 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size, // Setup the global history mask globalHistoryMask = (1 << globalHistoryBits) - 1; + if (!isPowerOf2(choicePredictorSize)) { + fatal("Invalid choice predictor size!\n"); + } + //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); + // @todo: Allow for different thresholds between the predictors. threshold = (1 << (localCtrBits - 1)) - 1; threshold = threshold / 2; } @@ -91,166 +109,185 @@ inline unsigned TournamentBP::calcLocHistIdx(Addr &branch_addr) { + // Get low order bits after removing instruction offset. return (branch_addr >> instShiftAmt) & (localHistoryTableSize - 1); } inline void -TournamentBP::updateHistoriesTaken(unsigned local_history_idx) +TournamentBP::updateGlobalHistTaken() { globalHistory = (globalHistory << 1) | 1; globalHistory = globalHistory & globalHistoryMask; - - localHistoryTable[local_history_idx] = - (localHistoryTable[local_history_idx] << 1) | 1; } inline void -TournamentBP::updateHistoriesNotTaken(unsigned local_history_idx) +TournamentBP::updateGlobalHistNotTaken() { globalHistory = (globalHistory << 1); globalHistory = globalHistory & globalHistoryMask; +} +inline +void +TournamentBP::updateLocalHistTaken(unsigned local_history_idx) +{ + localHistoryTable[local_history_idx] = + (localHistoryTable[local_history_idx] << 1) | 1; +} + +inline +void +TournamentBP::updateLocalHistNotTaken(unsigned local_history_idx) +{ localHistoryTable[local_history_idx] = (localHistoryTable[local_history_idx] << 1); } bool -TournamentBP::lookup(Addr &branch_addr) +TournamentBP::lookup(Addr &branch_addr, void * &bp_history) { - uint8_t local_prediction; + bool local_prediction; unsigned local_history_idx; unsigned local_predictor_idx; - uint8_t global_prediction; - uint8_t choice_prediction; + bool global_prediction; + bool choice_prediction; //Lookup in the local predictor to get its branch prediction local_history_idx = calcLocHistIdx(branch_addr); local_predictor_idx = localHistoryTable[local_history_idx] & localHistoryMask; - local_prediction = localCtrs[local_predictor_idx].read(); + local_prediction = localCtrs[local_predictor_idx].read() > threshold; //Lookup in the global predictor to get its branch prediction - global_prediction = globalCtrs[globalHistory].read(); + global_prediction = globalCtrs[globalHistory].read() > threshold; //Lookup in the choice predictor to see which one to use - choice_prediction = choiceCtrs[globalHistory].read(); - - //@todo Put a threshold value in for the three predictors that can - // be set through the constructor (so this isn't hard coded). - //Also should put some of this code into functions. - if (choice_prediction > threshold) { - if (global_prediction > threshold) { - updateHistoriesTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].increment(); - localCtrs[local_history_idx].increment(); - + choice_prediction = choiceCtrs[globalHistory].read() > threshold; + + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory; + history->localPredTaken = local_prediction; + history->globalPredTaken = global_prediction; + history->globalUsed = choice_prediction; + bp_history = (void *)history; + + assert(globalHistory < globalPredictorSize && + local_history_idx < localPredictorSize); + + // Commented code is for doing speculative update of counters and + // all histories. + if (choice_prediction) { + if (global_prediction) { +// updateHistoriesTaken(local_history_idx); +// globalCtrs[globalHistory].increment(); +// localCtrs[local_history_idx].increment(); + updateGlobalHistTaken(); return true; } else { - updateHistoriesNotTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].decrement(); - localCtrs[local_history_idx].decrement(); - +// updateHistoriesNotTaken(local_history_idx); +// globalCtrs[globalHistory].decrement(); +// localCtrs[local_history_idx].decrement(); + updateGlobalHistNotTaken(); return false; } } else { - if (local_prediction > threshold) { - updateHistoriesTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].increment(); - localCtrs[local_history_idx].increment(); - + if (local_prediction) { +// updateHistoriesTaken(local_history_idx); +// globalCtrs[globalHistory].increment(); +// localCtrs[local_history_idx].increment(); + updateGlobalHistTaken(); return true; } else { - updateHistoriesNotTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].decrement(); - localCtrs[local_history_idx].decrement(); - +// updateHistoriesNotTaken(local_history_idx); +// globalCtrs[globalHistory].decrement(); +// localCtrs[local_history_idx].decrement(); + updateGlobalHistNotTaken(); return false; } } } -// Update the branch predictor if it predicted a branch wrong. void -TournamentBP::update(Addr &branch_addr, unsigned correct_gh, bool taken) +TournamentBP::uncondBr(void * &bp_history) { + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory; + history->localPredTaken = true; + history->globalPredTaken = true; + bp_history = static_cast<void *>(history); + + updateGlobalHistTaken(); +} - uint8_t local_prediction; +void +TournamentBP::update(Addr &branch_addr, bool taken, void *bp_history) +{ unsigned local_history_idx; unsigned local_predictor_idx; - bool local_pred_taken; + unsigned local_predictor_hist; - uint8_t global_prediction; - bool global_pred_taken; - - // Load the correct global history into the register. - globalHistory = correct_gh; - - // Get the local predictor's current prediction, remove the incorrect - // update, and update the local predictor + // Get the local predictor's current prediction local_history_idx = calcLocHistIdx(branch_addr); - local_predictor_idx = localHistoryTable[local_history_idx]; - local_predictor_idx = (local_predictor_idx >> 1) & localHistoryMask; - - local_prediction = localCtrs[local_predictor_idx].read(); - local_pred_taken = local_prediction > threshold; - - //Get the global predictor's current prediction, and update the - //global predictor - global_prediction = globalCtrs[globalHistory].read(); - global_pred_taken = global_prediction > threshold; - - //Update the choice predictor to tell it which one was correct - if (local_pred_taken != global_pred_taken) { - //If the local prediction matches the actual outcome, decerement - //the counter. Otherwise increment the counter. - if (local_pred_taken == taken) { - choiceCtrs[globalHistory].decrement(); - } else { - choiceCtrs[globalHistory].increment(); + local_predictor_hist = localHistoryTable[local_history_idx]; + local_predictor_idx = local_predictor_hist & localHistoryMask; + + // Update the choice predictor to tell it which one was correct if + // there was a prediction. + if (bp_history) { + BPHistory *history = static_cast<BPHistory *>(bp_history); + if (history->localPredTaken != history->globalPredTaken) { + // If the local prediction matches the actual outcome, + // decerement the counter. Otherwise increment the + // counter. + if (history->localPredTaken == taken) { + choiceCtrs[globalHistory].decrement(); + } else if (history->globalPredTaken == taken){ + choiceCtrs[globalHistory].increment(); + } } + + // We're done with this history, now delete it. + delete history; } - if (taken) { - assert(globalHistory < globalPredictorSize && - local_predictor_idx < localPredictorSize); + assert(globalHistory < globalPredictorSize && + local_predictor_idx < localPredictorSize); + // Update the counters and local history with the proper + // resolution of the branch. Global history is updated + // speculatively and restored upon squash() calls, so it does not + // need to be updated. + if (taken) { localCtrs[local_predictor_idx].increment(); globalCtrs[globalHistory].increment(); - globalHistory = (globalHistory << 1) | 1; - globalHistory = globalHistory & globalHistoryMask; + updateLocalHistTaken(local_history_idx); + } else { + localCtrs[local_predictor_idx].decrement(); + globalCtrs[globalHistory].decrement(); - localHistoryTable[local_history_idx] |= 1; + updateLocalHistNotTaken(local_history_idx); } - else { - assert(globalHistory < globalPredictorSize && - local_predictor_idx < localPredictorSize); +} - localCtrs[local_predictor_idx].decrement(); - globalCtrs[globalHistory].decrement(); +void +TournamentBP::squash(void *bp_history) +{ + BPHistory *history = static_cast<BPHistory *>(bp_history); - globalHistory = (globalHistory << 1); - globalHistory = globalHistory & globalHistoryMask; + // Restore global history to state prior to this branch. + globalHistory = history->globalHistory; - localHistoryTable[local_history_idx] &= ~1; - } + // Delete this BPHistory now that we're done with it. + delete history; } + +#ifdef DEBUG +int +TournamentBP::BPHistory::newCount = 0; +#endif diff --git a/src/cpu/o3/tournament_pred.hh b/src/cpu/o3/tournament_pred.hh index cb93c2f67..92402adc6 100644 --- a/src/cpu/o3/tournament_pred.hh +++ b/src/cpu/o3/tournament_pred.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,61 +24,138 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#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 <vector> + +/** + * Implements a tournament branch predictor, hopefully identical to the one + * used in the 21264. It has a local predictor, which uses a local history + * table to index into a table of counters, and a global predictor, which + * uses a global history to index into a table of counters. A choice + * predictor chooses between the two. Only the global history register + * is speculatively updated, the rest are updated upon branches committing + * or misspeculating. + */ class TournamentBP { public: /** * 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); /** * Looks up the given address in the branch predictor and returns - * a true/false value as to whether it is taken. + * a true/false value as to whether it is taken. Also creates a + * BPHistory object to store any state it will need on squash/update. * @param branch_addr The address of the branch to look up. + * @param bp_history Pointer that will be set to the BPHistory object. * @return Whether or not the branch is taken. */ - bool lookup(Addr &branch_addr); + bool lookup(Addr &branch_addr, void * &bp_history); + + /** + * Records that there was an unconditional branch, and modifies + * the bp history to point to an object that has the previous + * global history stored in it. + * @param bp_history Pointer that will be set to the BPHistory object. + */ + void uncondBr(void * &bp_history); /** * Updates the branch predictor with the actual result of a branch. * @param branch_addr The address of the branch to update. * @param taken Whether or not the branch was taken. + * @param bp_history Pointer to the BPHistory object that was created + * when the branch was predicted. */ - void update(Addr &branch_addr, unsigned global_history, bool taken); + void update(Addr &branch_addr, bool taken, void *bp_history); + /** + * Restores the global branch history on a squash. + * @param bp_history Pointer to the BPHistory object that has the + * previous global branch history in it. + */ + void squash(void *bp_history); + + /** Returns the global history. */ inline unsigned readGlobalHist() { return globalHistory; } private: - + /** + * Returns if the branch should be taken or not, given a counter + * value. + * @param count The counter value. + */ inline bool getPrediction(uint8_t &count); + /** + * Returns the local history index, given a branch address. + * @param branch_addr The branch's PC address. + */ inline unsigned calcLocHistIdx(Addr &branch_addr); - inline void updateHistoriesTaken(unsigned local_history_idx); + /** Updates global history as taken. */ + inline void updateGlobalHistTaken(); + + /** Updates global history as not taken. */ + inline void updateGlobalHistNotTaken(); + + /** + * Updates local histories as taken. + * @param local_history_idx The local history table entry that + * will be updated. + */ + inline void updateLocalHistTaken(unsigned local_history_idx); + + /** + * Updates local histories as not taken. + * @param local_history_idx The local history table entry that + * will be updated. + */ + inline void updateLocalHistNotTaken(unsigned local_history_idx); - inline void updateHistoriesNotTaken(unsigned local_history_idx); + /** + * The branch history information that is created upon predicting + * a branch. It will be passed back upon updating and squashing, + * when the BP can use this information to update/restore its + * state properly. + */ + struct BPHistory { +#ifdef DEBUG + BPHistory() + { newCount++; } + ~BPHistory() + { newCount--; } + + static int newCount; +#endif + unsigned globalHistory; + bool localPredTaken; + bool globalPredTaken; + bool globalUsed; + }; /** Local counters. */ - SatCounter *localCtrs; + std::vector<SatCounter> localCtrs; /** Size of the local predictor. */ unsigned localPredictorSize; @@ -87,7 +164,7 @@ class TournamentBP unsigned localCtrBits; /** Array of local history table entries. */ - unsigned *localHistoryTable; + std::vector<unsigned> localHistoryTable; /** Size of the local history table. */ unsigned localHistoryTableSize; @@ -100,9 +177,8 @@ class TournamentBP /** Mask to get the proper local history. */ unsigned localHistoryMask; - /** Array of counters that make up the global predictor. */ - SatCounter *globalCtrs; + std::vector<SatCounter> globalCtrs; /** Size of the global predictor. */ unsigned globalPredictorSize; @@ -119,9 +195,8 @@ class TournamentBP /** Mask to get the proper global history. */ unsigned globalHistoryMask; - /** Array of counters that make up the choice predictor. */ - SatCounter *choiceCtrs; + std::vector<SatCounter> choiceCtrs; /** Size of the choice predictor (identical to the global predictor). */ unsigned choicePredictorSize; @@ -140,4 +215,4 @@ class TournamentBP unsigned threshold; }; -#endif // __CPU_O3_CPU_TOURNAMENT_PRED_HH__ +#endif // __CPU_O3_TOURNAMENT_PRED_HH__ diff --git a/src/cpu/op_class.cc b/src/cpu/op_class.cc index 00136ded5..f7ef49c0f 100644 --- a/src/cpu/op_class.cc +++ b/src/cpu/op_class.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include "cpu/op_class.hh" diff --git a/src/cpu/op_class.hh b/src/cpu/op_class.hh index cdb40a0fb..71819c904 100644 --- a/src/cpu/op_class.hh +++ b/src/cpu/op_class.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __CPU__OP_CLASS_HH__ @@ -59,6 +62,6 @@ enum OpClass { /** * Array mapping OpClass enum values to strings. Defined in op_class.cc. */ -extern const char *opClassStrings[]; +extern const char *opClassStrings[Num_OpClasses]; #endif // __CPU__OP_CLASS_HH__ diff --git a/src/cpu/ozone/back_end.cc b/src/cpu/ozone/back_end.cc new file mode 100644 index 000000000..a61a00c84 --- /dev/null +++ b/src/cpu/ozone/back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/back_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +//template class BackEnd<OzoneImpl>; diff --git a/src/cpu/ozone/back_end.hh b/src/cpu/ozone/back_end.hh new file mode 100644 index 000000000..9bab6a964 --- /dev/null +++ b/src/cpu/ozone/back_end.hh @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_BACK_END_HH__ +#define __CPU_OZONE_BACK_END_HH__ + +#include <list> +#include <queue> +#include <string> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +class ThreadContext; + +template <class Impl> +class OzoneThreadState; + +template <class Impl> +class BackEnd +{ + public: + typedef OzoneThreadState<Impl> Thread; + + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + struct SizeStruct { + int size; + }; + + typedef SizeStruct DispatchToIssue; + typedef SizeStruct IssueToExec; + typedef SizeStruct ExecToCommit; + typedef SizeStruct Writeback; + + TimeBuffer<DispatchToIssue> d2i; + typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; + TimeBuffer<IssueToExec> i2e; + typename TimeBuffer<IssueToExec>::wire instsToExecute; + TimeBuffer<ExecToCommit> e2c; + TimeBuffer<Writeback> numInstsToWB; + + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire toIEW; + typename TimeBuffer<CommStruct>::wire fromCommit; + + class InstQueue { + enum queue { + NonSpec, + IQ, + ToBeScheduled, + ReadyList, + ReplayList + }; + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + public: + InstQueue(Params *params); + + std::string name() const; + + void regStats(); + + void setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue); + + void setBE(BackEnd *_be) { be = _be; } + + void insert(DynInstPtr &inst); + + void scheduleReadyInsts(); + + void scheduleNonSpec(const InstSeqNum &sn); + + DynInstPtr getReadyInst(); + + void commit(const InstSeqNum &sn) {} + + void squash(const InstSeqNum &sn); + + int wakeDependents(DynInstPtr &inst); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst); + + void violation(DynInstPtr &inst, DynInstPtr &violation) { } + + bool isFull() { return numInsts >= size; } + + void dumpInsts(); + + private: + bool find(queue q, typename std::list<DynInstPtr>::iterator it); + BackEnd *be; + TimeBuffer<IssueToExec> *i2e; + typename TimeBuffer<IssueToExec>::wire numIssued; + typedef typename std::list<DynInstPtr> InstList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; + // Not sure I need the IQ list; it just needs to be a count. + InstList iq; + InstList toBeScheduled; + InstList readyList; + InstList nonSpec; + InstList replayList; + ReadyInstQueue readyQueue; + public: + int size; + int numInsts; + int width; + + Stats::VectorDistribution<> occ_dist; + + Stats::Vector<> inst_count; + Stats::Vector<> peak_inst_count; + Stats::Scalar<> empty_count; + Stats::Scalar<> current_count; + Stats::Scalar<> fullCount; + + Stats::Formula occ_rate; + Stats::Formula avg_residency; + Stats::Formula empty_rate; + Stats::Formula full_rate; + }; + + /** LdWriteback event for a load completion. */ + class LdWritebackEvent : public Event { + private: + /** Instruction that is writing back data to the register file. */ + DynInstPtr inst; + /** Pointer to IEW stage. */ + BackEnd *be; + + public: + /** Constructs a load writeback event. */ + LdWritebackEvent(DynInstPtr &_inst, BackEnd *be); + + /** Processes writeback event. */ + virtual void process(); + /** Returns the description of the writeback event. */ + virtual const char *description(); + }; + + BackEnd(Params *params); + + std::string name() const; + + void regStats(); + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setTC(ThreadContext *tc_ptr) + { tc = tc_ptr; } + + void setThreadState(Thread *thread_ptr) + { thread = thread_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void tick(); + void squash(); + void squashFromTC(); + bool tcSquash; + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return numInsts >= numROBEntries; } + bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst) + { IQ.rescheduleMemInst(inst); } + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst) + { IQ.replayMemInst(inst); } + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst) + { IQ.completeMemInst(inst); } + + void fetchFault(Fault &fault); + + private: + void updateStructures(); + void dispatchInsts(); + void dispatchStall(); + void checkDispatchStatus(); + void scheduleReadyInsts(); + void executeInsts(); + void commitInsts(); + void addToIQ(DynInstPtr &inst); + void addToLSQ(DynInstPtr &inst); + void instToCommit(DynInstPtr &inst); + void writebackInsts(); + bool commitInst(int inst_num); + void squash(const InstSeqNum &sn); + void squashDueToBranch(DynInstPtr &inst); + void squashDueToMemBlocked(DynInstPtr &inst); + void updateExeInstStats(DynInstPtr &inst); + void updateComInstStats(DynInstPtr &inst); + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + Thread *thread; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissComplete, + Blocked + }; + + Status status; + + Status dispatchStatus; + + Counter funcExeInst; + + private: +// typedef typename Impl::InstQueue InstQueue; + + InstQueue IQ; + + typedef typename Impl::LdstQueue LdstQueue; + + LdstQueue LSQ; + public: + RenameTable<Impl> commitRenameTable; + + RenameTable<Impl> renameTable; + private: + class DCacheCompletionEvent : public Event + { + private: + BackEnd *be; + + public: + DCacheCompletionEvent(BackEnd *_be); + + virtual void process(); + virtual const char *description(); + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + + MemInterface *dcacheInterface; + + Request *memReq; + + // General back end width. Used if the more specific isn't given. + int width; + + // Dispatch width. + int dispatchWidth; + int numDispatchEntries; + int dispatchSize; + + int issueWidth; + + // Writeback width + int wbWidth; + + // Commit width + int commitWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written + * back. Used in case there are too many instructions writing + * back at the current cycle and writesbacks need to be scheduled + * for the future. See comments in instToCommit(). + */ + unsigned wbCycle; + + int numROBEntries; + int numInsts; + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + + private: + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + std::list<DynInstPtr> dispatch; + std::list<DynInstPtr> writeback; + + int latency; + + int squashLatency; + + bool exactFullStall; + + bool fetchRedirect[Impl::MaxThreads]; + + // number of cycles stalled for D-cache misses +/* Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +*/ + Stats::Vector<> rob_cap_events; + Stats::Vector<> rob_cap_inst_count; + Stats::Vector<> iq_cap_events; + Stats::Vector<> iq_cap_inst_count; + // total number of instructions executed + Stats::Vector<> exe_inst; + Stats::Vector<> exe_swp; + Stats::Vector<> exe_nop; + Stats::Vector<> exe_refs; + Stats::Vector<> exe_loads; + Stats::Vector<> exe_branches; + + Stats::Vector<> issued_ops; + + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsq_forw_loads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> inv_addr_loads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> inv_addr_swpfs; + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsq_blocked_loads; + + Stats::Scalar<> lsqInversion; + + Stats::Vector<> n_issued_dist; + Stats::VectorDistribution<> issue_delay_dist; + + Stats::VectorDistribution<> queue_res_dist; +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; + + Stats::Formula misspec_cnt; + Stats::Formula misspec_ipc; + Stats::Formula issue_rate; + Stats::Formula issue_stores; + Stats::Formula issue_op_rate; + Stats::Formula fu_busy_rate; + Stats::Formula commit_stores; + Stats::Formula commit_ipc; + Stats::Formula commit_ipb; + Stats::Formula lsq_inv_rate; +*/ + Stats::Vector<> writeback_count; + Stats::Vector<> producer_inst; + Stats::Vector<> consumer_inst; + Stats::Vector<> wb_penalized; + + Stats::Formula wb_rate; + Stats::Formula wb_fanout; + Stats::Formula wb_penalized_rate; + + // total number of instructions committed + Stats::Vector<> stat_com_inst; + Stats::Vector<> stat_com_swp; + Stats::Vector<> stat_com_refs; + Stats::Vector<> stat_com_loads; + Stats::Vector<> stat_com_membars; + Stats::Vector<> stat_com_branches; + + Stats::Distribution<> n_committed_dist; + + Stats::Scalar<> commit_eligible_samples; + Stats::Vector<> commit_eligible; + + Stats::Scalar<> ROB_fcount; + Stats::Formula ROB_full_rate; + + Stats::Vector<> ROB_count; // cumulative ROB occupancy + Stats::Formula ROB_occ_rate; + Stats::VectorDistribution<> ROB_occ_dist; + public: + void dumpInsts(); +}; + +template <class Impl> +template <class T> +Fault +BackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) +{ +/* memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents()) { + // Fix this hack for keeping funcExeInst correct with loads that + // are executed twice. + --funcExeInst; + + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); +// status = DcacheMissStall; + DPRINTF(OzoneCPU, "Dcache miss stall!\n"); + } else { + // do functional access + fault = thread->mem->read(memReq, data); + + } + } +*/ +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return LSQ.read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +BackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) +{ +/* + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataWriteReq(memReq); + + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Write; + memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents()) { + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); +// status = DcacheMissStall; + DPRINTF(OzoneCPU, "Dcache miss stall!\n"); + } + } + + if (res && (fault == NoFault)) + *res = memReq->result; + */ +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return LSQ.write(req, data, store_idx); +} + +#endif // __CPU_OZONE_BACK_END_HH__ diff --git a/src/cpu/ozone/back_end_impl.hh b/src/cpu/ozone/back_end_impl.hh new file mode 100644 index 000000000..ac3218c02 --- /dev/null +++ b/src/cpu/ozone/back_end_impl.hh @@ -0,0 +1,1933 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "encumbered/cpu/full/op_class.hh" +#include "cpu/ozone/back_end.hh" + +template <class Impl> +BackEnd<Impl>::InstQueue::InstQueue(Params *params) + : size(params->numIQEntries), numInsts(0), width(params->issueWidth) +{ +} + +template <class Impl> +std::string +BackEnd<Impl>::InstQueue::name() const +{ + return be->name() + ".iq"; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::regStats() +{ + using namespace Stats; + + occ_dist + .init(1, 0, size, 2) + .name(name() + "occ_dist") + .desc("IQ Occupancy per cycle") + .flags(total | cdf) + ; + + inst_count + .init(1) + .name(name() + "cum_num_insts") + .desc("Total occupancy") + .flags(total) + ; + + peak_inst_count + .init(1) + .name(name() + "peak_occupancy") + .desc("Peak IQ occupancy") + .flags(total) + ; + + current_count + .name(name() + "current_count") + .desc("Occupancy this cycle") + ; + + empty_count + .name(name() + "empty_count") + .desc("Number of empty cycles") + ; + + fullCount + .name(name() + "full_count") + .desc("Number of full cycles") + ; + + + occ_rate + .name(name() + "occ_rate") + .desc("Average occupancy") + .flags(total) + ; + occ_rate = inst_count / be->cpu->numCycles; + + avg_residency + .name(name() + "avg_residency") + .desc("Average IQ residency") + .flags(total) + ; + avg_residency = occ_rate / be->cpu->numCycles; + + empty_rate + .name(name() + "empty_rate") + .desc("Fraction of cycles empty") + ; + empty_rate = 100 * empty_count / be->cpu->numCycles; + + full_rate + .name(name() + "full_rate") + .desc("Fraction of cycles full") + ; + full_rate = 100 * fullCount / be->cpu->numCycles; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue) +{ + i2e = i2e_queue; + numIssued = i2e->getWire(0); +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::insert(DynInstPtr &inst) +{ + numInsts++; + inst_count[0]++; + if (!inst->isNonSpeculative()) { + DPRINTF(BE, "Instruction [sn:%lli] added to IQ\n", inst->seqNum); + if (inst->readyToIssue()) { + toBeScheduled.push_front(inst); + inst->iqIt = toBeScheduled.begin(); + inst->iqItValid = true; + } else { + iq.push_front(inst); + inst->iqIt = iq.begin(); + inst->iqItValid = true; + } + } else { + DPRINTF(BE, "Nonspeculative instruction [sn:%lli] added to IQ\n", inst->seqNum); + nonSpec.push_front(inst); + inst->iqIt = nonSpec.begin(); + inst->iqItValid = true; + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::scheduleReadyInsts() +{ + int scheduled = numIssued->size; + InstListIt iq_it = --toBeScheduled.end(); + InstListIt iq_end_it = toBeScheduled.end(); + + while (iq_it != iq_end_it && scheduled < width) { +// if ((*iq_it)->readyToIssue()) { + DPRINTF(BE, "Instruction [sn:%lli] PC:%#x is ready\n", + (*iq_it)->seqNum, (*iq_it)->readPC()); + readyQueue.push(*iq_it); + readyList.push_front(*iq_it); + + (*iq_it)->iqIt = readyList.begin(); + + toBeScheduled.erase(iq_it--); + + ++scheduled; +// } else { +// iq_it++; +// } + } + + numIssued->size+= scheduled; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::scheduleNonSpec(const InstSeqNum &sn) +{ +/* + InstListIt non_spec_it = nonSpec.begin(); + InstListIt non_spec_end_it = nonSpec.end(); + + while ((*non_spec_it)->seqNum != sn) { + non_spec_it++; + assert(non_spec_it != non_spec_end_it); + } +*/ + DynInstPtr inst = nonSpec.back(); + + DPRINTF(BE, "Nonspeculative instruction [sn:%lli] scheduled\n", inst->seqNum); + + assert(inst->seqNum == sn); + + assert(find(NonSpec, inst->iqIt)); + nonSpec.erase(inst->iqIt); + readyList.push_front(inst); + inst->iqIt = readyList.begin(); + readyQueue.push(inst); + numIssued->size++; +} + +template <class Impl> +typename Impl::DynInstPtr +BackEnd<Impl>::InstQueue::getReadyInst() +{ + assert(!readyList.empty()); + + DynInstPtr inst = readyQueue.top(); + readyQueue.pop(); + assert(find(ReadyList, inst->iqIt)); + readyList.erase(inst->iqIt); + inst->iqItValid = false; +// if (!inst->isMemRef()) + --numInsts; + return inst; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::squash(const InstSeqNum &sn) +{ + InstListIt iq_it = iq.begin(); + InstListIt iq_end_it = iq.end(); + + while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + iq.erase(iq_it++); + --numInsts; + } + + iq_it = nonSpec.begin(); + iq_end_it = nonSpec.end(); + + while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + nonSpec.erase(iq_it++); + --numInsts; + } + + iq_it = replayList.begin(); + iq_end_it = replayList.end(); + + while (iq_it != iq_end_it) { + if ((*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + replayList.erase(iq_it++); + --numInsts; + } else { + iq_it++; + } + } + + assert(numInsts >= 0); +/* + InstListIt ready_it = readyList.begin(); + InstListIt ready_end_it = readyList.end(); + + while (ready_it != ready_end_it) { + if ((*ready_it)->seqNum > sn) { + readyList.erase(ready_it++); + } else { + ready_it++; + } + } +*/ +} + +template <class Impl> +int +BackEnd<Impl>::InstQueue::wakeDependents(DynInstPtr &inst) +{ + assert(!inst->isSquashed()); + std::vector<DynInstPtr> &dependents = inst->getDependents(); + int num_outputs = dependents.size(); + + DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum); + + for (int i = 0; i < num_outputs; i++) { + DynInstPtr dep_inst = dependents[i]; + dep_inst->markSrcRegReady(); + DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); + + if (dep_inst->readyToIssue() && dep_inst->iqItValid) { + if (dep_inst->isNonSpeculative()) { + assert(find(NonSpec, dep_inst->iqIt)); + nonSpec.erase(dep_inst->iqIt); + } else { + assert(find(IQ, dep_inst->iqIt)); + iq.erase(dep_inst->iqIt); + } + + toBeScheduled.push_front(dep_inst); + dep_inst->iqIt = toBeScheduled.begin(); + } + } + return num_outputs; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::rescheduleMemInst(DynInstPtr &inst) +{ + DPRINTF(BE, "Rescheduling memory instruction [sn:%lli]\n", inst->seqNum); + assert(!inst->iqItValid); + replayList.push_front(inst); + inst->iqIt = replayList.begin(); + inst->iqItValid = true; + ++numInsts; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::replayMemInst(DynInstPtr &inst) +{ + DPRINTF(BE, "Replaying memory instruction [sn:%lli]\n", inst->seqNum); + assert(find(ReplayList, inst->iqIt)); + InstListIt iq_it = --replayList.end(); + InstListIt iq_end_it = replayList.end(); + while (iq_it != iq_end_it) { + DynInstPtr rescheduled_inst = (*iq_it); + + DPRINTF(BE, "Memory instruction [sn:%lli] also replayed\n", inst->seqNum); + replayList.erase(iq_it--); + toBeScheduled.push_front(rescheduled_inst); + rescheduled_inst->iqIt = toBeScheduled.begin(); + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::completeMemInst(DynInstPtr &inst) +{ + panic("Not implemented."); +} + +template <class Impl> +bool +BackEnd<Impl>::InstQueue::find(queue q, InstListIt it) +{ + InstListIt iq_it, iq_end_it; + switch(q) { + case NonSpec: + iq_it = nonSpec.begin(); + iq_end_it = nonSpec.end(); + break; + case IQ: + iq_it = iq.begin(); + iq_end_it = iq.end(); + break; + case ToBeScheduled: + iq_it = toBeScheduled.begin(); + iq_end_it = toBeScheduled.end(); + break; + case ReadyList: + iq_it = readyList.begin(); + iq_end_it = readyList.end(); + break; + case ReplayList: + iq_it = replayList.begin(); + iq_end_it = replayList.end(); + } + + while (iq_it != it && iq_it != iq_end_it) { + iq_it++; + } + if (iq_it == it) { + return true; + } else { + return false; + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::dumpInsts() +{ + cprintf("IQ size: %i\n", iq.size()); + + InstListIt inst_list_it = --iq.end(); + + int num = 0; + int valid_num = 0; + while (inst_list_it != iq.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; + } + + cprintf("nonSpec size: %i\n", nonSpec.size()); + + inst_list_it = --nonSpec.end(); + + while (inst_list_it != nonSpec.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; + } + + cprintf("toBeScheduled size: %i\n", toBeScheduled.size()); + + inst_list_it = --toBeScheduled.end(); + + while (inst_list_it != toBeScheduled.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; + } + + cprintf("readyList size: %i\n", readyList.size()); + + inst_list_it = --readyList.end(); + + while (inst_list_it != readyList.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; + } +} + +template<class Impl> +BackEnd<Impl>::LdWritebackEvent::LdWritebackEvent(DynInstPtr &_inst, + BackEnd<Impl> *_be) + : Event(&mainEventQueue), inst(_inst), be(_be) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +BackEnd<Impl>::LdWritebackEvent::process() +{ + DPRINTF(BE, "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. + inst->completeAcc(); + } + + // Need to insert instruction into queue to commit + be->instToCommit(inst); + + //wroteToTimeBuffer = true; +// iewStage->activityThisCycle(); + + inst = NULL; +} + +template<class Impl> +const char * +BackEnd<Impl>::LdWritebackEvent::description() +{ + return "Load writeback event"; +} + + +template <class Impl> +BackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent(BackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +} + +template <class Impl> +void +BackEnd<Impl>::DCacheCompletionEvent::process() +{ +} + +template <class Impl> +const char * +BackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "Cache completion event"; +} + +template <class Impl> +BackEnd<Impl>::BackEnd(Params *params) + : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(5, 5), + xcSquash(false), IQ(params), + cacheCompletionEvent(this), width(params->backEndWidth), + exactFullStall(true) +{ + numROBEntries = params->numROBEntries; + numInsts = 0; + numDispatchEntries = 32; + IQ.setBE(this); + LSQ.setBE(this); + + // Setup IQ and LSQ with their parameters here. + instsToDispatch = d2i.getWire(-1); + + instsToExecute = i2e.getWire(-1); + + IQ.setIssueExecQueue(&i2e); + + dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width; + issueWidth = params->issueWidth ? params->issueWidth : width; + wbWidth = params->wbWidth ? params->wbWidth : width; + commitWidth = params->commitWidth ? params->commitWidth : width; + + LSQ.init(params, params->LQEntries, params->SQEntries, 0); + + dispatchStatus = Running; +} + +template <class Impl> +std::string +BackEnd<Impl>::name() const +{ + return cpu->name() + ".backend"; +} + +template <class Impl> +void +BackEnd<Impl>::regStats() +{ + using namespace Stats; + rob_cap_events + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_events") + .desc("number of cycles where ROB cap was active") + .flags(total) + ; + + rob_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_inst") + .desc("number of instructions held up by ROB cap") + .flags(total) + ; + + iq_cap_events + .init(cpu->number_of_threads) + .name(name() +".IQ:cap_events" ) + .desc("number of cycles where IQ cap was active") + .flags(total) + ; + + iq_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".IQ:cap_inst") + .desc("number of instructions held up by IQ cap") + .flags(total) + ; + + + exe_inst + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:count") + .desc("number of insts issued") + .flags(total) + ; + + exe_swp + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:swp") + .desc("number of swp insts issued") + .flags(total) + ; + + exe_nop + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:nop") + .desc("number of nop insts issued") + .flags(total) + ; + + exe_refs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:refs") + .desc("number of memory reference insts issued") + .flags(total) + ; + + exe_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:loads") + .desc("number of load insts issued") + .flags(total) + ; + + exe_branches + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:branches") + .desc("Number of branches issued") + .flags(total) + ; + + issued_ops + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:op_count") + .desc("number of insts issued") + .flags(total) + ; + +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + lsq_forw_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:forw_loads") + .desc("number of loads forwarded via LSQ") + .flags(total) + ; + + inv_addr_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_loads") + .desc("number of invalid-address loads") + .flags(total) + ; + + inv_addr_swpfs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_swpfs") + .desc("number of invalid-address SW prefetches") + .flags(total) + ; + + lsq_blocked_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:blocked_loads") + .desc("number of ready loads not issued due to memory disambiguation") + .flags(total) + ; + + lsqInversion + .name(name() + ".ISSUE:lsq_invert") + .desc("Number of times LSQ instruction issued early") + ; + + n_issued_dist + .init(issueWidth + 1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(total | pdf | dist) + ; + issue_delay_dist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + queue_res_dist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queue_res_dist.subname(i, opClassStrings[i]); + } + + writeback_count + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wb_penalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + + wb_penalized_rate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wb_penalized_rate = wb_penalized / writeback_count; + + wb_fanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wb_fanout = producer_inst / consumer_inst; + + wb_rate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wb_rate = writeback_count / cpu->numCycles; + + stat_com_inst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + stat_com_swp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + stat_com_refs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + stat_com_loads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + stat_com_membars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + stat_com_branches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + n_committed_dist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(pdf) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commit_eligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commit_eligible_samples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; + + ROB_fcount + .name(name() + ".ROB:full_count") + .desc("number of cycles where ROB was full") + ; + + ROB_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:occupancy") + .desc(name() + ".ROB occupancy (cumulative)") + .flags(total) + ; + + ROB_full_rate + .name(name() + ".ROB:full_rate") + .desc("ROB full per cycle") + ; + ROB_full_rate = ROB_fcount / cpu->numCycles; + + ROB_occ_rate + .name(name() + ".ROB:occ_rate") + .desc("ROB occupancy rate") + .flags(total) + ; + ROB_occ_rate = ROB_count / cpu->numCycles; + + ROB_occ_dist + .init(cpu->number_of_threads,0,numROBEntries,2) + .name(name() + ".ROB:occ_dist") + .desc("ROB Occupancy per cycle") + .flags(total | cdf) + ; + + IQ.regStats(); +} + +template <class Impl> +void +BackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + toIEW = comm->getWire(0); + fromCommit = comm->getWire(-1); +} + +template <class Impl> +void +BackEnd<Impl>::tick() +{ + DPRINTF(BE, "Ticking back end\n"); + + ROB_count[0]+= numInsts; + + wbCycle = 0; + + if (xcSquash) { + squashFromXC(); + } + + // Read in any done instruction information and update the IQ or LSQ. + updateStructures(); + + if (dispatchStatus != Blocked) { + d2i.advance(); + dispatchInsts(); + } else { + checkDispatchStatus(); + } + + i2e.advance(); + scheduleReadyInsts(); + + e2c.advance(); + executeInsts(); + + numInstsToWB.advance(); + writebackInsts(); + + commitInsts(); + + DPRINTF(BE, "IQ entries in use: %i, ROB entries in use: %i, LSQ loads: %i, LSQ stores: %i\n", + IQ.numInsts, numInsts, LSQ.numLoads(), LSQ.numStores()); + + assert(numInsts == instList.size()); +} + +template <class Impl> +void +BackEnd<Impl>::updateStructures() +{ + if (fromCommit->doneSeqNum) { + IQ.commit(fromCommit->doneSeqNum); + LSQ.commitLoads(fromCommit->doneSeqNum); + LSQ.commitStores(fromCommit->doneSeqNum); + } + + if (fromCommit->nonSpecSeqNum) { + if (fromCommit->uncached) { + LSQ.executeLoad(fromCommit->lqIdx); + } else { + IQ.scheduleNonSpec( + fromCommit->nonSpecSeqNum); + } + } +} + +template <class Impl> +void +BackEnd<Impl>::addToIQ(DynInstPtr &inst) +{ + // Do anything IQ specific here? + IQ.insert(inst); +} + +template <class Impl> +void +BackEnd<Impl>::addToLSQ(DynInstPtr &inst) +{ + // Do anything LSQ specific here? + LSQ.insert(inst); +} + +template <class Impl> +void +BackEnd<Impl>::dispatchInsts() +{ + DPRINTF(BE, "Trying to dispatch instructions.\n"); + + // Pull instructions out of the front end. + int disp_width = dispatchWidth ? dispatchWidth : width; + + // Could model dispatching time, but in general 1 cycle is probably + // good enough. + + if (dispatchSize < numDispatchEntries) { + for (int i = 0; i < disp_width; i++) { + // Get instructions + DynInstPtr inst = frontEnd->getInst(); + + if (!inst) { + // No more instructions to get + break; + } + + DPRINTF(BE, "Processing instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + for (int i = 0; i < inst->numDestRegs(); ++i) + renameTable[inst->destRegIdx(i)] = inst; + + // Add to queue to be dispatched. + dispatch.push_back(inst); + + d2i[0].size++; + ++dispatchSize; + } + } + + assert(dispatch.size() < 64); + + for (int i = 0; i < instsToDispatch->size; ++i) { + assert(!dispatch.empty()); + // Get instruction from front of time buffer + DynInstPtr inst = dispatch.front(); + dispatch.pop_front(); + --dispatchSize; + + if (inst->isSquashed()) + continue; + + ++numInsts; + instList.push_back(inst); + + DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + addToIQ(inst); + + if (inst->isMemRef()) { + addToLSQ(inst); + } + + if (inst->isNonSpeculative()) { + inst->setCanCommit(); + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. + if (exactFullStall) { + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + instsToDispatch->size-= i+1; + dispatchStall(); + return; + } + } + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. Check here if we don't care about exact stall + // conditions. + + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + d2i.advance(); + dispatchStall(); + return; + } +} + +template <class Impl> +void +BackEnd<Impl>::dispatchStall() +{ + dispatchStatus = Blocked; + if (!cpu->decoupledFrontEnd) { + // Tell front end to stall here through a timebuffer, or just tell + // it directly. + } +} + +template <class Impl> +void +BackEnd<Impl>::checkDispatchStatus() +{ + DPRINTF(BE, "Checking dispatch status\n"); + assert(dispatchStatus == Blocked); + if (!IQ.isFull() && !LSQ.isFull() && !isFull()) { + DPRINTF(BE, "Dispatch no longer blocked\n"); + dispatchStatus = Running; + dispatchInsts(); + } +} + +template <class Impl> +void +BackEnd<Impl>::scheduleReadyInsts() +{ + // Tell IQ to put any ready instructions into the instruction list. + // Probably want to have a list of DynInstPtrs returned here. Then I + // can choose to either put them into a time buffer to simulate + // IQ scheduling time, or hand them directly off to the next stage. + // Do you ever want to directly hand it off to the next stage? + DPRINTF(BE, "Trying to schedule ready instructions\n"); + IQ.scheduleReadyInsts(); +} + +template <class Impl> +void +BackEnd<Impl>::executeInsts() +{ + int insts_to_execute = instsToExecute->size; + + issued_ops[0]+= insts_to_execute; + n_issued_dist[insts_to_execute]++; + + DPRINTF(BE, "Trying to execute %i instructions\n", insts_to_execute); + + fetchRedirect[0] = false; + + while (insts_to_execute > 0) { + // Get ready instruction from the IQ (or queue coming out of IQ) + // Execute the ready instruction. + // Wakeup any dependents if it's done. + DynInstPtr inst = IQ.getReadyInst(); + + DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + ++funcExeInst; + + // Check if the instruction is squashed; if so then skip it + // and don't count it towards the FU usage. + if (inst->isSquashed()) { + DPRINTF(BE, "Execute: Instruction was squashed.\n"); + + // Not sure how to handle this plus the method of sending # of + // instructions to use. Probably will just have to count it + // towards the bandwidth usage, but not the FU usage. + --insts_to_execute; + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // 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; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(BE, "Execute: Initiating access for memory " + "reference.\n"); + + // Tell the LDSTQ to execute this instruction (if it is a load). + if (inst->isLoad()) { + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = LSQ.executeLoad(inst); + +// ++iewExecLoadInsts; + } else if (inst->isStore()) { + LSQ.executeStore(inst); + +// ++iewExecStoreInsts; + + if (!(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("Unexpected memory type!\n"); + } + + } else { + inst->execute(); + +// ++iewExecutedInsts; + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + // Probably should have some sort of function for this. + // More general question of how to handle squashes? Have some sort of + // squash unit that controls it? Probably... + // 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. + + // 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(BE, "Execute: Branch mispredict detected.\n"); + DPRINTF(BE, "Execute: Redirecting fetch to PC: %#x.\n", + inst->nextPC); + + // If incorrect, then signal the ROB that it must be squashed. + squashDueToBranch(inst); + + if (inst->predTaken()) { +// predictedTakenIncorrect++; + } else { +// predictedNotTakenIncorrect++; + } + } else if (LSQ.violation()) { + fetchRedirect[tid] = true; + + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = LSQ.getMemDepViolator(); + + DPRINTF(BE, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Tell the instruction queue that a violation has occured. +// IQ.violation(inst, violator); + + // Squash. +// squashDueToMemOrder(inst,tid); + squashDueToBranch(inst); + +// ++memOrderViolationEvents; + } else if (LSQ.loadBlocked()) { + fetchRedirect[tid] = true; + + DPRINTF(BE, "Load operation couldn't execute because the " + "memory system is blocked. PC: %#x [sn:%lli]\n", + inst->readPC(), inst->seqNum); + + squashDueToMemBlocked(inst); + } + } + +// instList.pop_front(); + + --insts_to_execute; + + // keep an instruction count + thread->numInst++; + thread->numInsts++; + } + + assert(insts_to_execute >= 0); +} + +template<class Impl> +void +BackEnd<Impl>::instToCommit(DynInstPtr &inst) +{ + int wb_width = wbWidth; + // 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. + + DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + while (numInstsToWB[wbCycle].size >= wb_width) { + ++wbCycle; + + assert(wbCycle < 5); + } + + // Add finished instruction to queue to commit. + writeback.push_back(inst); + numInstsToWB[wbCycle].size++; + + if (wbCycle) + wb_penalized[0]++; +} + +template <class Impl> +void +BackEnd<Impl>::writebackInsts() +{ + int wb_width = wbWidth; + // Using this method I'm not quite sure how to prevent an + // instruction from waking its own dependents multiple times, + // without the guarantee that commit always has enough bandwidth + // to accept all instructions being written back. This guarantee + // might not be too unrealistic. + InstListIt wb_inst_it = writeback.begin(); + InstListIt wb_end_it = writeback.end(); + int inst_num = 0; + int consumer_insts = 0; + + for (; inst_num < wb_width && + wb_inst_it != wb_end_it; inst_num++) { + DynInstPtr inst = (*wb_inst_it); + + // 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()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + inst->setResultReady(); + + if (inst->isExecuted()) { + int dependents = IQ.wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_insts+= dependents; + } + } + } + + writeback.erase(wb_inst_it++); + } + LSQ.writebackStores(); + consumer_inst[0]+= consumer_insts; + writeback_count[0]+= inst_num; +} + +template <class Impl> +bool +BackEnd<Impl>::commitInst(int inst_num) +{ + // Read instruction from the head of the ROB + DynInstPtr inst = instList.front(); + + // Make sure instruction is valid + assert(inst); + + if (!inst->readyToCommit()) + return false; + + DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + // If the instruction is not executed yet, then it is a non-speculative + // or store inst. Signal backwards that it should be executed. + if (!inst->isExecuted()) { + // Keep this number correct. We have not yet actually executed + // and committed this instruction. +// thread->funcExeInst--; + + if (inst->isNonSpeculative()) { +#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 || LSQ.hasStoresToWB()) { + DPRINTF(BE, "Waiting for all stores to writeback.\n"); + return false; + } +#endif + + DPRINTF(BE, "Encountered a store or non-speculative " + "instruction at the head of the ROB, PC %#x.\n", + inst->readPC()); + + // Send back the non-speculative instruction's sequence number. + toIEW->nonSpecSeqNum = inst->seqNum; + + // Change the instruction so it won't try to commit again until + // it is executed. + inst->clearCanCommit(); + +// ++commitNonSpecStalls; + + return false; + } else if (inst->isLoad()) { + DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n", + inst->seqNum, inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + toIEW->nonSpecSeqNum = inst->seqNum; + toIEW->uncached = true; + toIEW->lqIdx = inst->lqIdx; + + inst->clearCanCommit(); + + return false; + } else { + 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 (inst->isThreadSync()) + { + // Not handled for now. + panic("Barrier instructions are not handled yet.\n"); + } + + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = inst->getFault(); + + if (inst_fault != NoFault) { + if (!inst->isNop()) { +#if FULL_SYSTEM + DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + +// assert(!thread->inSyscall); + +// thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + inst_fault->invoke(thread->getXCProxy()); + + // Exit state update mode to avoid accidental updating. +// thread->inSyscall = false; + +// commitStatus = TrapPending; + + // Generate trap squash event. +// generateTrapEvent(); + + return false; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + } + + if (inst->isControl()) { +// ++commitCommittedBranches; + } + + int freed_regs = 0; + + for (int i = 0; i < inst->numDestRegs(); ++i) { + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum); + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + if (inst->traceData) { + inst->traceData->finalize(); + inst->traceData = NULL; + } + + inst->clearDependents(); + + frontEnd->addFreeRegs(freed_regs); + + instList.pop_front(); + + --numInsts; + cpu->numInst++; + thread->numInsts++; + ++thread->funcExeInst; + thread->PC = inst->readNextPC(); + updateComInstStats(inst); + + // Write the done sequence number here. + toIEW->doneSeqNum = inst->seqNum; + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getXCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(BE, "PC skip function event, stopping commit\n"); +// completed_last_inst = false; +// squashPending = true; + return false; + } +#endif + return true; +} + +template <class Impl> +void +BackEnd<Impl>::commitInsts() +{ + int commit_width = commitWidth ? commitWidth : width; + + // Not sure this should be a loop or not. + int inst_num = 0; + while (!instList.empty() && inst_num < commit_width) { + if (instList.front()->isSquashed()) { + panic("No squashed insts should still be on the list!"); + instList.front()->clearDependents(); + instList.pop_front(); + continue; + } + + if (!commitInst(inst_num++)) { + break; + } + } + n_committed_dist.sample(inst_num); +} + +template <class Impl> +void +BackEnd<Impl>::squash(const InstSeqNum &sn) +{ + IQ.squash(sn); + LSQ.squash(sn); + + int freed_regs = 0; + InstListIt dispatch_end = dispatch.end(); + InstListIt insts_it = dispatch.end(); + insts_it--; + + while (insts_it != dispatch_end && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + --insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on dispatch list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + // Be careful with IPRs and such here + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + --insts_it; + } + + insts_it = instList.end(); + insts_it--; + + while (!instList.empty() && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + --insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + instList.erase(insts_it--); + --numInsts; + } + + frontEnd->addFreeRegs(freed_regs); +} + +template <class Impl> +void +BackEnd<Impl>::squashFromXC() +{ + xcSquash = true; +} + +template <class Impl> +void +BackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + true, inst->mispredicted()); +} + +template <class Impl> +void +BackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst) +{ + DPRINTF(IEW, "Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum); + + squash(inst->seqNum - 1); + frontEnd->squash(inst->seqNum - 1, inst->readPC()); +} + +template <class Impl> +void +BackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; +} + +template <class Impl> +void +BackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exe_swp[thread_number]++; + else + exe_inst[thread_number]++; +#else + exe_inst[thread_number]++; +#endif + + // + // Control operations + // + if (inst->isControl()) + exe_branches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exe_refs[thread_number]++; + + if (inst->isLoad()) + exe_loads[thread_number]++; + } +} + +template <class Impl> +void +BackEnd<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned thread = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + stat_com_swp[thread]++; + } else { + stat_com_inst[thread]++; + } +#else + stat_com_inst[thread]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + stat_com_branches[thread]++; + + // + // Memory references + // + if (inst->isMemRef()) { + stat_com_refs[thread]++; + + if (inst->isLoad()) { + stat_com_loads[thread]++; + } + } + + if (inst->isMemBarrier()) { + stat_com_membars[thread]++; + } +} + +template <class Impl> +void +BackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = instList.begin(); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.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; + } + + cprintf("Dispatch list size: %i\n", dispatch.size()); + + inst_list_it = dispatch.begin(); + + while (inst_list_it != dispatch.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; + } + + cprintf("Writeback list size: %i\n", writeback.size()); + + inst_list_it = writeback.begin(); + + while (inst_list_it != writeback.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/src/cpu/ozone/cpu.cc b/src/cpu/ozone/cpu.cc index cbeca9d3b..303c78eea 100644 --- a/src/cpu/ozone/cpu.cc +++ b/src/cpu/ozone/cpu.cc @@ -24,10 +24,14 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Nathan Binkert */ -#include "cpu/ooo_cpu/ooo_cpu_impl.hh" -#include "cpu/ooo_cpu/ooo_dyn_inst.hh" -#include "cpu/ooo_cpu/ooo_impl.hh" +#include "cpu/ozone/cpu_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" -template class OoOCPU<OoOImpl>; +template class OzoneCPU<SimpleImpl>; +template class OzoneCPU<OzoneImpl>; diff --git a/src/cpu/ozone/cpu.hh b/src/cpu/ozone/cpu.hh index fa849bb09..e9550c39b 100644 --- a/src/cpu/ozone/cpu.hh +++ b/src/cpu/ozone/cpu.hh @@ -24,32 +24,44 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#ifndef __CPU_OOO_CPU_OOO_CPU_HH__ -#define __CPU_OOO_CPU_OOO_CPU_HH__ +#ifndef __CPU_OZONE_CPU_HH__ +#define __CPU_OZONE_CPU_HH__ + +#include <set> #include "base/statistics.hh" +#include "base/timebuf.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" -#include "encumbered/cpu/full/fu_pool.hh" -#include "cpu/ooo_cpu/ea_list.hh" +#include "cpu/thread_context.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" #include "cpu/pc_event.hh" #include "cpu/static_inst.hh" -#include "mem/mem_interface.hh" #include "sim/eventq.hh" // forward declarations #if FULL_SYSTEM -class Processor; +#include "arch/alpha/tlb.hh" + class AlphaITB; class AlphaDTB; class PhysicalMemory; +class MemoryController; +class Sampler; class RemoteGDB; class GDBListener; +namespace Kernel { + class Statistics; +}; + #else class Process; @@ -57,12 +69,16 @@ class Process; #endif // FULL_SYSTEM class Checkpoint; -class MemInterface; +class EndQuiesceEvent; +class Request; namespace Trace { class InstRecord; } +template <class> +class Checker; + /** * Declaration of Out-of-Order CPU class. Basically it is a SimpleCPU with * simple out-of-order capabilities added to it. It is still a 1 CPI machine @@ -72,23 +88,206 @@ namespace Trace { */ template <class Impl> -class OoOCPU : public BaseCPU +class OzoneCPU : public BaseCPU { private: + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::BackEnd BackEnd; typedef typename Impl::DynInst DynInst; typedef typename Impl::DynInstPtr DynInstPtr; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + + public: + class OzoneTC : public ThreadContext { + public: + OzoneCPU<Impl> *cpu; + + OzoneThreadState<Impl> *thread; + + BaseCPU *getCpuPtr(); + + void setCpuId(int id); + + int readCpuId() { return thread->cpuId; } + +#if FULL_SYSTEM + System *getSystemPtr() { return cpu->system; } + + PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + AlphaITB *getITBPtr() { return cpu->itb; } + + AlphaDTB * getDTBPtr() { return cpu->dtb; } + + Kernel::Statistics *getKernelStats() { return thread->kernelStats; } + + FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) + { return thread->getVirtPort(tc); } + + void delVirtPort(VirtualPort *vp) + { thread->delVirtPort(vp); } +#else + TranslatingPort *getMemPort() { return thread->port; } + + Process *getProcessPtr() { return thread->process; } +#endif + + Status status() const { return thread->_status; } + + void setStatus(Status new_status); + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1); + + /// Set the status to Suspended. + void suspend(); + + /// Set the status to Unallocated. + void deallocate(); + + /// Set the status to Halted. + void halt(); + +#if FULL_SYSTEM + void dumpFuncProfile(); +#endif + + void takeOverFrom(ThreadContext *old_context); + + void regStats(const std::string &name); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent(); + + Tick readLastActivate(); + Tick readLastSuspend(); + + void profileClear(); + void profileSample(); +#endif + + int getThreadNum(); + + // Also somewhat obnoxious. Really only used for the TLB fault. + TheISA::MachInst getInst(); + + void copyArchRegs(ThreadContext *tc); + + void clearArchRegs(); + + uint64_t readIntReg(int reg_idx); + + FloatReg readFloatReg(int reg_idx, int width); + + FloatReg readFloatReg(int reg_idx); + + FloatRegBits readFloatRegBits(int reg_idx, int width); + + FloatRegBits readFloatRegBits(int reg_idx); + + void setIntReg(int reg_idx, uint64_t val); + + void setFloatReg(int reg_idx, FloatReg val, int width); + + void setFloatReg(int reg_idx, FloatReg val); + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + void setFloatRegBits(int reg_idx, FloatRegBits val); + + uint64_t readPC() { return thread->PC; } + void setPC(Addr val); + + uint64_t readNextPC() { return thread->nextPC; } + void setNextPC(Addr val); + + uint64_t readNextNPC() + { + panic("Alpha has no NextNPC!"); + return 0; + } + + void setNextNPC(uint64_t val) + { panic("Alpha has no NextNPC!"); } + + public: + // ISA stuff: + MiscReg readMiscReg(int misc_reg); + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + Fault setMiscReg(int misc_reg, const MiscReg &val); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + unsigned readStCondFailures() + { return thread->storeCondFailures; } + + void setStCondFailures(unsigned sc_failures) + { thread->storeCondFailures = sc_failures; } + +#if FULL_SYSTEM + bool inPalMode() { return cpu->inPalMode(); } +#endif + + bool misspeculating() { return false; } + +#if !FULL_SYSTEM + TheISA::IntReg getSyscallArg(int i) + { return thread->renameTable[TheISA::ArgumentReg0 + i]->readIntResult(); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, TheISA::IntReg val) + { thread->renameTable[TheISA::ArgumentReg0 + i]->setIntResult(i); } + + void setSyscallReturn(SyscallReturn return_value) + { cpu->setSyscallReturn(return_value, thread->tid); } + + Counter readFuncExeInst() { return thread->funcExeInst; } + + void setFuncExeInst(Counter new_val) + { thread->funcExeInst = new_val; } +#endif + void changeRegFileContext(TheISA::RegFile::ContextParam param, + TheISA::RegFile::ContextVal val) + { panic("Not supported on Alpha!"); } + }; + + // Ozone specific thread context + OzoneTC ozoneTC; + // Thread context to be used + ThreadContext *tc; + // Checker thread context; will wrap the OzoneTC if a checker is + // being used. + ThreadContext *checkerTC; + + typedef OzoneThreadState<Impl> ImplState; + + private: + OzoneThreadState<Impl> thread; + public: // main simulation loop (one cycle) void tick(); + std::set<InstSeqNum> snList; + std::set<Addr> lockAddrList; private: struct TickEvent : public Event { - OoOCPU *cpu; + OzoneCPU *cpu; int width; - TickEvent(OoOCPU *c, int w); + TickEvent(OzoneCPU *c, int w); void process(); const char *description(); }; @@ -99,9 +298,9 @@ class OoOCPU : public BaseCPU void scheduleTickEvent(int delay) { if (tickEvent.squashed()) - tickEvent.reschedule(curTick + delay); + tickEvent.reschedule(curTick + cycles(delay)); else if (!tickEvent.scheduled()) - tickEvent.schedule(curTick + delay); + tickEvent.schedule(curTick + cycles(delay)); } /// Unschedule tick event, regardless of its current state. @@ -118,20 +317,17 @@ class OoOCPU : public BaseCPU void trace_data(T data); public: - // enum Status { Running, Idle, - IcacheMiss, - IcacheMissComplete, - DcacheMissStall, SwitchedOut }; - private: Status _status; public: + bool checkInterrupts; + void post_interrupt(int int_num, int index); void zero_fill_64(Addr addr) { @@ -142,97 +338,48 @@ class OoOCPU : public BaseCPU } }; - struct Params : public BaseCPU::Params - { - MemInterface *icache_interface; - MemInterface *dcache_interface; - int width; -#if FULL_SYSTEM - AlphaITB *itb; - AlphaDTB *dtb; - FunctionalMemory *mem; -#else - Process *process; -#endif - int issueWidth; - }; + typedef typename Impl::Params Params; - OoOCPU(Params *params); + OzoneCPU(Params *params); - virtual ~OoOCPU(); + virtual ~OzoneCPU(); void init(); - private: - void copyFromXC(); - public: - // execution context - ExecContext *xc; - - void switchOut(); - void takeOverFrom(BaseCPU *oldCPU); - -#if FULL_SYSTEM - Addr dbg_vtophys(Addr addr); - - bool interval_stats; -#endif - - // L1 instruction cache - MemInterface *icacheInterface; - - // L1 data cache - MemInterface *dcacheInterface; - - FuncUnitPool *fuPool; - - // Refcounted pointer to the one memory request. - MemReqPtr cacheMemReq; - - class ICacheCompletionEvent : public Event - { - private: - OoOCPU *cpu; + BaseCPU *getCpuPtr() { return this; } - public: - ICacheCompletionEvent(OoOCPU *_cpu); + void setCpuId(int id) { cpuId = id; } - virtual void process(); - virtual const char *description(); - }; + int readCpuId() { return cpuId; } - // Will need to create a cache completion event upon any memory miss. - ICacheCompletionEvent iCacheCompletionEvent; + int cpuId; - class DCacheCompletionEvent; + void switchOut(Sampler *sampler); + void signalSwitched(); + void takeOverFrom(BaseCPU *oldCPU); - typedef typename - std::list<DCacheCompletionEvent>::iterator DCacheCompEventIt; + Sampler *sampler; - class DCacheCompletionEvent : public Event - { - private: - OoOCPU *cpu; - DynInstPtr inst; - DCacheCompEventIt dcceIt; + int switchCount; - public: - DCacheCompletionEvent(OoOCPU *_cpu, DynInstPtr &_inst, - DCacheCompEventIt &_dcceIt); +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); - virtual void process(); - virtual const char *description(); - }; + bool interval_stats; - friend class DCacheCompletionEvent; + AlphaITB *itb; + AlphaDTB *dtb; + System *system; + PhysicalMemory *physmem; +#endif - protected: - std::list<DCacheCompletionEvent> dCacheCompList; - DCacheCompEventIt dcceIt; + FrontEnd *frontEnd; + BackEnd *backEnd; private: Status status() const { return _status; } + void setStatus(Status new_status) { _status = new_status; } virtual void activateContext(int thread_num, int delay); virtual void suspendContext(int thread_num); @@ -244,18 +391,16 @@ class OoOCPU : public BaseCPU virtual void resetStats(); // number of simulated instructions + public: Counter numInst; Counter startNumInst; - Stats::Scalar<> numInsts; virtual Counter totalInstructions() const { return numInst - startNumInst; } - // number of simulated memory references - Stats::Scalar<> numMemRefs; - + private: // number of simulated loads Counter numLoad; Counter startNumLoad; @@ -263,376 +408,226 @@ class OoOCPU : public BaseCPU // number of idle cycles Stats::Average<> notIdleFraction; Stats::Formula idleFraction; - - // number of cycles stalled for I-cache misses - Stats::Scalar<> icacheStallCycles; - Counter lastIcacheStall; - - // number of cycles stalled for D-cache misses - Stats::Scalar<> dcacheStallCycles; - Counter lastDcacheStall; - - void processICacheCompletion(); - public: virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); + #if FULL_SYSTEM bool validInstAddr(Addr addr) { return true; } bool validDataAddr(Addr addr) { return true; } - int getInstAsid() { return xc->regs.instAsid(); } - int getDataAsid() { return xc->regs.dataAsid(); } - Fault translateInstReq(MemReqPtr &req) + Fault translateInstReq(Request *req) { - return itb->translate(req); + return itb->translate(req, tc); } - Fault translateDataReadReq(MemReqPtr &req) + Fault translateDataReadReq(Request *req) { - return dtb->translate(req, false); + return dtb->translate(req, tc, false); } - Fault translateDataWriteReq(MemReqPtr &req) + Fault translateDataWriteReq(Request *req) { - return dtb->translate(req, true); + return dtb->translate(req, tc, true); } #else bool validInstAddr(Addr addr) - { return xc->validInstAddr(addr); } + { return true; } bool validDataAddr(Addr addr) - { return xc->validDataAddr(addr); } - - int getInstAsid() { return xc->asid; } - int getDataAsid() { return xc->asid; } + { return true; } - Fault dummyTranslation(MemReqPtr &req) - { -#if 0 - assert((req->vaddr >> 48 & 0xffff) == 0); -#endif + int getInstAsid() { return thread.asid; } + int getDataAsid() { return thread.asid; } - // 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) + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(Request *req) { - return dummyTranslation(req); + return thread.translateInstReq(req); } - Fault translateDataReadReq(MemReqPtr &req) + + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(Request *req) { - return dummyTranslation(req); + return thread.translateDataReadReq(req); } - Fault translateDataWriteReq(MemReqPtr &req) + + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(Request *req) { - return dummyTranslation(req); + return thread.translateDataWriteReq(req); } - #endif + /** Old CPU read from memory function. No longer used. */ template <class T> - Fault read(Addr addr, T &data, unsigned flags, DynInstPtr inst); - - template <class T> - Fault write(T data, Addr addr, unsigned flags, - uint64_t *res, DynInstPtr inst); - - void prefetch(Addr addr, unsigned flags) + Fault read(Request *req, T &data) { - // need to do this... - } - - void writeHint(Addr addr, int size, unsigned flags) - { - // need to do this... - } - - Fault copySrcTranslate(Addr src); - - Fault copy(Addr dest); - - private: - bool executeInst(DynInstPtr &inst); - - void renameInst(DynInstPtr &inst); - - void addInst(DynInstPtr &inst); - - void commitHeadInst(); - - bool getOneInst(); - - Fault fetchCacheLine(); - - InstSeqNum getAndIncrementInstSeq(); - - bool ambigMemAddr; - - private: - InstSeqNum globalSeqNum; - - DynInstPtr renameTable[TheISA::TotalNumRegs]; - DynInstPtr commitTable[TheISA::TotalNumRegs]; - - // Might need a table of the shadow registers as well. -#if FULL_SYSTEM - DynInstPtr palShadowTable[TheISA::NumIntRegs]; +#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 + if (req->flags & LOCKED) { + lockAddrList.insert(req->paddr); + lockFlag = true; + } +#endif + Fault error; - public: - // The register accessor methods provide the index of the - // instruction's operand (e.g., 0 or 1), not the architectural - // register index, to simplify the implementation of register - // renaming. We find the architectural register index by indexing - // into the instruction's own operand index table. Note that a - // raw pointer to the StaticInst is provided instead of a - // ref-counted StaticInstPtr to redice overhead. This is fine as - // long as these methods don't copy the pointer into any long-term - // storage (which is pretty hard to imagine they would have reason - // to do). - - // In the OoO case these shouldn't read from the XC but rather from the - // rename table of DynInsts. Also these likely shouldn't be called very - // often, other than when adding things into the xc during say a syscall. - - uint64_t readIntReg(StaticInst *si, int idx) - { - return xc->readIntReg(si->srcRegIdx(idx)); + error = this->mem->read(req, data); + data = gtoh(data); + return error; } - FloatReg readFloatReg(StaticInst *si, int idx, width) - { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatReg(reg_idx, width); - } - FloatReg readFloatReg(StaticInst *si, int idx) + /** CPU read function, forwards read to LSQ. */ + template <class T> + Fault read(Request *req, T &data, int load_idx) { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatReg(reg_idx); + return backEnd->read(req, data, load_idx); } - FloatRegBits readFloatRegBits(StaticInst *si, int idx, int width) + /** Old CPU write to memory function. No longer used. */ + template <class T> + Fault write(Request *req, T &data) { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatRegBits(reg_idx, width); - } +#if 0 +#if FULL_SYSTEM && defined(TARGET_ALPHA) + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } - FloatRegBits readFloatRegBits(StaticInst *si, int idx) - { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatRegBits(reg_idx); - } + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < this->system->threadContexts.size(); i++){ + xc = this->system->threadContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } - void setIntReg(StaticInst *si, int idx, uint64_t val) - { - xc->setIntReg(si->destRegIdx(idx), val); - } +#endif - void setFloatReg(StaticInst *si, int idx, FloatReg val, int width) - { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatReg(reg_idx, val, width); - } + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + req->result = 2; + } else { + if (this->lockFlag) { + if (lockAddrList.find(req->paddr) != + lockAddrList.end()) { + req->result = 1; + } else { + req->result = 0; + return NoFault; + } + } else { + req->result = 0; + return NoFault; + } + } + } +#endif - void setFloatReg(StaticInst *si, int idx, FloatReg val) - { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatReg(reg_idx, val); + return this->mem->write(req, (T)htog(data)); } - void setFloatRegBits(StaticInst *si, int idx, FloatRegBits val, int width) + /** CPU write function, forwards write to LSQ. */ + template <class T> + Fault write(Request *req, T &data, int store_idx) { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatRegBits(reg_idx, val, width); + return backEnd->write(req, data, store_idx); } - void setFloatRegBits(StaticInst *si, int idx, FloatRegBits val) + void prefetch(Addr addr, unsigned flags) { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatRegBits(reg_idx, val); + // need to do this... } - uint64_t readPC() { return PC; } - void setNextPC(Addr val) { nextPC = val; } - - private: - Addr PC; - Addr nextPC; - - unsigned issueWidth; - - bool fetchRedirExcp; - bool fetchRedirBranch; - - /** Mask to get a cache block's address. */ - Addr cacheBlkMask; - - unsigned cacheBlkSize; - - Addr cacheBlkPC; - - /** The cache line being fetched. */ - uint8_t *cacheData; - - protected: - bool cacheBlkValid; - - private: - - // 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. - Addr icacheBlockAlignPC(Addr addr) + void writeHint(Addr addr, int size, unsigned flags) { - addr = TheISA::realPCToFetchPC(addr); - return (addr & ~(cacheBlkMask)); + // need to do this... } - unsigned instSize; + Fault copySrcTranslate(Addr src); - // ROB tracking stuff. - DynInstPtr robHeadPtr; - DynInstPtr robTailPtr; - unsigned robSize; - unsigned robInsts; + Fault copy(Addr dest); - // List of outstanding EA instructions. - protected: - EAList eaList; + InstSeqNum globalSeqNum; public: - void branchToTarget(Addr val) - { - if (!fetchRedirExcp) { - fetchRedirBranch = true; - PC = val; - } - } + void squashFromTC(); - // ISA stuff: - uint64_t readUniq() { return xc->readUniq(); } - void setUniq(uint64_t val) { xc->setUniq(val); } - - uint64_t readFpcr() { return xc->readFpcr(); } - void setFpcr(uint64_t val) { xc->setFpcr(val); } + // @todo: This can be a useful debug function. Implement it. + void dumpInsts() { frontEnd->dumpInsts(); } #if FULL_SYSTEM - uint64_t readIpr(int idx, Fault &fault) { return xc->readIpr(idx, fault); } - Fault setIpr(int idx, uint64_t val) { return xc->setIpr(idx, val); } - Fault hwrei() { return xc->hwrei(); } - int readIntrFlag() { return xc->readIntrFlag(); } - void setIntrFlag(int val) { xc->setIntrFlag(val); } - bool inPalMode() { return xc->inPalMode(); } - void trap(Fault fault) { fault->invoke(xc); } - bool simPalCheck(int palFunc) { return xc->simPalCheck(palFunc); } + Fault hwrei(); + int readIntrFlag() { return thread.regs.intrflag; } + void setIntrFlag(int val) { thread.regs.intrflag = val; } + bool inPalMode() { return AlphaISA::PcPAL(thread.PC); } + bool inPalMode(Addr pc) { return AlphaISA::PcPAL(pc); } + bool simPalCheck(int palFunc); + void processInterrupts(); #else - void syscall() { xc->syscall(); } + void syscall(); + void setSyscallReturn(SyscallReturn return_value, int tid); #endif - ExecContext *xcBase() { return xc; } -}; + ThreadContext *tcBase() { return tc; } + bool decoupledFrontEnd; + struct CommStruct { + InstSeqNum doneSeqNum; + InstSeqNum nonSpecSeqNum; + bool uncached; + unsigned lqIdx; -// precise architected memory state accessor macros -template <class Impl> -template <class T> -Fault -OoOCPU<Impl>::read(Addr addr, T &data, unsigned flags, DynInstPtr inst) -{ - MemReqPtr readReq = new MemReq(); - readReq->xc = xc; - readReq->asid = 0; - readReq->data = new uint8_t[64]; - - readReq->reset(addr, sizeof(T), flags); - - // translate to physical address - This might be an ISA impl call - Fault fault = translateDataReadReq(readReq); - - // do functional access - if (fault == NoFault) - fault = xc->mem->read(readReq, data); -#if 0 - if (traceData) { - traceData->setAddr(addr); - if (fault == NoFault) - traceData->setData(data); - } -#endif - - // if we have a cache, do cache access too - if (fault == NoFault && dcacheInterface) { - readReq->cmd = Read; - readReq->completionEvent = NULL; - readReq->time = curTick; - /*MemAccessResult result = */dcacheInterface->access(readReq); - - if (dcacheInterface->doEvents()) { - readReq->completionEvent = new DCacheCompletionEvent(this, inst, - dcceIt); - } - } - - if (!dcacheInterface && (readReq->flags & UNCACHEABLE)) - recordEvent("Uncached Read"); - - return fault; -} - -template <class Impl> -template <class T> -Fault -OoOCPU<Impl>::write(T data, Addr addr, unsigned flags, - uint64_t *res, DynInstPtr inst) -{ - MemReqPtr writeReq = new MemReq(); - writeReq->xc = xc; - writeReq->asid = 0; - writeReq->data = new uint8_t[64]; - -#if 0 - if (traceData) { - traceData->setAddr(addr); - traceData->setData(data); - } -#endif - - writeReq->reset(addr, sizeof(T), flags); - - // translate to physical address - Fault fault = translateDataWriteReq(writeReq); - - // do functional access - if (fault == NoFault) - fault = xc->write(writeReq, data); - - if (fault == NoFault && dcacheInterface) { - writeReq->cmd = Write; - memcpy(writeReq->data,(uint8_t *)&data,writeReq->size); - writeReq->completionEvent = NULL; - writeReq->time = curTick; - /*MemAccessResult result = */dcacheInterface->access(writeReq); - - if (dcacheInterface->doEvents()) { - writeReq->completionEvent = new DCacheCompletionEvent(this, inst, - dcceIt); - } - } - - if (res && (fault == NoFault)) - *res = writeReq->result; + bool stall; + }; + TimeBuffer<CommStruct> comm; - if (!dcacheInterface && (writeReq->flags & UNCACHEABLE)) - recordEvent("Uncached Write"); + bool lockFlag; - return fault; -} + Stats::Scalar<> quiesceCycles; + Checker<DynInstPtr> *checker; +}; -#endif // __CPU_OOO_CPU_OOO_CPU_HH__ +#endif // __CPU_OZONE_CPU_HH__ diff --git a/src/cpu/ozone/cpu_builder.cc b/src/cpu/ozone/cpu_builder.cc new file mode 100644 index 000000000..18f257a25 --- /dev/null +++ b/src/cpu/ozone/cpu_builder.cc @@ -0,0 +1,863 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/simple_params.hh" +#include "mem/cache/base_cache.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class DerivOzoneCPU : public OzoneCPU<OzoneImpl> +{ + public: + DerivOzoneCPU(SimpleParams *p) + : OzoneCPU<OzoneImpl>(p) + { } +}; + +class SimpleOzoneCPU : public OzoneCPU<SimpleImpl> +{ + public: + SimpleOzoneCPU(SimpleParams *p) + : OzoneCPU<SimpleImpl>(p) + { } +}; + + +//////////////////////////////////////////////////////////////////////// +// +// OzoneCPU Simulation Object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivOzoneCPU) + + Param<int> clock; + Param<int> numThreads; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +#else +SimObjectVectorParam<Process *> workload; +//SimObjectParam<PageTable *> page_table; +#endif // FULL_SYSTEM + +SimObjectParam<FunctionalMemory *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; + +SimObjectParam<BaseCache *> icache; +SimObjectParam<BaseCache *> dcache; + +Param<unsigned> cachePorts; +Param<unsigned> width; +Param<unsigned> frontEndWidth; +Param<unsigned> backEndWidth; +Param<unsigned> backEndSquashLatency; +Param<unsigned> backEndLatency; +Param<unsigned> maxInstBufferSize; +Param<unsigned> numPhysicalRegs; +Param<unsigned> maxOutstandingMemOps; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> issueWidth; +Param<unsigned> executeWidth; +Param<unsigned> executeIntWidth; +Param<unsigned> executeFloatWidth; +Param<unsigned> executeBranchWidth; +Param<unsigned> executeMemoryWidth; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<bool> decoupledFrontEnd; +Param<int> dispatchWidth; +Param<int> wbWidth; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(DerivOzoneCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(DerivOzoneCPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + 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), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + + 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_DFLT(width, "Width", 1), + INIT_PARAM_DFLT(frontEndWidth, "Front end width", 1), + INIT_PARAM_DFLT(backEndWidth, "Back end width", 1), + INIT_PARAM_DFLT(backEndSquashLatency, "Back end squash latency", 1), + INIT_PARAM_DFLT(backEndLatency, "Back end latency", 1), + INIT_PARAM_DFLT(maxInstBufferSize, "Maximum instruction buffer size", 16), + INIT_PARAM(numPhysicalRegs, "Number of physical registers"), + INIT_PARAM_DFLT(maxOutstandingMemOps, "Maximum outstanding memory operations", 4), + + 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"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(executeWidth, "Execute width"), + INIT_PARAM(executeIntWidth, "Integer execute width"), + INIT_PARAM(executeFloatWidth, "Floating point execute width"), + INIT_PARAM(executeBranchWidth, "Branch execute width"), + INIT_PARAM(executeMemoryWidth, "Memory execute width"), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + 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"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(decoupledFrontEnd, "Decoupled front end", true), + INIT_PARAM_DFLT(dispatchWidth, "Dispatch width", 0), + INIT_PARAM_DFLT(wbWidth, "Writeback width", 0), + + 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)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(DerivOzoneCPU) + +CREATE_SIM_OBJECT(DerivOzoneCPU) +{ + DerivOzoneCPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + numThreads.isValid() ? numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + +#endif + + SimpleParams *params = new SimpleParams; + + params->clock = clock; + + 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; +#else + params->workload = workload; +// params->pTable = page_table; +#endif // FULL_SYSTEM + + params->mem = mem; + params->checker = checker; + 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->cachePorts = cachePorts; + + params->width = width; + params->frontEndWidth = frontEndWidth; + params->backEndWidth = backEndWidth; + params->backEndSquashLatency = backEndSquashLatency; + params->backEndLatency = backEndLatency; + params->maxInstBufferSize = maxInstBufferSize; + params->numPhysicalRegs = numPhysIntRegs + numPhysFloatRegs; + params->maxOutstandingMemOps = maxOutstandingMemOps; + + 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->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->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + + params->predType = predType; + 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->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->decoupledFrontEnd = decoupledFrontEnd; + params->dispatchWidth = dispatchWidth; + params->wbWidth = wbWidth; + + 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 DerivOzoneCPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("DerivOzoneCPU", DerivOzoneCPU) + + + +//////////////////////////////////////////////////////////////////////// +// +// OzoneCPU Simulation Object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + + Param<int> clock; + Param<int> numThreads; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +#else +SimObjectVectorParam<Process *> workload; +//SimObjectParam<PageTable *> page_table; +#endif // FULL_SYSTEM + +SimObjectParam<FunctionalMemory *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; + +SimObjectParam<BaseCache *> icache; +SimObjectParam<BaseCache *> dcache; + +Param<unsigned> cachePorts; +Param<unsigned> width; +Param<unsigned> frontEndWidth; +Param<unsigned> backEndWidth; +Param<unsigned> backEndSquashLatency; +Param<unsigned> backEndLatency; +Param<unsigned> maxInstBufferSize; +Param<unsigned> numPhysicalRegs; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> issueWidth; +Param<unsigned> executeWidth; +Param<unsigned> executeIntWidth; +Param<unsigned> executeFloatWidth; +Param<unsigned> executeBranchWidth; +Param<unsigned> executeMemoryWidth; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<bool> decoupledFrontEnd; +Param<int> dispatchWidth; +Param<int> wbWidth; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + 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), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + + 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_DFLT(width, "Width", 1), + INIT_PARAM_DFLT(frontEndWidth, "Front end width", 1), + INIT_PARAM_DFLT(backEndWidth, "Back end width", 1), + INIT_PARAM_DFLT(backEndSquashLatency, "Back end squash latency", 1), + INIT_PARAM_DFLT(backEndLatency, "Back end latency", 1), + INIT_PARAM_DFLT(maxInstBufferSize, "Maximum instruction buffer size", 16), + INIT_PARAM(numPhysicalRegs, "Number of physical registers"), + + 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"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(executeWidth, "Execute width"), + INIT_PARAM(executeIntWidth, "Integer execute width"), + INIT_PARAM(executeFloatWidth, "Floating point execute width"), + INIT_PARAM(executeBranchWidth, "Branch execute width"), + INIT_PARAM(executeMemoryWidth, "Memory execute width"), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + 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"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(decoupledFrontEnd, "Decoupled front end", true), + INIT_PARAM_DFLT(dispatchWidth, "Dispatch width", 0), + INIT_PARAM_DFLT(wbWidth, "Writeback width", 0), + + 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)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + +CREATE_SIM_OBJECT(SimpleOzoneCPU) +{ + SimpleOzoneCPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + numThreads.isValid() ? numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + +#endif + + SimpleParams *params = new SimpleParams; + + params->clock = clock; + + 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; +#else + params->workload = workload; +// params->pTable = page_table; +#endif // FULL_SYSTEM + + params->mem = mem; + params->checker = checker; + 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->cachePorts = cachePorts; + + params->width = width; + params->frontEndWidth = frontEndWidth; + params->backEndWidth = backEndWidth; + params->backEndSquashLatency = backEndSquashLatency; + params->backEndLatency = backEndLatency; + params->maxInstBufferSize = maxInstBufferSize; + params->numPhysicalRegs = numPhysIntRegs + numPhysFloatRegs; + + 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->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->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + + params->predType = predType; + 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->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->decoupledFrontEnd = decoupledFrontEnd; + params->dispatchWidth = dispatchWidth; + params->wbWidth = wbWidth; + + 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 SimpleOzoneCPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("SimpleOzoneCPU", SimpleOzoneCPU) + diff --git a/src/cpu/ozone/cpu_impl.hh b/src/cpu/ozone/cpu_impl.hh index e7ed3cfe0..76e2318aa 100644 --- a/src/cpu/ozone/cpu_impl.hh +++ b/src/cpu/ozone/cpu_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +24,1067 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Nathan Binkert */ -#ifndef __CPU_OOO_CPU_OOO_IMPL_HH__ -#define __CPU_OOO_CPU_OOO_IMPL_HH__ +//#include <cstdio> +//#include <cstdlib> + +#include "arch/isa_traits.hh" // For MachInst +#include "base/trace.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/checker/thread_context.hh" +#include "cpu/thread_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/quiesce_event.hh" +#include "cpu/static_inst.hh" +//#include "mem/base_mem.hh" +#include "mem/mem_interface.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#if FULL_SYSTEM +#include "arch/faults.hh" +#include "arch/alpha/osfpal.hh" +#include "arch/alpha/tlb.hh" +#include "arch/vtophys.hh" +#include "base/callback.hh" +//#include "base/remote_gdb.hh" +#include "cpu/profile.hh" +#include "kern/kernel_stats.hh" +#include "mem/functional/memory_control.hh" +#include "mem/functional/physical.hh" +#include "sim/faults.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" +#else // !FULL_SYSTEM +#include "mem/functional/functional.hh" +#include "sim/process.hh" +#endif // FULL_SYSTEM + +using namespace TheISA; + +template <class Impl> +template<typename T> +void +OzoneCPU<Impl>::trace_data(T data) { + if (traceData) { + traceData->setData(data); + } +} + +template <class Impl> +OzoneCPU<Impl>::TickEvent::TickEvent(OzoneCPU *c, int w) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w) +{ +} + +template <class Impl> +void +OzoneCPU<Impl>::TickEvent::process() +{ + cpu->tick(); +} + +template <class Impl> +const char * +OzoneCPU<Impl>::TickEvent::description() +{ + return "OzoneCPU tick event"; +} + +template <class Impl> +OzoneCPU<Impl>::OzoneCPU(Params *p) +#if FULL_SYSTEM + : BaseCPU(p), thread(this, 0, p->mem), tickEvent(this, p->width), + mem(p->mem), +#else + : BaseCPU(p), thread(this, 0, p->workload[0], 0), tickEvent(this, p->width), + mem(p->workload[0]->getMemory()), +#endif + comm(5, 5) +{ + frontEnd = new FrontEnd(p); + backEnd = new BackEnd(p); + + _status = Idle; + + if (p->checker) { + BaseCPU *temp_checker = p->checker; + checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker->setMemory(mem); +#if FULL_SYSTEM + checker->setSystem(p->system); +#endif + checkerTC = new CheckerThreadContext<OzoneTC>(&ozoneTC, checker); + thread.tc = checkerTC; + tc = checkerXC; + } else { + checker = NULL; + thread.tc = &ozoneTC; + tc = &ozoneTC; + } + + ozoneTC.cpu = this; + ozoneTC.thread = &thread; + + thread.inSyscall = false; + + thread.setStatus(ThreadContext::Suspended); +#if FULL_SYSTEM + /***** All thread state stuff *****/ + thread.cpu = this; + thread.tid = 0; + thread.mem = p->mem; + + thread.quiesceEvent = new EndQuiesceEvent(tc); + + system = p->system; + itb = p->itb; + dtb = p->dtb; + memctrl = p->system->memctrl; + physmem = p->system->physmem; + + if (p->profile) { + thread.profile = new FunctionProfile(p->system->kernelSymtab); + // @todo: This might be better as an ThreadContext instead of OzoneTC + Callback *cb = + new MakeCallback<OzoneTC, + &OzoneTC::dumpFuncProfile>(&ozoneTC); + registerExitCallback(cb); + } + + // let's fill with a dummy node for now so we don't get a segfault + // on the first cycle when there's no node available. + static ProfileNode dummyNode; + thread.profileNode = &dummyNode; + thread.profilePC = 3; +#else + thread.cpu = this; + thread.tid = 0; + thread.process = p->workload[0]; + thread.asid = 0; +#endif // !FULL_SYSTEM + + numInst = 0; + startNumInst = 0; + + threadContexts.push_back(tc); + + frontEnd->setCPU(this); + backEnd->setCPU(this); + + frontEnd->setTC(tc); + backEnd->setTC(tc); + + frontEnd->setThreadState(&thread); + backEnd->setThreadState(&thread); + + frontEnd->setCommBuffer(&comm); + backEnd->setCommBuffer(&comm); + + frontEnd->setBackEnd(backEnd); + backEnd->setFrontEnd(frontEnd); + + decoupledFrontEnd = p->decoupledFrontEnd; + + globalSeqNum = 1; + + checkInterrupts = false; + + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + thread.renameTable[i] = new DynInst(this); + thread.renameTable[i]->setResultReady(); + } + + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + +#if !FULL_SYSTEM +// pTable = p->pTable; +#endif + + lockFlag = 0; + + DPRINTF(OzoneCPU, "OzoneCPU: Created Ozone cpu object.\n"); +} + +template <class Impl> +OzoneCPU<Impl>::~OzoneCPU() +{ +} + +template <class Impl> +void +OzoneCPU<Impl>::switchOut(Sampler *_sampler) +{ + sampler = _sampler; + switchCount = 0; + // Front end needs state from back end, so switch out the back end first. + backEnd->switchOut(); + frontEnd->switchOut(); +} + +template <class Impl> +void +OzoneCPU<Impl>::signalSwitched() +{ + if (++switchCount == 2) { + backEnd->doSwitchOut(); + frontEnd->doSwitchOut(); + if (checker) + checker->switchOut(sampler); + _status = SwitchedOut; + if (tickEvent.scheduled()) + tickEvent.squash(); + sampler->signalSwitched(); + } + assert(switchCount <= 2); +} + +template <class Impl> +void +OzoneCPU<Impl>::takeOverFrom(BaseCPU *oldCPU) +{ + BaseCPU::takeOverFrom(oldCPU); + + backEnd->takeOverFrom(); + frontEnd->takeOverFrom(); + assert(!tickEvent.scheduled()); + + // @todo: Fix hardcoded number + // Clear out any old information in time buffer. + for (int i = 0; i < 6; ++i) { + comm.advance(); + } + + // if any of this CPU's ThreadContexts are active, mark the CPU as + // running and schedule its tick event. + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && + _status != Running) { + _status = Running; + tickEvent.schedule(curTick); + } + } + // Nothing running, change status to reflect that we're no longer + // switched out. + if (_status == SwitchedOut) { + _status = Idle; + } +} + +template <class Impl> +void +OzoneCPU<Impl>::activateContext(int thread_num, int delay) +{ + // Eventually change this in SMT. + assert(thread_num == 0); + + assert(_status == Idle); + notIdleFraction++; + scheduleTickEvent(delay); + _status = Running; + thread._status = ThreadContext::Active; + frontEnd->wakeFromQuiesce(); +} + +template <class Impl> +void +OzoneCPU<Impl>::suspendContext(int thread_num) +{ + // Eventually change this in SMT. + assert(thread_num == 0); + // @todo: Figure out how to initially set the status properly so + // this is running. +// assert(_status == Running); + notIdleFraction--; + unscheduleTickEvent(); + _status = Idle; +} + +template <class Impl> +void +OzoneCPU<Impl>::deallocateContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + +template <class Impl> +void +OzoneCPU<Impl>::haltContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + +template <class Impl> +void +OzoneCPU<Impl>::regStats() +{ + using namespace Stats; + + BaseCPU::regStats(); + + thread.numInsts + .name(name() + ".num_insts") + .desc("Number of instructions executed") + ; + + thread.numMemRefs + .name(name() + ".num_refs") + .desc("Number of memory references") + ; + + notIdleFraction + .name(name() + ".not_idle_fraction") + .desc("Percentage of non-idle cycles") + ; + + idleFraction + .name(name() + ".idle_fraction") + .desc("Percentage of idle cycles") + ; + + quiesceCycles + .name(name() + ".quiesce_cycles") + .desc("Number of cycles spent in quiesce") + ; + + idleFraction = constant(1.0) - notIdleFraction; + + frontEnd->regStats(); + backEnd->regStats(); +} + +template <class Impl> +void +OzoneCPU<Impl>::resetStats() +{ + startNumInst = numInst; + notIdleFraction = (_status != Idle); +} + +template <class Impl> +void +OzoneCPU<Impl>::init() +{ + BaseCPU::init(); + + // Mark this as in syscall so it won't need to squash + thread.inSyscall = true; +#if FULL_SYSTEM + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + + // initialize CPU, including PC + TheISA::initCPU(tc, tc->readCpuId()); + } +#endif + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + + thread.inSyscall = false; +} + +template <class Impl> +void +OzoneCPU<Impl>::serialize(std::ostream &os) +{ + BaseCPU::serialize(os); + SERIALIZE_ENUM(_status); + nameOut(os, csprintf("%s.tc", name())); + ozoneTC.serialize(os); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); +} -#include "arch/isa_traits.hh" +template <class Impl> +void +OzoneCPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ + BaseCPU::unserialize(cp, section); + UNSERIALIZE_ENUM(_status); + ozoneTC.unserialize(cp, csprintf("%s.tc", section)); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); +} template <class Impl> -class OoOCPU; +Fault +OzoneCPU<Impl>::copySrcTranslate(Addr src) +{ + panic("Copy not implemented!\n"); + return NoFault; +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + int offset = src & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (src & TheISA::PageMask) != ((src + blk_size) & TheISA::PageMask) && + (src >> 40) != 0xfffffc) { + warn("Copied block source spans pages %x.", src); + no_warn = false; + } + + memReq->reset(src & ~(blk_size - 1), blk_size); + + // translate to physical address + Fault fault = tc->translateDataReadReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == NoFault) { + tc->copySrcAddr = src; + tc->copySrcPhysAddr = memReq->paddr + offset; + } else { + tc->copySrcAddr = 0; + tc->copySrcPhysAddr = 0; + } + return fault; +#endif +} + +template <class Impl> +Fault +OzoneCPU<Impl>::copy(Addr dest) +{ + panic("Copy not implemented!\n"); + return NoFault; +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + uint8_t data[blk_size]; + //assert(tc->copySrcAddr); + int offset = dest & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (dest & TheISA::PageMask) != ((dest + blk_size) & TheISA::PageMask) && + (dest >> 40) != 0xfffffc) { + no_warn = false; + warn("Copied block destination spans pages %x. ", dest); + } + + memReq->reset(dest & ~(blk_size -1), blk_size); + // translate to physical address + Fault fault = tc->translateDataWriteReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == NoFault) { + Addr dest_addr = memReq->paddr + offset; + // Need to read straight from memory since we have more than 8 bytes. + memReq->paddr = tc->copySrcPhysAddr; + tc->mem->read(memReq, data); + memReq->paddr = dest_addr; + tc->mem->write(memReq, data); + if (dcacheInterface) { + memReq->cmd = Copy; + memReq->completionEvent = NULL; + memReq->paddr = tc->copySrcPhysAddr; + memReq->dest = dest_addr; + memReq->size = 64; + memReq->time = curTick; + dcacheInterface->access(memReq); + } + } + return fault; +#endif +} + +#if FULL_SYSTEM +template <class Impl> +Addr +OzoneCPU<Impl>::dbg_vtophys(Addr addr) +{ + return vtophys(tcProxy, addr); +} +#endif // FULL_SYSTEM + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (_status == Idle) { + DPRINTF(IPI,"Suspended Processor awoke\n"); +// thread.activate(); + // Hack for now. Otherwise might have to go through the tc, or + // I need to figure out what's the right thing to call. + activateContext(thread.tid, 1); + } +} +#endif // FULL_SYSTEM + +/* start simulation, program loaded, processor precise state initialized */ +template <class Impl> +void +OzoneCPU<Impl>::tick() +{ + DPRINTF(OzoneCPU, "\n\nOzoneCPU: Ticking cpu.\n"); + + _status = Running; + thread.renameTable[ZeroReg]->setIntResult(0); + thread.renameTable[ZeroReg+TheISA::FP_Base_DepTag]-> + setDoubleResult(0.0); + + comm.advance(); + frontEnd->tick(); + backEnd->tick(); + + // check for instruction-count-based events + comInstEventQueue[0]->serviceEvents(numInst); + + if (!tickEvent.scheduled() && _status == Running) + tickEvent.schedule(curTick + cycles(1)); +} + +template <class Impl> +void +OzoneCPU<Impl>::squashFromTC() +{ + thread.inSyscall = true; + backEnd->generateTCEvent(); +} + +#if !FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::syscall() +{ + // Not sure this copy is needed, depending on how the TC proxy is made. + thread.renameTable.copyFrom(backEnd->renameTable); + + thread.inSyscall = true; + + thread.funcExeInst++; + + DPRINTF(OzoneCPU, "FuncExeInst: %i\n", thread.funcExeInst); + + thread.process->syscall(yc); + + thread.funcExeInst--; + + thread.inSyscall = false; + + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); +} + +template <class Impl> +void +OzoneCPU<Impl>::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 + thread.renameTable[SyscallSuccessReg]->setIntResult(0); + thread.renameTable[ReturnValueReg]->setIntResult( + return_value.value()); + } else { + // got an error, return details + thread.renameTable[SyscallSuccessReg]->setIntResult((IntReg) -1); + thread.renameTable[ReturnValueReg]->setIntResult( + -return_value.value()); + } +} +#else +template <class Impl> +Fault +OzoneCPU<Impl>::hwrei() +{ + // Need to move this to ISA code + // May also need to make this per thread + + lockFlag = false; + lockAddrList.clear(); + thread.kernelStats->hwrei(); + + checkInterrupts = true; + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +void +OzoneCPU<Impl>::processInterrupts() +{ + // 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. + + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + checkInterrupts = false; + + if (thread.readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (thread.readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread.readMiscReg(IPR_SIRR) & (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 = 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 > thread.readMiscReg(IPR_IPLR)) { + thread.setMiscReg(IPR_ISR, summary); + thread.setMiscReg(IPR_INTID, ipl); + // @todo: Make this more transparent + if (checker) { + checker->threadBase()->setMiscReg(IPR_ISR, summary); + checker->threadBase()->setMiscReg(IPR_INTID, ipl); + } + Fault fault = new InterruptFault; + fault->invoke(thread.getTC()); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread.readMiscReg(IPR_IPLR), ipl, summary); + } +} + +template <class Impl> +bool +OzoneCPU<Impl>::simPalCheck(int palFunc) +{ + // Need to move this to ISA code + // May also need to make this per thread + thread.kernelStats->callpal(palFunc, tc); + + switch (palFunc) { + case PAL::halt: + haltContext(thread.tid); + if (--System::numSystemsRunning == 0) + new SimExitEvent("all cpus halted"); + break; + + case PAL::bpt: + case PAL::bugchk: + if (system->breakpoint()) + return false; + break; + } + + return true; +} +#endif + +template <class Impl> +BaseCPU * +OzoneCPU<Impl>::OzoneTC::getCpuPtr() +{ + return cpu; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setCpuId(int id) +{ + cpu->cpuId = id; + thread->cpuId = id; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setStatus(Status new_status) +{ + thread->_status = new_status; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::activate(int delay) +{ + cpu->activateContext(thread->tid, delay); +} + +/// Set the status to Suspended. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::suspend() +{ + cpu->suspendContext(thread->tid); +} + +/// Set the status to Unallocated. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::deallocate() +{ + cpu->deallocateContext(thread->tid); +} + +/// Set the status to Halted. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::halt() +{ + cpu->haltContext(thread->tid); +} + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::dumpFuncProfile() +{ } +#endif + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::takeOverFrom(ThreadContext *old_context) +{ + // some things should already be set up + assert(getMemPtr() == old_context->getMemPtr()); +#if FULL_SYSTEM + assert(getSystemPtr() == old_context->getSystemPtr()); +#else + assert(getProcessPtr() == old_context->getProcessPtr()); +#endif + + // copy over functional state + setStatus(old_context->status()); + copyArchRegs(old_context); + setCpuId(old_context->readCpuId()); + +#if !FULL_SYSTEM + setFuncExeInst(old_context->readFuncExeInst()); +#else + EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); + if (other_quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + other_quiesce->tc = this; + } + if (thread->quiesceEvent) { + thread->quiesceEvent->tc = this; + } + + thread->kernelStats = old_context->getKernelStats(); +// storeCondFailures = 0; + cpu->lockFlag = false; +#endif + + old_context->setStatus(ThreadContext::Unallocated); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::regStats(const std::string &name) +{ +#if FULL_SYSTEM + thread->kernelStats = new Kernel::Statistics(cpu->system); + thread->kernelStats->regStats(name + ".kern"); +#endif +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::serialize(std::ostream &os) +{ } + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::unserialize(Checkpoint *cp, const std::string §ion) +{ } + +#if FULL_SYSTEM +template <class Impl> +EndQuiesceEvent * +OzoneCPU<Impl>::OzoneTC::getQuiesceEvent() +{ + return thread->quiesceEvent; +} + +template <class Impl> +Tick +OzoneCPU<Impl>::OzoneTC::readLastActivate() +{ + return thread->lastActivate; +} + +template <class Impl> +Tick +OzoneCPU<Impl>::OzoneTC::readLastSuspend() +{ + return thread->lastSuspend; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::profileClear() +{ + if (thread->profile) + thread->profile->clear(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::profileSample() +{ + if (thread->profile) + thread->profile->sample(thread->profileNode, thread->profilePC); +} +#endif + +template <class Impl> +int +OzoneCPU<Impl>::OzoneTC::getThreadNum() +{ + return thread->tid; +} + +// Also somewhat obnoxious. Really only used for the TLB fault. +template <class Impl> +TheISA::MachInst +OzoneCPU<Impl>::OzoneTC::getInst() +{ + return thread->inst; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::copyArchRegs(ThreadContext *tc) +{ + thread->PC = tc->readPC(); + thread->nextPC = tc->readNextPC(); + + cpu->frontEnd->setPC(thread->PC); + cpu->frontEnd->setNextPC(thread->nextPC); + + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + if (i < TheISA::FP_Base_DepTag) { + thread->renameTable[i]->setIntResult(tc->readIntReg(i)); + } else if (i < (TheISA::FP_Base_DepTag + TheISA::NumFloatRegs)) { + int fp_idx = i - TheISA::FP_Base_DepTag; + thread->renameTable[i]->setDoubleResult( + tc->readFloatRegDouble(fp_idx)); + } + } + +#if !FULL_SYSTEM + thread->funcExeInst = tc->readFuncExeInst(); +#endif + + // Need to copy the TC values into the current rename table, + // copy the misc regs. + thread->regs.miscRegs.copyMiscRegs(tc); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::clearArchRegs() +{ + panic("Unimplemented!"); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readIntReg(int reg_idx) +{ + return thread->renameTable[reg_idx]->readIntResult(); +} + +template <class Impl> +float +OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + switch(width) { + case 32: + return thread->renameTable[idx]->readFloatResult(); + case 64: + return thread->renameTable[idx]->readDoubleResult(); + default: + panic("Unsupported width!"); + return 0; + } +} + +template <class Impl> +double +OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readFloatResult(); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readIntResult(); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readIntResult(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setIntReg(int reg_idx, uint64_t val) +{ + thread->renameTable[reg_idx]->setIntResult(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + switch(width) { + case 32: + panic("Unimplemented!"); + break; + case 64: + thread->renameTable[idx]->setDoubleResult(val); + break; + default: + panic("Unsupported width!"); + } + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + + thread->renameTable[idx]->setDoubleResult(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val, + int width) +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setPC(Addr val) +{ + thread->PC = val; + cpu->frontEnd->setPC(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setNextPC(Addr val) +{ + thread->nextPC = val; + cpu->frontEnd->setNextPC(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +TheISA::MiscReg +OzoneCPU<Impl>::OzoneTC::readMiscReg(int misc_reg) +{ + return thread->regs.miscRegs.readReg(misc_reg); +} + +template <class Impl> +TheISA::MiscReg +OzoneCPU<Impl>::OzoneTC::readMiscRegWithEffect(int misc_reg, Fault &fault) +{ + return thread->regs.miscRegs.readRegWithEffect(misc_reg, + fault, this); +} + +template <class Impl> +Fault +OzoneCPU<Impl>::OzoneTC::setMiscReg(int misc_reg, const MiscReg &val) +{ + // Needs to setup a squash event unless we're in syscall mode + Fault ret_fault = thread->regs.miscRegs.setReg(misc_reg, val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } + + return ret_fault; +} template <class Impl> -class OoODynInst; +Fault +OzoneCPU<Impl>::OzoneTC::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + // Needs to setup a squash event unless we're in syscall mode + Fault ret_fault = thread->regs.miscRegs.setRegWithEffect(misc_reg, val, + this); -struct OoOImpl { - typedef AlphaISA ISA; - typedef OoOCPU<OoOImpl> OoOCPU; - typedef OoOCPU FullCPU; - typedef OoODynInst<OoOImpl> DynInst; - typedef RefCountingPtr<DynInst> DynInstPtr; -}; + if (!thread->inSyscall) { + cpu->squashFromTC(); + } -#endif // __CPU_OOO_CPU_OOO_IMPL_HH__ + return ret_fault; +} diff --git a/src/cpu/ozone/dyn_inst.cc b/src/cpu/ozone/dyn_inst.cc new file mode 100644 index 000000000..732fb96b8 --- /dev/null +++ b/src/cpu/ozone/dyn_inst.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class OzoneDynInst<OzoneImpl>; +//template class OzoneDynInst<SimpleImpl>; + diff --git a/src/cpu/ozone/dyn_inst.hh b/src/cpu/ozone/dyn_inst.hh new file mode 100644 index 000000000..0bb50bd69 --- /dev/null +++ b/src/cpu/ozone/dyn_inst.hh @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2005-2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_DYN_INST_HH__ +#define __CPU_OZONE_DYN_INST_HH__ + +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/ozone/cpu.hh" // MUST include this +#include "cpu/inst_seq.hh" +//#include "cpu/ozone/simple_impl.hh" // Would be nice to not have to include this +#include "cpu/ozone/ozone_impl.hh" + +#include <list> +#include <vector> + +template <class Impl> +class OzoneDynInst : public BaseDynInst<Impl> +{ + public: + // Typedefs + typedef typename Impl::FullCPU FullCPU; + + typedef typename FullCPU::ImplState ImplState; + + // Typedef for DynInstPtr. This is really just a RefCountingPtr<OoODynInst>. + typedef typename Impl::DynInstPtr DynInstPtr; + + typedef TheISA::ExtMachInst ExtMachInst; + typedef TheISA::MachInst MachInst; + typedef TheISA::MiscReg MiscReg; + typedef typename std::list<DynInstPtr>::iterator ListIt; + + // Note that this is duplicated from the BaseDynInst class; I'm + // simply not sure the enum would carry through so I could use it + // in array declarations in this class. + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, + MaxInstDestRegs = TheISA::MaxInstDestRegs + }; + + OzoneDynInst(FullCPU *cpu); + + OzoneDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, FullCPU *cpu); + + OzoneDynInst(StaticInstPtr inst); + + ~OzoneDynInst(); + + void setSrcInst(DynInstPtr &newSrcInst, int regIdx) + { srcInsts[regIdx] = newSrcInst; } + + bool srcInstReady(int regIdx); + + void setPrevDestInst(DynInstPtr &oldDestInst, int regIdx) + { prevDestInst[regIdx] = oldDestInst; } + + DynInstPtr &getPrevDestInst(int regIdx) + { return prevDestInst[regIdx]; } + + void addDependent(DynInstPtr &dependent_inst); + + std::vector<DynInstPtr> &getDependents() { return dependents; } + std::vector<DynInstPtr> &getMemDeps() { return memDependents; } + std::list<DynInstPtr> &getMemSrcs() { return srcMemInsts; } + + void wakeDependents(); + + void wakeMemDependents(); + + void addMemDependent(DynInstPtr &inst) { memDependents.push_back(inst); } + + void addSrcMemInst(DynInstPtr &inst) { srcMemInsts.push_back(inst); } + + void markMemInstReady(OzoneDynInst<Impl> *inst); + + // For now I will remove instructions from the list when they wake + // up. In the future, you only really need a counter. + bool memDepReady() { return srcMemInsts.empty(); } + + private: + void initInstPtrs(); + + std::vector<DynInstPtr> dependents; + + std::vector<DynInstPtr> memDependents; + + std::list<DynInstPtr> srcMemInsts; + + /** The instruction that produces the value of the source + * registers. These may be NULL if the value has already been + * read from the source instruction. + */ + DynInstPtr srcInsts[MaxInstSrcRegs]; + + /** + * Previous rename instruction for this destination. + */ + DynInstPtr prevDestInst[MaxInstSrcRegs]; + + public: + + Fault initiateAcc(); + + Fault completeAcc(); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return srcInsts[idx]->readIntResult(); + } + + float readFloatRegSingle(const StaticInst *si, int idx) + { + return srcInsts[idx]->readFloatResult(); + } + + double readFloatRegDouble(const StaticInst *si, int idx) + { + return srcInsts[idx]->readDoubleResult(); + } + + uint64_t readFloatRegInt(const StaticInst *si, int idx) + { + return srcInsts[idx]->readIntResult(); + } + + /** @todo: Make results into arrays so they can handle multiple dest + * registers. + */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + BaseDynInst<Impl>::setIntReg(si, idx, val); + } + + void setFloatRegSingle(const StaticInst *si, int idx, float val) + { + BaseDynInst<Impl>::setFloatRegSingle(si, idx, val); + } + + void setFloatRegDouble(const StaticInst *si, int idx, double val) + { + BaseDynInst<Impl>::setFloatRegDouble(si, idx, val); + } + + void setFloatRegInt(const StaticInst *si, int idx, uint64_t val) + { + BaseDynInst<Impl>::setFloatRegInt(si, idx, val); + } + + void setIntResult(uint64_t result) { this->instResult.integer = result; } + void setDoubleResult(double result) { this->instResult.dbl = result; } + + bool srcsReady(); + bool eaSrcsReady(); + + Fault execute(); + + Fault executeEAComp() + { return NoFault; } + + Fault executeMemAcc() + { return this->staticInst->memAccInst()->execute(this, this->traceData); } + + void clearDependents(); + + void clearMemDependents(); + + public: + // ISA stuff + MiscReg readMiscReg(int misc_reg); + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + Fault setMiscReg(int misc_reg, const MiscReg &val); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + +#if FULL_SYSTEM + Fault hwrei(); + int readIntrFlag(); + void setIntrFlag(int val); + bool inPalMode(); + void trap(Fault fault); + bool simPalCheck(int palFunc); +#else + void syscall(); +#endif + + ListIt iqIt; + bool iqItValid; +}; + +#endif // __CPU_OZONE_DYN_INST_HH__ diff --git a/src/cpu/ozone/dyn_inst_impl.hh b/src/cpu/ozone/dyn_inst_impl.hh new file mode 100644 index 000000000..4149bf144 --- /dev/null +++ b/src/cpu/ozone/dyn_inst_impl.hh @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2005-2006 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. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "kern/kernel_stats.hh" + +using namespace TheISA; + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(FullCPU *cpu) + : BaseDynInst<Impl>(0, 0, 0, 0, cpu) +{ + this->setResultReady(); + + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, FullCPU *cpu) + : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) +{ + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(StaticInstPtr _staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::~OzoneDynInst() +{ + DPRINTF(BE, "[sn:%lli] destructor called\n", this->seqNum); + for (int i = 0; i < this->numSrcRegs(); ++i) { + srcInsts[i] = NULL; + } + + for (int i = 0; i < this->numDestRegs(); ++i) { + prevDestInst[i] = NULL; + } + + dependents.clear(); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::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 <class Impl> +Fault +OzoneDynInst<Impl>::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 <class Impl> +Fault +OzoneDynInst<Impl>::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; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::srcInstReady(int regIdx) +{ + return srcInsts[regIdx]->isResultReady(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::addDependent(DynInstPtr &dependent_inst) +{ + dependents.push_back(dependent_inst); +} + +template <class Impl> +void +OzoneDynInst<Impl>::wakeDependents() +{ + for (int i = 0; i < dependents.size(); ++i) { + dependents[i]->markSrcRegReady(); + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::wakeMemDependents() +{ + for (int i = 0; i < memDependents.size(); ++i) { + memDependents[i]->markMemInstReady(this); + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::markMemInstReady(OzoneDynInst<Impl> *inst) +{ + ListIt mem_it = srcMemInsts.begin(); + while ((*mem_it) != inst && mem_it != srcMemInsts.end()) { + mem_it++; + } + assert(mem_it != srcMemInsts.end()); + + srcMemInsts.erase(mem_it); +} + +template <class Impl> +void +OzoneDynInst<Impl>::initInstPtrs() +{ + for (int i = 0; i < MaxInstSrcRegs; ++i) { + srcInsts[i] = NULL; + } + iqItValid = false; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::srcsReady() +{ + for (int i = 0; i < this->numSrcRegs(); ++i) { + if (!srcInsts[i]->isResultReady()) + return false; + } + + return true; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::eaSrcsReady() +{ + for (int i = 1; i < this->numSrcRegs(); ++i) { + if (!srcInsts[i]->isResultReady()) + return false; + } + + return true; +} + +template <class Impl> +void +OzoneDynInst<Impl>::clearDependents() +{ + dependents.clear(); + for (int i = 0; i < this->numSrcRegs(); ++i) { + srcInsts[i] = NULL; + } + for (int i = 0; i < this->numDestRegs(); ++i) { + prevDestInst[i] = NULL; + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::clearMemDependents() +{ + memDependents.clear(); +} + +template <class Impl> +MiscReg +OzoneDynInst<Impl>::readMiscReg(int misc_reg) +{ + return this->thread->readMiscReg(misc_reg); +} + +template <class Impl> +MiscReg +OzoneDynInst<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault) +{ + return this->thread->readMiscRegWithEffect(misc_reg, fault); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::setMiscReg(int misc_reg, const MiscReg &val) +{ + this->setIntResult(val); + return this->thread->setMiscReg(misc_reg, val); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + return this->thread->setMiscRegWithEffect(misc_reg, val); +} + +#if FULL_SYSTEM + +template <class Impl> +Fault +OzoneDynInst<Impl>::hwrei() +{ + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + this->setNextPC(this->thread->readMiscReg(AlphaISA::IPR_EXC_ADDR)); + + this->cpu->hwrei(); + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +int +OzoneDynInst<Impl>::readIntrFlag() +{ +return this->cpu->readIntrFlag(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::setIntrFlag(int val) +{ + this->cpu->setIntrFlag(val); +} + +template <class Impl> +bool +OzoneDynInst<Impl>::inPalMode() +{ + return this->cpu->inPalMode(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::trap(Fault fault) +{ + fault->invoke(this->thread->getXCProxy()); +} + +template <class Impl> +bool +OzoneDynInst<Impl>::simPalCheck(int palFunc) +{ + return this->cpu->simPalCheck(palFunc); +} +#else +template <class Impl> +void +OzoneDynInst<Impl>::syscall() +{ + this->cpu->syscall(); +} +#endif diff --git a/src/cpu/ozone/ea_list.cc b/src/cpu/ozone/ea_list.cc index 6114a0ca1..5ef240700 100644 --- a/src/cpu/ozone/ea_list.cc +++ b/src/cpu/ozone/ea_list.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Nathan Binkert */ #include "arch/isa_traits.hh" diff --git a/src/cpu/ozone/ea_list.hh b/src/cpu/ozone/ea_list.hh index c0eee4bb8..64882632c 100644 --- a/src/cpu/ozone/ea_list.hh +++ b/src/cpu/ozone/ea_list.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Nathan Binkert */ #ifndef __CPU_EA_LIST_HH__ diff --git a/src/cpu/ozone/front_end.cc b/src/cpu/ozone/front_end.cc new file mode 100644 index 000000000..f0ea8eae1 --- /dev/null +++ b/src/cpu/ozone/front_end.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/front_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +template class FrontEnd<OzoneImpl>; +template class FrontEnd<SimpleImpl>; diff --git a/src/cpu/ozone/front_end.hh b/src/cpu/ozone/front_end.hh new file mode 100644 index 000000000..af190008c --- /dev/null +++ b/src/cpu/ozone/front_end.hh @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_FRONT_END_HH__ +#define __CPU_OZONE_FRONT_END_HH__ + +#include <deque> + +#include "cpu/inst_seq.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/rename_table.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" +#include "sim/stats.hh" + +class ThreadContext; +class MemInterface; +template <class> +class OzoneThreadState; +class PageTable; +template <class> +class TimeBuffer; + +template <class Impl> +class FrontEnd +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::BackEnd BackEnd; + + typedef typename Impl::FullCPU::OzoneTC OzoneTC; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + FrontEnd(Params *params); + + std::string name() const; + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setBackEnd(BackEnd *back_end_ptr) + { backEnd = back_end_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void setTC(ThreadContext *tc_ptr); + + void setThreadState(OzoneThreadState<Impl> *thread_ptr) + { thread = thread_ptr; } + + void regStats(); + + void tick(); + Fault fetchCacheLine(); + void processInst(DynInstPtr &inst); + void squash(const InstSeqNum &squash_num, const Addr &next_PC, + const bool is_branch = false, const bool branch_taken = false); + DynInstPtr getInst(); + + void processCacheCompletion(Packet *pkt); + + void addFreeRegs(int num_freed); + + bool isEmpty() { return instBuffer.empty(); } + + void switchOut(); + + void doSwitchOut(); + + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + bool switchedOut; + + private: + bool updateStatus(); + + void checkBE(); + DynInstPtr getInstFromCacheline(); + void renameInst(DynInstPtr &inst); + // Returns true if we need to stop the front end this cycle + bool processBarriers(DynInstPtr &inst); + + void handleFault(Fault &fault); + public: + Fault getFault() { return fetchFault; } + private: + Fault fetchFault; + + // 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. + Addr icacheBlockAlignPC(Addr addr) + { + addr = TheISA::realPCToFetchPC(addr); + return (addr & ~(cacheBlkMask)); + } + + InstSeqNum getAndIncrementInstSeq() + { return cpu->globalSeqNum++; } + + public: + FullCPU *cpu; + + BackEnd *backEnd; + + ThreadContext *tc; + + OzoneThreadState<Impl> *thread; + + enum Status { + Running, + Idle, + IcacheMissStall, + IcacheMissComplete, + SerializeBlocked, + SerializeComplete, + RenameBlocked, + QuiescePending, + TrapPending, + BEBlocked + }; + + Status status; + + private: + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire fromCommit; + + typedef typename Impl::BranchPred BranchPred; + + BranchPred branchPred; + + class IcachePort : public Port + { + protected: + FrontEnd *fe; + + public: + IcachePort(const std::string &_name, FrontEnd *_fe) + : Port(_name), fe(_fe) + { } + + protected: + virtual Tick recvAtomic(PacketPtr pkt); + + virtual void recvFunctional(PacketPtr pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + virtual bool recvTiming(PacketPtr pkt); + + virtual void recvRetry(); + }; + + IcachePort icachePort; + +#if !FULL_SYSTEM + PageTable *pTable; +#endif + + RequestPtr memReq; + + /** Mask to get a cache block's address. */ + Addr cacheBlkMask; + + unsigned cacheBlkSize; + + Addr cacheBlkPC; + + /** The cache line being fetched. */ + uint8_t *cacheData; + + bool fetchCacheLineNextCycle; + + bool cacheBlkValid; + + public: + RenameTable<Impl> renameTable; + + private: + Addr PC; + Addr nextPC; + + public: + void setPC(Addr val) { PC = val; } + void setNextPC(Addr val) { nextPC = val; } + + void wakeFromQuiesce(); + + void dumpInsts(); + + private: + typedef typename std::deque<DynInstPtr> InstBuff; + typedef typename InstBuff::iterator InstBuffIt; + + InstBuff instBuffer; + + int instBufferSize; + + int maxInstBufferSize; + + int width; + + int freeRegs; + + int numPhysRegs; + + bool serializeNext; + + DynInstPtr barrierInst; + + public: + bool interruptPending; + private: + // number of idle cycles +/* + Stats::Average<> notIdleFraction; + Stats::Formula idleFraction; +*/ + // @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; + Stats::Scalar<> fetchedBranches; + /** Stat for total number of predicted branches. */ + Stats::Scalar<> predictedBranches; + /** Stat for total number of cycles spent fetching. */ + Stats::Scalar<> fetchCycles; + + Stats::Scalar<> fetchIdleCycles; + /** 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<> fetchBlockedCycles; + /** Stat for total number of fetched cache lines. */ + Stats::Scalar<> fetchedCacheLines; + + Stats::Scalar<> fetchIcacheSquashes; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; +// Stats::Vector<> qfull_iq_occupancy; +// Stats::VectorDistribution<> qfull_iq_occ_dist_; + Stats::Formula idleRate; + Stats::Formula branchRate; + Stats::Formula fetchRate; + Stats::Scalar<> IFQCount; // cumulative IFQ occupancy + Stats::Formula IFQOccupancy; + Stats::Formula IFQLatency; + Stats::Scalar<> IFQFcount; // cumulative IFQ full count + Stats::Formula IFQFullRate; + + Stats::Scalar<> dispatchCountStat; + Stats::Scalar<> dispatchedSerializing; + Stats::Scalar<> dispatchedTempSerializing; + Stats::Scalar<> dispatchSerializeStallCycles; + Stats::Formula dispatchRate; + Stats::Formula regIntFull; + Stats::Formula regFpFull; +}; + +#endif // __CPU_OZONE_FRONT_END_HH__ diff --git a/src/cpu/ozone/front_end_impl.hh b/src/cpu/ozone/front_end_impl.hh new file mode 100644 index 000000000..467567c10 --- /dev/null +++ b/src/cpu/ozone/front_end_impl.hh @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "base/statistics.hh" +#include "cpu/thread_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/ozone/front_end.hh" +#include "mem/mem_interface.hh" +#include "sim/byte_swap.hh" + +using namespace TheISA; + +template <class Impl> +FrontEnd<Impl>::FrontEnd(Params *params) + : branchPred(params), + icacheInterface(params->icacheInterface), + instBufferSize(0), + maxInstBufferSize(params->maxInstBufferSize), + width(params->frontEndWidth), + freeRegs(params->numPhysicalRegs), + numPhysRegs(params->numPhysicalRegs), + serializeNext(false), + interruptPending(false) +{ + switchedOut = false; + + status = Idle; + + memReq = NULL; + // Size of cache block. + cacheBlkSize = icacheInterface ? icacheInterface->getBlockSize() : 64; + + assert(isPowerOf2(cacheBlkSize)); + + // Create mask to get rid of offset bits. + cacheBlkMask = (cacheBlkSize - 1); + + // Create space to store a cache line. + cacheData = new uint8_t[cacheBlkSize]; + + fetchCacheLineNextCycle = true; + + cacheBlkValid = false; + +#if !FULL_SYSTEM +// pTable = params->pTable; +#endif + fetchFault = NoFault; +} + +template <class Impl> +std::string +FrontEnd<Impl>::name() const +{ + return cpu->name() + ".frontend"; +} + +template <class Impl> +void +FrontEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + // @todo: Hardcoded for now. Allow this to be set by a latency. + fromCommit = comm->getWire(-1); +} + +template <class Impl> +void +FrontEnd<Impl>::setTC(ThreadContext *tc_ptr) +{ + tc = tc_ptr; +} + +template <class Impl> +void +FrontEnd<Impl>::regStats() +{ + icacheStallCycles + .name(name() + ".icacheStallCycles") + .desc("Number of cycles fetch is stalled on an Icache miss") + .prereq(icacheStallCycles); + + fetchedInsts + .name(name() + ".fetchedInsts") + .desc("Number of instructions fetch has processed") + .prereq(fetchedInsts); + + fetchedBranches + .name(name() + ".fetchedBranches") + .desc("Number of fetched branches") + .prereq(fetchedBranches); + + 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); + + fetchIdleCycles + .name(name() + ".fetchIdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + + fetchSquashCycles + .name(name() + ".fetchSquashCycles") + .desc("Number of cycles fetch has spent squashing") + .prereq(fetchSquashCycles); + + 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); + + fetchIcacheSquashes + .name(name() + ".fetchIcacheSquashes") + .desc("Number of outstanding Icache misses that were squashed") + .prereq(fetchIcacheSquashes); + + fetchNisnDist + .init(/* base value */ 0, + /* last value */ width, + /* bucket size */ 1) + .name(name() + ".rateDist") + .desc("Number of instructions fetched each cycle (Total)") + .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 = fetchedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; + + IFQCount + .name(name() + ".IFQ:count") + .desc("cumulative IFQ occupancy") + ; + + IFQFcount + .name(name() + ".IFQ:fullCount") + .desc("cumulative IFQ full count") + .flags(Stats::total) + ; + + IFQOccupancy + .name(name() + ".IFQ:occupancy") + .desc("avg IFQ occupancy (inst's)") + ; + IFQOccupancy = IFQCount / cpu->numCycles; + + IFQLatency + .name(name() + ".IFQ:latency") + .desc("avg IFQ occupant latency (cycle's)") + .flags(Stats::total) + ; + + IFQFullRate + .name(name() + ".IFQ:fullRate") + .desc("fraction of time (cycles) IFQ was full") + .flags(Stats::total); + ; + IFQFullRate = IFQFcount * Stats::constant(100) / cpu->numCycles; + + dispatchCountStat + .name(name() + ".DIS:count") + .desc("cumulative count of dispatched insts") + .flags(Stats::total) + ; + + dispatchedSerializing + .name(name() + ".DIS:serializingInsts") + .desc("count of serializing insts dispatched") + .flags(Stats::total) + ; + + dispatchedTempSerializing + .name(name() + ".DIS:tempSerializingInsts") + .desc("count of temporary serializing insts dispatched") + .flags(Stats::total) + ; + + dispatchSerializeStallCycles + .name(name() + ".DIS:serializeStallCycles") + .desc("count of cycles dispatch stalled for serializing inst") + .flags(Stats::total) + ; + + dispatchRate + .name(name() + ".DIS:rate") + .desc("dispatched insts per cycle") + .flags(Stats::total) + ; + dispatchRate = dispatchCountStat / cpu->numCycles; + + regIntFull + .name(name() + ".REG:int:full") + .desc("number of cycles where there were no INT registers") + ; + + regFpFull + .name(name() + ".REG:fp:full") + .desc("number of cycles where there were no FP registers") + ; + IFQLatency = IFQOccupancy / dispatchRate; + + branchPred.regStats(); +} + +template <class Impl> +void +FrontEnd<Impl>::tick() +{ + if (switchedOut) + return; + + // @todo: Maybe I want to just have direct communication... + if (fromCommit->doneSeqNum) { + branchPred.update(fromCommit->doneSeqNum, 0); + } + + IFQCount += instBufferSize; + IFQFcount += instBufferSize == maxInstBufferSize; + + // Fetch cache line + if (status == IcacheMissComplete) { + cacheBlkValid = true; + + status = Running; + if (barrierInst) + status = SerializeBlocked; + if (freeRegs <= 0) + status = RenameBlocked; + checkBE(); + } else if (status == IcacheMissStall) { + DPRINTF(FE, "Still in Icache miss stall.\n"); + icacheStallCycles++; + return; + } + + if (status == RenameBlocked || status == SerializeBlocked || + status == TrapPending || status == BEBlocked) { + // Will cause a one cycle bubble between changing state and + // restarting. + DPRINTF(FE, "In blocked status.\n"); + + fetchBlockedCycles++; + + if (status == SerializeBlocked) { + dispatchSerializeStallCycles++; + } + updateStatus(); + return; + } else if (status == QuiescePending) { + DPRINTF(FE, "Waiting for quiesce to execute or get squashed.\n"); + return; + } else if (status != IcacheMissComplete) { + if (fetchCacheLineNextCycle) { + Fault fault = fetchCacheLine(); + if (fault != NoFault) { + handleFault(fault); + fetchFault = fault; + return; + } + fetchCacheLineNextCycle = false; + } + // If miss, stall until it returns. + if (status == IcacheMissStall) { + // Tell CPU to not tick me for now. + return; + } + } + + fetchCycles++; + + int num_inst = 0; + + // Otherwise loop and process instructions. + // One way to hack infinite width is to set width and maxInstBufferSize + // both really high. Inelegant, but probably will work. + while (num_inst < width && + instBufferSize < maxInstBufferSize) { + // Get instruction from cache line. + DynInstPtr inst = getInstFromCacheline(); + + if (!inst) { + // PC is no longer in the cache line, end fetch. + // Might want to check this at the end of the cycle so that + // there's no cycle lost to checking for a new cache line. + DPRINTF(FE, "Need to get new cache line\n"); + fetchCacheLineNextCycle = true; + break; + } + + processInst(inst); + + if (status == SerializeBlocked) { + break; + } + + // Possibly push into a time buffer that estimates the front end + // latency + instBuffer.push_back(inst); + ++instBufferSize; + ++num_inst; + +#if FULL_SYSTEM + if (inst->isQuiesce()) { + warn("%lli: Quiesce instruction encountered, halting fetch!", curTick); + status = QuiescePending; + break; + } +#endif + + if (inst->predTaken()) { + // Start over with tick? + break; + } else if (freeRegs <= 0) { + DPRINTF(FE, "Ran out of free registers to rename to!\n"); + status = RenameBlocked; + break; + } else if (serializeNext) { + break; + } + } + + fetchNisnDist.sample(num_inst); + checkBE(); + + DPRINTF(FE, "Num insts processed: %i, Inst Buffer size: %i, Free " + "Regs %i\n", num_inst, instBufferSize, freeRegs); +} + +template <class Impl> +Fault +FrontEnd<Impl>::fetchCacheLine() +{ + // Read a cache line, based on the current PC. +#if FULL_SYSTEM + // Flag to say whether or not address is physical addr. + unsigned flags = cpu->inPalMode(PC) ? PHYSICAL : 0; +#else + unsigned flags = 0; +#endif // FULL_SYSTEM + Fault fault = NoFault; + + if (interruptPending && flags == 0) { + return fault; + } + + // Align the fetch PC so it's at the start of a cache block. + Addr fetch_PC = icacheBlockAlignPC(PC); + + DPRINTF(FE, "Fetching cache line starting at %#x.\n", fetch_PC); + + // Setup the memReq to do a read of the first isntruction's address. + // Set the appropriate read size and flags as well. + memReq = new MemReq(); + + memReq->asid = 0; + memReq->thread_num = 0; + memReq->data = new uint8_t[64]; + memReq->tc = tc; + memReq->cmd = Read; + memReq->reset(fetch_PC, cacheBlkSize, flags); + + // Translate the instruction request. + fault = cpu->translateInstReq(memReq); + + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (icacheInterface && fault == NoFault) { +#if FULL_SYSTEM + if (cpu->system->memctrl->badaddr(memReq->paddr) || + memReq->flags & UNCACHEABLE) { + DPRINTF(FE, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path!", + memReq->paddr); + return TheISA::genMachineCheckFault(); + } +#endif + + memReq->completionEvent = NULL; + + memReq->time = curTick; + fault = cpu->mem->read(memReq, cacheData); + + MemAccessResult res = icacheInterface->access(memReq); + + // If the cache missed then schedule an event to wake + // up this stage once the cache miss completes. + if (icacheInterface->doEvents() && res != MA_HIT) { + memReq->completionEvent = new ICacheCompletionEvent(memReq, this); + + status = IcacheMissStall; + + cacheBlkValid = false; + + DPRINTF(FE, "Cache miss.\n"); + } else { + DPRINTF(FE, "Cache hit.\n"); + + cacheBlkValid = true; + +// memcpy(cacheData, memReq->data, memReq->size); + } + } + + // Note that this will set the cache block PC a bit earlier than it should + // be set. + cacheBlkPC = fetch_PC; + + ++fetchedCacheLines; + + DPRINTF(FE, "Done fetching cache line.\n"); + + return fault; +} + +template <class Impl> +void +FrontEnd<Impl>::processInst(DynInstPtr &inst) +{ + if (processBarriers(inst)) { + return; + } + + Addr inst_PC = inst->readPC(); + + if (!inst->isControl()) { + inst->setPredTarg(inst->readNextPC()); + } else { + fetchedBranches++; + if (branchPred.predict(inst, inst_PC, inst->threadNumber)) { + predictedBranches++; + } + } + + Addr next_PC = inst->readPredTarg(); + + DPRINTF(FE, "[sn:%lli] Predicted and processed inst PC %#x, next PC " + "%#x\n", inst->seqNum, inst_PC, next_PC); + +// inst->setNextPC(next_PC); + + // Not sure where I should set this + PC = next_PC; + + renameInst(inst); +} + +template <class Impl> +bool +FrontEnd<Impl>::processBarriers(DynInstPtr &inst) +{ + if (serializeNext) { + inst->setSerializeBefore(); + serializeNext = false; + } else if (!inst->isSerializing() && + !inst->isIprAccess() && + !inst->isStoreConditional()) { + return false; + } + + if ((inst->isIprAccess() || inst->isSerializeBefore()) && + !inst->isSerializeHandled()) { + DPRINTF(FE, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) { + dispatchedSerializing++; + inst->setSerializeHandled(); + } else { + dispatchedTempSerializing++; + } + + // Change status over to SerializeBlocked so that other stages know + // what this is blocked on. + status = SerializeBlocked; + + barrierInst = inst; + return true; + } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) + && !inst->isSerializeHandled()) { + DPRINTF(FE, "Serialize after instruction encountered.\n"); + + inst->setSerializeHandled(); + + dispatchedSerializing++; + + serializeNext = true; + return false; + } + return false; +} + +template <class Impl> +void +FrontEnd<Impl>::handleFault(Fault &fault) +{ + DPRINTF(FE, "Fault at fetch, telling commit\n"); + + // We're blocked on the back end until it handles this fault. + status = TrapPending; + + // Get a sequence number. + InstSeqNum inst_seq = getAndIncrementInstSeq(); + // We will use a nop in order to carry the fault. + ExtMachInst ext_inst = TheISA::NoopMachInst; + + // Create a new DynInst from the dummy nop. + DynInstPtr instruction = new DynInst(ext_inst, PC, + PC+sizeof(MachInst), + inst_seq, cpu); + instruction->setPredTarg(instruction->readNextPC()); +// instruction->setThread(tid); + +// instruction->setASID(tid); + + instruction->setState(thread); + + instruction->traceData = NULL; + + instruction->fault = fault; + instruction->setCanIssue(); + instBuffer.push_back(instruction); + ++instBufferSize; +} + +template <class Impl> +void +FrontEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC, + const bool is_branch, const bool branch_taken) +{ + DPRINTF(FE, "Squashing from [sn:%lli], setting PC to %#x\n", + squash_num, next_PC); + + if (fetchFault != NoFault) + fetchFault = NoFault; + + while (!instBuffer.empty() && + instBuffer.back()->seqNum > squash_num) { + DynInstPtr inst = instBuffer.back(); + + DPRINTF(FE, "Squashing instruction [sn:%lli] PC %#x\n", + inst->seqNum, inst->readPC()); + + inst->clearDependents(); + + instBuffer.pop_back(); + --instBufferSize; + + freeRegs+= inst->numDestRegs(); + } + + // Copy over rename table from the back end. + renameTable.copyFrom(backEnd->renameTable); + + PC = next_PC; + + // Update BP with proper information. + if (is_branch) { + branchPred.squash(squash_num, next_PC, branch_taken, 0); + } else { + branchPred.squash(squash_num, 0); + } + + // Clear the icache miss if it's outstanding. + if (status == IcacheMissStall && icacheInterface) { + DPRINTF(FE, "Squashing outstanding Icache miss.\n"); + memReq = NULL; + } + + if (status == SerializeBlocked) { + assert(barrierInst->seqNum > squash_num); + barrierInst = NULL; + } + + // Unless this squash originated from the front end, we're probably + // in running mode now. + // Actually might want to make this latency dependent. + status = Running; + fetchCacheLineNextCycle = true; +} + +template <class Impl> +typename Impl::DynInstPtr +FrontEnd<Impl>::getInst() +{ + if (instBufferSize == 0) { + return NULL; + } + + DynInstPtr inst = instBuffer.front(); + + instBuffer.pop_front(); + + --instBufferSize; + + dispatchCountStat++; + + return inst; +} + +template <class Impl> +void +FrontEnd<Impl>::processCacheCompletion(MemReqPtr &req) +{ + DPRINTF(FE, "Processing cache completion\n"); + + // Do something here. + if (status != IcacheMissStall || + req != memReq || + switchedOut) { + DPRINTF(FE, "Previous fetch was squashed.\n"); + fetchIcacheSquashes++; + return; + } + + status = IcacheMissComplete; + +/* if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheMissComplete; + } +*/ +// memcpy(cacheData, memReq->data, memReq->size); + + // Reset the completion event to NULL. +// memReq->completionEvent = NULL; + memReq = NULL; +} + +template <class Impl> +void +FrontEnd<Impl>::addFreeRegs(int num_freed) +{ + if (status == RenameBlocked && freeRegs + num_freed > 0) { + status = Running; + } + + DPRINTF(FE, "Adding %i freed registers\n", num_freed); + + freeRegs+= num_freed; + +// assert(freeRegs <= numPhysRegs); + if (freeRegs > numPhysRegs) + freeRegs = numPhysRegs; +} + +template <class Impl> +bool +FrontEnd<Impl>::updateStatus() +{ + bool serialize_block = !backEnd->robEmpty() || instBufferSize; + bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); + bool ret_val = false; + + if (status == SerializeBlocked && !serialize_block) { + status = SerializeComplete; + ret_val = true; + } + + if (status == BEBlocked && !be_block) { + if (barrierInst) { + status = SerializeBlocked; + } else { + status = Running; + } + ret_val = true; + } + return ret_val; +} + +template <class Impl> +void +FrontEnd<Impl>::checkBE() +{ + bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); + if (be_block) { + if (status == Running || status == Idle) { + status = BEBlocked; + } + } +} + +template <class Impl> +typename Impl::DynInstPtr +FrontEnd<Impl>::getInstFromCacheline() +{ + if (status == SerializeComplete) { + DynInstPtr inst = barrierInst; + status = Running; + barrierInst = NULL; + inst->clearSerializeBefore(); + return inst; + } + + InstSeqNum inst_seq; + MachInst inst; + // @todo: Fix this magic number used here to handle word offset (and + // getting rid of PAL bit) + unsigned offset = (PC & cacheBlkMask) & ~3; + + // PC of inst is not in this cache block + if (PC >= (cacheBlkPC + cacheBlkSize) || PC < cacheBlkPC || !cacheBlkValid) { + return NULL; + } + + ////////////////////////// + // Fetch one instruction + ////////////////////////// + + // Get a sequence number. + inst_seq = getAndIncrementInstSeq(); + + // Make sure this is a valid index. + assert(offset <= cacheBlkSize - sizeof(MachInst)); + + // Get the instruction from the array of the cache line. + inst = htog(*reinterpret_cast<MachInst *>(&cacheData[offset])); + + ExtMachInst decode_inst = TheISA::makeExtMI(inst, PC); + + // Create a new DynInst from the instruction fetched. + DynInstPtr instruction = new DynInst(decode_inst, PC, PC+sizeof(MachInst), + inst_seq, cpu); + + instruction->setState(thread); + + DPRINTF(FE, "Instruction [sn:%lli] created, with PC %#x\n%s\n", + inst_seq, instruction->readPC(), + instruction->staticInst->disassemble(PC)); + + instruction->traceData = + Trace::getInstRecord(curTick, tc, cpu, + instruction->staticInst, + instruction->readPC(), 0); + + // Increment stat of fetched instructions. + ++fetchedInsts; + + return instruction; +} + +template <class Impl> +void +FrontEnd<Impl>::renameInst(DynInstPtr &inst) +{ + DynInstPtr src_inst = NULL; + int num_src_regs = inst->numSrcRegs(); + if (num_src_regs == 0) { + inst->setCanIssue(); + } else { + for (int i = 0; i < num_src_regs; ++i) { + src_inst = renameTable[inst->srcRegIdx(i)]; + + inst->setSrcInst(src_inst, i); + + DPRINTF(FE, "[sn:%lli]: Src reg %i is inst [sn:%lli]\n", + inst->seqNum, (int)inst->srcRegIdx(i), src_inst->seqNum); + + if (src_inst->isResultReady()) { + DPRINTF(FE, "Reg ready.\n"); + inst->markSrcRegReady(i); + } else { + DPRINTF(FE, "Adding to dependent list.\n"); + src_inst->addDependent(inst); + } + } + } + + for (int i = 0; i < inst->numDestRegs(); ++i) { + RegIndex idx = inst->destRegIdx(i); + + DPRINTF(FE, "Dest reg %i is now inst [sn:%lli], was previously " + "[sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum, + renameTable[idx]->seqNum); + + inst->setPrevDestInst(renameTable[idx], i); + + renameTable[idx] = inst; + --freeRegs; + } +} + +template <class Impl> +void +FrontEnd<Impl>::wakeFromQuiesce() +{ + DPRINTF(FE, "Waking up from quiesce\n"); + // Hopefully this is safe + status = Running; +} + +template <class Impl> +void +FrontEnd<Impl>::switchOut() +{ + switchedOut = true; + cpu->signalSwitched(); +} + +template <class Impl> +void +FrontEnd<Impl>::doSwitchOut() +{ + memReq = NULL; + squash(0, 0); + instBuffer.clear(); + instBufferSize = 0; + status = Idle; +} + +template <class Impl> +void +FrontEnd<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + assert(freeRegs == numPhysRegs); + fetchCacheLineNextCycle = true; + + cacheBlkValid = false; + +#if !FULL_SYSTEM +// pTable = params->pTable; +#endif + fetchFault = NoFault; + serializeNext = false; + barrierInst = NULL; + status = Running; + switchedOut = false; + interruptPending = false; +} + +template <class Impl> +void +FrontEnd<Impl>::dumpInsts() +{ + cprintf("instBuffer size: %i\n", instBuffer.size()); + + InstBuffIt buff_it = instBuffer.begin(); + + for (int num = 0; buff_it != instBuffer.end(); num++) { + cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*buff_it)->readPC(), (*buff_it)->threadNumber, + (*buff_it)->seqNum, (*buff_it)->isIssued(), + (*buff_it)->isSquashed()); + buff_it++; + } +} + +template <class Impl> +FrontEnd<Impl>::ICacheCompletionEvent::ICacheCompletionEvent(MemReqPtr &_req, FrontEnd *fe) + : Event(&mainEventQueue, Delayed_Writeback_Pri), req(_req), frontEnd(fe) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +FrontEnd<Impl>::ICacheCompletionEvent::process() +{ + frontEnd->processCacheCompletion(req); +} + +template <class Impl> +const char * +FrontEnd<Impl>::ICacheCompletionEvent::description() +{ + return "ICache completion event"; +} diff --git a/src/cpu/ozone/inorder_back_end.cc b/src/cpu/ozone/inorder_back_end.cc new file mode 100644 index 000000000..bc6618e4a --- /dev/null +++ b/src/cpu/ozone/inorder_back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/inorder_back_end_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +template class InorderBackEnd<SimpleImpl>; diff --git a/src/cpu/ozone/inorder_back_end.hh b/src/cpu/ozone/inorder_back_end.hh new file mode 100644 index 000000000..ffdba2f6c --- /dev/null +++ b/src/cpu/ozone/inorder_back_end.hh @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_INORDER_BACK_END_HH__ +#define __CPU_OZONE_INORDER_BACK_END_HH__ + +#include <list> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/thread_context.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +template <class Impl> +class InorderBackEnd +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + + typedef typename FullCPU::OzoneTC OzoneTC; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + InorderBackEnd(Params *params); + + std::string name() const; + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm) + { comm = _comm; } + + void setTC(ThreadContext *tc_ptr); + + void setThreadState(OzoneThreadState<Impl> *thread_ptr); + + void regStats() { } + +#if FULL_SYSTEM + void checkInterrupts(); +#endif + + void tick(); + void executeInsts(); + void squash(const InstSeqNum &squash_num, const Addr &next_PC); + + void squashFromXC(); + void generateXCEvent() { } + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return false; } + bool isBlocked() { return status == DcacheMissStoreStall || + status == DcacheMissLoadStall || + interruptBlocked; } + + void fetchFault(Fault &fault); + + void dumpInsts(); + + private: + void handleFault(); + + void setSquashInfoFromTC(); + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + + bool interruptBlocked; + + public: + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + void switchOut() { panic("Not implemented!"); } + void doSwitchOut() { panic("Not implemented!"); } + void takeOverFrom(ThreadContext *old_tc = NULL) { panic("Not implemented!"); } + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + OzoneThreadState<Impl> *thread; + + RenameTable<Impl> renameTable; + + protected: + enum Status { + Running, + Idle, + DcacheMissLoadStall, + DcacheMissStoreStall, + DcacheMissComplete, + Blocked + }; + + Status status; + + class DCacheCompletionEvent : public Event + { + private: + InorderBackEnd *be; + + public: + DCacheCompletionEvent(InorderBackEnd *_be); + + virtual void process(); + virtual const char *description(); + + DynInstPtr inst; + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + +// MemInterface *dcacheInterface; + + RequestPtr memReq; + + private: + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + + // General back end width. Used if the more specific isn't given. + int width; + + int latency; + + int squashLatency; + + TimeBuffer<int> numInstsToWB; + TimeBuffer<int>::wire instsAdded; + TimeBuffer<int>::wire instsToExecute; + + TimeBuffer<CommStruct> *comm; + // number of cycles stalled for D-cache misses + Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +}; + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(Addr addr, T &data, unsigned flags) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + // Fix this hack for keeping funcExeInst correct with loads that + // are executed twice. + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissLoadStall; + DPRINTF(IBE, "Dcache miss stall!\n"); + } else { + // do functional access + DPRINTF(IBE, "Dcache hit!\n"); + } + } +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return fault; +} +#if 0 +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(MemReqPtr &req, T &data) +{ +#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 + + Fault error; + error = thread->mem->read(req, data); + data = LittleEndianGuest::gtoh(data); + return error; +} +#endif + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataWriteReq(memReq); + + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Write; +// memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissStoreStall; + DPRINTF(IBE, "Dcache miss stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + } + } + + if (res && (fault == NoFault)) + *res = memReq->result; +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return fault; +} +#if 0 +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(MemReqPtr &req, T &data) +{ +#if FULL_SYSTEM && defined(TARGET_ALPHA) + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } + + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < cpu->system->execContexts.size(); i++){ + xc = cpu->system->execContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } + +#endif + return thread->mem->write(req, (T)LittleEndianGuest::htog(data)); +} +#endif + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(MemReqPtr &req, T &data, int load_idx) +{ +// panic("Unimplemented!"); +// memReq->reset(addr, sizeof(T), flags); + + // translate to physical address +// Fault fault = cpu->translateDataReadReq(req); + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + req->flags &= ~INST_READ; + Fault fault = cpu->read(req, data); + memcpy(req->data, &data, sizeof(T)); + + // if we have a cache, do cache access too + if (dcacheInterface) { + 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. + if (result != MA_HIT) { + req->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissLoadStall; + DPRINTF(IBE, "Dcache miss load stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + + } + } + +/* + if (!dcacheInterface && (req->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return NoFault; +} + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(MemReqPtr &req, T &data, int store_idx) +{ +// req->reset(addr, sizeof(T), flags); + + // translate to physical address +// 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 *)&data, req->size); + + switch(req->size) { + case 1: + cpu->write(req, (uint8_t &)data); + break; + case 2: + cpu->write(req, (uint16_t &)data); + break; + case 4: + cpu->write(req, (uint32_t &)data); + break; + case 8: + cpu->write(req, (uint64_t &)data); + break; + default: + panic("Unexpected store size!\n"); + } + + if (dcacheInterface) { + req->cmd = Write; + req->data = new uint8_t[64]; + memcpy(req->data,(uint8_t *)&data,req->size); + req->completionEvent = NULL; + req->time = curTick; + req->flags &= ~INST_READ; + 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. + if (result != MA_HIT) { + req->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissStoreStall; + DPRINTF(IBE, "Dcache miss store stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + + } + } +/* + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + } else { + req->result = 1; + } + } +*/ +/* + if (res && (fault == NoFault)) + *res = req->result; + */ +/* + if (!dcacheInterface && (req->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return NoFault; +} + +#endif // __CPU_OZONE_INORDER_BACK_END_HH__ diff --git a/src/cpu/ozone/inorder_back_end_impl.hh b/src/cpu/ozone/inorder_back_end_impl.hh new file mode 100644 index 000000000..cbb73364e --- /dev/null +++ b/src/cpu/ozone/inorder_back_end_impl.hh @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/ozone/inorder_back_end.hh" +#include "cpu/ozone/thread_state.hh" + +using namespace TheISA; + +template <class Impl> +InorderBackEnd<Impl>::InorderBackEnd(Params *params) + : squashPending(false), + squashSeqNum(0), + squashNextPC(0), + faultFromFetch(NoFault), + interruptBlocked(false), + cacheCompletionEvent(this), + dcacheInterface(params->dcacheInterface), + width(params->backEndWidth), + latency(params->backEndLatency), + squashLatency(params->backEndSquashLatency), + numInstsToWB(0, latency + 1) +{ + instsAdded = numInstsToWB.getWire(latency); + instsToExecute = numInstsToWB.getWire(0); + + memReq = new MemReq; + memReq->data = new uint8_t[64]; + status = Running; +} + +template <class Impl> +std::string +InorderBackEnd<Impl>::name() const +{ + return cpu->name() + ".inorderbackend"; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setXC(ExecContext *xc_ptr) +{ + xc = xc_ptr; + memReq->xc = xc; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setThreadState(OzoneThreadState<Impl> *thread_ptr) +{ + thread = thread_ptr; + thread->setFuncExeInst(0); +} + +#if FULL_SYSTEM +template <class Impl> +void +InorderBackEnd<Impl>::checkInterrupts() +{ + //Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + cpu->checkInterrupts = false; + + if (thread->readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (thread->readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread->readMiscReg(IPR_SIRR) & (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 = cpu->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 > thread->readMiscReg(IPR_IPLR)) { + thread->inSyscall = true; + + thread->setMiscReg(IPR_ISR, summary); + thread->setMiscReg(IPR_INTID, ipl); + Fault(new InterruptFault)->invoke(xc); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread->readMiscReg(IPR_IPLR), ipl, summary); + + // May need to go 1 inst prior + squashPending = true; + + thread->inSyscall = false; + + setSquashInfoFromXC(); + } +} +#endif + +template <class Impl> +void +InorderBackEnd<Impl>::tick() +{ + // Squash due to an external source + // Not sure if this or an interrupt has higher priority + if (squashPending) { + squash(squashSeqNum, squashNextPC); + return; + } + + // if (interrupt) then set thread PC, stall front end, record that + // I'm waiting for it to drain. (for now just squash) +#if FULL_SYSTEM + if (interruptBlocked || + (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode())) { + if (!robEmpty()) { + interruptBlocked = true; + } else if (robEmpty() && cpu->inPalMode()) { + // Will need to let the front end continue a bit until + // we're out of pal mode. Hopefully we never get into an + // infinite loop... + interruptBlocked = false; + } else { + interruptBlocked = false; + checkInterrupts(); + return; + } + } +#endif + + if (status != DcacheMissLoadStall && + status != DcacheMissStoreStall) { + for (int i = 0; i < width && (*instsAdded) < width; ++i) { + DynInstPtr inst = frontEnd->getInst(); + + if (!inst) + break; + + instList.push_back(inst); + + (*instsAdded)++; + } + +#if FULL_SYSTEM + if (faultFromFetch && robEmpty() && frontEnd->isEmpty()) { + handleFault(); + } else { + executeInsts(); + } +#else + executeInsts(); +#endif + } +} + +template <class Impl> +void +InorderBackEnd<Impl>::executeInsts() +{ + bool completed_last_inst = true; + int insts_to_execute = *instsToExecute; + int freed_regs = 0; + + while (insts_to_execute > 0) { + assert(!instList.empty()); + DynInstPtr inst = instList.front(); + + commitPC = inst->readPC(); + + thread->setPC(commitPC); + thread->setNextPC(inst->readNextPC()); + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getXCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(IBE, "PC skip function event, stopping commit\n"); + completed_last_inst = false; + squashPending = true; + break; + } +#endif + + Fault inst_fault = NoFault; + + if (status == DcacheMissComplete) { + DPRINTF(IBE, "Completing inst [sn:%lli]\n", inst->seqNum); + status = Running; + } else if (inst->isMemRef() && status != DcacheMissComplete && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(IBE, "Initiating mem op inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + cacheCompletionEvent.inst = inst; + inst_fault = inst->initiateAcc(); + if (inst_fault == NoFault && + status != DcacheMissLoadStall && + status != DcacheMissStoreStall) { + inst_fault = inst->completeAcc(); + } + ++thread->funcExeInst; + } else { + DPRINTF(IBE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + inst_fault = inst->execute(); + ++thread->funcExeInst; + } + + // Will need to be able to break this loop in case the load + // misses. Split access/complete ops would be useful here + // with writeback events. + if (status == DcacheMissLoadStall) { + *instsToExecute = insts_to_execute; + + completed_last_inst = false; + break; + } else if (status == DcacheMissStoreStall) { + // Figure out how to fix this hack. Probably have DcacheMissLoad + // vs DcacheMissStore. + *instsToExecute = insts_to_execute; + completed_last_inst = false; +/* + instList.pop_front(); + --insts_to_execute; + if (inst->traceData) { + inst->traceData->finalize(); + } +*/ + + // Don't really need to stop for a store stall as long as + // the memory system is able to handle store forwarding + // and such. Breaking out might help avoid the cache + // interface becoming blocked. + break; + } + + inst->setExecuted(); + inst->setCompleted(); + inst->setCanCommit(); + + instList.pop_front(); + + --insts_to_execute; + --(*instsToExecute); + + if (inst->traceData) { + inst->traceData->finalize(); + inst->traceData = NULL; + } + + if (inst_fault != NoFault) { +#if FULL_SYSTEM + DPRINTF(IBE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + + assert(!thread->inSyscall); + + thread->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->setInst( + static_cast<TheISA::MachInst>(inst->staticInst->machInst)); + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + inst_fault->invoke(xc); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + squashPending = true; + + // Generate trap squash event. +// generateTrapEvent(tid); + completed_last_inst = false; + break; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + + for (int i = 0; i < inst->numDestRegs(); ++i) { + renameTable[inst->destRegIdx(i)] = inst; + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + inst->clearDependents(); + + comm->access(0)->doneSeqNum = inst->seqNum; + + if (inst->mispredicted()) { + squash(inst->seqNum, inst->readNextPC()); + + thread->setNextPC(inst->readNextPC()); + + break; + } else if (squashPending) { + // Something external happened that caused the CPU to squash. + // Break out of commit and handle the squash next cycle. + break; + } + // If it didn't mispredict, then it executed fine. Send back its + // registers and BP info? What about insts that may still have + // latency, like loads? Probably can send back the information after + // it is completed. + + // keep an instruction count + cpu->numInst++; + thread->numInsts++; + } + + frontEnd->addFreeRegs(freed_regs); + + assert(insts_to_execute >= 0); + + // Should only advance this if I have executed all instructions. + if (insts_to_execute == 0) { + numInstsToWB.advance(); + } + + // Should I set the PC to the next PC here? What do I set next PC to? + if (completed_last_inst) { + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readPC() + sizeof(MachInst)); + } + + if (squashPending) { + setSquashInfoFromXC(); + } +} + +template <class Impl> +void +InorderBackEnd<Impl>::handleFault() +{ + DPRINTF(Commit, "Handling fault from fetch\n"); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + faultFromFetch->invoke(xc); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + squashPending = true; + + setSquashInfoFromXC(); +} + +template <class Impl> +void +InorderBackEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC) +{ + DPRINTF(IBE, "Squashing from [sn:%lli], setting PC to %#x\n", + squash_num, next_PC); + + InstListIt squash_it = --(instList.end()); + + int freed_regs = 0; + + while (!instList.empty() && (*squash_it)->seqNum > squash_num) { + DynInstPtr inst = *squash_it; + + DPRINTF(IBE, "Squashing instruction PC %#x, [sn:%lli].\n", + inst->readPC(), + inst->seqNum); + + // May cause problems with misc regs + freed_regs+= inst->numDestRegs(); + inst->clearDependents(); + squash_it--; + instList.pop_back(); + } + + frontEnd->addFreeRegs(freed_regs); + + for (int i = 0; i < latency+1; ++i) { + numInstsToWB.advance(); + } + + squashPending = false; + + // Probably want to make sure that this squash is the one that set the + // thread into inSyscall mode. + thread->inSyscall = false; + + // Tell front end to squash, reset PC to new one. + frontEnd->squash(squash_num, next_PC); + + faultFromFetch = NULL; +} + +template <class Impl> +void +InorderBackEnd<Impl>::squashFromXC() +{ + // Record that I need to squash + squashPending = true; + + thread->inSyscall = true; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setSquashInfoFromXC() +{ + // Need to handle the case of the instList being empty. In that case + // probably any number works, except maybe with stores in the store buffer. + squashSeqNum = instList.empty() ? 0 : instList.front()->seqNum - 1; + + squashNextPC = thread->PC; +} + +template <class Impl> +void +InorderBackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; +} + +template <class Impl> +void +InorderBackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = instList.begin(); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.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; + } +} + +template <class Impl> +InorderBackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent( + InorderBackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +// this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InorderBackEnd<Impl>::DCacheCompletionEvent::process() +{ + inst->completeAcc(); + be->status = DcacheMissComplete; +} + +template <class Impl> +const char * +InorderBackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "DCache completion event"; +} diff --git a/src/cpu/ozone/inst_queue.cc b/src/cpu/ozone/inst_queue.cc new file mode 100644 index 000000000..7ce5d67ad --- /dev/null +++ b/src/cpu/ozone/inst_queue.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/inst_queue_impl.hh" + +// Force instantiation of InstructionQueue. +template class InstQueue<SimpleImpl>; +template class InstQueue<OzoneImpl>; diff --git a/src/cpu/ozone/inst_queue.hh b/src/cpu/ozone/inst_queue.hh new file mode 100644 index 000000000..0158fd2d2 --- /dev/null +++ b/src/cpu/ozone/inst_queue.hh @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_INST_QUEUE_HH__ +#define __CPU_OZONE_INST_QUEUE_HH__ + +#include <list> +#include <map> +#include <queue> +#include <vector> + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.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 + * instructions. The IQ uses a separate linked list to track dependencies. + * Similar to the rename map and the free list, it expects that + * 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. 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 Impl> +class InstQueue +{ + public: + //Typedefs from the Impl. + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::Params Params; + typedef typename Impl::IssueStruct IssueStruct; +/* + 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. + typedef typename std::list<DynInstPtr>::iterator ListIt; + + friend class Impl::FullCPU; +#if 0 + /** 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. */ + InstQueue<Impl> *iqPtr; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstQueue<Impl> *iq_ptr); + + virtual void process(); + virtual const char *description(); + }; +#endif + /** Constructs an IQ. */ + InstQueue(Params *params); + + /** Destructs the IQ. */ + ~InstQueue(); + + /** Returns the name of the IQ. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets CPU pointer. */ + void setCPU(FullCPU *_cpu) { cpu = _cpu; } +#if 0 + /** Sets active threads list. */ + void setActiveThreads(list<unsigned> *at_ptr); + + /** Sets the IEW pointer. */ + void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; } +#endif + /** Sets the timer buffer between issue and execute. */ + void setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2eQueue); +#if 0 + /** Sets the global time buffer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Number of entries needed for given amount of threads. */ + int entryAmount(int num_threads); + + /** Resets max entries for all threads. */ + void resetEntries(); +#endif + /** 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); +#if 0 + /** + * 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); +#endif + /** + * 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); +#if 0 + /** + * 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); +#endif + /** Completes a memory operation. */ + void completeMemInst(DynInstPtr &completed_inst); +#if 0 + /** Indicates an ordering violation between a store and a load. */ + void violation(DynInstPtr &store, DynInstPtr &faulting_load); +#endif + /** + * Squashes instructions for a thread. Squashing information is obtained + * from the time buffer. + */ + void squash(unsigned tid); // Probably want the ISN + + /** Returns the number of used entries for a thread. */ + unsigned getCount(unsigned tid) { return count[tid]; }; + + /** 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; +#if 0 + /** Pointer to IEW stage. */ + IEW *iewStage; + + /** The memory dependence unit, which tracks/predicts memory dependences + * between instructions. + */ + MemDepUnit memDepUnit[Impl::MaxThreads]; +#endif + /** The queue to the execute stage. Issued instructions will be written + * into it. + */ + TimeBuffer<IssueStruct> *issueToExecuteQueue; +#if 0 + /** The backwards time buffer. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to read information from timebuffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Function unit pool. */ + FUPool *fuPool; +#endif + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list<DynInstPtr> 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; + } + }; + + /** + * Struct for an IQ entry. It includes the instruction and an iterator + * to the instruction's spot in the IQ. + */ + struct IQEntry { + DynInstPtr inst; + ListIt iqIt; + }; + + typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> + ReadyInstQueue; + + typedef std::map<DynInstPtr, pqCompare> ReadyInstMap; + typedef typename std::map<DynInstPtr, pqCompare>::iterator ReadyMapIt; + + /** List of ready instructions. + */ + ReadyInstQueue readyInsts; + + /** List of non-speculative instructions that will be scheduled + * once the IQ gets a signal from commit. While it's redundant to + * have the key be a part of the value (the sequence number is stored + * 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. + */ + std::map<InstSeqNum, DynInstPtr> nonSpecInsts; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt; +#if 0 + /** 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<ListOrderEntry> listOrder; + + typedef typename std::list<ListOrderEntry>::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); +#endif + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// +#if 0 + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; +#endif + /** Number of Total Threads*/ + unsigned numThreads; +#if 0 + /** Pointer to list of active threads. */ + list<unsigned> *activeThreads; +#endif + /** 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; + + /** The number of entries in the instruction queue. */ + unsigned numEntries; + + /** The total number of instructions that can be issued in one cycle. */ + unsigned totalWidth; +#if 0 + /** The number of physical registers in the CPU. */ + unsigned numPhysRegs; + + /** The number of physical integer registers in the CPU. */ + unsigned numPhysIntRegs; + + /** The number of floating point registers in the CPU. */ + unsigned numPhysFloatRegs; +#endif + /** Delay between commit stage and the IQ. + * @todo: Make there be a distinction between the delays within IEW. + */ + unsigned commitToIEWDelay; + + ////////////////////////////////// + // Variables needed for squashing + ////////////////////////////////// + + /** The sequence number of the squashed instruction. */ + 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[Impl::MaxThreads]; +#if 0 + /////////////////////////////////// + // Dependency graph stuff + /////////////////////////////////// + + class DependencyEntry + { + public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + + DynInstPtr inst; + //Might want to include data about what arch. register the + //dependence is waiting on. + DependencyEntry *next; + + //This function, and perhaps this whole class, stand out a little + //bit as they don't fit a classification well. I want access + //to the underlying structure of the linked list, yet at + //the same time it feels like this should be something abstracted + //away. So for now it will sit here, within the IQ, until + //a better implementation is decided upon. + // This function probably shouldn't be within the entry... + void insert(DynInstPtr &new_inst); + + void remove(DynInstPtr &inst_to_remove); + + // Debug variable, remove when done testing. + static unsigned mem_alloc_counter; + }; + + /** Array of linked lists. Each linked list is a list of all the + * instructions that depend upon a given register. The actual + * register's index is used to index into the graph; ie all + * instructions in flight that are dependent upon r34 will be + * in the linked list of dependGraph[34]. + */ + DependencyEntry *dependGraph; + + /** A cache of the recently woken registers. It is 1 if the register + * has been woken up recently, and 0 if the register has been added + * to the dependency graph and has not yet received its value. It + * is basically a secondary scoreboard, and should pretty much mirror + * the scoreboard that exists in the rename map. + */ + vector<bool> regScoreboard; + + /** Adds an instruction to the dependency graph, as a producer. */ + bool addToDependents(DynInstPtr &new_inst); + + /** Adds an instruction to the dependency graph, as a consumer. */ + void createDependency(DynInstPtr &new_inst); +#endif + /** Moves an instruction to the ready queue if it is ready. */ + void addIfReady(DynInstPtr &inst); + + /** 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. + */ + int countInsts(); +#if 0 + /** Debugging function to dump out the dependency graph. + */ + void dumpDependGraph(); +#endif + /** Debugging function to dump all the list sizes, as well as print + * out the list of nonspeculative instructions. Should not be used + * in any other capacity, but it has no harmful sideaffects. + */ + 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; + /** 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_OZONE_INST_QUEUE_HH__ diff --git a/src/cpu/ozone/inst_queue_impl.hh b/src/cpu/ozone/inst_queue_impl.hh new file mode 100644 index 000000000..f2d80e621 --- /dev/null +++ b/src/cpu/ozone/inst_queue_impl.hh @@ -0,0 +1,1343 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + */ + +// Todo: +// Current ordering allows for 0 cycle added-to-scheduled. Could maybe fake +// it; either do in reverse order, or have added instructions put into a +// different ready queue that, in scheduleRreadyInsts(), gets put onto the +// normal ready queue. This would however give only a one cycle delay, +// but probably is more flexible to actually add in a delay parameter than +// just running it backwards. + +#include <vector> + +#include "sim/root.hh" + +#include "cpu/ozone/inst_queue.hh" +#if 0 +template <class Impl> +InstQueue<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstQueue<Impl> *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InstQueue<Impl>::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, fuIdx); +} + + +template <class Impl> +const char * +InstQueue<Impl>::FUCompletion::description() +{ + return "Functional unit completion event"; +} +#endif +template <class Impl> +InstQueue<Impl>::InstQueue(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; + numThreads = 1; + + //Initialize thread IQ counts + for (int i = 0; i <numThreads; i++) { + count[i] = 0; + } + + // Initialize the number of free IQ entries. + freeEntries = numEntries; + + // Set the number of physical registers as the number of int + float +// numPhysRegs = numPhysIntRegs + numPhysFloatRegs; + +// DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs); + + //Create an entry for each physical register within the + //dependency graph. +// dependGraph = new DependencyEntry[numPhysRegs]; + + // 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 + // registers start off as ready. However this doesn't matter for the + // 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) { + 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 <class Impl> +InstQueue<Impl>::~InstQueue() +{ + // 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 <class Impl> +std::string +InstQueue<Impl>::name() const +{ + return cpu->name() + ".iq"; +} + +template <class Impl> +void +InstQueue<Impl>::regStats() +{ + iqInstsAdded + .name(name() + ".iqInstsAdded") + .desc("Number of instructions added to the IQ (excludes non-spec)") + .prereq(iqInstsAdded); + + iqNonSpecInstsAdded + .name(name() + ".iqNonSpecInstsAdded") + .desc("Number of non-speculative instructions added to the IQ") + .prereq(iqNonSpecInstsAdded); + +// iqIntInstsAdded; + + iqIntInstsIssued + .name(name() + ".iqIntInstsIssued") + .desc("Number of integer instructions issued") + .prereq(iqIntInstsIssued); + +// iqFloatInstsAdded; + + iqFloatInstsIssued + .name(name() + ".iqFloatInstsIssued") + .desc("Number of float instructions issued") + .prereq(iqFloatInstsIssued); + +// iqBranchInstsAdded; + + iqBranchInstsIssued + .name(name() + ".iqBranchInstsIssued") + .desc("Number of branch instructions issued") + .prereq(iqBranchInstsIssued); + +// iqMemInstsAdded; + + iqMemInstsIssued + .name(name() + ".iqMemInstsIssued") + .desc("Number of memory instructions issued") + .prereq(iqMemInstsIssued); + +// iqMiscInstsAdded; + + iqMiscInstsIssued + .name(name() + ".iqMiscInstsIssued") + .desc("Number of miscellaneous instructions issued") + .prereq(iqMiscInstsIssued); + + iqSquashedInstsIssued + .name(name() + ".iqSquashedInstsIssued") + .desc("Number of squashed instructions issued") + .prereq(iqSquashedInstsIssued); + + iqSquashedInstsExamined + .name(name() + ".iqSquashedInstsExamined") + .desc("Number of squashed instructions iterated over during squash;" + " mainly for profiling") + .prereq(iqSquashedInstsExamined); + + iqSquashedOperandsExamined + .name(name() + ".iqSquashedOperandsExamined") + .desc("Number of squashed operands that are examined and possibly " + "removed from graph") + .prereq(iqSquashedOperandsExamined); + + iqSquashedNonSpecRemoved + .name(name() + ".iqSquashedNonSpecRemoved") + .desc("Number of squashed non-spec instructions that were removed") + .prereq(iqSquashedNonSpecRemoved); +/* + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } +*/ +} +/* +template <class Impl> +void +InstQueue<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} +*/ +template <class Impl> +void +InstQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2e_ptr) +{ + DPRINTF(IQ, "Set the issue to execute queue.\n"); + issueToExecuteQueue = i2e_ptr; +} +/* +template <class Impl> +void +InstQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(IQ, "Set the time buffer.\n"); + timeBuffer = tb_ptr; + + fromCommit = timeBuffer->getWire(-commitToIEWDelay); +} + +template <class Impl> +int +InstQueue<Impl>::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template <class Impl> +void +InstQueue<Impl>::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::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 <class Impl> +unsigned +InstQueue<Impl>::numFreeEntries() +{ + return freeEntries; +} + +template <class Impl> +unsigned +InstQueue<Impl>::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 <class Impl> +bool +InstQueue<Impl>::isFull() +{ + if (freeEntries == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstQueue<Impl>::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstQueue<Impl>::hasReadyInsts() +{ +/* + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +*/ + return readyInsts.empty(); +} + +template <class Impl> +void +InstQueue<Impl>::insert(DynInstPtr &new_inst) +{ + // Make sure the instruction is valid + assert(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); + + 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); + + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(new_inst); +*/ + // If it's a memory instruction, add it to the memory dependency + // unit. +// if (new_inst->isMemRef()) { +// memDepUnit[new_inst->threadNumber].insert(new_inst); +// } else { + // If the instruction is ready then add it to the ready list. + addIfReady(new_inst); +// } + + ++iqInstsAdded; + + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstQueue<Impl>::insertNonSpec(DynInstPtr &new_inst) +{ + nonSpecInsts[new_inst->seqNum] = new_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(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); + + 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(new_inst); + + // If it's a memory instruction, add it to the memory dependency + // unit. + 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 <class Impl> +void +InstQueue<Impl>::advanceTail(DynInstPtr &inst) +{ + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(inst); +} + +template <class Impl> +void +InstQueue<Impl>::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 <class Impl> +void +InstQueue<Impl>::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 <class Impl> +void +InstQueue<Impl>::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. +template <class Impl> +void +InstQueue<Impl>::scheduleReadyInsts() +{ + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); + +// IssueStruct *i2e_info = issueToExecuteQueue->access(0); +/* + // 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)); + } + } + } + + // 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 != -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 <class Impl> +void +InstQueue<Impl>::scheduleNonSpec(const InstSeqNum &inst) +{ + DPRINTF(IQ, "Marking nonspeculative instruction with sequence " + "number %i as ready to execute.\n", 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(); + + // Now schedule the instruction. +// if (!(*inst_it).second->isMemRef()) { + addIfReady((*inst_it).second); +// } else { +// memDepUnit[tid].nonSpecInstReady((*inst_it).second); +// } + + nonSpecInsts.erase(inst_it); +} + +template <class Impl> +void +InstQueue<Impl>::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 <class Impl> +void +InstQueue<Impl>::wakeDependents(DynInstPtr &completed_inst) +{ + DPRINTF(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. +/* + DependencyEntry *curr; + DependencyEntry *prev; +*/ + // Tell the memory dependence unit to wake any dependents on this + // 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[completed_inst->threadNumber].wakeDependents(completed_inst); + completeMemInst(completed_inst); + } + completed_inst->wakeDependents(); +/* + for (int dest_reg_idx = 0; + dest_reg_idx < completed_inst->numDestRegs(); + dest_reg_idx++) + { + PhysRegIndex dest_reg = + completed_inst->renamedDestRegIdx(dest_reg_idx); + + // Special case of uniq or control registers. They are not + // handled by the IQ and thus have no dependency graph entry. + // @todo Figure out a cleaner way to handle this. + if (dest_reg >= numPhysRegs) { + continue; + } + + 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. + + curr = dependGraph[dest_reg].next; + + while (curr) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", + curr->inst->readPC()); + + // Might want to give more information to the instruction + // so that it knows which of its source registers is ready. + // However that would mean that the dependency graph entries + // would need to hold the src_reg_idx. + curr->inst->markSrcRegReady(); + + addIfReady(curr->inst); + + DependencyEntry::mem_alloc_counter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + // Reset the head node now that all of its dependents have been woken + // up. + dependGraph[dest_reg].next = NULL; + dependGraph[dest_reg].inst = NULL; + + // Mark the scoreboard as having that register ready. + regScoreboard[dest_reg] = true; + } +*/ +} + +template <class Impl> +void +InstQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst) +{ + OpClass op_class = ready_inst->opClass(); + + readyInsts.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 <class Impl> +void +InstQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template <class Impl> +void +InstQueue<Impl>::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} +*/ +template <class Impl> +void +InstQueue<Impl>::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 <class Impl> +void +InstQueue<Impl>::violation(DynInstPtr &store, + DynInstPtr &faulting_load) +{ + memDepUnit[store->threadNumber].violation(store, faulting_load); +} +*/ +template <class Impl> +void +InstQueue<Impl>::squash(unsigned tid) +{ + 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[tid] = fromCommit->commitInfo[tid].doneSeqNum; + + // Setup the squash iterator to point to the tail. + squashIt[tid] = instList[tid].end(); + --squashIt[tid]; + + // Call doSquash if there are insts in the IQ + if (count[tid] > 0) { + doSquash(tid); + } + + // Also tell the memory dependence unit to squash. +// memDepUnit[tid].squash(squashedSeqNum[tid], tid); +} + +template <class Impl> +void +InstQueue<Impl>::doSquash(unsigned tid) +{ + // Make sure the squashed sequence number is valid. + assert(squashedSeqNum[tid] != 0); + + 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[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->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. + if (!squashed_inst->isNonSpeculative()) { +/* + for (int src_reg_idx = 0; + src_reg_idx < squashed_inst->numSrcRegs(); + src_reg_idx++) + { + PhysRegIndex src_reg = + squashed_inst->renamedSrcRegIdx(src_reg_idx); + + // Only remove it from the dependency graph if it was + // placed there in the first place. + // HACK: This assumes that instructions woken up from the + // 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; + + nonSpecInsts.erase(squashed_inst->seqNum); + + ++iqSquashedNonSpecRemoved; + } + + // Might want to also clear out the head of the dependency graph. + + // Mark it as squashed within the IQ. + squashed_inst->setSquashedInIQ(); + + // @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; + + if (numThreads > 1) { + DPRINTF(IQ, "[tid:%i]: Instruction PC %#x squashed.\n", + tid, squashed_inst->readPC()); + } else { + DPRINTF(IQ, "Instruction PC %#x squashed.\n", + squashed_inst->readPC()); + } + } + + --squashIt[tid]; + ++iqSquashedInstsExamined; + } +} +/* +template <class Impl> +void +InstQueue<Impl>::DependencyEntry::insert(DynInstPtr &new_inst) +{ + //Add this new, dependent instruction at the head of the dependency + //chain. + + // First create the entry that will be added to the head of the + // dependency chain. + DependencyEntry *new_entry = new DependencyEntry; + new_entry->next = this->next; + new_entry->inst = new_inst; + + // Then actually add it to the chain. + this->next = new_entry; + + ++mem_alloc_counter; +} + +template <class Impl> +void +InstQueue<Impl>::DependencyEntry::remove(DynInstPtr &inst_to_remove) +{ + DependencyEntry *prev = this; + DependencyEntry *curr = this->next; + + // Make sure curr isn't NULL. Because this instruction is being + // removed from a dependency list, it must have been placed there at + // an earlier time. The dependency chain should not be empty, + // unless the instruction dependent upon it is already ready. + if (curr == NULL) { + return; + } + + // Find the instruction to remove within the dependency linked list. + while (curr->inst != inst_to_remove) { + prev = curr; + curr = curr->next; + + assert(curr != NULL); + } + + // Now remove this instruction from the list. + prev->next = curr->next; + + --mem_alloc_counter; + + // Could push this off to the destructor of DependencyEntry + curr->inst = NULL; + + delete curr; +} + +template <class Impl> +bool +InstQueue<Impl>::addToDependents(DynInstPtr &new_inst) +{ + // Loop through the instruction's source registers, adding + // them to the dependency list if they are not ready. + int8_t total_src_regs = new_inst->numSrcRegs(); + bool return_val = false; + + for (int src_reg_idx = 0; + src_reg_idx < total_src_regs; + src_reg_idx++) + { + // Only add it to the dependency graph if it's not ready. + if (!new_inst->isReadySrcRegIdx(src_reg_idx)) { + PhysRegIndex src_reg = new_inst->renamedSrcRegIdx(src_reg_idx); + + // Check the IQ's scoreboard to make sure the register + // hasn't become ready while the instruction was in flight + // between stages. Only if it really isn't ready should + // it be added to the dependency graph. + if (src_reg >= numPhysRegs) { + continue; + } else if (regScoreboard[src_reg] == false) { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "is being added to the dependency chain.\n", + new_inst->readPC(), src_reg); + + dependGraph[src_reg].insert(new_inst); + + // Change the return value to indicate that something + // was added to the dependency graph. + return_val = true; + } else { + 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. + new_inst->markSrcRegReady(); + } + } + } + + return return_val; +} + +template <class Impl> +void +InstQueue<Impl>::createDependency(DynInstPtr &new_inst) +{ + //Actually nothing really needs to be marked when an + //instruction becomes the producer of a register's value, + //but for convenience a ptr to the producing instruction will + //be placed in the head node of the dependency links. + int8_t total_dest_regs = new_inst->numDestRegs(); + + for (int dest_reg_idx = 0; + dest_reg_idx < total_dest_regs; + dest_reg_idx++) + { + PhysRegIndex dest_reg = new_inst->renamedDestRegIdx(dest_reg_idx); + + // Instructions that use the misc regs will have a reg number + // higher than the normal physical registers. In this case these + // registers are not renamed, and there is no need to track + // dependencies as these instructions must be executed at commit. + if (dest_reg >= numPhysRegs) { + continue; + } + + if (dependGraph[dest_reg].next) { + dumpDependGraph(); + 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; + } +} +*/ +template <class Impl> +void +InstQueue<Impl>::addIfReady(DynInstPtr &inst) +{ + //If the instruction now has all of its source registers + // available, then add it to the list of ready instructions. + if (inst->readyToIssue()) { + + //Add the instruction to the proper ready list. + if (inst->isMemRef()) { + + DPRINTF(IQ, "Checking if memory instruction can issue.\n"); + + // Message to the mem dependence unit that this instruction has + // its registers ready. + +// memDepUnit[inst->threadNumber].regsReady(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.push(inst); + } +} + +template <class Impl> +int +InstQueue<Impl>::countInsts() +{ + //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; + + for (int i = 0; i < numThreads; ++i) { + ListIt count_it = instList[i].begin(); + + 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; + } + } + + return total_insts; +#else + return numEntries - freeEntries; +#endif +} +/* +template <class Impl> +void +InstQueue<Impl>::dumpDependGraph() +{ + DependencyEntry *curr; + + for (int i = 0; i < numPhysRegs; ++i) + { + curr = &dependGraph[i]; + + if (curr->inst) { + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); + } else { + cprintf("dependGraph[%i]: No producer. consumer: ", i); + } + + while (curr->next != NULL) { + curr = curr->next; + + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); + } + + cprintf("\n"); + } +} +*/ +template <class Impl> +void +InstQueue<Impl>::dumpLists() +{ + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts.size()); + + cprintf("\n"); + } + + cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); + + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); + + cprintf("Non speculative list: "); + + 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 <class Impl> +void +InstQueue<Impl>::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/src/cpu/ozone/lsq_unit.cc b/src/cpu/ozone/lsq_unit.cc new file mode 100644 index 000000000..e37971dba --- /dev/null +++ b/src/cpu/ozone/lsq_unit.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/lsq_unit_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class OzoneLSQ<OzoneImpl>; + diff --git a/src/cpu/ozone/lsq_unit.hh b/src/cpu/ozone/lsq_unit.hh new file mode 100644 index 000000000..1b5340e55 --- /dev/null +++ b/src/cpu/ozone/lsq_unit.hh @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LSQ_UNIT_HH__ +#define __CPU_OZONE_LSQ_UNIT_HH__ + +#include <map> +#include <queue> +#include <algorithm> + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#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" + +class PageTable; + +/** + * 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 Impl> +class OzoneLSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::IssueStruct IssueStruct; + + typedef TheISA::IntReg IntReg; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator LdMapIt; + + private: + class StoreCompletionEvent : public Event { + public: + /** Constructs a store completion event. */ + StoreCompletionEvent(int store_idx, Event *wb_event, OzoneLSQ *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. */ + OzoneLSQ<Impl> *lsqPtr; + }; + + friend class StoreCompletionEvent; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + OzoneLSQ(); + + /** 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 back-end stage pointer. */ + void setBE(BackEnd *be_ptr) + { be = be_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. + */ + inline bool loadBlocked(); + + /** 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 back-end stage. */ + BackEnd *be; + + /** 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 OzoneLSQ thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::vector<SQEntry> storeQueue; + + /** The load queue. */ + std::vector<DynInstPtr> 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<InstSeqNum> mshrSeqNums; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::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; + + /** 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 <class T> + Fault read(MemReqPtr &req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + 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 <class Impl> +template <class T> +Fault +OzoneLSQ<Impl>::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]->readyToCommit())) { + + 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(OzoneLSQ, "Read called, load idx: %i, store idx: %i, " + "storeHead: %i addr: %#x\n", + load_idx, store_idx, storeHead, req->paddr); + + 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(OzoneLSQ, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->vaddr, *(req->data)); + + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(loadQueue[load_idx], + be); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // FIXME - 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 + be->rescheduleMemInst(loadQueue[load_idx]); + + DPRINTF(OzoneLSQ, "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]; + + ++usedPorts; + + // 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(OzoneLSQ, "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); + typedef typename BackEnd::LdWritebackEvent LdWritebackEvent; + + LdWritebackEvent *wb = new LdWritebackEvent(loadQueue[load_idx], be); + + req->completionEvent = wb; + + // 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(OzoneLSQ, "D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + wb->setDcacheMiss(); + + } else { +// DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", +// inst->seqNum); + + DPRINTF(OzoneLSQ, "D-cache hit!\n"); + } + } else { + fatal("Must use D-cache with new memory system"); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +OzoneLSQ<Impl>::write(MemReqPtr &req, T &data, int store_idx) +{ + assert(storeQueue[store_idx].inst); + + DPRINTF(OzoneLSQ, "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); + + 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; +} + +template <class Impl> +inline bool +OzoneLSQ<Impl>::loadBlocked() +{ + bool ret_val = isLoadBlocked; + isLoadBlocked = false; + return ret_val; +} + +#endif // __CPU_OZONE_LSQ_UNIT_HH__ diff --git a/src/cpu/ozone/lsq_unit_impl.hh b/src/cpu/ozone/lsq_unit_impl.hh new file mode 100644 index 000000000..f8cb18634 --- /dev/null +++ b/src/cpu/ozone/lsq_unit_impl.hh @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2004-2006 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. + * + * Authors: Kevin Lim + */ + +#include "arch/isa_traits.hh" +#include "base/str.hh" +#include "cpu/ozone/lsq_unit.hh" + +template <class Impl> +OzoneLSQ<Impl>::StoreCompletionEvent::StoreCompletionEvent(int store_idx, + Event *wb_event, + OzoneLSQ<Impl> *lsq_ptr) + : Event(&mainEventQueue), + storeIdx(store_idx), + wbEvent(wb_event), + lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +OzoneLSQ<Impl>::StoreCompletionEvent::process() +{ + DPRINTF(OzoneLSQ, "Cache miss complete for store idx:%i\n", storeIdx); + + //lsqPtr->removeMSHR(lsqPtr->storeQueue[storeIdx].inst->seqNum); + +// lsqPtr->cpu->wakeCPU(); + if (wbEvent) + wbEvent->process(); + lsqPtr->completeStore(storeIdx); +} + +template <class Impl> +const char * +OzoneLSQ<Impl>::StoreCompletionEvent::description() +{ + return "LSQ store completion event"; +} + +template <class Impl> +OzoneLSQ<Impl>::OzoneLSQ() + : loads(0), stores(0), storesToWB(0), stalled(false), isLoadBlocked(false) +{ +} + +template<class Impl> +void +OzoneLSQ<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) + +{ + DPRINTF(OzoneLSQ, "Creating OzoneLSQ%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; +} + +template<class Impl> +std::string +OzoneLSQ<Impl>::name() const +{ + return "lsqunit"; +} + +template<class Impl> +void +OzoneLSQ<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +OzoneLSQ<Impl>::clearSQ() +{ + storeQueue.clear(); +} + +template<class Impl> +void +OzoneLSQ<Impl>::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template<class Impl> +void +OzoneLSQ<Impl>::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::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 <class Impl> +void +OzoneLSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead && loads < LQEntries); + + DPRINTF(OzoneLSQ, "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 <class Impl> +void +OzoneLSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert((storeTail + 1) % SQEntries != storeHead); + assert(stores < SQEntries); + + DPRINTF(OzoneLSQ, "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 <class Impl> +typename Impl::DynInstPtr +OzoneLSQ<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +OzoneLSQ<Impl>::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 <class Impl> +int +OzoneLSQ<Impl>::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 <class Impl> +Fault +OzoneLSQ<Impl>::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 <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(OzoneLSQ, "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(); + + // Might want to make sure that I'm not overwriting a previously faulting + // instruction that hasn't been checked yet. + // Actually probably want the oldest faulting load + 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 <class Impl> +Fault +OzoneLSQ<Impl>::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 <class Impl> +Fault +OzoneLSQ<Impl>::executeStore(DynInstPtr &store_inst) +{ + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + + DPRINTF(OzoneLSQ, "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(); + + // 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(OzoneLSQ,"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 TheISA::genMachineCheckFault(); + } + + incrLdIdx(load_idx); + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(OzoneLSQ, "[sn:%lli] Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->seqNum, loadQueue[loadHead]->readPC()); + + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::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 <class Impl> +void +OzoneLSQ<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::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(OzoneLSQ, "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 <class Impl> +void +OzoneLSQ<Impl>::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(OzoneLSQ, "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->translateDataReadReq(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(OzoneLSQ, "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"); +// } + + if (dcacheInterface) { + MemAccessResult result = dcacheInterface->access(req); + + //@todo temp fix for LL/SC (works fine for 1 CPU) + if (req->flags & LOCKED) { + req->result=1; + panic("LL/SC! oh no no support!!!"); + } + + if (isStalled() && + storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst(loadQueue[stallingLoadIdx]); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + Event *wb = NULL; +/* + typename IEW::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. + req->result=0; + wb = new typename IEW::LdWritebackEvent(storeQueue[storeWBIdx].inst, + iewStage); + } +*/ + DPRINTF(OzoneLSQ,"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(OzoneLSQ, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // Increment stat here or something + } else { + DPRINTF(OzoneLSQ,"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. + req->result=1; + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(storeQueue[storeWBIdx].inst, + be); + 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 <class Impl> +void +OzoneLSQ<Impl>::removeMSHR(InstSeqNum seqNum) +{ + list<InstSeqNum>::iterator mshr_it = find(mshrSeqNums.begin(), + mshrSeqNums.end(), + seqNum); + + if (mshr_it != mshrSeqNums.end()) { + mshrSeqNums.erase(mshr_it); + DPRINTF(OzoneLSQ, "Removing MSHR. count = %i\n",mshrSeqNums.size()); + } +}*/ + +template <class Impl> +void +OzoneLSQ<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(OzoneLSQ, "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(OzoneLSQ,"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); + } + + int store_idx = storeTail; + decrStIdx(store_idx); + + while (stores != 0 && storeQueue[store_idx].inst->seqNum > squashed_num) { + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"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 <class Impl> +void +OzoneLSQ<Impl>::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("[sn:%lli] %#x ", loadQueue[load_idx]->seqNum, + loadQueue[load_idx]->readPC()); + + incrLdIdx(load_idx); + } + + cprintf("\nStore queue size: %i\n", stores); + cprintf("Store queue: "); + + int store_idx = storeHead; + + while (store_idx != storeTail && storeQueue[store_idx].inst) { + cprintf("[sn:%lli] %#x ", storeQueue[store_idx].inst->seqNum, + storeQueue[store_idx].inst->readPC()); + + incrStIdx(store_idx); + } + + cprintf("\n"); +} + +template <class Impl> +void +OzoneLSQ<Impl>::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); + +// be->updateLSQNextCycle = true; + } + + DPRINTF(OzoneLSQ, "Store head idx:%i\n", storeHead); + + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst(loadQueue[stallingLoadIdx]); + } +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} diff --git a/src/cpu/ozone/lw_back_end.cc b/src/cpu/ozone/lw_back_end.cc new file mode 100644 index 000000000..71e51f038 --- /dev/null +++ b/src/cpu/ozone/lw_back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/lw_back_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +template class LWBackEnd<OzoneImpl>; diff --git a/src/cpu/ozone/lw_back_end.hh b/src/cpu/ozone/lw_back_end.hh new file mode 100644 index 000000000..bb81f60c8 --- /dev/null +++ b/src/cpu/ozone/lw_back_end.hh @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LW_BACK_END_HH__ +#define __CPU_OZONE_LW_BACK_END_HH__ + +#include <list> +#include <queue> +#include <set> +#include <string> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +template <class> +class Checker; +class ThreadContext; + +template <class Impl> +class OzoneThreadState; + +template <class Impl> +class LWBackEnd +{ + public: + typedef OzoneThreadState<Impl> Thread; + + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + struct SizeStruct { + int size; + }; + + typedef SizeStruct DispatchToIssue; + typedef SizeStruct IssueToExec; + typedef SizeStruct ExecToCommit; + typedef SizeStruct Writeback; + + TimeBuffer<DispatchToIssue> d2i; + typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; + TimeBuffer<IssueToExec> i2e; + typename TimeBuffer<IssueToExec>::wire instsToExecute; + TimeBuffer<ExecToCommit> e2c; + TimeBuffer<Writeback> numInstsToWB; + + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire toIEW; + typename TimeBuffer<CommStruct>::wire fromCommit; + + class TrapEvent : public Event { + private: + LWBackEnd<Impl> *be; + + public: + TrapEvent(LWBackEnd<Impl> *_be); + + void process(); + const char *description(); + }; + + /** LdWriteback event for a load completion. */ + class LdWritebackEvent : public Event { + private: + /** Instruction that is writing back data to the register file. */ + DynInstPtr inst; + /** Pointer to IEW stage. */ + LWBackEnd *be; + + bool dcacheMiss; + + public: + /** Constructs a load writeback event. */ + LdWritebackEvent(DynInstPtr &_inst, LWBackEnd *be); + + /** Processes writeback event. */ + virtual void process(); + /** Returns the description of the writeback event. */ + virtual const char *description(); + + void setDcacheMiss() { dcacheMiss = true; be->addDcacheMiss(inst); } + }; + + LWBackEnd(Params *params); + + std::string name() const; + + void regStats(); + + void setCPU(FullCPU *cpu_ptr); + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setTC(ThreadContext *tc_ptr) + { tc = tc_ptr; } + + void setThreadState(Thread *thread_ptr) + { thread = thread_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void tick(); + void squash(); + void generateTCEvent() { tcSquash = true; } + void squashFromTC(); + void squashFromTrap(); + void checkInterrupts(); + bool trapSquash; + bool tcSquash; + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + Tick lastCommitCycle; + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return numInsts >= numROBEntries; } + bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } + + void fetchFault(Fault &fault); + + int wakeDependents(DynInstPtr &inst, bool memory_deps = false); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst) { } + + void addDcacheMiss(DynInstPtr &inst) + { + waitingMemOps.insert(inst->seqNum); + numWaitingMemOps++; + DPRINTF(BE, "Adding a Dcache miss mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void removeDcacheMiss(DynInstPtr &inst) + { + assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); + waitingMemOps.erase(inst->seqNum); + numWaitingMemOps--; + DPRINTF(BE, "Removing a Dcache miss mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void addWaitingMemOp(DynInstPtr &inst) + { + waitingMemOps.insert(inst->seqNum); + numWaitingMemOps++; + DPRINTF(BE, "Adding a waiting mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void removeWaitingMemOp(DynInstPtr &inst) + { + assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); + waitingMemOps.erase(inst->seqNum); + numWaitingMemOps--; + DPRINTF(BE, "Removing a waiting mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void instToCommit(DynInstPtr &inst); + + void switchOut(); + void doSwitchOut(); + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + private: + void generateTrapEvent(Tick latency = 0); + void handleFault(Fault &fault, Tick latency = 0); + void updateStructures(); + void dispatchInsts(); + void dispatchStall(); + void checkDispatchStatus(); + void executeInsts(); + void commitInsts(); + void addToLSQ(DynInstPtr &inst); + void writebackInsts(); + bool commitInst(int inst_num); + void squash(const InstSeqNum &sn); + void squashDueToBranch(DynInstPtr &inst); + void squashDueToMemViolation(DynInstPtr &inst); + void squashDueToMemBlocked(DynInstPtr &inst); + void updateExeInstStats(DynInstPtr &inst); + void updateComInstStats(DynInstPtr &inst); + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + Thread *thread; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissComplete, + Blocked, + TrapPending + }; + + Status status; + + Status dispatchStatus; + + Status commitStatus; + + Counter funcExeInst; + + private: + typedef typename Impl::LdstQueue LdstQueue; + + LdstQueue LSQ; + public: + RenameTable<Impl> commitRenameTable; + + RenameTable<Impl> renameTable; + private: + class DCacheCompletionEvent : public Event + { + private: + LWBackEnd *be; + + public: + DCacheCompletionEvent(LWBackEnd *_be); + + virtual void process(); + virtual const char *description(); + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + + MemInterface *dcacheInterface; + + // General back end width. Used if the more specific isn't given. + int width; + + // Dispatch width. + int dispatchWidth; + int numDispatchEntries; + int dispatchSize; + + int waitingInsts; + + int issueWidth; + + // Writeback width + int wbWidth; + + // Commit width + int commitWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written + * back. Used in case there are too many instructions writing + * back at the current cycle and writesbacks need to be scheduled + * for the future. See comments in instToCommit(). + */ + unsigned wbCycle; + + int numROBEntries; + int numInsts; + + std::set<InstSeqNum> waitingMemOps; + typedef std::set<InstSeqNum>::iterator MemIt; + int numWaitingMemOps; + unsigned maxOutstandingMemOps; + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + bool fetchHasFault; + + bool switchedOut; + bool switchPending; + + DynInstPtr memBarrier; + + private: + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + + typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; + ReadyInstQueue exeList; + + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + std::list<DynInstPtr> waitingList; + std::list<DynInstPtr> replayList; + std::list<DynInstPtr> writeback; + + int latency; + + int squashLatency; + + bool exactFullStall; + + // number of cycles stalled for D-cache misses +/* Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +*/ + Stats::Vector<> rob_cap_events; + Stats::Vector<> rob_cap_inst_count; + Stats::Vector<> iq_cap_events; + Stats::Vector<> iq_cap_inst_count; + // total number of instructions executed + Stats::Vector<> exe_inst; + Stats::Vector<> exe_swp; + Stats::Vector<> exe_nop; + Stats::Vector<> exe_refs; + Stats::Vector<> exe_loads; + Stats::Vector<> exe_branches; + + Stats::Vector<> issued_ops; + + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsq_forw_loads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> inv_addr_loads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> inv_addr_swpfs; + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsq_blocked_loads; + + Stats::Scalar<> lsqInversion; + + Stats::Vector<> n_issued_dist; + Stats::VectorDistribution<> issue_delay_dist; + + Stats::VectorDistribution<> queue_res_dist; +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; + + Stats::Formula misspec_cnt; + Stats::Formula misspec_ipc; + Stats::Formula issue_rate; + Stats::Formula issue_stores; + Stats::Formula issue_op_rate; + Stats::Formula fu_busy_rate; + Stats::Formula commit_stores; + Stats::Formula commit_ipc; + Stats::Formula commit_ipb; + Stats::Formula lsq_inv_rate; +*/ + Stats::Vector<> writeback_count; + Stats::Vector<> producer_inst; + Stats::Vector<> consumer_inst; + Stats::Vector<> wb_penalized; + + Stats::Formula wb_rate; + Stats::Formula wb_fanout; + Stats::Formula wb_penalized_rate; + + // total number of instructions committed + Stats::Vector<> stat_com_inst; + Stats::Vector<> stat_com_swp; + Stats::Vector<> stat_com_refs; + Stats::Vector<> stat_com_loads; + Stats::Vector<> stat_com_membars; + Stats::Vector<> stat_com_branches; + + Stats::Distribution<> n_committed_dist; + + Stats::Scalar<> commit_eligible_samples; + Stats::Vector<> commit_eligible; + + Stats::Vector<> squashedInsts; + Stats::Vector<> ROBSquashedInsts; + + Stats::Scalar<> ROB_fcount; + Stats::Formula ROB_full_rate; + + Stats::Vector<> ROB_count; // cumulative ROB occupancy + Stats::Formula ROB_occ_rate; + Stats::VectorDistribution<> ROB_occ_dist; + public: + void dumpInsts(); + + Checker<DynInstPtr> *checker; +}; + +template <class Impl> +template <class T> +Fault +LWBackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + return LSQ.read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +LWBackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + return LSQ.write(req, data, store_idx); +} + +#endif // __CPU_OZONE_LW_BACK_END_HH__ diff --git a/src/cpu/ozone/lw_back_end_impl.hh b/src/cpu/ozone/lw_back_end_impl.hh new file mode 100644 index 000000000..ed406d5a3 --- /dev/null +++ b/src/cpu/ozone/lw_back_end_impl.hh @@ -0,0 +1,1695 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/checker/cpu.hh" +#include "cpu/ozone/lw_back_end.hh" +#include "encumbered/cpu/full/op_class.hh" + +template <class Impl> +void +LWBackEnd<Impl>::generateTrapEvent(Tick latency) +{ + DPRINTF(BE, "Generating trap event\n"); + + TrapEvent *trap = new TrapEvent(this); + + trap->schedule(curTick + cpu->cycles(latency)); + + thread->trapPending = true; +} + +template <class Impl> +int +LWBackEnd<Impl>::wakeDependents(DynInstPtr &inst, bool memory_deps) +{ + assert(!inst->isSquashed()); + std::vector<DynInstPtr> &dependents = memory_deps ? inst->getMemDeps() : + inst->getDependents(); + int num_outputs = dependents.size(); + + DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum); + + for (int i = 0; i < num_outputs; i++) { + DynInstPtr dep_inst = dependents[i]; + if (!memory_deps) { + dep_inst->markSrcRegReady(); + } else { + if (!dep_inst->isSquashed()) + dep_inst->markMemInstReady(inst.get()); + } + + DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); + + if (dep_inst->readyToIssue() && dep_inst->isInROB() && + !dep_inst->isNonSpeculative() && !dep_inst->isStoreConditional() && + dep_inst->memDepReady() && !dep_inst->isMemBarrier() && + !dep_inst->isWriteBarrier()) { + DPRINTF(BE, "Adding instruction to exeList [sn:%lli]\n", + dep_inst->seqNum); + exeList.push(dep_inst); + if (dep_inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(dep_inst->iqIt); + waitingInsts--; + dep_inst->iqItValid = false; + assert(waitingInsts >= 0); + } + if (dep_inst->isMemRef()) { + removeWaitingMemOp(dep_inst); + DPRINTF(BE, "Issued a waiting mem op [sn:%lli]\n", + dep_inst->seqNum); + } + } + } + return num_outputs; +} + +template <class Impl> +void +LWBackEnd<Impl>::rescheduleMemInst(DynInstPtr &inst) +{ + replayList.push_front(inst); +} + +template <class Impl> +LWBackEnd<Impl>::TrapEvent::TrapEvent(LWBackEnd<Impl> *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +LWBackEnd<Impl>::TrapEvent::process() +{ + be->trapSquash = true; +} + +template <class Impl> +const char * +LWBackEnd<Impl>::TrapEvent::description() +{ + return "Trap event"; +} + +template <class Impl> +void +LWBackEnd<Impl>::replayMemInst(DynInstPtr &inst) +{ + bool found_inst = false; + while (!replayList.empty()) { + exeList.push(replayList.front()); + if (replayList.front() == inst) { + found_inst = true; + } + replayList.pop_front(); + } + assert(found_inst); +} + +template<class Impl> +LWBackEnd<Impl>::LdWritebackEvent::LdWritebackEvent(DynInstPtr &_inst, + LWBackEnd<Impl> *_be) + : Event(&mainEventQueue), inst(_inst), be(_be), dcacheMiss(false) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +LWBackEnd<Impl>::LdWritebackEvent::process() +{ + DPRINTF(BE, "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 (be->isSwitchedOut()) + return; + + if (dcacheMiss) { + be->removeDcacheMiss(inst); + } + + if (inst->isSquashed()) { + inst = NULL; + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Execute again to copy data to proper place. + inst->completeAcc(); + } + + // Need to insert instruction into queue to commit + be->instToCommit(inst); + + //wroteToTimeBuffer = true; +// iewStage->activityThisCycle(); + + inst = NULL; +} + +template<class Impl> +const char * +LWBackEnd<Impl>::LdWritebackEvent::description() +{ + return "Load writeback event"; +} + + +template <class Impl> +LWBackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent(LWBackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +} + +template <class Impl> +void +LWBackEnd<Impl>::DCacheCompletionEvent::process() +{ +} + +template <class Impl> +const char * +LWBackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "Cache completion event"; +} + +template <class Impl> +LWBackEnd<Impl>::LWBackEnd(Params *params) + : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(5, 5), + trapSquash(false), tcSquash(false), cacheCompletionEvent(this), + dcacheInterface(params->dcacheInterface), width(params->backEndWidth), + exactFullStall(true) +{ + numROBEntries = params->numROBEntries; + numInsts = 0; + numDispatchEntries = 32; + maxOutstandingMemOps = params->maxOutstandingMemOps; + numWaitingMemOps = 0; + waitingInsts = 0; + switchedOut = false; + switchPending = false; + + LSQ.setBE(this); + + // Setup IQ and LSQ with their parameters here. + instsToDispatch = d2i.getWire(-1); + + instsToExecute = i2e.getWire(-1); + + dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width; + issueWidth = params->issueWidth ? params->issueWidth : width; + wbWidth = params->wbWidth ? params->wbWidth : width; + commitWidth = params->commitWidth ? params->commitWidth : width; + + LSQ.init(params, params->LQEntries, params->SQEntries, 0); + + dispatchStatus = Running; +} + +template <class Impl> +std::string +LWBackEnd<Impl>::name() const +{ + return cpu->name() + ".backend"; +} + +template <class Impl> +void +LWBackEnd<Impl>::regStats() +{ + using namespace Stats; + rob_cap_events + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_events") + .desc("number of cycles where ROB cap was active") + .flags(total) + ; + + rob_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_inst") + .desc("number of instructions held up by ROB cap") + .flags(total) + ; + + iq_cap_events + .init(cpu->number_of_threads) + .name(name() +".IQ:cap_events" ) + .desc("number of cycles where IQ cap was active") + .flags(total) + ; + + iq_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".IQ:cap_inst") + .desc("number of instructions held up by IQ cap") + .flags(total) + ; + + + exe_inst + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:count") + .desc("number of insts issued") + .flags(total) + ; + + exe_swp + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:swp") + .desc("number of swp insts issued") + .flags(total) + ; + + exe_nop + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:nop") + .desc("number of nop insts issued") + .flags(total) + ; + + exe_refs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:refs") + .desc("number of memory reference insts issued") + .flags(total) + ; + + exe_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:loads") + .desc("number of load insts issued") + .flags(total) + ; + + exe_branches + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:branches") + .desc("Number of branches issued") + .flags(total) + ; + + issued_ops + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:op_count") + .desc("number of insts issued") + .flags(total) + ; + +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + lsq_forw_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:forw_loads") + .desc("number of loads forwarded via LSQ") + .flags(total) + ; + + inv_addr_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_loads") + .desc("number of invalid-address loads") + .flags(total) + ; + + inv_addr_swpfs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_swpfs") + .desc("number of invalid-address SW prefetches") + .flags(total) + ; + + lsq_blocked_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:blocked_loads") + .desc("number of ready loads not issued due to memory disambiguation") + .flags(total) + ; + + lsqInversion + .name(name() + ".ISSUE:lsq_invert") + .desc("Number of times LSQ instruction issued early") + ; + + n_issued_dist + .init(issueWidth + 1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(total | pdf | dist) + ; + issue_delay_dist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + queue_res_dist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queue_res_dist.subname(i, opClassStrings[i]); + } + + writeback_count + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wb_penalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + + wb_penalized_rate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wb_penalized_rate = wb_penalized / writeback_count; + + wb_fanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wb_fanout = producer_inst / consumer_inst; + + wb_rate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wb_rate = writeback_count / cpu->numCycles; + + stat_com_inst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + stat_com_swp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + stat_com_refs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + stat_com_loads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + stat_com_membars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + stat_com_branches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + n_committed_dist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(pdf) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commit_eligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commit_eligible_samples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; + + squashedInsts + .init(cpu->number_of_threads) + .name(name() + ".COM:squashed_insts") + .desc("Number of instructions removed from inst list") + ; + + ROBSquashedInsts + .init(cpu->number_of_threads) + .name(name() + ".COM:rob_squashed_insts") + .desc("Number of instructions removed from inst list when they reached the head of the ROB") + ; + + ROB_fcount + .name(name() + ".ROB:full_count") + .desc("number of cycles where ROB was full") + ; + + ROB_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:occupancy") + .desc(name() + ".ROB occupancy (cumulative)") + .flags(total) + ; + + ROB_full_rate + .name(name() + ".ROB:full_rate") + .desc("ROB full per cycle") + ; + ROB_full_rate = ROB_fcount / cpu->numCycles; + + ROB_occ_rate + .name(name() + ".ROB:occ_rate") + .desc("ROB occupancy rate") + .flags(total) + ; + ROB_occ_rate = ROB_count / cpu->numCycles; + + ROB_occ_dist + .init(cpu->number_of_threads,0,numROBEntries,2) + .name(name() + ".ROB:occ_dist") + .desc("ROB Occupancy per cycle") + .flags(total | cdf) + ; +} + +template <class Impl> +void +LWBackEnd<Impl>::setCPU(FullCPU *cpu_ptr) +{ + cpu = cpu_ptr; + LSQ.setCPU(cpu_ptr); + checker = cpu->checker; +} + +template <class Impl> +void +LWBackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + toIEW = comm->getWire(0); + fromCommit = comm->getWire(-1); +} + +#if FULL_SYSTEM +template <class Impl> +void +LWBackEnd<Impl>::checkInterrupts() +{ + if (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode(thread->readPC()) && + !trapSquash && + !tcSquash) { + frontEnd->interruptPending = true; + if (robEmpty() && !LSQ.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. + + // Not sure which thread should be the one to interrupt. For now + // always do thread 0. + assert(!thread->inSyscall); + thread->inSyscall = true; + + // CPU will handle implementation of the interrupt. + cpu->processInterrupts(); + + // Now squash or record that I need to squash this cycle. + commitStatus = TrapPending; + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + // Generate trap squash event. + generateTrapEvent(); + + DPRINTF(BE, "Interrupt detected.\n"); + } else { + DPRINTF(BE, "Interrupt must wait for ROB to drain.\n"); + } + } +} + +template <class Impl> +void +LWBackEnd<Impl>::handleFault(Fault &fault, Tick latency) +{ + DPRINTF(BE, "Handling fault!\n"); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + fault->invoke(thread->getTCProxy()); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + commitStatus = TrapPending; + + // Generate trap squash event. + generateTrapEvent(latency); +} +#endif + +template <class Impl> +void +LWBackEnd<Impl>::tick() +{ + DPRINTF(BE, "Ticking back end\n"); + + if (switchPending && robEmpty() && !LSQ.hasStoresToWB()) { + cpu->signalSwitched(); + return; + } + + ROB_count[0]+= numInsts; + + wbCycle = 0; + + // Read in any done instruction information and update the IQ or LSQ. + updateStructures(); + +#if FULL_SYSTEM + checkInterrupts(); + + if (trapSquash) { + assert(!tcSquash); + squashFromTrap(); + } else if (tcSquash) { + squashFromTC(); + } +#endif + + if (dispatchStatus != Blocked) { + dispatchInsts(); + } else { + checkDispatchStatus(); + } + + if (commitStatus != TrapPending) { + executeInsts(); + + commitInsts(); + } + + LSQ.writebackStores(); + + DPRINTF(BE, "Waiting insts: %i, mem ops: %i, ROB entries in use: %i, " + "LSQ loads: %i, LSQ stores: %i\n", + waitingInsts, numWaitingMemOps, numInsts, + LSQ.numLoads(), LSQ.numStores()); + +#ifdef DEBUG + assert(numInsts == instList.size()); + assert(waitingInsts == waitingList.size()); + assert(numWaitingMemOps == waitingMemOps.size()); + assert(!switchedOut); +#endif +} + +template <class Impl> +void +LWBackEnd<Impl>::updateStructures() +{ + if (fromCommit->doneSeqNum) { + LSQ.commitLoads(fromCommit->doneSeqNum); + LSQ.commitStores(fromCommit->doneSeqNum); + } + + if (fromCommit->nonSpecSeqNum) { + if (fromCommit->uncached) { +// LSQ.executeLoad(fromCommit->lqIdx); + } else { +// IQ.scheduleNonSpec( +// fromCommit->nonSpecSeqNum); + } + } +} + +template <class Impl> +void +LWBackEnd<Impl>::addToLSQ(DynInstPtr &inst) +{ + // Do anything LSQ specific here? + LSQ.insert(inst); +} + +template <class Impl> +void +LWBackEnd<Impl>::dispatchInsts() +{ + DPRINTF(BE, "Trying to dispatch instructions.\n"); + + while (numInsts < numROBEntries && + numWaitingMemOps < maxOutstandingMemOps) { + // Get instruction from front of time buffer + DynInstPtr inst = frontEnd->getInst(); + if (!inst) { + break; + } else if (inst->isSquashed()) { + continue; + } + + ++numInsts; + instList.push_front(inst); + + inst->setInROB(); + + DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + for (int i = 0; i < inst->numDestRegs(); ++i) + renameTable[inst->destRegIdx(i)] = inst; + + if (inst->isMemBarrier() || inst->isWriteBarrier()) { + if (memBarrier) { + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + } + memBarrier = inst; + inst->setCanCommit(); + } else if (inst->readyToIssue() && + !inst->isNonSpeculative() && + !inst->isStoreConditional()) { + if (inst->isMemRef()) { + + LSQ.insert(inst); + if (memBarrier) { + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + addWaitingMemOp(inst); + + waitingList.push_front(inst); + inst->iqIt = waitingList.begin(); + inst->iqItValid = true; + waitingInsts++; + } else { + DPRINTF(BE, "Instruction [sn:%lli] ready, addding to " + "exeList.\n", + inst->seqNum); + exeList.push(inst); + } + } else if (inst->isNop()) { + DPRINTF(BE, "Nop encountered [sn:%lli], skipping exeList.\n", + inst->seqNum); + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); + } else { + DPRINTF(BE, "Instruction [sn:%lli] ready, addding to " + "exeList.\n", + inst->seqNum); + exeList.push(inst); + } + } else { + if (inst->isNonSpeculative() || inst->isStoreConditional()) { + inst->setCanCommit(); + DPRINTF(BE, "Adding non speculative instruction\n"); + } + + if (inst->isMemRef()) { + addWaitingMemOp(inst); + LSQ.insert(inst); + if (memBarrier) { + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + } + } + + DPRINTF(BE, "Instruction [sn:%lli] not ready, addding to " + "waitingList.\n", + inst->seqNum); + waitingList.push_front(inst); + inst->iqIt = waitingList.begin(); + inst->iqItValid = true; + waitingInsts++; + } + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. Check here if we don't care about exact stall + // conditions. +/* + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + d2i.advance(); + dispatchStall(); + return; + } +*/ +} + +template <class Impl> +void +LWBackEnd<Impl>::dispatchStall() +{ + dispatchStatus = Blocked; + if (!cpu->decoupledFrontEnd) { + // Tell front end to stall here through a timebuffer, or just tell + // it directly. + } +} + +template <class Impl> +void +LWBackEnd<Impl>::checkDispatchStatus() +{ + DPRINTF(BE, "Checking dispatch status\n"); + assert(dispatchStatus == Blocked); + if (!LSQ.isFull() && !isFull()) { + DPRINTF(BE, "Dispatch no longer blocked\n"); + dispatchStatus = Running; + dispatchInsts(); + } +} + +template <class Impl> +void +LWBackEnd<Impl>::executeInsts() +{ + DPRINTF(BE, "Trying to execute instructions\n"); + + int num_executed = 0; + while (!exeList.empty() && num_executed < issueWidth) { + DynInstPtr inst = exeList.top(); + + DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + // Check if the instruction is squashed; if so then skip it + // and don't count it towards the FU usage. + if (inst->isSquashed()) { + DPRINTF(BE, "Execute: Instruction was squashed.\n"); + + // Not sure how to handle this plus the method of sending # of + // instructions to use. Probably will just have to count it + // towards the bandwidth usage, but not the FU usage. + ++num_executed; + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // 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; + exeList.pop(); + + continue; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + if (dcacheInterface->isBlocked()) { + // Should I move the instruction aside? + DPRINTF(BE, "Execute: dcache is blocked\n"); + break; + } + DPRINTF(BE, "Execute: Initiating access for memory " + "reference.\n"); + + if (inst->isLoad()) { + LSQ.executeLoad(inst); + } else if (inst->isStore()) { + LSQ.executeStore(inst); + if (inst->req && !(inst->req->flags & LOCKED)) { + inst->setExecuted(); + + instToCommit(inst); + } + } else { + panic("Unknown mem type!"); + } + } else { + inst->execute(); + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + ++funcExeInst; + ++num_executed; + + exeList.pop(); + + if (inst->mispredicted()) { + squashDueToBranch(inst); + break; + } else if (LSQ.violation()) { + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = LSQ.getMemDepViolator(); + + DPRINTF(BE, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Squash. + squashDueToMemViolation(inst); + } + } + + issued_ops[0]+= num_executed; + n_issued_dist[num_executed]++; +} + +template<class Impl> +void +LWBackEnd<Impl>::instToCommit(DynInstPtr &inst) +{ + + DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + + if (inst->isExecuted()) { + inst->setResultReady(); + int dependents = wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_inst[0]+= dependents; + } + } + } + + writeback_count[0]++; +} +#if 0 +template <class Impl> +void +LWBackEnd<Impl>::writebackInsts() +{ + int wb_width = wbWidth; + // Using this method I'm not quite sure how to prevent an + // instruction from waking its own dependents multiple times, + // without the guarantee that commit always has enough bandwidth + // to accept all instructions being written back. This guarantee + // might not be too unrealistic. + InstListIt wb_inst_it = writeback.begin(); + InstListIt wb_end_it = writeback.end(); + int inst_num = 0; + int consumer_insts = 0; + + for (; inst_num < wb_width && + wb_inst_it != wb_end_it; inst_num++) { + DynInstPtr inst = (*wb_inst_it); + + // 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()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + inst->setResultReady(); + + if (inst->isExecuted()) { + int dependents = wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_insts+= dependents; + } + } + } + + writeback.erase(wb_inst_it++); + } + LSQ.writebackStores(); + consumer_inst[0]+= consumer_insts; + writeback_count[0]+= inst_num; +} +#endif +template <class Impl> +bool +LWBackEnd<Impl>::commitInst(int inst_num) +{ + // Read instruction from the head of the ROB + DynInstPtr inst = instList.back(); + + // Make sure instruction is valid + assert(inst); + + if (!inst->readyToCommit()) + return false; + + DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + thread->setPC(inst->readPC()); + thread->setNextPC(inst->readNextPC()); + inst->reachedCommit = true; + + // If the instruction is not executed yet, then it is a non-speculative + // or store inst. Signal backwards that it should be executed. + if (!inst->isExecuted()) { + if (inst->isNonSpeculative() || + inst->isStoreConditional() || + inst->isMemBarrier() || + 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 || LSQ.hasStoresToWB()) +#else + if ((inst->isMemBarrier() || inst->isWriteBarrier() || + inst->isQuiesce()) && + LSQ.hasStoresToWB()) +#endif + { + DPRINTF(BE, "Waiting for all stores to writeback.\n"); + return false; + } + + DPRINTF(BE, "Encountered a store or non-speculative " + "instruction at the head of the ROB, PC %#x.\n", + inst->readPC()); + + if (inst->isMemBarrier() || inst->isWriteBarrier()) { + DPRINTF(BE, "Waking dependents on barrier [sn:%lli]\n", + inst->seqNum); + assert(memBarrier); + wakeDependents(inst, true); + if (memBarrier == inst) + memBarrier = NULL; + inst->clearMemDependents(); + } + + // Send back the non-speculative instruction's sequence number. + if (inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(inst->iqIt); + inst->iqItValid = false; + waitingInsts--; + assert(waitingInsts >= 0); + if (inst->isStore()) + removeWaitingMemOp(inst); + } + + exeList.push(inst); + + // Change the instruction so it won't try to commit again until + // it is executed. + inst->clearCanCommit(); + +// ++commitNonSpecStalls; + + return false; + } else if (inst->isLoad()) { + DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n", + inst->seqNum, inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + + // Send back the non-speculative instruction's sequence number. + if (inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(inst->iqIt); + inst->iqItValid = false; + waitingInsts--; + assert(waitingInsts >= 0); + removeWaitingMemOp(inst); + } + replayMemInst(inst); + + inst->clearCanCommit(); + + return false; + } else { + panic("Trying to commit un-executed instruction " + "of unknown type!\n"); + } + } + + // Not handled for now. + assert(!inst->isThreadSync()); + assert(inst->memDepReady()); + // Stores will mark themselves as totally completed as they need + // to wait to writeback to memory. @todo: Hack...attempt to fix + // having the checker be forced to wait until a store completes in + // order to check all of the instructions. If the store at the + // head of the check list misses, but a later store hits, then + // loads in the checker may see the younger store values instead + // of the store they should see. Either the checker needs its own + // memory (annoying to update), its own store buffer (how to tell + // which value is correct?), or something else... + if (!inst->isStore()) { + inst->setCompleted(); + } + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = inst->getFault(); + + // Use checker prior to updating anything due to traps or PC + // based events. + if (checker) { + checker->tick(inst); + } + + if (inst_fault != NoFault) { + DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + + // Instruction is completed as it has a fault. + inst->setCompleted(); + + if (LSQ.hasStoresToWB()) { + DPRINTF(BE, "Stores still in flight, will wait until drained.\n"); + return false; + } else if (inst_num != 0) { + DPRINTF(BE, "Will wait until instruction is head of commit group.\n"); + return false; + } else if (checker && inst->isStore()) { + checker->tick(inst); + } + + thread->setInst( + static_cast<TheISA::MachInst>(inst->staticInst->machInst)); +#if FULL_SYSTEM + handleFault(inst_fault); + return false; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + + int freed_regs = 0; + + for (int i = 0; i < inst->numDestRegs(); ++i) { + DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum); + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + if (inst->traceData) { + inst->traceData->setFetchSeq(inst->seqNum); + inst->traceData->setCPSeq(thread->numInst); + inst->traceData->finalize(); + inst->traceData = NULL; + } + + inst->clearDependents(); + + frontEnd->addFreeRegs(freed_regs); + + instList.pop_back(); + + --numInsts; + ++thread->funcExeInst; + // Maybe move this to where the fault is handled; if the fault is + // handled, don't try to set this myself as the fault will set it. + // If not, then I set thread->PC = thread->nextPC and + // thread->nextPC = thread->nextPC + 4. + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(TheISA::MachInst)); + updateComInstStats(inst); + + // Write the done sequence number here. + toIEW->doneSeqNum = inst->seqNum; + lastCommitCycle = curTick; + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getTCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(BE, "PC skip function event, stopping commit\n"); + tcSquash = true; + return false; + } +#endif + return true; +} + +template <class Impl> +void +LWBackEnd<Impl>::commitInsts() +{ + // Not sure this should be a loop or not. + int inst_num = 0; + while (!instList.empty() && inst_num < commitWidth) { + if (instList.back()->isSquashed()) { + instList.back()->clearDependents(); + instList.pop_back(); + --numInsts; + ROBSquashedInsts[instList.back()->threadNumber]++; + continue; + } + + if (!commitInst(inst_num++)) { + DPRINTF(BE, "Can't commit, Instruction [sn:%lli] PC " + "%#x is head of ROB and not ready\n", + instList.back()->seqNum, instList.back()->readPC()); + --inst_num; + break; + } + } + n_committed_dist.sample(inst_num); +} + +template <class Impl> +void +LWBackEnd<Impl>::squash(const InstSeqNum &sn) +{ + LSQ.squash(sn); + + int freed_regs = 0; + InstListIt waiting_list_end = waitingList.end(); + InstListIt insts_it = waitingList.begin(); + + while (insts_it != waiting_list_end && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + ++insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on waitingList PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + if ((*insts_it)->isMemRef()) { + DPRINTF(BE, "Squashing a waiting mem op [sn:%lli]\n", + (*insts_it)->seqNum); + removeWaitingMemOp((*insts_it)); + } + + waitingList.erase(insts_it++); + waitingInsts--; + } + assert(waitingInsts >= 0); + + insts_it = instList.begin(); + + while (!instList.empty() && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + ++insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + (*insts_it)->removeInROB(); + + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest->seqNum); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + squashedInsts[(*insts_it)->threadNumber]++; + + instList.erase(insts_it++); + --numInsts; + } + + insts_it = waitingList.begin(); + while (!waitingList.empty() && insts_it != waitingList.end()) { + if ((*insts_it)->seqNum < sn) { + ++insts_it; + continue; + } + assert((*insts_it)->isSquashed()); + + waitingList.erase(insts_it++); + waitingInsts--; + } + + while (memBarrier && memBarrier->seqNum > sn) { + DPRINTF(BE, "[sn:%lli] Memory barrier squashed (or previously " + "squashed)\n", memBarrier->seqNum); + memBarrier->clearMemDependents(); + if (memBarrier->memDepReady()) { + DPRINTF(BE, "No previous barrier\n"); + memBarrier = NULL; + } else { + std::list<DynInstPtr> &srcs = memBarrier->getMemSrcs(); + memBarrier = srcs.front(); + srcs.pop_front(); + assert(srcs.empty()); + DPRINTF(BE, "Previous barrier: [sn:%lli]\n", + memBarrier->seqNum); + } + } + + frontEnd->addFreeRegs(freed_regs); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashFromTC() +{ + InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1; + squash(squashed_inst); + frontEnd->squash(squashed_inst, thread->readPC(), + false, false); + frontEnd->interruptPending = false; + + thread->trapPending = false; + thread->inSyscall = false; + tcSquash = false; + commitStatus = Running; +} + +template <class Impl> +void +LWBackEnd<Impl>::squashFromTrap() +{ + InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1; + squash(squashed_inst); + frontEnd->squash(squashed_inst, thread->readPC(), + false, false); + frontEnd->interruptPending = false; + + thread->trapPending = false; + thread->inSyscall = false; + trapSquash = false; + commitStatus = Running; +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + DPRINTF(BE, "Squashing due to branch [sn:%lli], will restart at PC %#x\n", + inst->seqNum, inst->readNextPC()); + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + true, inst->mispredicted()); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToMemViolation(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + DPRINTF(BE, "Squashing due to violation [sn:%lli], will restart at PC %#x\n", + inst->seqNum, inst->readNextPC()); + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + false, inst->mispredicted()); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst) +{ + DPRINTF(IEW, "Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum); + + squash(inst->seqNum - 1); + frontEnd->squash(inst->seqNum - 1, inst->readPC()); +} + +template <class Impl> +void +LWBackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; + fetchHasFault = true; +} + +template <class Impl> +void +LWBackEnd<Impl>::switchOut() +{ + switchPending = true; +} + +template <class Impl> +void +LWBackEnd<Impl>::doSwitchOut() +{ + switchedOut = true; + switchPending = false; + // Need to get rid of all committed, non-speculative state and write it + // to memory/TC. In this case this is stores that have committed and not + // yet written back. + assert(robEmpty()); + assert(!LSQ.hasStoresToWB()); + + LSQ.switchOut(); + + squash(0); +} + +template <class Impl> +void +LWBackEnd<Impl>::takeOverFrom(ThreadContext *old_xc) +{ + switchedOut = false; + xcSquash = false; + trapSquash = false; + + numInsts = 0; + numWaitingMemOps = 0; + waitingMemOps.clear(); + waitingInsts = 0; + switchedOut = false; + dispatchStatus = Running; + commitStatus = Running; + LSQ.takeOverFrom(old_xc); +} + +template <class Impl> +void +LWBackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exe_swp[thread_number]++; + else + exe_inst[thread_number]++; +#else + exe_inst[thread_number]++; +#endif + + // + // Control operations + // + if (inst->isControl()) + exe_branches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exe_refs[thread_number]++; + + if (inst->isLoad()) + exe_loads[thread_number]++; + } +} + +template <class Impl> +void +LWBackEnd<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + // keep an instruction count + thread->numInst++; + thread->numInsts++; + + cpu->numInst++; + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + stat_com_swp[tid]++; + } else { + stat_com_inst[tid]++; + } +#else + stat_com_inst[tid]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + stat_com_branches[tid]++; + + // + // Memory references + // + if (inst->isMemRef()) { + stat_com_refs[tid]++; + + if (inst->isLoad()) { + stat_com_loads[tid]++; + } + } + + if (inst->isMemBarrier()) { + stat_com_membars[tid]++; + } +} + +template <class Impl> +void +LWBackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("ExeList size: %i\n", exeList.size()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.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; + } + + cprintf("Waiting list size: %i\n", waitingList.size()); + + inst_list_it = --(waitingList.end()); + + while (inst_list_it != waitingList.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; + } + + cprintf("waitingMemOps list size: %i\n", waitingMemOps.size()); + + MemIt waiting_it = waitingMemOps.begin(); + + while (waiting_it != waitingMemOps.end()) + { + cprintf("[sn:%lli] ", (*waiting_it)); + waiting_it++; + ++num; + } + cprintf("\n"); +} diff --git a/src/cpu/ozone/lw_lsq.cc b/src/cpu/ozone/lw_lsq.cc new file mode 100644 index 000000000..8674e83a0 --- /dev/null +++ b/src/cpu/ozone/lw_lsq.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/lw_lsq_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class OzoneLWLSQ<OzoneImpl>; + diff --git a/src/cpu/ozone/lw_lsq.hh b/src/cpu/ozone/lw_lsq.hh new file mode 100644 index 000000000..b2924db54 --- /dev/null +++ b/src/cpu/ozone/lw_lsq.hh @@ -0,0 +1,658 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LW_LSQ_HH__ +#define __CPU_OZONE_LW_LSQ_HH__ + +#include <list> +#include <map> +#include <queue> +#include <algorithm> + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +//#include "mem/page_table.hh" +#include "sim/debug.hh" +#include "sim/sim_object.hh" + +//class PageTable; + +/** + * 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 Impl> +class OzoneLWLSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::IssueStruct IssueStruct; + + typedef TheISA::IntReg IntReg; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator LdMapIt; + + private: + class StoreCompletionEvent : public Event { + public: + /** Constructs a store completion event. */ + StoreCompletionEvent(DynInstPtr &inst, BackEnd *be, + Event *wb_event, OzoneLWLSQ *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. */ + DynInstPtr inst; + + BackEnd *be; + /** The writeback event for the store. Needed for store + * conditionals. + */ + public: + Event *wbEvent; + bool miss; + private: + /** The pointer to the LSQ unit that issued the store. */ + OzoneLWLSQ<Impl> *lsqPtr; + }; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + OzoneLWLSQ(); + + /** 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 back-end stage pointer. */ + void setBE(BackEnd *be_ptr) + { be = be_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); + + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** 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.back().canWB && + !storeQueue.back().completed/* && + !dcacheInterface->isBlocked()*/; } + + void switchOut(); + + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + bool switchedOut; + + private: + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + private: + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Pointer to the back-end stage. */ + BackEnd *be; + + MemObject *mem; + + class DcachePort : public Port + { + protected: + FullCPU *cpu; + + public: + DcachePort(const std::string &_name, FullCPU *_cpu) + : Port(_name), cpu(_cpu) + { } + + protected: + virtual Tick recvAtomic(PacketPtr pkt); + + virtual void recvFunctional(PacketPtr pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + virtual bool recvTiming(PacketPtr pkt); + + virtual void recvRetry(); + }; + + /** Pointer to the D-cache. */ + DcachePort dcachePort; + + /** 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), lqIt(NULL) + { } + + /** 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), lqIt(NULL) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The memory request for the store. */ + RequestPtr 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; + + typename std::list<DynInstPtr>::iterator lqIt; + }; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissSwitch + }; + + private: + /** The OzoneLWLSQ thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::list<SQEntry> storeQueue; + /** The load queue. */ + std::list<DynInstPtr> loadQueue; + + typedef typename std::list<SQEntry>::iterator SQIt; + typedef typename std::list<DynInstPtr>::iterator LQIt; + + + struct HashFn { + size_t operator() (const int a) const + { + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; + } + }; + + m5::hash_map<int, SQIt, HashFn> SQItHash; + std::queue<int> SQIndices; + m5::hash_map<int, LQIt, HashFn> LQItHash; + std::queue<int> LQIndices; + + typedef typename m5::hash_map<int, LQIt, HashFn>::iterator LQHashIt; + typedef typename m5::hash_map<int, SQIt, HashFn>::iterator SQHashIt; + // 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; + + int storesToWB; + + /// @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<InstSeqNum> mshrSeqNums; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + // 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. */ + LQIt stallingLoad; + + /** 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 <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (!loadQueue.empty()) { + return loadQueue.back()->seqNum; + } else { + return 0; + } + + } + + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (!storeQueue.empty()) { + return storeQueue.back().inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +OzoneLWLSQ<Impl>::read(RequestPtr 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 + typename m5::hash_map<int, LQIt, HashFn>::iterator + lq_hash_it = LQItHash.find(load_idx); + assert(lq_hash_it != LQItHash.end()); + DynInstPtr inst = (*(*lq_hash_it).second); + + // 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->getFlags() & UNCACHEABLE && + (inst != loadQueue.back() || !inst->reachedCommit)) { + DPRINTF(OzoneLSQ, "[sn:%lli] Uncached load and not head of " + "commit/LSQ!\n", + inst->seqNum); + be->rescheduleMemInst(inst); + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + SQIt sq_it = storeQueue.begin(); + int store_size = 0; + + DPRINTF(OzoneLSQ, "Read called, load idx: %i addr: %#x\n", + load_idx, req->getPaddr()); + + while (sq_it != storeQueue.end() && (*sq_it).inst->seqNum > inst->seqNum) + ++sq_it; + + while (1) { + // End once we've reached the top of the LSQ + if (sq_it == storeQueue.end()) { + break; + } + + assert((*sq_it).inst); + + store_size = (*sq_it).size; + + if (store_size == 0) { + sq_it++; + 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->getVaddr() >= (*sq_it).inst->effAddr; + bool store_has_upper_limit = + (req->getVaddr() + req->getSize()) <= ((*sq_it).inst->effAddr + + store_size); + bool lower_load_has_store_part = + req->getVaddr() < ((*sq_it).inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->getVaddr() + req->getSize()) > (*sq_it).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->getVaddr() & (store_size - 1); + // Assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = (*sq_it).data >> shift_amt; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + + memcpy(inst->memData, &data, req->getSize()); + + DPRINTF(OzoneLSQ, "Forwarding from store [sn:%lli] to load to " + "[sn:%lli] addr %#x, data %#x\n", + (*sq_it).inst->seqNum, inst->seqNum, req->vaddr, *(inst->memData)); +/* + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(inst, + be); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // FIXME - 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 ((*sq_it).completed) { + sq_it++; + break; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + inst->seqNum < + (*stallingLoad)->seqNum)) { + stalled = true; + stallingStoreIsn = (*sq_it).inst->seqNum; + stallingLoad = (*lq_hash_it).second; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + be->rescheduleMemInst(inst); + + DPRINTF(OzoneLSQ, "Load-store forwarding mis-match. " + "Store [sn:%lli] to load addr %#x\n", + (*sq_it).inst->seqNum, req->vaddr); + + return NoFault; + } + sq_it++; + } + + // If there's no forwarding case, then go access memory + DPRINTF(OzoneLSQ, "Doing functional access for inst PC %#x\n", + inst->readPC()); + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + + ++usedPorts; + + DPRINTF(OzoneLSQ, "Doing timing access for inst PC %#x\n", + inst->readPC()); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + // if we have a cache, do cache access too + if (!dcachePort.sendTiming(data_pkt)) { + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < inst->seqNum) + return NoFault; + + // Record that the load was blocked due to memory. This + // load will squash all instructions after it, be + // refetched, and re-executed. + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + if (data_pkt->result != Packet::Success) { + DPRINTF(OzoneLSQ, "OzoneLSQ: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + } else { + DPRINTF(OzoneLSQ, "OzoneLSQ: D-cache hit!\n"); + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + inst->seqNum); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +OzoneLWLSQ<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + + SQIt sq_it = (*sq_hash_it).second; + assert((*sq_it).inst); + + DPRINTF(OzoneLSQ, "Doing write to store idx %i, addr %#x data %#x" + " | [sn:%lli]\n", + store_idx, req->getPaddr(), data, (*sq_it).inst->seqNum); + + (*sq_it).req = req; + (*sq_it).size = sizeof(T); + (*sq_it).data = data; +/* + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&(*sq_it).data, req->size); +*/ + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +#endif // __CPU_OZONE_LW_LSQ_HH__ diff --git a/src/cpu/ozone/lw_lsq_impl.hh b/src/cpu/ozone/lw_lsq_impl.hh new file mode 100644 index 000000000..05db3028a --- /dev/null +++ b/src/cpu/ozone/lw_lsq_impl.hh @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "arch/isa_traits.hh" +#include "base/str.hh" +#include "cpu/ozone/lw_lsq.hh" +#include "cpu/checker/cpu.hh" + +template <class Impl> +OzoneLWLSQ<Impl>::StoreCompletionEvent::StoreCompletionEvent(DynInstPtr &_inst, + BackEnd *_be, + Event *wb_event, + OzoneLWLSQ<Impl> *lsq_ptr) + : Event(&mainEventQueue), + inst(_inst), + be(_be), + wbEvent(wb_event), + miss(false), + lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::StoreCompletionEvent::process() +{ + DPRINTF(OzoneLSQ, "Cache miss complete for store [sn:%lli]\n", + inst->seqNum); + + //lsqPtr->removeMSHR(lsqPtr->storeQueue[storeIdx].inst->seqNum); + +// lsqPtr->cpu->wakeCPU(); + if (lsqPtr->isSwitchedOut()) { + if (wbEvent) + delete wbEvent; + + return; + } + + if (wbEvent) { + wbEvent->process(); + delete wbEvent; + } + + lsqPtr->completeStore(inst->sqIdx); + if (miss) + be->removeDcacheMiss(inst); +} + +template <class Impl> +const char * +OzoneLWLSQ<Impl>::StoreCompletionEvent::description() +{ + return "LSQ store completion event"; +} + +template <class Impl> +OzoneLWLSQ<Impl>::OzoneLWLSQ() + : loads(0), stores(0), storesToWB(0), stalled(false), isLoadBlocked(false), + loadBlockedHandled(false) +{ +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) +{ + DPRINTF(OzoneLSQ, "Creating OzoneLWLSQ%i object.\n",id); + + lsqID = id; + + LQEntries = maxLQEntries; + SQEntries = maxSQEntries; + + for (int i = 0; i < LQEntries * 2; i++) { + LQIndices.push(i); + SQIndices.push(i); + } + + usedPorts = 0; + cachePorts = params->cachePorts; + + dcacheInterface = params->dcacheInterface; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template<class Impl> +std::string +OzoneLWLSQ<Impl>::name() const +{ + return "lsqunit"; +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::clearSQ() +{ + storeQueue.clear(); +} +/* +template<class Impl> +void +OzoneLWLSQ<Impl>::setPageTable(PageTable *pt_ptr) +{ + DPRINTF(OzoneLSQ, "Setting the page table pointer.\n"); + pTable = pt_ptr; +} +*/ +template<class Impl> +void +OzoneLWLSQ<Impl>::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::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); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert(loads < LQEntries * 2); + assert(!LQIndices.empty()); + int load_index = LQIndices.front(); + LQIndices.pop(); + + DPRINTF(OzoneLSQ, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), load_index, load_inst->seqNum); + + load_inst->lqIdx = load_index; + + loadQueue.push_front(load_inst); + LQItHash[load_index] = loadQueue.begin(); + + ++loads; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert(stores - storesToWB < SQEntries); + + assert(!SQIndices.empty()); + int store_index = SQIndices.front(); + SQIndices.pop(); + + DPRINTF(OzoneLSQ, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), store_index, store_inst->seqNum); + + store_inst->sqIdx = store_index; + SQEntry entry(store_inst); + if (loadQueue.empty()) { + entry.lqIt = loadQueue.end(); + } else { + entry.lqIt = loadQueue.begin(); + } + storeQueue.push_front(entry); + + SQItHash[store_index] = storeQueue.begin(); + + ++stores; +} + +template <class Impl> +typename Impl::DynInstPtr +OzoneLWLSQ<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +OzoneLWLSQ<Impl>::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 <class Impl> +int +OzoneLWLSQ<Impl>::numLoadsReady() +{ + int retval = 0; + LQIt lq_it = loadQueue.begin(); + LQIt end_it = loadQueue.end(); + + while (lq_it != end_it) { + if ((*lq_it)->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +template <class Impl> +Fault +OzoneLWLSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(OzoneLSQ, "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(); + + // Might want to make sure that I'm not overwriting a previously faulting + // instruction that hasn't been checked yet. + // Actually probably want the oldest faulting load + if (load_fault != NoFault) { + DPRINTF(OzoneLSQ, "Load [sn:%lli] has a fault\n", inst->seqNum); + // Maybe just set it as can commit here, although that might cause + // some other problems with sending traps to the ROB too quickly. + be->instToCommit(inst); +// iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +OzoneLWLSQ<Impl>::executeStore(DynInstPtr &store_inst) +{ + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + DPRINTF(OzoneLSQ, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + SQIt sq_it = (*sq_hash_it).second; + + Fault store_fault = store_inst->initiateAcc(); + + // Store size should now be available. Use it to get proper offset for + // addr comparisons. + int size = (*sq_it).size; + + if (size == 0) { + DPRINTF(OzoneLSQ,"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->isStoreConditional()) { + // Store conditionals need to set themselves as able to + // writeback if we haven't had a fault by here. + (*sq_it).canWB = true; + + ++storesToWB; + DPRINTF(OzoneLSQ, "Nonspeculative store! storesToWB:%i\n", + storesToWB); + } + } + + LQIt lq_it = --(loadQueue.end()); + + if (!memDepViolator) { + while (lq_it != loadQueue.end()) { + if ((*lq_it)->seqNum < store_inst->seqNum) { + lq_it--; + continue; + } + // 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 (((*lq_it)->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 = (*lq_it); + + return TheISA::genMachineCheckFault(); + } + + lq_it--; + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitLoad() +{ + assert(!loadQueue.empty()); + + DPRINTF(OzoneLSQ, "[sn:%lli] Committing head load instruction, PC %#x\n", + loadQueue.back()->seqNum, loadQueue.back()->readPC()); + + LQIndices.push(loadQueue.back()->lqIdx); + LQItHash.erase(loadQueue.back()->lqIdx); + + loadQueue.pop_back(); + + --loads; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || !loadQueue.empty()); + + while (loads != 0 && + loadQueue.back()->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || !storeQueue.empty()); + + SQIt sq_it = --(storeQueue.end()); + while (!storeQueue.empty() && sq_it != storeQueue.end()) { + assert((*sq_it).inst); + if (!(*sq_it).canWB) { + if ((*sq_it).inst->seqNum > youngest_inst) { + break; + } + ++storesToWB; + + DPRINTF(OzoneLSQ, "Marking store as able to write back, PC " + "%#x [sn:%lli], storesToWB:%i\n", + (*sq_it).inst->readPC(), + (*sq_it).inst->seqNum, + storesToWB); + + (*sq_it).canWB = true; + } + + sq_it--; + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::writebackStores() +{ + SQIt sq_it = --(storeQueue.end()); + while (storesToWB > 0 && + sq_it != storeQueue.end() && + (*sq_it).inst && + (*sq_it).canWB && + usedPorts < cachePorts) { + + DynInstPtr inst = (*sq_it).inst; + + if ((*sq_it).size == 0 && !(*sq_it).completed) { + sq_it--; + completeStore(inst->sqIdx); + + continue; + } + + if (inst->isDataPrefetch() || (*sq_it).committed) { + sq_it--; + continue; + } + + if (dcacheInterface && dcacheInterface->isBlocked()) { + DPRINTF(OzoneLSQ, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + ++usedPorts; + + assert((*sq_it).req); + assert(!(*sq_it).committed); + + (*sq_it).committed = true; + + MemReqPtr req = (*sq_it).req; + + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + + switch((*sq_it).size) { + case 1: + cpu->write(req, (uint8_t &)(*sq_it).data); + break; + case 2: + cpu->write(req, (uint16_t &)(*sq_it).data); + break; + case 4: + cpu->write(req, (uint32_t &)(*sq_it).data); + break; + case 8: + cpu->write(req, (uint64_t &)(*sq_it).data); + break; + default: + panic("Unexpected store size!\n"); + } + if (!(req->flags & LOCKED)) { + (*sq_it).inst->setCompleted(); + if (cpu->checker) { + cpu->checker->tick((*sq_it).inst); + } + } + + DPRINTF(OzoneLSQ, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + inst->sqIdx,inst->readPC(), + req->paddr, *(req->data), + inst->seqNum); + + if (dcacheInterface) { + assert(!req->completionEvent); + StoreCompletionEvent *store_event = new + StoreCompletionEvent(inst, be, NULL, this); + req->completionEvent = store_event; + + MemAccessResult result = dcacheInterface->access(req); + + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + store_event->miss = true; + typename BackEnd::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + wb = new typename BackEnd::LdWritebackEvent(inst, + be); + store_event->wbEvent = wb; + } + + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + +// DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", +// inst->seqNum); + + be->addDcacheMiss(inst); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + // Increment stat here or something + + sq_it--; + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit on idx:%i !\n", + inst->sqIdx); + +// DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", +// inst->seqNum); + + if (req->flags & LOCKED) { + // Stx_C does not generate a system port + // transaction in the 21264, but that might be + // hard to accomplish in this model. + + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(inst, + be); + store_event->wbEvent = wb; + } + sq_it--; + } + } else { + panic("Must HAVE DCACHE!!!!!\n"); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(OzoneLSQ, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n",squashed_num,loads,stores); + + + LQIt lq_it = loadQueue.begin(); + + while (loads != 0 && (*lq_it)->seqNum > squashed_num) { + assert(!loadQueue.empty()); + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + (*lq_it)->readPC(), + (*lq_it)->seqNum); + + if (isStalled() && lq_it == stallingLoad) { + stalled = false; + stallingStoreIsn = 0; + stallingLoad = NULL; + } + + --loads; + + // Inefficient! + LQHashIt lq_hash_it = LQItHash.find((*lq_it)->lqIdx); + assert(lq_hash_it != LQItHash.end()); + LQItHash.erase(lq_hash_it); + LQIndices.push((*lq_it)->lqIdx); + loadQueue.erase(lq_it++); + } + + if (isLoadBlocked) { + if (squashed_num < blockedLoadSeqNum) { + isLoadBlocked = false; + loadBlockedHandled = false; + blockedLoadSeqNum = 0; + } + } + + SQIt sq_it = storeQueue.begin(); + + while (stores != 0 && (*sq_it).inst->seqNum > squashed_num) { + assert(!storeQueue.empty()); + + if ((*sq_it).canWB) { + break; + } + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Store Instruction PC %#x idx:%i squashed [sn:%lli]\n", + (*sq_it).inst->readPC(), (*sq_it).inst->sqIdx, + (*sq_it).inst->seqNum); + + // I don't think this can happen. It should have been cleared by the + // stalling load. + if (isStalled() && + (*sq_it).inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + + SQHashIt sq_hash_it = SQItHash.find((*sq_it).inst->sqIdx); + assert(sq_hash_it != SQItHash.end()); + SQItHash.erase(sq_hash_it); + SQIndices.push((*sq_it).inst->sqIdx); + (*sq_it).inst = NULL; + (*sq_it).canWB = 0; + + if ((*sq_it).req) { + assert(!(*sq_it).req->completionEvent); + } + (*sq_it).req = NULL; + --stores; + storeQueue.erase(sq_it++); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + LQIt lq_it = --(loadQueue.end()); + + while (lq_it != loadQueue.end() && (*lq_it)) { + cprintf("[sn:%lli] %#x ", (*lq_it)->seqNum, + (*lq_it)->readPC()); + + lq_it--; + } + + cprintf("\nStore queue size: %i\n", stores); + cprintf("Store queue: "); + + SQIt sq_it = --(storeQueue.end()); + + while (sq_it != storeQueue.end() && (*sq_it).inst) { + cprintf("[sn:%lli]\nPC:%#x\nSize:%i\nCommitted:%i\nCompleted:%i\ncanWB:%i\n", + (*sq_it).inst->seqNum, + (*sq_it).inst->readPC(), + (*sq_it).size, + (*sq_it).committed, + (*sq_it).completed, + (*sq_it).canWB); + + sq_it--; + } + + cprintf("\n"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::completeStore(int store_idx) +{ + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + SQIt sq_it = (*sq_hash_it).second; + + assert((*sq_it).inst); + (*sq_it).completed = true; + DynInstPtr inst = (*sq_it).inst; + + --storesToWB; + + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + DPRINTF(OzoneLSQ, "Completing store idx:%i [sn:%lli], storesToWB:%i\n", + inst->sqIdx, inst->seqNum, storesToWB); + + assert(!storeQueue.empty()); + SQItHash.erase(sq_hash_it); + SQIndices.push(inst->sqIdx); + storeQueue.erase(sq_it); + --stores; + + inst->setCompleted(); + if (cpu->checker) { + cpu->checker->tick(inst); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::switchOut() +{ + assert(storesToWB == 0); + switchedOut = true; + SQIt sq_it = --(storeQueue.end()); + while (storesToWB > 0 && + sq_it != storeQueue.end() && + (*sq_it).inst && + (*sq_it).canWB) { + + DynInstPtr inst = (*sq_it).inst; + + if ((*sq_it).size == 0 && !(*sq_it).completed) { + sq_it--; + continue; + } + + // Store conditionals don't complete until *after* they have written + // back. If it's here and not yet sent to memory, then don't bother + // as it's not part of committed state. + if (inst->isDataPrefetch() || (*sq_it).committed) { + sq_it--; + continue; + } else if ((*sq_it).req->flags & LOCKED) { + sq_it--; + assert(!(*sq_it).canWB || + ((*sq_it).canWB && (*sq_it).req->flags & LOCKED)); + continue; + } + + assert((*sq_it).req); + assert(!(*sq_it).committed); + + MemReqPtr req = (*sq_it).req; + (*sq_it).committed = true; + + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&(*sq_it).data, req->size); + + DPRINTF(OzoneLSQ, "Switching out : Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x directly to memory [sn:%lli]\n", + inst->sqIdx,inst->readPC(), + req->paddr, *(req->data), + inst->seqNum); + + switch((*sq_it).size) { + case 1: + cpu->write(req, (uint8_t &)(*sq_it).data); + break; + case 2: + cpu->write(req, (uint16_t &)(*sq_it).data); + break; + case 4: + cpu->write(req, (uint32_t &)(*sq_it).data); + break; + case 8: + cpu->write(req, (uint64_t &)(*sq_it).data); + break; + default: + panic("Unexpected store size!\n"); + } + } + + // Clear the queue to free up resources + storeQueue.clear(); + loadQueue.clear(); + loads = stores = storesToWB = 0; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + // Clear out any old state. May be redundant if this is the first time + // the CPU is being used. + stalled = false; + isLoadBlocked = false; + loadBlockedHandled = false; + switchedOut = false; + + // Could do simple checks here to see if indices are on twice + while (!LQIndices.empty()) + LQIndices.pop(); + while (!SQIndices.empty()) + SQIndices.pop(); + + for (int i = 0; i < LQEntries * 2; i++) { + LQIndices.push(i); + SQIndices.push(i); + } + + usedPorts = 0; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} diff --git a/src/cpu/ozone/null_predictor.hh b/src/cpu/ozone/null_predictor.hh new file mode 100644 index 000000000..a98c89d69 --- /dev/null +++ b/src/cpu/ozone/null_predictor.hh @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_NULL_PREDICTOR_HH__ +#define __CPU_OZONE_NULL_PREDICTOR_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/inst_seq.hh" + +template <class Impl> +class NullPredictor +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + + NullPredictor(Params *p) { } + + struct BPredInfo { + BPredInfo() + : PC(0), nextPC(0) + { } + + BPredInfo(const Addr &pc, const Addr &next_pc) + : PC(pc), nextPC(next_pc) + { } + + Addr PC; + Addr nextPC; + }; + + BPredInfo lookup(Addr &PC) { return BPredInfo(PC, PC+4); } + + void undo(BPredInfo &bp_info) { return; } + + /** + * 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) + { return false; } + + /** + * 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) { } + + /** + * 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, unsigned tid) + { } + +}; + +#endif // __CPU_OZONE_NULL_PREDICTOR_HH__ diff --git a/src/cpu/ozone/ozone_impl.hh b/src/cpu/ozone/ozone_impl.hh new file mode 100644 index 000000000..e977d06a9 --- /dev/null +++ b/src/cpu/ozone/ozone_impl.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_OZONE_IMPL_HH__ +#define __CPU_OZONE_OZONE_IMPL_HH__ + +#include "arch/alpha/isa_traits.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/front_end.hh" +#include "cpu/ozone/inst_queue.hh" +#include "cpu/ozone/lw_lsq.hh" +#include "cpu/ozone/lw_back_end.hh" +#include "cpu/ozone/null_predictor.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/simple_params.hh" + +template <class Impl> +class OzoneCPU; + +template <class Impl> +class OzoneDynInst; + +struct OzoneImpl { + typedef SimpleParams Params; + typedef OzoneCPU<OzoneImpl> OzoneCPU; + typedef OzoneCPU FullCPU; + + // Would like to put these into their own area. +// typedef NullPredictor BranchPred; + typedef BPredUnit<OzoneImpl> BranchPred; + typedef FrontEnd<OzoneImpl> FrontEnd; + // Will need IQ, LSQ eventually + typedef LWBackEnd<OzoneImpl> BackEnd; + + typedef InstQueue<OzoneImpl> InstQueue; + typedef OzoneLWLSQ<OzoneImpl> LdstQueue; + + typedef OzoneDynInst<OzoneImpl> DynInst; + typedef RefCountingPtr<DynInst> DynInstPtr; + + typedef uint64_t IssueStruct; + + enum { + MaxThreads = 1 + }; +}; + +#endif // __CPU_OZONE_OZONE_IMPL_HH__ diff --git a/src/cpu/ozone/rename_table.cc b/src/cpu/ozone/rename_table.cc new file mode 100644 index 000000000..b0a36afbe --- /dev/null +++ b/src/cpu/ozone/rename_table.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/rename_table_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +template class RenameTable<OzoneImpl>; +template class RenameTable<SimpleImpl>; diff --git a/src/cpu/ozone/rename_table.hh b/src/cpu/ozone/rename_table.hh new file mode 100644 index 000000000..0b67d9635 --- /dev/null +++ b/src/cpu/ozone/rename_table.hh @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_RENAME_TABLE_HH__ +#define __CPU_OZONE_RENAME_TABLE_HH__ + +#include "arch/isa_traits.hh" + +/** Rename table that holds the rename of each architectural register to + * producing DynInst. Needs to support copying from one table to another. + */ + +template <class Impl> +class RenameTable { + public: + typedef typename Impl::DynInstPtr DynInstPtr; + + RenameTable(); + + void copyFrom(const RenameTable<Impl> &table_to_copy); + + DynInstPtr &operator [] (int index) + { return table[index]; } + + DynInstPtr table[TheISA::TotalNumRegs]; +}; + +#endif // __CPU_OZONE_RENAME_TABLE_HH__ diff --git a/src/cpu/ozone/rename_table_impl.hh b/src/cpu/ozone/rename_table_impl.hh new file mode 100644 index 000000000..67bab7337 --- /dev/null +++ b/src/cpu/ozone/rename_table_impl.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include <cstdlib> // Not really sure what to include to get NULL +#include "cpu/ozone/rename_table.hh" + +template <class Impl> +RenameTable<Impl>::RenameTable() +{ + // Actually should set these to dummy dyn insts that have the initial value + // and force their values to be initialized. This keeps everything the + // same. + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + table[i] = NULL; + } +} + +template <class Impl> +void +RenameTable<Impl>::copyFrom(const RenameTable<Impl> &table_to_copy) +{ + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + table[i] = table_to_copy.table[i]; + } +} diff --git a/src/cpu/ozone/simple_impl.hh b/src/cpu/ozone/simple_impl.hh new file mode 100644 index 000000000..3199d8d8a --- /dev/null +++ b/src/cpu/ozone/simple_impl.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_SIMPLE_IMPL_HH__ +#define __CPU_OZONE_SIMPLE_IMPL_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/ozone/front_end.hh" +#include "cpu/ozone/inorder_back_end.hh" +#include "cpu/ozone/null_predictor.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/simple_params.hh" + +//template <class Impl> +//class OzoneCPU; + +template <class Impl> +class OzoneDynInst; + +struct SimpleImpl { + typedef SimpleParams Params; + typedef OzoneCPU<SimpleImpl> OzoneCPU; + typedef OzoneCPU FullCPU; + + // Would like to put these into their own area. +// typedef NullPredictor BranchPred; + typedef BPredUnit<SimpleImpl> BranchPred; + typedef FrontEnd<SimpleImpl> FrontEnd; + // Will need IQ, LSQ eventually + typedef InorderBackEnd<SimpleImpl> BackEnd; + + typedef OzoneDynInst<SimpleImpl> DynInst; + typedef RefCountingPtr<DynInst> DynInstPtr; + + typedef uint64_t IssueStruct; + + enum { + MaxThreads = 1 + }; +}; + +#endif // __CPU_OZONE_SIMPLE_IMPL_HH__ diff --git a/src/cpu/ozone/simple_params.hh b/src/cpu/ozone/simple_params.hh new file mode 100644 index 000000000..13eb05e77 --- /dev/null +++ b/src/cpu/ozone/simple_params.hh @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_SIMPLE_PARAMS_HH__ +#define __CPU_OZONE_SIMPLE_PARAMS_HH__ + +#include "cpu/ozone/cpu.hh" + +//Forward declarations +class AlphaDTB; +class AlphaITB; +class FUPool; +class FunctionalMemory; +class MemInterface; +class PageTable; +class Process; +class System; + +/** + * This file defines the parameters that will be used for the OzoneCPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ + +class SimpleParams : public BaseCPU::Params +{ + public: + +#if FULL_SYSTEM + AlphaITB *itb; AlphaDTB *dtb; +#else + std::vector<Process *> workload; +#endif // FULL_SYSTEM + + //Page Table + PageTable *pTable; + + FunctionalMemory *mem; + + // + // Caches + // + MemInterface *icacheInterface; + MemInterface *dcacheInterface; + + unsigned cachePorts; + unsigned width; + unsigned frontEndWidth; + unsigned backEndWidth; + unsigned backEndSquashLatency; + unsigned backEndLatency; + unsigned maxInstBufferSize; + unsigned numPhysicalRegs; + unsigned maxOutstandingMemOps; + // + // Fetch + // + unsigned decodeToFetchDelay; + unsigned renameToFetchDelay; + unsigned iewToFetchDelay; + unsigned commitToFetchDelay; + unsigned fetchWidth; + + // + // Decode + // + unsigned renameToDecodeDelay; + unsigned iewToDecodeDelay; + unsigned commitToDecodeDelay; + unsigned fetchToDecodeDelay; + unsigned decodeWidth; + + // + // Rename + // + unsigned iewToRenameDelay; + unsigned commitToRenameDelay; + unsigned decodeToRenameDelay; + unsigned renameWidth; + + // + // IEW + // + unsigned commitToIEWDelay; + unsigned renameToIEWDelay; + unsigned issueToExecuteDelay; + unsigned issueWidth; + unsigned executeWidth; + unsigned executeIntWidth; + unsigned executeFloatWidth; + unsigned executeBranchWidth; + unsigned executeMemoryWidth; + FUPool *fuPool; + + // + // Commit + // + unsigned iewToCommitDelay; + unsigned renameToROBDelay; + unsigned commitWidth; + unsigned squashWidth; + + // + // Branch predictor (BP & BTB) + // + std::string predType; + unsigned localPredictorSize; + unsigned localCtrBits; + unsigned localHistoryTableSize; + unsigned localHistoryBits; + unsigned globalPredictorSize; + unsigned globalCtrBits; + unsigned globalHistoryBits; + unsigned choicePredictorSize; + unsigned choiceCtrBits; + + unsigned BTBEntries; + unsigned BTBTagSize; + + unsigned RASSize; + + // + // Load store queue + // + unsigned LQEntries; + unsigned SQEntries; + + // + // Memory dependence + // + unsigned SSITSize; + unsigned LFSTSize; + + // + // Miscellaneous + // + unsigned numPhysIntRegs; + unsigned numPhysFloatRegs; + unsigned numIQEntries; + unsigned numROBEntries; + + bool decoupledFrontEnd; + int dispatchWidth; + int wbWidth; + + //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; +}; + +#endif // __CPU_OZONE_SIMPLE_PARAMS_HH__ diff --git a/src/cpu/ozone/thread_state.hh b/src/cpu/ozone/thread_state.hh new file mode 100644 index 000000000..299878c29 --- /dev/null +++ b/src/cpu/ozone/thread_state.hh @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_THREAD_STATE_HH__ +#define __CPU_OZONE_THREAD_STATE_HH__ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" +#include "sim/process.hh" + +class Event; +//class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class Process; +class FunctionalMemory; +#endif + +// 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 SimpleThread. +// 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 TC proxy within it? +template <class Impl> +struct OzoneThreadState : public ThreadState { + typedef typename ThreadContext::Status Status; + typedef typename Impl::FullCPU FullCPU; + typedef TheISA::MiscReg MiscReg; + +#if FULL_SYSTEM + OzoneThreadState(FullCPU *_cpu, int _thread_num) + : ThreadState(-1, _thread_num), + inSyscall(0), trapPending(0) + { + memset(®s, 0, sizeof(TheISA::RegFile)); + } +#else + OzoneThreadState(FullCPU *_cpu, int _thread_num, Process *_process, int _asid) + : ThreadState(-1, _thread_num, NULL, _process, _asid), + cpu(_cpu), inSyscall(0), trapPending(0) + { + memset(®s, 0, sizeof(TheISA::RegFile)); + } + + OzoneThreadState(FullCPU *_cpu, int _thread_num, + int _asid) + : ThreadState(-1, _thread_num, NULL, NULL, _asid), + cpu(_cpu), inSyscall(0), trapPending(0) + { + memset(®s, 0, sizeof(TheISA::RegFile)); + } +#endif + + RenameTable<Impl> renameTable; + + Addr PC; + + Addr nextPC; + + TheISA::RegFile regs; + + typename Impl::FullCPU *cpu; + + bool inSyscall; + + bool trapPending; + + ThreadContext *tc; + + ThreadContext *getTC() { return tc; } + +#if !FULL_SYSTEM + Fault translateInstReq(Request *req) + { + return process->pTable->translate(req); + } + Fault translateDataReadReq(Request *req) + { + return process->pTable->translate(req); + } + Fault translateDataWriteReq(Request *req) + { + return process->pTable->translate(req); + } +#else + Fault translateInstReq(Request *req) + { + return cpu->itb->translate(req); + } + + Fault translateDataReadReq(Request *req) + { + return cpu->dtb->translate(req, false); + } + + Fault translateDataWriteReq(Request *req) + { + return cpu->dtb->translate(req, true); + } +#endif + + MiscReg readMiscReg(int misc_reg) + { + return regs.readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return regs.readMiscRegWithEffect(misc_reg, fault, tc); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + return regs.setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return regs.setMiscRegWithEffect(misc_reg, val, tc); + } + + uint64_t readPC() + { return PC; } + + void setPC(uint64_t val) + { PC = val; } + + uint64_t readNextPC() + { return nextPC; } + + void setNextPC(uint64_t val) + { nextPC = val; } +}; + +#endif // __CPU_OZONE_THREAD_STATE_HH__ diff --git a/src/cpu/pc_event.cc b/src/cpu/pc_event.cc index 050bf1a88..fca357fe3 100644 --- a/src/cpu/pc_event.cc +++ b/src/cpu/pc_event.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <algorithm> @@ -34,7 +37,7 @@ #include "base/trace.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/pc_event.hh" #include "sim/debug.hh" #include "sim/root.hh" @@ -78,9 +81,9 @@ PCEventQueue::schedule(PCEvent *event) } bool -PCEventQueue::doService(ExecContext *xc) +PCEventQueue::doService(ThreadContext *tc) { - Addr pc = xc->readPC() & ~0x3; + Addr pc = tc->readPC() & ~0x3; int serviced = 0; range_t range = equal_range(pc); for (iterator i = range.first; i != range.second; ++i) { @@ -88,13 +91,13 @@ PCEventQueue::doService(ExecContext *xc) // another event. This for example, prevents two invocations // of the SkipFuncEvent. Maybe we should have separate PC // event queues for each processor? - if (pc != (xc->readPC() & ~0x3)) + if (pc != (tc->readPC() & ~0x3)) continue; DPRINTF(PCEvent, "PC based event serviced at %#x: %s\n", (*i)->pc(), (*i)->descr()); - (*i)->process(xc); + (*i)->process(tc); ++serviced; } @@ -125,9 +128,9 @@ BreakPCEvent::BreakPCEvent(PCEventQueue *q, const std::string &desc, Addr addr, } void -BreakPCEvent::process(ExecContext *xc) +BreakPCEvent::process(ThreadContext *tc) { - StringWrap name(xc->getCpuPtr()->name() + ".break_event"); + StringWrap name(tc->getCpuPtr()->name() + ".break_event"); DPRINTFN("break event %s triggered\n", descr()); debug_break(); if (remove) diff --git a/src/cpu/pc_event.hh b/src/cpu/pc_event.hh index 32b7f3ef5..6b048b2c2 100644 --- a/src/cpu/pc_event.hh +++ b/src/cpu/pc_event.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __PC_EVENT_HH__ @@ -33,7 +36,7 @@ #include "base/misc.hh" -class ExecContext; +class ThreadContext; class PCEventQueue; class PCEvent @@ -55,7 +58,7 @@ class PCEvent Addr pc() const { return evpc; } bool remove(); - virtual void process(ExecContext *xc) = 0; + virtual void process(ThreadContext *tc) = 0; }; class PCEventQueue @@ -87,7 +90,7 @@ class PCEventQueue protected: map_t pc_map; - bool doService(ExecContext *xc); + bool doService(ThreadContext *tc); public: PCEventQueue(); @@ -95,12 +98,12 @@ class PCEventQueue bool remove(PCEvent *event); bool schedule(PCEvent *event); - bool service(ExecContext *xc) + bool service(ThreadContext *tc) { if (pc_map.empty()) return false; - return doService(xc); + return doService(tc); } range_t equal_range(Addr pc); @@ -134,7 +137,7 @@ class BreakPCEvent : public PCEvent public: BreakPCEvent(PCEventQueue *q, const std::string &desc, Addr addr, bool del = false); - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; #endif // __PC_EVENT_HH__ diff --git a/src/cpu/profile.cc b/src/cpu/profile.cc index fe3458b61..4f04615e9 100644 --- a/src/cpu/profile.cc +++ b/src/cpu/profile.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <string> @@ -34,7 +36,7 @@ #include "base/trace.hh" #include "base/loader/symtab.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/profile.hh" using namespace std; @@ -118,7 +120,7 @@ FunctionProfile::clear() } void -FunctionProfile::dump(ExecContext *xc, ostream &os) const +FunctionProfile::dump(ThreadContext *tc, ostream &os) const { ccprintf(os, ">>>PC data\n"); map<Addr, Counter>::const_iterator i, end = pc_count.end(); diff --git a/src/cpu/profile.hh b/src/cpu/profile.hh index d55c9eec9..7f9625241 100644 --- a/src/cpu/profile.hh +++ b/src/cpu/profile.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __CPU_PROFILE_HH__ @@ -35,7 +37,7 @@ #include "sim/host.hh" #include "arch/stacktrace.hh" -class ExecContext; +class ThreadContext; class ProfileNode { @@ -70,17 +72,17 @@ class FunctionProfile FunctionProfile(const SymbolTable *symtab); ~FunctionProfile(); - ProfileNode *consume(ExecContext *xc, StaticInstPtr inst); + ProfileNode *consume(ThreadContext *tc, StaticInstPtr inst); ProfileNode *consume(const std::vector<Addr> &stack); void clear(); - void dump(ExecContext *xc, std::ostream &out) const; + void dump(ThreadContext *tc, std::ostream &out) const; void sample(ProfileNode *node, Addr pc); }; inline ProfileNode * -FunctionProfile::consume(ExecContext *xc, StaticInstPtr inst) +FunctionProfile::consume(ThreadContext *tc, StaticInstPtr inst) { - if (!trace.trace(xc, inst)) + if (!trace.trace(tc, inst)) return NULL; trace.dprintf(); return consume(trace.getstack()); diff --git a/src/cpu/quiesce_event.cc b/src/cpu/quiesce_event.cc new file mode 100644 index 000000000..8dd20db02 --- /dev/null +++ b/src/cpu/quiesce_event.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/thread_context.hh" +#include "cpu/quiesce_event.hh" + +EndQuiesceEvent::EndQuiesceEvent(ThreadContext *_tc) + : Event(&mainEventQueue), tc(_tc) +{ +} + +void +EndQuiesceEvent::process() +{ + tc->activate(); +} + +const char* +EndQuiesceEvent::description() +{ + return "End Quiesce Event."; +} diff --git a/src/cpu/quiesce_event.hh b/src/cpu/quiesce_event.hh new file mode 100644 index 000000000..3de40f97e --- /dev/null +++ b/src/cpu/quiesce_event.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_QUIESCE_EVENT_HH__ +#define __CPU_QUIESCE_EVENT_HH__ + +#include "sim/eventq.hh" + +class ThreadContext; + +/** Event for timing out quiesce instruction */ +struct EndQuiesceEvent : public Event +{ + /** A pointer to the thread context that is quiesced */ + ThreadContext *tc; + + EndQuiesceEvent(ThreadContext *_tc); + + /** Event process to occur at interrupt*/ + virtual void process(); + + /** Event description */ + virtual const char *description(); +}; + +#endif // __CPU_QUIESCE_EVENT_HH__ diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc index 3cad6e43f..071193f02 100644 --- a/src/cpu/simple/atomic.cc +++ b/src/cpu/simple/atomic.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include "arch/utility.hh" @@ -68,11 +70,11 @@ AtomicSimpleCPU::init() BaseCPU::init(); #if FULL_SYSTEM - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; // initialize CPU, including PC - TheISA::initCPU(xc, xc->readCpuId()); + TheISA::initCPU(tc, tc->readCpuId()); } #endif } @@ -106,11 +108,10 @@ AtomicSimpleCPU::CpuPort::recvStatusChange(Status status) panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!"); } -Packet * +void AtomicSimpleCPU::CpuPort::recvRetry() { panic("AtomicSimpleCPU doesn't expect recvRetry callback!"); - return NULL; } @@ -121,26 +122,17 @@ AtomicSimpleCPU::AtomicSimpleCPU(Params *p) { _status = Idle; - ifetch_req = new Request(true); - ifetch_req->setAsid(0); - // @todo fix me and get the real cpu iD!!! - ifetch_req->setCpuNum(0); - ifetch_req->setSize(sizeof(MachInst)); + // @todo fix me and get the real cpu id & thread number!!! + ifetch_req = new Request(); ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); ifetch_pkt->dataStatic(&inst); - data_read_req = new Request(true); - // @todo fix me and get the real cpu iD!!! - data_read_req->setCpuNum(0); - data_read_req->setAsid(0); + data_read_req = new Request(); data_read_pkt = new Packet(data_read_req, Packet::ReadReq, Packet::Broadcast); data_read_pkt->dataStatic(&dataReg); - data_write_req = new Request(true); - // @todo fix me and get the real cpu iD!!! - data_write_req->setCpuNum(0); - data_write_req->setAsid(0); + data_write_req = new Request(); data_write_pkt = new Packet(data_write_req, Packet::WriteReq, Packet::Broadcast); } @@ -187,11 +179,11 @@ AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) assert(!tickEvent.scheduled()); - // if any of this CPU's ExecContexts are active, mark the CPU as + // if any of this CPU's ThreadContexts are active, mark the CPU as // running and schedule its tick event. - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; - if (xc->status() == ExecContext::Active && _status != Running) { + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { _status = Running; tickEvent.schedule(curTick); break; @@ -204,7 +196,7 @@ void AtomicSimpleCPU::activateContext(int thread_num, int delay) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Idle); assert(!tickEvent.scheduled()); @@ -219,7 +211,7 @@ void AtomicSimpleCPU::suspendContext(int thread_num) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Running); @@ -237,24 +229,20 @@ template <class T> Fault AtomicSimpleCPU::read(Addr addr, T &data, unsigned flags) { - data_read_req->setVaddr(addr); - data_read_req->setSize(sizeof(T)); - data_read_req->setFlags(flags); - data_read_req->setTime(curTick); + data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); if (traceData) { traceData->setAddr(addr); } // translate to physical address - Fault fault = cpuXC->translateDataReadReq(data_read_req); + Fault fault = thread->translateDataReadReq(data_read_req); // Now do the access. if (fault == NoFault) { - data_read_pkt->reset(); data_read_pkt->reinitFromRequest(); - dcache_complete = dcachePort.sendAtomic(data_read_pkt); + dcache_latency = dcachePort.sendAtomic(data_read_pkt); dcache_access = true; assert(data_read_pkt->result == Packet::Success); @@ -316,26 +304,22 @@ template <class T> Fault AtomicSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) { - data_write_req->setVaddr(addr); - data_write_req->setTime(curTick); - data_write_req->setSize(sizeof(T)); - data_write_req->setFlags(flags); + data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); if (traceData) { traceData->setAddr(addr); } // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(data_write_req); + Fault fault = thread->translateDataWriteReq(data_write_req); // Now do the access. if (fault == NoFault) { - data_write_pkt->reset(); data = htog(data); - data_write_pkt->dataStatic(&data); data_write_pkt->reinitFromRequest(); + data_write_pkt->dataStatic(&data); - dcache_complete = dcachePort.sendAtomic(data_write_pkt); + dcache_latency = dcachePort.sendAtomic(data_write_pkt); dcache_access = true; assert(data_write_pkt->result == Packet::Success); @@ -411,12 +395,12 @@ AtomicSimpleCPU::tick() checkForInterrupts(); - ifetch_req->resetMin(); - ifetch_pkt->reset(); - Fault fault = setupFetchPacket(ifetch_pkt); + Fault fault = setupFetchRequest(ifetch_req); if (fault == NoFault) { - Tick icache_complete = icachePort.sendAtomic(ifetch_pkt); + ifetch_pkt->reinitFromRequest(); + + Tick icache_latency = icachePort.sendAtomic(ifetch_pkt); // ifetch_req is initialized to read the instruction directly // into the CPU object's inst field. @@ -431,9 +415,9 @@ AtomicSimpleCPU::tick() // cycle time. If not, the next tick event may get // scheduled at a non-integer multiple of the CPU // cycle time. - Tick icache_stall = icache_complete - curTick - cycles(1); + Tick icache_stall = icache_latency - cycles(1); Tick dcache_stall = - dcache_access ? dcache_complete - curTick - cycles(1) : 0; + dcache_access ? dcache_latency - cycles(1) : 0; latency += icache_stall + dcache_stall; } diff --git a/src/cpu/simple/atomic.hh b/src/cpu/simple/atomic.hh index ab3a3e8ef..7f4956da9 100644 --- a/src/cpu/simple/atomic.hh +++ b/src/cpu/simple/atomic.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __CPU_SIMPLE_ATOMIC_HH__ @@ -98,7 +100,7 @@ class AtomicSimpleCPU : public BaseSimpleCPU virtual void recvStatusChange(Status status); - virtual Packet *recvRetry(); + virtual void recvRetry(); virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) @@ -116,7 +118,7 @@ class AtomicSimpleCPU : public BaseSimpleCPU Packet *data_write_pkt; bool dcache_access; - Tick dcache_complete; + Tick dcache_latency; public: diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc index 18f170449..d94b0e079 100644 --- a/src/cpu/simple/base.cc +++ b/src/cpu/simple/base.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include "arch/utility.hh" @@ -36,18 +38,18 @@ #include "base/stats/events.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" #include "cpu/exetrace.hh" #include "cpu/profile.hh" #include "cpu/sampler/sampler.hh" #include "cpu/simple/base.hh" +#include "cpu/simple_thread.hh" #include "cpu/smt.hh" #include "cpu/static_inst.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "mem/packet_impl.hh" -#include "sim/byteswap.hh" #include "sim/builder.hh" +#include "sim/byteswap.hh" #include "sim/debug.hh" #include "sim/host.hh" #include "sim/sim_events.hh" @@ -68,16 +70,18 @@ using namespace std; using namespace TheISA; BaseSimpleCPU::BaseSimpleCPU(Params *p) - : BaseCPU(p), mem(p->mem), cpuXC(NULL) + : BaseCPU(p), mem(p->mem), thread(NULL) { #if FULL_SYSTEM - cpuXC = new CPUExecContext(this, 0, p->system, p->itb, p->dtb); + thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb); #else - cpuXC = new CPUExecContext(this, /* thread_num */ 0, p->process, + thread = new SimpleThread(this, /* thread_num */ 0, p->process, /* asid */ 0, mem); #endif // !FULL_SYSTEM - xcProxy = cpuXC->getProxy(); + thread->setStatus(ThreadContext::Suspended); + + tc = thread->getTC(); numInst = 0; startNumInst = 0; @@ -86,7 +90,7 @@ BaseSimpleCPU::BaseSimpleCPU(Params *p) lastIcacheStall = 0; lastDcacheStall = 0; - execContexts.push_back(xcProxy); + threadContexts.push_back(tc); } BaseSimpleCPU::~BaseSimpleCPU() @@ -176,7 +180,7 @@ BaseSimpleCPU::serialize(ostream &os) BaseCPU::serialize(os); SERIALIZE_SCALAR(inst); nameOut(os, csprintf("%s.xc", name())); - cpuXC->serialize(os); + thread->serialize(os); } void @@ -184,7 +188,7 @@ BaseSimpleCPU::unserialize(Checkpoint *cp, const string §ion) { BaseCPU::unserialize(cp, section); UNSERIALIZE_SCALAR(inst); - cpuXC->unserialize(cp, csprintf("%s.xc", section)); + thread->unserialize(cp, csprintf("%s.xc", section)); } void @@ -213,16 +217,16 @@ BaseSimpleCPU::copySrcTranslate(Addr src) memReq->reset(src & ~(blk_size - 1), blk_size); // translate to physical address - Fault fault = cpuXC->translateDataReadReq(req); + Fault fault = thread->translateDataReadReq(req); if (fault == NoFault) { - cpuXC->copySrcAddr = src; - cpuXC->copySrcPhysAddr = memReq->paddr + offset; + thread->copySrcAddr = src; + thread->copySrcPhysAddr = memReq->paddr + offset; } else { assert(!fault->isAlignmentFault()); - cpuXC->copySrcAddr = 0; - cpuXC->copySrcPhysAddr = 0; + thread->copySrcAddr = 0; + thread->copySrcPhysAddr = 0; } return fault; #else @@ -239,7 +243,7 @@ BaseSimpleCPU::copy(Addr dest) // Only support block sizes of 64 atm. assert(blk_size == 64); uint8_t data[blk_size]; - //assert(cpuXC->copySrcAddr); + //assert(thread->copySrcAddr); int offset = dest & (blk_size - 1); // Make sure block doesn't span page @@ -252,19 +256,19 @@ BaseSimpleCPU::copy(Addr dest) memReq->reset(dest & ~(blk_size -1), blk_size); // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(req); + Fault fault = thread->translateDataWriteReq(req); if (fault == NoFault) { Addr dest_addr = memReq->paddr + offset; // Need to read straight from memory since we have more than 8 bytes. - memReq->paddr = cpuXC->copySrcPhysAddr; - cpuXC->mem->read(memReq, data); + memReq->paddr = thread->copySrcPhysAddr; + thread->mem->read(memReq, data); memReq->paddr = dest_addr; - cpuXC->mem->write(memReq, data); + thread->mem->write(memReq, data); if (dcacheInterface) { memReq->cmd = Copy; memReq->completionEvent = NULL; - memReq->paddr = cpuXC->copySrcPhysAddr; + memReq->paddr = thread->copySrcPhysAddr; memReq->dest = dest_addr; memReq->size = 64; memReq->time = curTick; @@ -286,7 +290,7 @@ BaseSimpleCPU::copy(Addr dest) Addr BaseSimpleCPU::dbg_vtophys(Addr addr) { - return vtophys(xcProxy, addr); + return vtophys(tc, addr); } #endif // FULL_SYSTEM @@ -296,9 +300,9 @@ BaseSimpleCPU::post_interrupt(int int_num, int index) { BaseCPU::post_interrupt(int_num, index); - if (cpuXC->status() == ExecContext::Suspended) { + if (thread->status() == ThreadContext::Suspended) { DPRINTF(IPI,"Suspended Processor awoke\n"); - cpuXC->activate(); + thread->activate(); } } #endif // FULL_SYSTEM @@ -307,15 +311,15 @@ void BaseSimpleCPU::checkForInterrupts() { #if FULL_SYSTEM - if (checkInterrupts && check_interrupts() && !cpuXC->inPalMode()) { + if (checkInterrupts && check_interrupts() && !thread->inPalMode()) { int ipl = 0; int summary = 0; checkInterrupts = false; - if (cpuXC->readMiscReg(IPR_SIRR)) { + if (thread->readMiscReg(IPR_SIRR)) { for (int i = INTLEVEL_SOFTWARE_MIN; i < INTLEVEL_SOFTWARE_MAX; i++) { - if (cpuXC->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + if (thread->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { // See table 4-19 of 21164 hardware reference ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; summary |= (ULL(1) << i); @@ -323,7 +327,7 @@ BaseSimpleCPU::checkForInterrupts() } } - uint64_t interrupts = cpuXC->cpu->intr_status(); + uint64_t interrupts = thread->cpu->intr_status(); for (int i = INTLEVEL_EXTERNAL_MIN; i < INTLEVEL_EXTERNAL_MAX; i++) { if (interrupts & (ULL(1) << i)) { @@ -333,17 +337,17 @@ BaseSimpleCPU::checkForInterrupts() } } - if (cpuXC->readMiscReg(IPR_ASTRR)) + if (thread->readMiscReg(IPR_ASTRR)) panic("asynchronous traps not implemented\n"); - if (ipl && ipl > cpuXC->readMiscReg(IPR_IPLR)) { - cpuXC->setMiscReg(IPR_ISR, summary); - cpuXC->setMiscReg(IPR_INTID, ipl); + if (ipl && ipl > thread->readMiscReg(IPR_IPLR)) { + thread->setMiscReg(IPR_ISR, summary); + thread->setMiscReg(IPR_INTID, ipl); - Fault(new InterruptFault)->invoke(xcProxy); + Fault(new InterruptFault)->invoke(tc); DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", - cpuXC->readMiscReg(IPR_IPLR), ipl, summary); + thread->readMiscReg(IPR_IPLR), ipl, summary); } } #endif @@ -351,29 +355,17 @@ BaseSimpleCPU::checkForInterrupts() Fault -BaseSimpleCPU::setupFetchPacket(Packet *ifetch_pkt) +BaseSimpleCPU::setupFetchRequest(Request *req) { - // Try to fetch an instruction - // set up memory request for instruction fetch + DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(), + thread->readNextPC(),thread->readNextNPC()); - DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",cpuXC->readPC(), - cpuXC->readNextPC(),cpuXC->readNextNPC()); - - Request *ifetch_req = ifetch_pkt->req; - ifetch_req->setVaddr(cpuXC->readPC() & ~3); - ifetch_req->setTime(curTick); -#if FULL_SYSTEM - ifetch_req->setFlags((cpuXC->readPC() & 1) ? PHYSICAL : 0); -#else - ifetch_req->setFlags(0); -#endif - - Fault fault = cpuXC->translateInstReq(ifetch_req); + req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst), + (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0, + thread->readPC()); - if (fault == NoFault) { - ifetch_pkt->reinitFromRequest(); - } + Fault fault = thread->translateInstReq(req); return fault; } @@ -383,33 +375,33 @@ void BaseSimpleCPU::preExecute() { // maintain $r0 semantics - cpuXC->setIntReg(ZeroReg, 0); + thread->setIntReg(ZeroReg, 0); #if THE_ISA == ALPHA_ISA - cpuXC->setFloatReg(ZeroReg, 0.0); + thread->setFloatReg(ZeroReg, 0.0); #endif // ALPHA_ISA // keep an instruction count numInst++; numInsts++; - cpuXC->func_exe_inst++; + thread->funcExeInst++; // check for instruction-count-based events comInstEventQueue[0]->serviceEvents(numInst); // decode the instruction inst = gtoh(inst); - curStaticInst = StaticInst::decode(makeExtMI(inst, cpuXC->readPC())); + curStaticInst = StaticInst::decode(makeExtMI(inst, thread->readPC())); - traceData = Trace::getInstRecord(curTick, xcProxy, this, curStaticInst, - cpuXC->readPC()); + traceData = Trace::getInstRecord(curTick, tc, this, curStaticInst, + thread->readPC()); DPRINTF(Decode,"Decode: Decoded %s instruction (opcode: 0x%x): 0x%x\n", curStaticInst->getName(), curStaticInst->getOpcode(), curStaticInst->machInst); #if FULL_SYSTEM - cpuXC->setInst(inst); + thread->setInst(inst); #endif // FULL_SYSTEM } @@ -417,18 +409,13 @@ void BaseSimpleCPU::postExecute() { #if FULL_SYSTEM - if (system->kernelBinning->fnbin) { - assert(kernelStats); - system->kernelBinning->execute(xcProxy, inst); - } - - if (cpuXC->profile) { + if (thread->profile) { bool usermode = - (cpuXC->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; - cpuXC->profilePC = usermode ? 1 : cpuXC->readPC(); - ProfileNode *node = cpuXC->profile->consume(xcProxy, inst); + (thread->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; + thread->profilePC = usermode ? 1 : thread->readPC(); + ProfileNode *node = thread->profile->consume(tc, inst); if (node) - cpuXC->profileNode = node; + thread->profileNode = node; } #endif @@ -441,7 +428,7 @@ BaseSimpleCPU::postExecute() comLoadEventQueue[0]->serviceEvents(numLoad); } - traceFunctions(cpuXC->readPC()); + traceFunctions(thread->readPC()); if (traceData) { traceData->finalize(); @@ -454,19 +441,19 @@ BaseSimpleCPU::advancePC(Fault fault) { if (fault != NoFault) { #if FULL_SYSTEM - fault->invoke(xcProxy); + fault->invoke(tc); #else // !FULL_SYSTEM - fatal("fault (%s) detected @ PC %08p", fault->name(), cpuXC->readPC()); + fatal("fault (%s) detected @ PC %08p", fault->name(), thread->readPC()); #endif // FULL_SYSTEM } else { // go to the next instruction - cpuXC->setPC(cpuXC->readNextPC()); + thread->setPC(thread->readNextPC()); #if THE_ISA == ALPHA_ISA - cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst)); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); #else - cpuXC->setNextPC(cpuXC->readNextNPC()); - cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst)); + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); #endif } @@ -474,9 +461,9 @@ BaseSimpleCPU::advancePC(Fault fault) #if FULL_SYSTEM Addr oldpc; do { - oldpc = cpuXC->readPC(); - system->pcEventQueue.service(xcProxy); - } while (oldpc != cpuXC->readPC()); + oldpc = thread->readPC(); + system->pcEventQueue.service(tc); + } while (oldpc != thread->readPC()); #endif } diff --git a/src/cpu/simple/base.hh b/src/cpu/simple/base.hh index 4c0e6f3c7..39bc86050 100644 --- a/src/cpu/simple/base.hh +++ b/src/cpu/simple/base.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Dave Greene + * Nathan Binkert */ #ifndef __CPU_SIMPLE_BASE_HH__ @@ -32,7 +36,7 @@ #include "base/statistics.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/simple_thread.hh" #include "cpu/pc_event.hh" #include "cpu/sampler/sampler.hh" #include "cpu/static_inst.hh" @@ -57,7 +61,7 @@ class Process; #endif // FULL_SYSTEM -class ExecContext; +class ThreadContext; class Checkpoint; namespace Trace { @@ -104,10 +108,13 @@ class BaseSimpleCPU : public BaseCPU virtual ~BaseSimpleCPU(); public: - // execution context - CPUExecContext *cpuXC; + /** SimpleThread object, provides all the architectural state. */ + SimpleThread *thread; - ExecContext *xcProxy; + /** ThreadContext object, provides an interface for external + * objects to modify this thread's state. + */ + ThreadContext *tc; #if FULL_SYSTEM Addr dbg_vtophys(Addr addr); @@ -129,7 +136,7 @@ class BaseSimpleCPU : public BaseCPU StaticInstPtr curStaticInst; void checkForInterrupts(); - Fault setupFetchPacket(Packet *ifetch_pkt); + Fault setupFetchRequest(Request *req); void preExecute(); void postExecute(); void advancePC(Fault fault); @@ -213,104 +220,104 @@ class BaseSimpleCPU : public BaseCPU uint64_t readIntReg(const StaticInst *si, int idx) { - return cpuXC->readIntReg(si->srcRegIdx(idx)); + return thread->readIntReg(si->srcRegIdx(idx)); } FloatReg readFloatReg(const StaticInst *si, int idx, int width) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatReg(reg_idx, width); + return thread->readFloatReg(reg_idx, width); } FloatReg readFloatReg(const StaticInst *si, int idx) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatReg(reg_idx); + return thread->readFloatReg(reg_idx); } FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatRegBits(reg_idx, width); + return thread->readFloatRegBits(reg_idx, width); } FloatRegBits readFloatRegBits(const StaticInst *si, int idx) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatRegBits(reg_idx); + return thread->readFloatRegBits(reg_idx); } void setIntReg(const StaticInst *si, int idx, uint64_t val) { - cpuXC->setIntReg(si->destRegIdx(idx), val); + thread->setIntReg(si->destRegIdx(idx), val); } void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatReg(reg_idx, val, width); + thread->setFloatReg(reg_idx, val, width); } void setFloatReg(const StaticInst *si, int idx, FloatReg val) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatReg(reg_idx, val); + thread->setFloatReg(reg_idx, val); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, int width) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatRegBits(reg_idx, val, width); + thread->setFloatRegBits(reg_idx, val, width); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatRegBits(reg_idx, val); + thread->setFloatRegBits(reg_idx, val); } - uint64_t readPC() { return cpuXC->readPC(); } - uint64_t readNextPC() { return cpuXC->readNextPC(); } - uint64_t readNextNPC() { return cpuXC->readNextNPC(); } + uint64_t readPC() { return thread->readPC(); } + uint64_t readNextPC() { return thread->readNextPC(); } + uint64_t readNextNPC() { return thread->readNextNPC(); } - void setPC(uint64_t val) { cpuXC->setPC(val); } - void setNextPC(uint64_t val) { cpuXC->setNextPC(val); } - void setNextNPC(uint64_t val) { cpuXC->setNextNPC(val); } + void setPC(uint64_t val) { thread->setPC(val); } + void setNextPC(uint64_t val) { thread->setNextPC(val); } + void setNextNPC(uint64_t val) { thread->setNextNPC(val); } MiscReg readMiscReg(int misc_reg) { - return cpuXC->readMiscReg(misc_reg); + return thread->readMiscReg(misc_reg); } MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) { - return cpuXC->readMiscRegWithEffect(misc_reg, fault); + return thread->readMiscRegWithEffect(misc_reg, fault); } Fault setMiscReg(int misc_reg, const MiscReg &val) { - return cpuXC->setMiscReg(misc_reg, val); + return thread->setMiscReg(misc_reg, val); } Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) { - return cpuXC->setMiscRegWithEffect(misc_reg, val); + return thread->setMiscRegWithEffect(misc_reg, val); } #if FULL_SYSTEM - Fault hwrei() { return cpuXC->hwrei(); } - int readIntrFlag() { return cpuXC->readIntrFlag(); } - void setIntrFlag(int val) { cpuXC->setIntrFlag(val); } - bool inPalMode() { return cpuXC->inPalMode(); } - void ev5_trap(Fault fault) { fault->invoke(xcProxy); } - bool simPalCheck(int palFunc) { return cpuXC->simPalCheck(palFunc); } + Fault hwrei() { return thread->hwrei(); } + int readIntrFlag() { return thread->readIntrFlag(); } + void setIntrFlag(int val) { thread->setIntrFlag(val); } + bool inPalMode() { return thread->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(tc); } + bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } #else - void syscall(int64_t callnum) { cpuXC->syscall(callnum); } + void syscall(int64_t callnum) { thread->syscall(callnum); } #endif - bool misspeculating() { return cpuXC->misspeculating(); } - ExecContext *xcBase() { return xcProxy; } + bool misspeculating() { return thread->misspeculating(); } + ThreadContext *tcBase() { return tc; } }; #endif // __CPU_SIMPLE_BASE_HH__ diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc index 7cdcdafa1..c99db8fbf 100644 --- a/src/cpu/simple/timing.cc +++ b/src/cpu/simple/timing.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include "arch/utility.hh" @@ -50,11 +52,11 @@ TimingSimpleCPU::init() BaseCPU::init(); #if FULL_SYSTEM - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; // initialize CPU, including PC - TheISA::initCPU(xc, xc->readCpuId()); + TheISA::initCPU(tc, tc->readCpuId()); } #endif } @@ -123,11 +125,11 @@ TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) { BaseCPU::takeOverFrom(oldCPU); - // if any of this CPU's ExecContexts are active, mark the CPU as + // if any of this CPU's ThreadContexts are active, mark the CPU as // running and schedule its tick event. - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; - if (xc->status() == ExecContext::Active && _status != Running) { + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { _status = Running; break; } @@ -139,7 +141,7 @@ void TimingSimpleCPU::activateContext(int thread_num, int delay) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Idle); @@ -156,7 +158,7 @@ void TimingSimpleCPU::suspendContext(int thread_num) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Running); @@ -172,19 +174,17 @@ template <class T> Fault TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) { - Request *data_read_req = new Request(true); + // need to fill in CPU & thread IDs here + Request *data_read_req = new Request(); - data_read_req->setVaddr(addr); - data_read_req->setSize(sizeof(T)); - data_read_req->setFlags(flags); - data_read_req->setTime(curTick); + data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); if (traceData) { traceData->setAddr(data_read_req->getVaddr()); } // translate to physical address - Fault fault = cpuXC->translateDataReadReq(data_read_req); + Fault fault = thread->translateDataReadReq(data_read_req); // Now do the access. if (fault == NoFault) { @@ -255,14 +255,12 @@ template <class T> Fault TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) { - Request *data_write_req = new Request(true); - data_write_req->setVaddr(addr); - data_write_req->setTime(curTick); - data_write_req->setSize(sizeof(T)); - data_write_req->setFlags(flags); + // need to fill in CPU & thread IDs here + Request *data_write_req = new Request(); + data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(data_write_req); + Fault fault = thread->translateDataWriteReq(data_write_req); // Now do the access. if (fault == NoFault) { Packet *data_write_pkt = @@ -340,13 +338,13 @@ TimingSimpleCPU::fetch() { checkForInterrupts(); - Request *ifetch_req = new Request(true); - ifetch_req->setSize(sizeof(MachInst)); + // need to fill in CPU & thread IDs here + Request *ifetch_req = new Request(); + Fault fault = setupFetchRequest(ifetch_req); ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); ifetch_pkt->dataStatic(&inst); - Fault fault = setupFetchPacket(ifetch_pkt); if (fault == NoFault) { if (!icachePort.sendTiming(ifetch_pkt)) { // Need to wait for retry @@ -419,17 +417,18 @@ TimingSimpleCPU::IcachePort::recvTiming(Packet *pkt) return true; } -Packet * +void TimingSimpleCPU::IcachePort::recvRetry() { // we shouldn't get a retry unless we have a packet that we're // waiting to transmit assert(cpu->ifetch_pkt != NULL); assert(cpu->_status == IcacheRetry); - cpu->_status = IcacheWaitResponse; Packet *tmp = cpu->ifetch_pkt; - cpu->ifetch_pkt = NULL; - return tmp; + if (sendTiming(tmp)) { + cpu->_status = IcacheWaitResponse; + cpu->ifetch_pkt = NULL; + } } void @@ -459,17 +458,18 @@ TimingSimpleCPU::DcachePort::recvTiming(Packet *pkt) return true; } -Packet * +void TimingSimpleCPU::DcachePort::recvRetry() { // we shouldn't get a retry unless we have a packet that we're // waiting to transmit assert(cpu->dcache_pkt != NULL); assert(cpu->_status == DcacheRetry); - cpu->_status = DcacheWaitResponse; Packet *tmp = cpu->dcache_pkt; - cpu->dcache_pkt = NULL; - return tmp; + if (sendTiming(tmp)) { + cpu->_status = DcacheWaitResponse; + cpu->dcache_pkt = NULL; + } } diff --git a/src/cpu/simple/timing.hh b/src/cpu/simple/timing.hh index b46631d5a..ab0b2d2ca 100644 --- a/src/cpu/simple/timing.hh +++ b/src/cpu/simple/timing.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __CPU_SIMPLE_TIMING_HH__ @@ -100,7 +102,7 @@ class TimingSimpleCPU : public BaseSimpleCPU virtual bool recvTiming(Packet *pkt); - virtual Packet *recvRetry(); + virtual void recvRetry(); }; class DcachePort : public CpuPort @@ -115,7 +117,7 @@ class TimingSimpleCPU : public BaseSimpleCPU virtual bool recvTiming(Packet *pkt); - virtual Packet *recvRetry(); + virtual void recvRetry(); }; IcachePort icachePort; diff --git a/src/cpu/cpu_exec_context.cc b/src/cpu/simple_thread.cc index b8aa9a67e..48383ca93 100644 --- a/src/cpu/cpu_exec_context.cc +++ b/src/cpu/simple_thread.cc @@ -24,14 +24,19 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Lisa Hsu + * Kevin Lim */ #include <string> #include "arch/isa_traits.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" #if FULL_SYSTEM #include "base/callback.hh" @@ -39,6 +44,7 @@ #include "base/output.hh" #include "base/trace.hh" #include "cpu/profile.hh" +#include "cpu/quiesce_event.hh" #include "kern/kernel_stats.hh" #include "sim/serialize.hh" #include "sim/sim_exit.hh" @@ -53,23 +59,24 @@ using namespace std; // constructor #if FULL_SYSTEM -CPUExecContext::CPUExecContext(BaseCPU *_cpu, int _thread_num, System *_sys, - AlphaITB *_itb, AlphaDTB *_dtb) - : _status(ExecContext::Unallocated), cpu(_cpu), thread_num(_thread_num), - cpu_id(-1), lastActivate(0), lastSuspend(0), system(_sys), itb(_itb), - dtb(_dtb), profile(NULL), quiesceEvent(this), func_exe_inst(0), - storeCondFailures(0) +SimpleThread::SimpleThread(BaseCPU *_cpu, int _thread_num, System *_sys, + AlphaITB *_itb, AlphaDTB *_dtb, + bool use_kernel_stats) + : ThreadState(-1, _thread_num), cpu(_cpu), system(_sys), itb(_itb), + dtb(_dtb) { - proxy = new ProxyExecContext<CPUExecContext>(this); + tc = new ProxyThreadContext<SimpleThread>(this); + + quiesceEvent = new EndQuiesceEvent(tc); regs.clear(); if (cpu->params->profile) { profile = new FunctionProfile(system->kernelSymtab); Callback *cb = - new MakeCallback<CPUExecContext, - &CPUExecContext::dumpFuncProfile>(this); + new MakeCallback<SimpleThread, + &SimpleThread::dumpFuncProfile>(this); registerExitCallback(cb); } @@ -79,98 +86,59 @@ CPUExecContext::CPUExecContext(BaseCPU *_cpu, int _thread_num, System *_sys, profileNode = &dummyNode; profilePC = 3; + if (use_kernel_stats) { + kernelStats = new Kernel::Statistics(system); + } else { + kernelStats = NULL; + } Port *mem_port; physPort = new FunctionalPort(csprintf("%s-%d-funcport", - cpu->name(), thread_num)); + cpu->name(), tid)); mem_port = system->physmem->getPort("functional"); mem_port->setPeer(physPort); physPort->setPeer(mem_port); virtPort = new VirtualPort(csprintf("%s-%d-vport", - cpu->name(), thread_num)); + cpu->name(), tid)); mem_port = system->physmem->getPort("functional"); mem_port->setPeer(virtPort); virtPort->setPeer(mem_port); } #else -CPUExecContext::CPUExecContext(BaseCPU *_cpu, int _thread_num, +SimpleThread::SimpleThread(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, MemObject* memobj) - : _status(ExecContext::Unallocated), - cpu(_cpu), thread_num(_thread_num), cpu_id(-1), lastActivate(0), - lastSuspend(0), process(_process), asid(_asid), - func_exe_inst(0), storeCondFailures(0) + : ThreadState(-1, _thread_num, memobj, _process, _asid), + cpu(_cpu) { /* Use this port to for syscall emulation writes to memory. */ Port *mem_port; port = new TranslatingPort(csprintf("%s-%d-funcport", - cpu->name(), thread_num), + cpu->name(), tid), process->pTable, false); mem_port = memobj->getPort("functional"); mem_port->setPeer(port); port->setPeer(mem_port); regs.clear(); - proxy = new ProxyExecContext<CPUExecContext>(this); + tc = new ProxyThreadContext<SimpleThread>(this); } -CPUExecContext::CPUExecContext(RegFile *regFile) - : cpu(NULL), thread_num(-1), process(NULL), asid(-1), - func_exe_inst(0), storeCondFailures(0) +SimpleThread::SimpleThread(RegFile *regFile) + : ThreadState(-1, -1, NULL, NULL, -1), cpu(NULL) { regs = *regFile; - proxy = new ProxyExecContext<CPUExecContext>(this); + tc = new ProxyThreadContext<SimpleThread>(this); } #endif -CPUExecContext::~CPUExecContext() -{ - delete proxy; -} - -#if FULL_SYSTEM -void -CPUExecContext::dumpFuncProfile() -{ - std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); - profile->dump(proxy, *os); -} - -CPUExecContext::EndQuiesceEvent::EndQuiesceEvent(CPUExecContext *_cpuXC) - : Event(&mainEventQueue), cpuXC(_cpuXC) -{ -} - -void -CPUExecContext::EndQuiesceEvent::process() -{ - cpuXC->activate(); -} - -const char* -CPUExecContext::EndQuiesceEvent::description() -{ - return "End Quiesce Event."; -} - -void -CPUExecContext::profileClear() -{ - if (profile) - profile->clear(); -} - -void -CPUExecContext::profileSample() +SimpleThread::~SimpleThread() { - if (profile) - profile->sample(profileNode, profilePC); + delete tc; } -#endif - void -CPUExecContext::takeOverFrom(ExecContext *oldContext) +SimpleThread::takeOverFrom(ThreadContext *oldContext) { // some things should already be set up #if FULL_SYSTEM @@ -182,69 +150,97 @@ CPUExecContext::takeOverFrom(ExecContext *oldContext) // copy over functional state _status = oldContext->status(); copyArchRegs(oldContext); - cpu_id = oldContext->readCpuId(); + cpuId = oldContext->readCpuId(); #if !FULL_SYSTEM - func_exe_inst = oldContext->readFuncExeInst(); + funcExeInst = oldContext->readFuncExeInst(); +#else + EndQuiesceEvent *quiesce = oldContext->getQuiesceEvent(); + if (quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + quiesce->tc = tc; + } + if (quiesceEvent) { + quiesceEvent->tc = tc; + } #endif storeCondFailures = 0; - oldContext->setStatus(ExecContext::Unallocated); + oldContext->setStatus(ThreadContext::Unallocated); } void -CPUExecContext::serialize(ostream &os) +SimpleThread::serialize(ostream &os) { SERIALIZE_ENUM(_status); regs.serialize(os); // thread_num and cpu_id are deterministic from the config - SERIALIZE_SCALAR(func_exe_inst); + SERIALIZE_SCALAR(funcExeInst); SERIALIZE_SCALAR(inst); #if FULL_SYSTEM Tick quiesceEndTick = 0; - if (quiesceEvent.scheduled()) - quiesceEndTick = quiesceEvent.when(); + if (quiesceEvent->scheduled()) + quiesceEndTick = quiesceEvent->when(); SERIALIZE_SCALAR(quiesceEndTick); - + if (kernelStats) + kernelStats->serialize(os); #endif } void -CPUExecContext::unserialize(Checkpoint *cp, const std::string §ion) +SimpleThread::unserialize(Checkpoint *cp, const std::string §ion) { UNSERIALIZE_ENUM(_status); regs.unserialize(cp, section); // thread_num and cpu_id are deterministic from the config - UNSERIALIZE_SCALAR(func_exe_inst); + UNSERIALIZE_SCALAR(funcExeInst); UNSERIALIZE_SCALAR(inst); #if FULL_SYSTEM Tick quiesceEndTick; UNSERIALIZE_SCALAR(quiesceEndTick); if (quiesceEndTick) - quiesceEvent.schedule(quiesceEndTick); + quiesceEvent->schedule(quiesceEndTick); + if (kernelStats) + kernelStats->unserialize(cp, section); #endif } +#if FULL_SYSTEM +void +SimpleThread::dumpFuncProfile() +{ + std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); + profile->dump(tc, *os); +} +#endif void -CPUExecContext::activate(int delay) +SimpleThread::activate(int delay) { - if (status() == ExecContext::Active) + if (status() == ThreadContext::Active) return; lastActivate = curTick; - _status = ExecContext::Active; - cpu->activateContext(thread_num, delay); + if (status() == ThreadContext::Unallocated) { + cpu->activateWhenReady(tid); + return; + } + + _status = ThreadContext::Active; + + // status() == Suspended + cpu->activateContext(tid, delay); } void -CPUExecContext::suspend() +SimpleThread::suspend() { - if (status() == ExecContext::Suspended) + if (status() == ThreadContext::Suspended) return; lastActivate = curTick; @@ -253,58 +249,62 @@ CPUExecContext::suspend() #if FULL_SYSTEM // Don't change the status from active if there are pending interrupts if (cpu->check_interrupts()) { - assert(status() == ExecContext::Active); + assert(status() == ThreadContext::Active); return; } #endif */ - _status = ExecContext::Suspended; - cpu->suspendContext(thread_num); + _status = ThreadContext::Suspended; + cpu->suspendContext(tid); } void -CPUExecContext::deallocate() +SimpleThread::deallocate() { - if (status() == ExecContext::Unallocated) + if (status() == ThreadContext::Unallocated) return; - _status = ExecContext::Unallocated; - cpu->deallocateContext(thread_num); + _status = ThreadContext::Unallocated; + cpu->deallocateContext(tid); } void -CPUExecContext::halt() +SimpleThread::halt() { - if (status() == ExecContext::Halted) + if (status() == ThreadContext::Halted) return; - _status = ExecContext::Halted; - cpu->haltContext(thread_num); + _status = ThreadContext::Halted; + cpu->haltContext(tid); } void -CPUExecContext::regStats(const string &name) +SimpleThread::regStats(const string &name) { +#if FULL_SYSTEM + if (kernelStats) + kernelStats->regStats(name + ".kern"); +#endif } void -CPUExecContext::copyArchRegs(ExecContext *xc) +SimpleThread::copyArchRegs(ThreadContext *src_tc) { - TheISA::copyRegs(xc, proxy); + TheISA::copyRegs(src_tc, tc); } #if FULL_SYSTEM VirtualPort* -CPUExecContext::getVirtPort(ExecContext *xc) +SimpleThread::getVirtPort(ThreadContext *src_tc) { - if (!xc) + if (!src_tc) return virtPort; VirtualPort *vp; Port *mem_port; - vp = new VirtualPort("xc-vport", xc); + vp = new VirtualPort("tc-vport", src_tc); mem_port = system->physmem->getPort("functional"); mem_port->setPeer(vp); vp->setPeer(mem_port); @@ -312,11 +312,12 @@ CPUExecContext::getVirtPort(ExecContext *xc) } void -CPUExecContext::delVirtPort(VirtualPort *vp) +SimpleThread::delVirtPort(VirtualPort *vp) { -// assert(!vp->nullExecContext()); - delete vp->getPeer(); - delete vp; + if (vp != virtPort) { + delete vp->getPeer(); + delete vp; + } } diff --git a/src/cpu/cpu_exec_context.hh b/src/cpu/simple_thread.hh index 2c06a7b3b..de65e9891 100644 --- a/src/cpu/cpu_exec_context.hh +++ b/src/cpu/simple_thread.hh @@ -24,14 +24,18 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ -#ifndef __CPU_CPU_EXEC_CONTEXT_HH__ -#define __CPU_CPU_EXEC_CONTEXT_HH__ +#ifndef __CPU_SIMPLE_THREAD_HH__ +#define __CPU_SIMPLE_THREAD_HH__ #include "arch/isa_traits.hh" #include "config/full_system.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" #include "mem/physical.hh" #include "mem/request.hh" #include "sim/byteswap.hh" @@ -51,6 +55,9 @@ class ProfileNode; class FunctionalPort; class PhysicalPort; +namespace Kernel { + class Statistics; +}; #else // !FULL_SYSTEM @@ -58,16 +65,25 @@ class PhysicalPort; #include "mem/page_table.hh" class TranslatingPort; - #endif // FULL_SYSTEM -// -// The CPUExecContext object represents a functional context for -// instruction execution. It incorporates everything required for -// architecture-level functional simulation of a single thread. -// +/** + * The SimpleThread object provides a combination of the ThreadState + * object and the ThreadContext interface. It implements the + * ThreadContext interface so that a ProxyThreadContext class can be + * made using SimpleThread as the template parameter (see + * thread_context.hh). It adds to the ThreadState object by adding all + * the objects needed for simple functional execution, including a + * simple architectural register file, and pointers to the ITB and DTB + * in full system mode. For CPU models that do not need more advanced + * ways to hold state (i.e. a separate physical register file, or + * separate fetch and commit PC's), this SimpleThread class provides + * all the necessary state for full architecture-level functional + * simulation. See the AtomicSimpleCPU or TimingSimpleCPU for + * examples. + */ -class CPUExecContext +class SimpleThread : public ThreadState { protected: typedef TheISA::RegFile RegFile; @@ -77,202 +93,83 @@ class CPUExecContext typedef TheISA::FloatReg FloatReg; typedef TheISA::FloatRegBits FloatRegBits; public: - typedef ExecContext::Status Status; - - private: - Status _status; - - public: - Status status() const { return _status; } - - void setStatus(Status newStatus) { _status = newStatus; } - - /// Set the status to Active. Optional delay indicates number of - /// cycles to wait before beginning execution. - void activate(int delay = 1); - - /// Set the status to Suspended. - void suspend(); - - /// Set the status to Unallocated. - void deallocate(); - - /// Set the status to Halted. - void halt(); + typedef ThreadContext::Status Status; protected: RegFile regs; // correct-path register context public: - // pointer to CPU associated with this context + // pointer to CPU associated with this SimpleThread BaseCPU *cpu; - ProxyExecContext<CPUExecContext> *proxy; - - // Current instruction - MachInst inst; - - // Index of hardware thread context on the CPU that this represents. - int thread_num; - - // ID of this context w.r.t. the System or Process object to which - // it belongs. For full-system mode, this is the system CPU ID. - int cpu_id; - - Tick lastActivate; - Tick lastSuspend; + ProxyThreadContext<SimpleThread> *tc; System *system; - #if FULL_SYSTEM AlphaITB *itb; AlphaDTB *dtb; - - /** A functional port outgoing only for functional accesses to physical - * addresses.*/ - FunctionalPort *physPort; - - /** A functional port, outgoing only, for functional accesse to virtual - * addresses. That doen't require execution context information */ - VirtualPort *virtPort; - - FunctionProfile *profile; - ProfileNode *profileNode; - Addr profilePC; - void dumpFuncProfile(); - - /** Event for timing out quiesce instruction */ - struct EndQuiesceEvent : public Event - { - /** A pointer to the execution context that is quiesced */ - CPUExecContext *cpuXC; - - EndQuiesceEvent(CPUExecContext *_cpuXC); - - /** Event process to occur at interrupt*/ - virtual void process(); - - /** Event description */ - virtual const char *description(); - }; - EndQuiesceEvent quiesceEvent; - - Event *getQuiesceEvent() { return &quiesceEvent; } - - Tick readLastActivate() { return lastActivate; } - - Tick readLastSuspend() { return lastSuspend; } - - void profileClear(); - - void profileSample(); - -#else - /// Port that syscalls can use to access memory (provides translation step). - TranslatingPort *port; - - 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 func_exe_inst; - - // - // Count failed store conditionals so we can warn of apparent - // application deadlock situations. - unsigned storeCondFailures; - - // constructor: initialize context from given process structure + // constructor: initialize SimpleThread from given process structure #if FULL_SYSTEM - CPUExecContext(BaseCPU *_cpu, int _thread_num, System *_system, - AlphaITB *_itb, AlphaDTB *_dtb); + SimpleThread(BaseCPU *_cpu, int _thread_num, System *_system, + AlphaITB *_itb, AlphaDTB *_dtb, + bool use_kernel_stats = true); #else - CPUExecContext(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, - MemObject *memobj); - // Constructor to use XC to pass reg file around. Not used for anything - // else. - CPUExecContext(RegFile *regFile); + SimpleThread(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, + MemObject *memobj); + // Constructor to use SimpleThread to pass reg file around. Not + // used for anything else. + SimpleThread(RegFile *regFile); #endif - virtual ~CPUExecContext(); + virtual ~SimpleThread(); - virtual void takeOverFrom(ExecContext *oldContext); + virtual void takeOverFrom(ThreadContext *oldContext); void regStats(const std::string &name); void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); - BaseCPU *getCpuPtr() { return cpu; } - - ExecContext *getProxy() { return proxy; } + /*************************************************************** + * SimpleThread functions to provide CPU with access to various + * state, and to provide address translation methods. + **************************************************************/ - int getThreadNum() { return thread_num; } + /** Returns the pointer to this SimpleThread's ThreadContext. Used + * when a ThreadContext must be passed to objects outside of the + * CPU. + */ + ThreadContext *getTC() { return tc; } #if FULL_SYSTEM - System *getSystemPtr() { return system; } - - AlphaITB *getITBPtr() { return itb; } - - AlphaDTB *getDTBPtr() { return dtb; } - int getInstAsid() { return regs.instAsid(); } int getDataAsid() { return regs.dataAsid(); } Fault translateInstReq(RequestPtr &req) { - return itb->translate(req, proxy); + return itb->translate(req, tc); } Fault translateDataReadReq(RequestPtr &req) { - return dtb->translate(req, proxy, false); + return dtb->translate(req, tc, false); } Fault translateDataWriteReq(RequestPtr &req) { - return dtb->translate(req, proxy, true); + return dtb->translate(req, tc, true); } - FunctionalPort *getPhysPort() { return physPort; } - - /** Return a virtual port. If no exec context is specified then a static - * port is returned. Otherwise a port is created and returned. It must be - * deleted by deleteVirtPort(). */ - VirtualPort *getVirtPort(ExecContext *xc); + void dumpFuncProfile(); - void delVirtPort(VirtualPort *vp); + int readIntrFlag() { return regs.intrflag; } + void setIntrFlag(int val) { regs.intrflag = val; } + Fault hwrei(); + bool simPalCheck(int palFunc); #else - TranslatingPort *getMemPort() { return port; } - - Process *getProcessPtr() { return process; } - - int getInstAsid() { return asid; } - int getDataAsid() { return asid; } - Fault translateInstReq(RequestPtr &req) { return process->pTable->translate(req); @@ -287,9 +184,50 @@ class CPUExecContext { return process->pTable->translate(req); } +#endif + + /******************************************* + * ThreadContext interface functions. + ******************************************/ + + BaseCPU *getCpuPtr() { return cpu; } + + int getThreadNum() { return tid; } + +#if FULL_SYSTEM + System *getSystemPtr() { return system; } + + AlphaITB *getITBPtr() { return itb; } + + AlphaDTB *getDTBPtr() { return dtb; } + + FunctionalPort *getPhysPort() { return physPort; } + + /** Return a virtual port. If no thread context is specified then a static + * port is returned. Otherwise a port is created and returned. It must be + * deleted by deleteVirtPort(). */ + VirtualPort *getVirtPort(ThreadContext *tc); + void delVirtPort(VirtualPort *vp); #endif + Status status() const { return _status; } + + void setStatus(Status newStatus) { _status = newStatus; } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1); + + /// Set the status to Suspended. + void suspend(); + + /// Set the status to Unallocated. + void deallocate(); + + /// Set the status to Halted. + void halt(); + /* template <class T> Fault read(RequestPtr &req, T &data) @@ -361,14 +299,6 @@ class CPUExecContext */ virtual bool misspeculating(); - - MachInst getInst() { return inst; } - - void setInst(MachInst new_inst) - { - inst = new_inst; - } - Fault instRead(RequestPtr &req) { panic("instRead not implemented"); @@ -376,11 +306,9 @@ class CPUExecContext return NoFault; } - void setCpuId(int id) { cpu_id = id; } - - int readCpuId() { return cpu_id; } + void copyArchRegs(ThreadContext *tc); - void copyArchRegs(ExecContext *xc); + void clearArchRegs() { regs.clear(); } // // New accessors for new decoder. @@ -465,7 +393,6 @@ class CPUExecContext regs.setNextNPC(val); } - MiscReg readMiscReg(int misc_reg) { return regs.readMiscReg(misc_reg); @@ -473,7 +400,7 @@ class CPUExecContext MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) { - return regs.readMiscRegWithEffect(misc_reg, fault, proxy); + return regs.readMiscRegWithEffect(misc_reg, fault, tc); } Fault setMiscReg(int misc_reg, const MiscReg &val) @@ -483,7 +410,7 @@ class CPUExecContext Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) { - return regs.setMiscRegWithEffect(misc_reg, val, proxy); + return regs.setMiscRegWithEffect(misc_reg, val, tc); } unsigned readStCondFailures() { return storeCondFailures; } @@ -491,14 +418,8 @@ class CPUExecContext void setStCondFailures(unsigned sc_failures) { storeCondFailures = sc_failures; } - void clearArchRegs() { regs.clear(); } - #if FULL_SYSTEM - int readIntrFlag() { return regs.intrflag; } - void setIntrFlag(int val) { regs.intrflag = val; } - Fault hwrei(); bool inPalMode() { return AlphaISA::PcPAL(regs.readPC()); } - bool simPalCheck(int palFunc); #endif #if !FULL_SYSTEM @@ -520,12 +441,8 @@ class CPUExecContext void syscall(int64_t callnum) { - process->syscall(callnum, proxy); + process->syscall(callnum, tc); } - - Counter readFuncExeInst() { return func_exe_inst; } - - void setFuncExeInst(Counter new_val) { func_exe_inst = new_val; } #endif void changeRegFileContext(RegFile::ContextParam param, @@ -538,7 +455,7 @@ class CPUExecContext // for non-speculative execution context, spec_mode is always false inline bool -CPUExecContext::misspeculating() +SimpleThread::misspeculating() { return false; } diff --git a/src/cpu/smt.hh b/src/cpu/smt.hh index 9c52abf95..cac1a8fd5 100644 --- a/src/cpu/smt.hh +++ b/src/cpu/smt.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /** diff --git a/src/cpu/static_inst.cc b/src/cpu/static_inst.cc index c307dc6fc..c311d2282 100644 --- a/src/cpu/static_inst.cc +++ b/src/cpu/static_inst.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include <iostream> @@ -57,7 +60,7 @@ StaticInst::dumpDecodeCacheStats() } bool -StaticInst::hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) const +StaticInst::hasBranchTarget(Addr pc, ThreadContext *tc, Addr &tgt) const { if (isDirectCtrl()) { tgt = branchTarget(pc); @@ -65,7 +68,7 @@ StaticInst::hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) const } if (isIndirectCtrl()) { - tgt = branchTarget(xc); + tgt = branchTarget(tc); return true; } diff --git a/src/cpu/static_inst.hh b/src/cpu/static_inst.hh index 33c9144fb..bea52f510 100644 --- a/src/cpu/static_inst.hh +++ b/src/cpu/static_inst.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __CPU_STATIC_INST_HH__ @@ -41,13 +43,19 @@ // forward declarations struct AlphaSimpleImpl; -class ExecContext; +struct OzoneImpl; +struct SimpleImpl; +class ThreadContext; class DynInst; class Packet; template <class Impl> class AlphaDynInst; +template <class Impl> +class OzoneDynInst; + +class CheckerCPU; class FastCPU; class AtomicSimpleCPU; class TimingSimpleCPU; @@ -100,6 +108,7 @@ class StaticInstBase : public RefCounted IsMemRef, ///< References memory (load, store, or prefetch). IsLoad, ///< Reads from memory (load or prefetch). IsStore, ///< Writes to memory. + IsStoreConditional, ///< Store conditional instruction. IsInstPrefetch, ///< Instruction-cache prefetch. IsDataPrefetch, ///< Data-cache prefetch. IsCopy, ///< Fast Cache block copy @@ -124,6 +133,10 @@ class StaticInstBase : public RefCounted IsWriteBarrier, ///< Is a write barrier IsNonSpeculative, ///< Should not be executed speculatively + IsQuiesce, ///< Is a quiesce instruction + + IsIprAccess, ///< Accesses IPRs + IsUnverifiable, ///< Can't be verified by a checker NumFlags }; @@ -187,6 +200,7 @@ class StaticInstBase : public RefCounted bool isMemRef() const { return flags[IsMemRef]; } bool isLoad() const { return flags[IsLoad]; } bool isStore() const { return flags[IsStore]; } + bool isStoreConditional() const { return flags[IsStoreConditional]; } bool isInstPrefetch() const { return flags[IsInstPrefetch]; } bool isDataPrefetch() const { return flags[IsDataPrefetch]; } bool isCopy() const { return flags[IsCopy];} @@ -211,6 +225,9 @@ class StaticInstBase : public RefCounted bool isMemBarrier() const { return flags[IsMemBarrier]; } bool isWriteBarrier() const { return flags[IsWriteBarrier]; } bool isNonSpeculative() const { return flags[IsNonSpeculative]; } + bool isQuiesce() const { return flags[IsQuiesce]; } + bool isIprAccess() const { return flags[IsIprAccess]; } + bool isUnverifiable() const { return flags[IsUnverifiable]; } //@} /// Operation class. Used to select appropriate function unit in issue. @@ -340,12 +357,12 @@ class StaticInst : public StaticInstBase /** * Return the target address for an indirect branch (jump). The - * register value is read from the supplied execution context, so - * the result is valid only if the execution context is about to + * register value is read from the supplied thread context, so + * the result is valid only if the thread context is about to * execute the branch in question. Invalid if not an indirect * branch (i.e. isIndirectCtrl() should be true). */ - virtual Addr branchTarget(ExecContext *xc) const + virtual Addr branchTarget(ThreadContext *tc) const { panic("StaticInst::branchTarget() called on instruction " "that is not an indirect branch."); @@ -355,7 +372,7 @@ class StaticInst : public StaticInstBase * Return true if the instruction is a control transfer, and if so, * return the target address as well. */ - bool hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) const; + bool hasBranchTarget(Addr pc, ThreadContext *tc, Addr &tgt) const; /** * Return string representation of disassembled instruction. diff --git a/src/cpu/exec_context.hh b/src/cpu/thread_context.hh index 1f26183ab..48c8fa28d 100644 --- a/src/cpu/exec_context.hh +++ b/src/cpu/thread_context.hh @@ -24,10 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim */ -#ifndef __CPU_EXEC_CONTEXT_HH__ -#define __CPU_EXEC_CONTEXT_HH__ +#ifndef __CPU_THREAD_CONTEXT_HH__ +#define __CPU_THREAD_CONTEXT_HH__ #include "config/full_system.hh" #include "mem/request.hh" @@ -41,14 +43,34 @@ class AlphaDTB; class AlphaITB; class BaseCPU; +class EndQuiesceEvent; class Event; class TranslatingPort; class FunctionalPort; class VirtualPort; class Process; class System; +namespace Kernel { + class Statistics; +}; -class ExecContext +/** + * ThreadContext is the external interface to all thread state for + * anything outside of the CPU. It provides all accessor methods to + * state that might be needed by external objects, ranging from + * register values to things such as kernel stats. It is an abstract + * base class; the CPU can create its own ThreadContext by either + * deriving from it, or using the templated ProxyThreadContext. + * + * The ThreadContext is slightly different than the ExecContext. The + * ThreadContext provides access to an individual thread's state; an + * ExecContext provides ISA access to the CPU (meaning it is + * implicitly multithreaded on SMT systems). Additionally the + * ThreadState is an abstract class that exactly defines the + * interface; the ExecContext is a more implicit interface that must + * be implemented so that the ISA can access whatever state it needs. + */ +class ThreadContext { protected: typedef TheISA::RegFile RegFile; @@ -81,7 +103,7 @@ class ExecContext Halted }; - virtual ~ExecContext() { }; + virtual ~ThreadContext() { }; virtual BaseCPU *getCpuPtr() = 0; @@ -96,9 +118,11 @@ class ExecContext virtual AlphaDTB * getDTBPtr() = 0; + virtual Kernel::Statistics *getKernelStats() = 0; + virtual FunctionalPort *getPhysPort() = 0; - virtual VirtualPort *getVirtPort(ExecContext *xc = NULL) = 0; + virtual VirtualPort *getVirtPort(ThreadContext *tc = NULL) = 0; virtual void delVirtPort(VirtualPort *vp) = 0; #else @@ -128,7 +152,7 @@ class ExecContext virtual void dumpFuncProfile() = 0; #endif - virtual void takeOverFrom(ExecContext *old_context) = 0; + virtual void takeOverFrom(ThreadContext *old_context) = 0; virtual void regStats(const std::string &name) = 0; @@ -136,7 +160,7 @@ class ExecContext virtual void unserialize(Checkpoint *cp, const std::string §ion) = 0; #if FULL_SYSTEM - virtual Event *getQuiesceEvent() = 0; + virtual EndQuiesceEvent *getQuiesceEvent() = 0; // Not necessarily the best location for these... // Having an extra function just to read these is obnoxious @@ -149,20 +173,11 @@ class ExecContext virtual int getThreadNum() = 0; - virtual int getInstAsid() = 0; - virtual int getDataAsid() = 0; - - virtual Fault translateInstReq(RequestPtr &req) = 0; - - virtual Fault translateDataReadReq(RequestPtr &req) = 0; - - virtual Fault translateDataWriteReq(RequestPtr &req) = 0; - // Also somewhat obnoxious. Really only used for the TLB fault. // However, may be quite useful in SPARC. virtual TheISA::MachInst getInst() = 0; - virtual void copyArchRegs(ExecContext *xc) = 0; + virtual void copyArchRegs(ThreadContext *tc) = 0; virtual void clearArchRegs() = 0; @@ -216,11 +231,7 @@ class ExecContext virtual void setStCondFailures(unsigned sc_failures) = 0; #if FULL_SYSTEM - virtual int readIntrFlag() = 0; - virtual void setIntrFlag(int val) = 0; - virtual Fault hwrei() = 0; virtual bool inPalMode() = 0; - virtual bool simPalCheck(int palFunc) = 0; #endif // Only really makes sense for old CPU model. Still could be useful though. @@ -238,212 +249,198 @@ class ExecContext // Same with st cond failures. virtual Counter readFuncExeInst() = 0; - - virtual void setFuncExeInst(Counter new_val) = 0; #endif virtual void changeRegFileContext(RegFile::ContextParam param, RegFile::ContextVal val) = 0; }; -template <class XC> -class ProxyExecContext : public ExecContext +/** + * ProxyThreadContext class that provides a way to implement a + * ThreadContext without having to derive from it. ThreadContext is an + * abstract class, so anything that derives from it and uses its + * interface will pay the overhead of virtual function calls. This + * class is created to enable a user-defined Thread object to be used + * wherever ThreadContexts are used, without paying the overhead of + * virtual function calls when it is used by itself. See + * simple_thread.hh for an example of this. + */ +template <class TC> +class ProxyThreadContext : public ThreadContext { public: - ProxyExecContext(XC *actual_xc) - { actualXC = actual_xc; } + ProxyThreadContext(TC *actual_tc) + { actualTC = actual_tc; } private: - XC *actualXC; + TC *actualTC; public: - BaseCPU *getCpuPtr() { return actualXC->getCpuPtr(); } + BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } - void setCpuId(int id) { actualXC->setCpuId(id); } + void setCpuId(int id) { actualTC->setCpuId(id); } - int readCpuId() { return actualXC->readCpuId(); } + int readCpuId() { return actualTC->readCpuId(); } #if FULL_SYSTEM - System *getSystemPtr() { return actualXC->getSystemPtr(); } + System *getSystemPtr() { return actualTC->getSystemPtr(); } - AlphaITB *getITBPtr() { return actualXC->getITBPtr(); } + AlphaITB *getITBPtr() { return actualTC->getITBPtr(); } - AlphaDTB *getDTBPtr() { return actualXC->getDTBPtr(); } + AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); } - FunctionalPort *getPhysPort() { return actualXC->getPhysPort(); } + Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } - VirtualPort *getVirtPort(ExecContext *xc = NULL) { return actualXC->getVirtPort(xc); } + FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } - void delVirtPort(VirtualPort *vp) { return actualXC->delVirtPort(vp); } + VirtualPort *getVirtPort(ThreadContext *tc = NULL) { return actualTC->getVirtPort(tc); } + + void delVirtPort(VirtualPort *vp) { return actualTC->delVirtPort(vp); } #else - TranslatingPort *getMemPort() { return actualXC->getMemPort(); } + TranslatingPort *getMemPort() { return actualTC->getMemPort(); } - Process *getProcessPtr() { return actualXC->getProcessPtr(); } + Process *getProcessPtr() { return actualTC->getProcessPtr(); } #endif - Status status() const { return actualXC->status(); } + Status status() const { return actualTC->status(); } - void setStatus(Status new_status) { actualXC->setStatus(new_status); } + void setStatus(Status new_status) { actualTC->setStatus(new_status); } /// Set the status to Active. Optional delay indicates number of /// cycles to wait before beginning execution. - void activate(int delay = 1) { actualXC->activate(delay); } + void activate(int delay = 1) { actualTC->activate(delay); } /// Set the status to Suspended. - void suspend() { actualXC->suspend(); } + void suspend() { actualTC->suspend(); } /// Set the status to Unallocated. - void deallocate() { actualXC->deallocate(); } + void deallocate() { actualTC->deallocate(); } /// Set the status to Halted. - void halt() { actualXC->halt(); } + void halt() { actualTC->halt(); } #if FULL_SYSTEM - void dumpFuncProfile() { actualXC->dumpFuncProfile(); } + void dumpFuncProfile() { actualTC->dumpFuncProfile(); } #endif - void takeOverFrom(ExecContext *oldContext) - { actualXC->takeOverFrom(oldContext); } + void takeOverFrom(ThreadContext *oldContext) + { actualTC->takeOverFrom(oldContext); } - void regStats(const std::string &name) { actualXC->regStats(name); } + void regStats(const std::string &name) { actualTC->regStats(name); } - void serialize(std::ostream &os) { actualXC->serialize(os); } + void serialize(std::ostream &os) { actualTC->serialize(os); } void unserialize(Checkpoint *cp, const std::string §ion) - { actualXC->unserialize(cp, section); } + { actualTC->unserialize(cp, section); } #if FULL_SYSTEM - Event *getQuiesceEvent() { return actualXC->getQuiesceEvent(); } + EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); } - Tick readLastActivate() { return actualXC->readLastActivate(); } - Tick readLastSuspend() { return actualXC->readLastSuspend(); } + Tick readLastActivate() { return actualTC->readLastActivate(); } + Tick readLastSuspend() { return actualTC->readLastSuspend(); } - void profileClear() { return actualXC->profileClear(); } - void profileSample() { return actualXC->profileSample(); } + void profileClear() { return actualTC->profileClear(); } + void profileSample() { return actualTC->profileSample(); } #endif - int getThreadNum() { return actualXC->getThreadNum(); } - - int getInstAsid() { return actualXC->getInstAsid(); } - int getDataAsid() { return actualXC->getDataAsid(); } - - Fault translateInstReq(RequestPtr &req) - { return actualXC->translateInstReq(req); } - - Fault translateDataReadReq(RequestPtr &req) - { return actualXC->translateDataReadReq(req); } - - Fault translateDataWriteReq(RequestPtr &req) - { return actualXC->translateDataWriteReq(req); } + int getThreadNum() { return actualTC->getThreadNum(); } // @todo: Do I need this? - MachInst getInst() { return actualXC->getInst(); } + MachInst getInst() { return actualTC->getInst(); } // @todo: Do I need this? - void copyArchRegs(ExecContext *xc) { actualXC->copyArchRegs(xc); } + void copyArchRegs(ThreadContext *tc) { actualTC->copyArchRegs(tc); } - void clearArchRegs() { actualXC->clearArchRegs(); } + void clearArchRegs() { actualTC->clearArchRegs(); } // // New accessors for new decoder. // uint64_t readIntReg(int reg_idx) - { return actualXC->readIntReg(reg_idx); } + { return actualTC->readIntReg(reg_idx); } FloatReg readFloatReg(int reg_idx, int width) - { return actualXC->readFloatReg(reg_idx, width); } + { return actualTC->readFloatReg(reg_idx, width); } FloatReg readFloatReg(int reg_idx) - { return actualXC->readFloatReg(reg_idx); } + { return actualTC->readFloatReg(reg_idx); } FloatRegBits readFloatRegBits(int reg_idx, int width) - { return actualXC->readFloatRegBits(reg_idx, width); } + { return actualTC->readFloatRegBits(reg_idx, width); } FloatRegBits readFloatRegBits(int reg_idx) - { return actualXC->readFloatRegBits(reg_idx); } + { return actualTC->readFloatRegBits(reg_idx); } void setIntReg(int reg_idx, uint64_t val) - { actualXC->setIntReg(reg_idx, val); } + { actualTC->setIntReg(reg_idx, val); } void setFloatReg(int reg_idx, FloatReg val, int width) - { actualXC->setFloatReg(reg_idx, val, width); } + { actualTC->setFloatReg(reg_idx, val, width); } void setFloatReg(int reg_idx, FloatReg val) - { actualXC->setFloatReg(reg_idx, val); } + { actualTC->setFloatReg(reg_idx, val); } void setFloatRegBits(int reg_idx, FloatRegBits val, int width) - { actualXC->setFloatRegBits(reg_idx, val, width); } + { actualTC->setFloatRegBits(reg_idx, val, width); } void setFloatRegBits(int reg_idx, FloatRegBits val) - { actualXC->setFloatRegBits(reg_idx, val); } + { actualTC->setFloatRegBits(reg_idx, val); } - uint64_t readPC() { return actualXC->readPC(); } + uint64_t readPC() { return actualTC->readPC(); } - void setPC(uint64_t val) { actualXC->setPC(val); } + void setPC(uint64_t val) { actualTC->setPC(val); } - uint64_t readNextPC() { return actualXC->readNextPC(); } + uint64_t readNextPC() { return actualTC->readNextPC(); } - void setNextPC(uint64_t val) { actualXC->setNextPC(val); } + void setNextPC(uint64_t val) { actualTC->setNextPC(val); } - uint64_t readNextNPC() { return actualXC->readNextNPC(); } + uint64_t readNextNPC() { return actualTC->readNextNPC(); } - void setNextNPC(uint64_t val) { actualXC->setNextNPC(val); } + void setNextNPC(uint64_t val) { actualTC->setNextNPC(val); } MiscReg readMiscReg(int misc_reg) - { return actualXC->readMiscReg(misc_reg); } + { return actualTC->readMiscReg(misc_reg); } MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) - { return actualXC->readMiscRegWithEffect(misc_reg, fault); } + { return actualTC->readMiscRegWithEffect(misc_reg, fault); } Fault setMiscReg(int misc_reg, const MiscReg &val) - { return actualXC->setMiscReg(misc_reg, val); } + { return actualTC->setMiscReg(misc_reg, val); } Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) - { return actualXC->setMiscRegWithEffect(misc_reg, val); } + { return actualTC->setMiscRegWithEffect(misc_reg, val); } unsigned readStCondFailures() - { return actualXC->readStCondFailures(); } + { return actualTC->readStCondFailures(); } void setStCondFailures(unsigned sc_failures) - { actualXC->setStCondFailures(sc_failures); } - + { actualTC->setStCondFailures(sc_failures); } #if FULL_SYSTEM - int readIntrFlag() { return actualXC->readIntrFlag(); } - - void setIntrFlag(int val) { actualXC->setIntrFlag(val); } - - Fault hwrei() { return actualXC->hwrei(); } - - bool inPalMode() { return actualXC->inPalMode(); } - - bool simPalCheck(int palFunc) { return actualXC->simPalCheck(palFunc); } + bool inPalMode() { return actualTC->inPalMode(); } #endif // @todo: Fix this! - bool misspeculating() { return actualXC->misspeculating(); } + bool misspeculating() { return actualTC->misspeculating(); } #if !FULL_SYSTEM - IntReg getSyscallArg(int i) { return actualXC->getSyscallArg(i); } + IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); } // used to shift args for indirect syscall void setSyscallArg(int i, IntReg val) - { actualXC->setSyscallArg(i, val); } + { actualTC->setSyscallArg(i, val); } void setSyscallReturn(SyscallReturn return_value) - { actualXC->setSyscallReturn(return_value); } - - void syscall(int64_t callnum) { actualXC->syscall(callnum); } + { actualTC->setSyscallReturn(return_value); } - Counter readFuncExeInst() { return actualXC->readFuncExeInst(); } + void syscall(int64_t callnum) { actualTC->syscall(callnum); } - void setFuncExeInst(Counter new_val) - { return actualXC->setFuncExeInst(new_val); } + Counter readFuncExeInst() { return actualTC->readFuncExeInst(); } #endif void changeRegFileContext(RegFile::ContextParam param, RegFile::ContextVal val) { - actualXC->changeRegFileContext(param, val); + actualTC->changeRegFileContext(param, val); } }; diff --git a/src/cpu/thread_state.cc b/src/cpu/thread_state.cc new file mode 100644 index 000000000..dcfa93c3e --- /dev/null +++ b/src/cpu/thread_state.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#include "base/output.hh" +#include "cpu/profile.hh" +#include "cpu/thread_state.hh" + +#if FULL_SYSTEM +ThreadState::ThreadState(int _cpuId, int _tid) + : cpuId(_cpuId), tid(_tid), lastActivate(0), lastSuspend(0), + profile(NULL), profileNode(NULL), profilePC(0), quiesceEvent(NULL), + funcExeInst(0), storeCondFailures(0) +#else +ThreadState::ThreadState(int _cpuId, int _tid, MemObject *mem, + Process *_process, short _asid) + : cpuId(_cpuId), tid(_tid), lastActivate(0), lastSuspend(0), + process(_process), asid(_asid), + funcExeInst(0), storeCondFailures(0) +#endif +{ + numInst = 0; + numLoad = 0; +} + +#if FULL_SYSTEM + +void +ThreadState::profileClear() +{ + if (profile) + profile->clear(); +} + +void +ThreadState::profileSample() +{ + if (profile) + profile->sample(profileNode, profilePC); +} + +#endif diff --git a/src/cpu/thread_state.hh b/src/cpu/thread_state.hh new file mode 100644 index 000000000..de9b2f14e --- /dev/null +++ b/src/cpu/thread_state.hh @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_THREAD_STATE_HH__ +#define __CPU_THREAD_STATE_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" + +#if !FULL_SYSTEM +#include "mem/mem_object.hh" +#include "mem/translating_port.hh" +#include "sim/process.hh" +#endif + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +namespace Kernel { + class Statistics; +}; +#endif + +/** + * Struct for holding general thread state that is needed across CPU + * models. This includes things such as pointers to the process, + * memory, quiesce events, and certain stats. This can be expanded + * to hold more thread-specific stats within it. + */ +struct ThreadState { + typedef ThreadContext::Status Status; + +#if FULL_SYSTEM + ThreadState(int _cpuId, int _tid); +#else + ThreadState(int _cpuId, int _tid, MemObject *mem, + Process *_process, short _asid); +#endif + + void setCpuId(int id) { cpuId = id; } + + int readCpuId() { return cpuId; } + + void setTid(int id) { tid = id; } + + int readTid() { return tid; } + + Tick readLastActivate() { return lastActivate; } + + Tick readLastSuspend() { return lastSuspend; } + +#if FULL_SYSTEM + void dumpFuncProfile(); + + EndQuiesceEvent *getQuiesceEvent() { return quiesceEvent; } + + void profileClear(); + + void profileSample(); + + Kernel::Statistics *getKernelStats() { return kernelStats; } + + FunctionalPort *getPhysPort() { return physPort; } + + void setPhysPort(FunctionalPort *port) { physPort = port; } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) { return virtPort; } + + void setVirtPort(VirtualPort *port) { virtPort = port; } +#else + Process *getProcessPtr() { return process; } + + TranslatingPort *getMemPort() { return port; } + + void setMemPort(TranslatingPort *_port) { port = _port; } + + int getInstAsid() { return asid; } + int getDataAsid() { return asid; } +#endif + + /** Sets the current instruction being committed. */ + void setInst(TheISA::MachInst _inst) { inst = _inst; } + + /** Returns the current instruction being committed. */ + TheISA::MachInst getInst() { return inst; } + + /** Reads the number of instructions functionally executed and + * committed. + */ + Counter readFuncExeInst() { return funcExeInst; } + + /** Sets the total number of instructions functionally executed + * and committed. + */ + void setFuncExeInst(Counter new_val) { funcExeInst = new_val; } + + /** Returns the status of this thread. */ + Status status() const { return _status; } + + /** Sets the status of this thread. */ + void setStatus(Status new_status) { _status = new_status; } + + /** Number of instructions committed. */ + Counter numInst; + /** Stat for number instructions committed. */ + Stats::Scalar<> numInsts; + /** Stat for number of memory references. */ + Stats::Scalar<> numMemRefs; + + /** Number of simulated loads, used for tracking events based on + * the number of loads committed. + */ + Counter numLoad; + + /** The number of simulated loads committed prior to this run. */ + Counter startNumLoad; + + protected: + ThreadContext::Status _status; + + // ID of this context w.r.t. the System or Process object to which + // it belongs. For full-system mode, this is the system CPU ID. + int cpuId; + + // Index of hardware thread context on the CPU that this represents. + int tid; + + public: + /** Last time activate was called on this thread. */ + Tick lastActivate; + + /** Last time suspend was called on this thread. */ + Tick lastSuspend; + +#if FULL_SYSTEM + public: + FunctionProfile *profile; + ProfileNode *profileNode; + Addr profilePC; + EndQuiesceEvent *quiesceEvent; + + Kernel::Statistics *kernelStats; + protected: + /** A functional port outgoing only for functional accesses to physical + * addresses.*/ + FunctionalPort *physPort; + + /** A functional port, outgoing only, for functional accesse to virtual + * addresses. That doen't require execution context information */ + VirtualPort *virtPort; +#else + TranslatingPort *port; + + 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 + + /** Current instruction the thread is committing. Only set and + * used for DTB faults currently. + */ + TheISA::MachInst inst; + + public: + /** + * 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/src/cpu/trace/opt_cpu.cc b/src/cpu/trace/opt_cpu.cc index 6cd23b0dd..996e89f01 100644 --- a/src/cpu/trace/opt_cpu.cc +++ b/src/cpu/trace/opt_cpu.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** @@ -174,7 +176,7 @@ OptCPU::tick() fprintf(stderr,"sys.cpu.misses %d #opt cache misses\n",misses); fprintf(stderr,"sys.cpu.hits %d #opt cache hits\n", hits); fprintf(stderr,"sys.cpu.accesses %d #opt cache acceses\n", references); - new SimExitEvent("Finshed Memory Trace"); + exitSimLoop("end of memory trace reached"); } void diff --git a/src/cpu/trace/opt_cpu.hh b/src/cpu/trace/opt_cpu.hh index f81691733..dfb122319 100644 --- a/src/cpu/trace/opt_cpu.hh +++ b/src/cpu/trace/opt_cpu.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/ibm_reader.cc b/src/cpu/trace/reader/ibm_reader.cc index 420101b63..87e13f307 100644 --- a/src/cpu/trace/reader/ibm_reader.cc +++ b/src/cpu/trace/reader/ibm_reader.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/ibm_reader.hh b/src/cpu/trace/reader/ibm_reader.hh index ce29206a2..a72f62e03 100644 --- a/src/cpu/trace/reader/ibm_reader.hh +++ b/src/cpu/trace/reader/ibm_reader.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/itx_reader.cc b/src/cpu/trace/reader/itx_reader.cc index 39ba27393..e4738eed8 100644 --- a/src/cpu/trace/reader/itx_reader.cc +++ b/src/cpu/trace/reader/itx_reader.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/itx_reader.hh b/src/cpu/trace/reader/itx_reader.hh index a16a08085..63a4c9ac9 100644 --- a/src/cpu/trace/reader/itx_reader.hh +++ b/src/cpu/trace/reader/itx_reader.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/m5_reader.cc b/src/cpu/trace/reader/m5_reader.cc index ce44672f2..8efcb022b 100644 --- a/src/cpu/trace/reader/m5_reader.cc +++ b/src/cpu/trace/reader/m5_reader.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/m5_reader.hh b/src/cpu/trace/reader/m5_reader.hh index 974a83ffa..5007bfd5b 100644 --- a/src/cpu/trace/reader/m5_reader.hh +++ b/src/cpu/trace/reader/m5_reader.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/mem_trace_reader.cc b/src/cpu/trace/reader/mem_trace_reader.cc index 769f0be27..5623f168a 100644 --- a/src/cpu/trace/reader/mem_trace_reader.cc +++ b/src/cpu/trace/reader/mem_trace_reader.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/mem_trace_reader.hh b/src/cpu/trace/reader/mem_trace_reader.hh index b433cdbdd..628a3ecdc 100644 --- a/src/cpu/trace/reader/mem_trace_reader.hh +++ b/src/cpu/trace/reader/mem_trace_reader.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/trace_cpu.cc b/src/cpu/trace/trace_cpu.cc index 20d0a567f..3c9da4849 100644 --- a/src/cpu/trace/trace_cpu.cc +++ b/src/cpu/trace/trace_cpu.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** @@ -106,7 +108,7 @@ TraceCPU::tick() if (!nextReq) { // No more requests to send. Finish trailing events and exit. if (mainEventQueue.empty()) { - new SimExitEvent("Finshed Memory Trace"); + exitSimLoop("end of memory trace reached"); } else { tickEvent.schedule(mainEventQueue.nextEventTime() + cycles(1)); } diff --git a/src/cpu/trace/trace_cpu.hh b/src/cpu/trace/trace_cpu.hh index 69ca35321..9c96d71d5 100644 --- a/src/cpu/trace/trace_cpu.hh +++ b/src/cpu/trace/trace_cpu.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor */ /** diff --git a/src/dev/alpha_access.h b/src/dev/alpha_access.h index 5a1df6f39..4adeaf84b 100644 --- a/src/dev/alpha_access.h +++ b/src/dev/alpha_access.h @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __ALPHA_ACCESS_H__ diff --git a/src/dev/alpha_console.cc b/src/dev/alpha_console.cc index 0b4bb048c..181bbf934 100644 --- a/src/dev/alpha_console.cc +++ b/src/dev/alpha_console.cc @@ -24,6 +24,11 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ali Saidi + * Steve Reinhardt + * Erik Hallnor */ /** @file @@ -38,7 +43,7 @@ #include "base/str.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "dev/alpha_console.hh" #include "dev/platform.hh" #include "dev/simconsole.hh" @@ -98,7 +103,6 @@ AlphaConsole::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; pkt->allocate(); @@ -191,8 +195,6 @@ AlphaConsole::read(Packet *pkt) Tick AlphaConsole::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff --git a/src/dev/alpha_console.hh b/src/dev/alpha_console.hh index 34d21e15c..b6360d40f 100644 --- a/src/dev/alpha_console.hh +++ b/src/dev/alpha_console.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /** @file diff --git a/src/dev/baddev.cc b/src/dev/baddev.cc index 66fbd8a86..e4297be19 100644 --- a/src/dev/baddev.cc +++ b/src/dev/baddev.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/baddev.hh b/src/dev/baddev.hh index 35ae0382a..d7d778af4 100644 --- a/src/dev/baddev.hh +++ b/src/dev/baddev.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/disk_image.cc b/src/dev/disk_image.cc index 185a8b083..f70d2ccdb 100644 --- a/src/dev/disk_image.cc +++ b/src/dev/disk_image.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /** @file diff --git a/src/dev/disk_image.hh b/src/dev/disk_image.hh index 648aa20c6..45d5af649 100644 --- a/src/dev/disk_image.hh +++ b/src/dev/disk_image.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /** @file diff --git a/src/dev/etherbus.cc b/src/dev/etherbus.cc index 906e324d3..348bb818a 100644 --- a/src/dev/etherbus.cc +++ b/src/dev/etherbus.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherbus.hh b/src/dev/etherbus.hh index 4a364abd8..2ff1d0a5e 100644 --- a/src/dev/etherbus.hh +++ b/src/dev/etherbus.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherdump.cc b/src/dev/etherdump.cc index cb5f0b70e..0c986cc21 100644 --- a/src/dev/etherdump.cc +++ b/src/dev/etherdump.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherdump.hh b/src/dev/etherdump.hh index 8bba073fe..f3080f341 100644 --- a/src/dev/etherdump.hh +++ b/src/dev/etherdump.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherint.cc b/src/dev/etherint.cc index 8fb047373..3f81591e9 100644 --- a/src/dev/etherint.cc +++ b/src/dev/etherint.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "dev/etherint.hh" diff --git a/src/dev/etherint.hh b/src/dev/etherint.hh index 1f641fadb..dfc224ecc 100644 --- a/src/dev/etherint.hh +++ b/src/dev/etherint.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherlink.cc b/src/dev/etherlink.cc index 5b6531c2e..cd3812270 100644 --- a/src/dev/etherlink.cc +++ b/src/dev/etherlink.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ron Dreslinski */ /* @file diff --git a/src/dev/etherlink.hh b/src/dev/etherlink.hh index 570444e1b..bb2854810 100644 --- a/src/dev/etherlink.hh +++ b/src/dev/etherlink.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherpkt.cc b/src/dev/etherpkt.cc index 85e18e981..5c552b4bd 100644 --- a/src/dev/etherpkt.cc +++ b/src/dev/etherpkt.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/dev/etherpkt.hh b/src/dev/etherpkt.hh index 01741b3d5..80c7baff7 100644 --- a/src/dev/etherpkt.hh +++ b/src/dev/etherpkt.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Lisa Hsu */ /* @file diff --git a/src/dev/ethertap.cc b/src/dev/ethertap.cc index b5abb1d62..2d72383c5 100644 --- a/src/dev/ethertap.cc +++ b/src/dev/ethertap.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/ethertap.hh b/src/dev/ethertap.hh index 40ce6af0b..f64ed7187 100644 --- a/src/dev/ethertap.hh +++ b/src/dev/ethertap.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/ide_ctrl.cc b/src/dev/ide_ctrl.cc index eb03d5db8..63435e87c 100644 --- a/src/dev/ide_ctrl.cc +++ b/src/dev/ide_ctrl.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz + * Ali Saidi + * Miguel Serrano */ #include <cstddef> @@ -430,7 +434,6 @@ IdeController::read(Packet *pkt) IdeRegType reg_type; int disk; - pkt->time += pioDelay; pkt->allocate(); if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) panic("Bad IDE read size: %d\n", pkt->getSize()); @@ -518,8 +521,6 @@ IdeController::write(Packet *pkt) int disk; uint8_t oldVal, newVal; - pkt->time += pioDelay; - parseAddr(pkt->getAddr(), offset, channel, reg_type); if (!io_enabled) { diff --git a/src/dev/ide_ctrl.hh b/src/dev/ide_ctrl.hh index dda2cbb66..1d30c8b31 100644 --- a/src/dev/ide_ctrl.hh +++ b/src/dev/ide_ctrl.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz + * Miguel Serrano */ /** @file diff --git a/src/dev/ide_disk.cc b/src/dev/ide_disk.cc index 909f35c60..dc78021f8 100644 --- a/src/dev/ide_disk.cc +++ b/src/dev/ide_disk.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz + * Ali Saidi */ /** @file diff --git a/src/dev/ide_disk.hh b/src/dev/ide_disk.hh index 5379e5e73..fb0614d4d 100644 --- a/src/dev/ide_disk.hh +++ b/src/dev/ide_disk.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz */ /** @file diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc index 87fdbb765..485216874 100644 --- a/src/dev/io_device.cc +++ b/src/dev/io_device.cc @@ -24,8 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ +#include "base/trace.hh" #include "dev/io_device.hh" #include "sim/builder.hh" @@ -55,12 +59,13 @@ PioPort::getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) } -Packet * +void PioPort::recvRetry() { Packet* pkt = transmitList.front(); - transmitList.pop_front(); - return pkt; + if (Port::sendTiming(pkt)) { + transmitList.pop_front(); + } } @@ -74,13 +79,14 @@ PioPort::SendEvent::process() } + bool PioPort::recvTiming(Packet *pkt) { - device->recvAtomic(pkt); + Tick latency = device->recvAtomic(pkt); // turn packet around to go back to requester pkt->makeTimingResponse(); - sendTiming(pkt, pkt->time - pkt->req->getTime()); + sendTiming(pkt, latency); return true; } @@ -114,18 +120,35 @@ DmaPort::DmaPort(DmaDevice *dev, Platform *p) bool DmaPort::recvTiming(Packet *pkt) { - if (pkt->senderState) { + + + if (pkt->result == Packet::Nacked) { + DPRINTF(DMA, "Received nacked Pkt %#x with State: %#x Addr: %#x\n", + pkt, pkt->senderState, pkt->getAddr()); + pkt->reinitNacked(); + sendDma(pkt, true); + } else if (pkt->senderState) { DmaReqState *state; + DPRINTF(DMA, "Received response Pkt %#x with State: %#x Addr: %#x\n", + pkt, pkt->senderState, pkt->getAddr()); state = dynamic_cast<DmaReqState*>(pkt->senderState); - state->completionEvent->schedule(pkt->time - pkt->req->getTime()); + pendingCount--; + + assert(pendingCount >= 0); + assert(state); + + state->numBytes += pkt->req->getSize(); + if (state->totBytes == state->numBytes) { + state->completionEvent->process(); + delete state; + } delete pkt->req; delete pkt; } else { - delete pkt->req; - delete pkt; + panic("Got packet without sender state... huh?\n"); } - return Packet::Success; + return true; } DmaDevice::DmaDevice(Params *p) @@ -133,20 +156,21 @@ DmaDevice::DmaDevice(Params *p) { } void -DmaPort::SendEvent::process() -{ - if (port->Port::sendTiming(packet)) - return; - - port->transmitList.push_back(packet); -} - -Packet * DmaPort::recvRetry() { Packet* pkt = transmitList.front(); - transmitList.pop_front(); - return pkt; + bool result = true; + while (result && transmitList.size()) { + DPRINTF(DMA, "Retry on Packet %#x with senderState: %#x\n", + pkt, pkt->senderState); + result = sendTiming(pkt); + if (result) { + DPRINTF(DMA, "-- Done\n"); + transmitList.pop_front(); + } else { + DPRINTF(DMA, "-- Failed, queued\n"); + } + } } @@ -156,27 +180,19 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, { assert(event); - int prevSize = 0; + DmaReqState *reqState = new DmaReqState(event, this, size); for (ChunkGenerator gen(addr, size, peerBlockSize()); !gen.done(); gen.next()) { - Request *req = new Request(false); - req->setPaddr(gen.addr()); - req->setSize(gen.size()); - req->setTime(curTick); + Request *req = new Request(gen.addr(), gen.size(), 0); Packet *pkt = new Packet(req, cmd, Packet::Broadcast); // Increment the data pointer on a write if (data) - pkt->dataStatic(data + prevSize) ; + pkt->dataStatic(data + gen.complete()); - prevSize += gen.size(); + pkt->senderState = reqState; - // Set the last bit of the dma as the final packet for this dma - // and set it's completion event. - if (prevSize == size) { - pkt->senderState = new DmaReqState(event, true); - } assert(pendingCount >= 0); pendingCount++; sendDma(pkt); @@ -185,28 +201,40 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, void -DmaPort::sendDma(Packet *pkt) +DmaPort::sendDma(Packet *pkt, bool front) { // some kind of selction between access methods // more work is going to have to be done to make // switching actually work /* MemState state = device->platform->system->memState; - if (state == Timing) { - if (!sendTiming(pkt)) - transmitList.push_back(&packet); - } else if (state == Atomic) {*/ + if (state == Timing) { */ + DPRINTF(DMA, "Attempting to send Packet %#x with addr: %#x\n", + pkt, pkt->getAddr()); + if (transmitList.size() || !sendTiming(pkt)) { + if (front) + transmitList.push_front(pkt); + else + transmitList.push_back(pkt); + DPRINTF(DMA, "-- Failed: queued\n"); + } else { + DPRINTF(DMA, "-- Done\n"); + } + /* } else if (state == Atomic) { sendAtomic(pkt); if (pkt->senderState) { DmaReqState *state = dynamic_cast<DmaReqState*>(pkt->senderState); - state->completionEvent->schedule(curTick + (pkt->time - pkt->req->getTime()) +1); + assert(state); + state->completionEvent->schedule(curTick + (pkt->time - + pkt->req->getTime()) +1); + delete state; } pendingCount--; assert(pendingCount >= 0); delete pkt->req; delete pkt; -/* } else if (state == Functional) { + } else if (state == Functional) { sendFunctional(pkt); // Is this correct??? completionEvent->schedule(pkt->req->responseTime - pkt->req->requestTime); diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh index 74730ad92..195ca0fb7 100644 --- a/src/dev/io_device.hh +++ b/src/dev/io_device.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __DEV_IO_DEVICE_HH__ @@ -105,8 +108,9 @@ class PioPort : public Port void sendTiming(Packet *pkt, Tick time) { new PioPort::SendEvent(this, pkt, time); } - /** This function pops the last element off the transmit list and sends it.*/ - virtual Packet *recvRetry(); + /** This function is notification that the device should attempt to send a + * packet again. */ + virtual void recvRetry(); public: PioPort(PioDevice *dev, Platform *p); @@ -117,10 +121,22 @@ class PioPort : public Port struct DmaReqState : public Packet::SenderState { + /** Event to call on the device when this transaction (all packets) + * complete. */ Event *completionEvent; + + /** Where we came from for some sanity checking. */ + Port *outPort; + + /** Total number of bytes that this transaction involves. */ + Addr totBytes; + + /** Number of bytes that have been acked for this transaction. */ + Addr numBytes; + bool final; - DmaReqState(Event *ce, bool f) - : completionEvent(ce), final(f) + DmaReqState(Event *ce, Port *p, Addr tb) + : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0) {} }; @@ -146,29 +162,12 @@ class DmaPort : public Port virtual void recvStatusChange(Status status) { ; } - virtual Packet *recvRetry() ; + virtual void recvRetry() ; virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) { resp.clear(); snoop.clear(); } - class SendEvent : public Event - { - DmaPort *port; - Packet *packet; - - SendEvent(PioPort *p, Packet *pkt, Tick t) - : Event(&mainEventQueue), packet(pkt) - { schedule(curTick + t); } - - virtual void process(); - - virtual const char *description() - { return "Future scheduled sendTiming event"; } - - friend class DmaPort; - }; - - void sendDma(Packet *pkt); + void sendDma(Packet *pkt, bool front = false); public: DmaPort(DmaDevice *dev, Platform *p); @@ -178,8 +177,6 @@ class DmaPort : public Port bool dmaPending() { return pendingCount > 0; } - friend class DmaPort::SendEvent; - }; /** diff --git a/src/dev/isa_fake.cc b/src/dev/isa_fake.cc index c303ffc30..9622f015c 100644 --- a/src/dev/isa_fake.cc +++ b/src/dev/isa_fake.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Miguel Serrano + * Ali Saidi */ /** @file @@ -54,8 +57,6 @@ IsaFake::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; - DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); switch (pkt->getSize()) { @@ -80,7 +81,6 @@ IsaFake::read(Packet *pkt) Tick IsaFake::write(Packet *pkt) { - pkt->time += pioDelay; DPRINTF(Tsunami, "write - va=%#x size=%d \n", pkt->getAddr(), pkt->getSize()); pkt->result = Packet::Success; return pioDelay; diff --git a/src/dev/isa_fake.hh b/src/dev/isa_fake.hh index 65d44f6a5..a7a469e17 100644 --- a/src/dev/isa_fake.hh +++ b/src/dev/isa_fake.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Miguel Serrano + * Ali Saidi */ /** @file diff --git a/src/dev/ns_gige.cc b/src/dev/ns_gige.cc index 963675847..decffaf73 100644 --- a/src/dev/ns_gige.cc +++ b/src/dev/ns_gige.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu */ /** @file @@ -35,7 +37,7 @@ #include "arch/alpha/ev5.hh" #include "base/inet.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "dev/etherlink.hh" #include "dev/ns_gige.hh" #include "dev/pciconfigall.hh" @@ -492,7 +494,6 @@ NSGigE::read(Packet *pkt) { assert(ioEnable); - pkt->time += pioDelay; pkt->allocate(); //The mask is to give you only the offset into the device register file @@ -728,8 +729,6 @@ NSGigE::write(Packet *pkt) DPRINTF(EthernetPIO, "write da=%#x pa=%#x size=%d\n", daddr, pkt->getAddr(), pkt->getSize()); - pkt->time += pioDelay; - if (daddr > LAST && daddr <= RESERVED) { panic("Accessing reserved register"); } else if (daddr > RESERVED && daddr <= 0x3FC) { diff --git a/src/dev/ns_gige.hh b/src/dev/ns_gige.hh index 51520fd00..2de11c951 100644 --- a/src/dev/ns_gige.hh +++ b/src/dev/ns_gige.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu */ /** @file diff --git a/src/dev/ns_gige_reg.h b/src/dev/ns_gige_reg.h index 5f6fa2cc5..80ac5800d 100644 --- a/src/dev/ns_gige_reg.h +++ b/src/dev/ns_gige_reg.h @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu */ /** @file diff --git a/src/dev/pciconfigall.cc b/src/dev/pciconfigall.cc index 14a28e86d..785774ff4 100644 --- a/src/dev/pciconfigall.cc +++ b/src/dev/pciconfigall.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz + * Ali Saidi */ /* @file @@ -99,7 +102,6 @@ PciConfigAll::read(Packet *pkt) int func = (daddr >> 8) & 0x7; int reg = daddr & 0xFF; - pkt->time += pioDelay; pkt->allocate(); DPRINTF(PciConfigAll, "read va=%#x da=%#x size=%d\n", pkt->getAddr(), daddr, @@ -134,8 +136,6 @@ PciConfigAll::read(Packet *pkt) Tick PciConfigAll::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == sizeof(uint8_t) || pkt->getSize() == sizeof(uint16_t) || diff --git a/src/dev/pciconfigall.hh b/src/dev/pciconfigall.hh index df4eae448..e60fd949b 100644 --- a/src/dev/pciconfigall.hh +++ b/src/dev/pciconfigall.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz + * Ali Saidi */ /* diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc index 76392ccfe..f8db2efbc 100644 --- a/src/dev/pcidev.cc +++ b/src/dev/pcidev.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano */ /* @file diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh index fc4773908..92786427b 100644 --- a/src/dev/pcidev.hh +++ b/src/dev/pcidev.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz + * Nathan Binkert */ /* @file diff --git a/src/dev/pcireg.h b/src/dev/pcireg.h index 9d2737c20..0aa4ba8ef 100644 --- a/src/dev/pcireg.h +++ b/src/dev/pcireg.h @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Miguel Serrano */ /* @file diff --git a/src/dev/pitreg.h b/src/dev/pitreg.h index 5fe1cf03f..d42925a41 100644 --- a/src/dev/pitreg.h +++ b/src/dev/pitreg.h @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Miguel Serrano */ /* @file diff --git a/src/dev/pktfifo.cc b/src/dev/pktfifo.cc index 922a66912..37f7ff680 100644 --- a/src/dev/pktfifo.cc +++ b/src/dev/pktfifo.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "base/misc.hh" diff --git a/src/dev/pktfifo.hh b/src/dev/pktfifo.hh index 336da22d8..45157ba41 100644 --- a/src/dev/pktfifo.hh +++ b/src/dev/pktfifo.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __DEV_PKTFIFO_HH__ diff --git a/src/dev/platform.cc b/src/dev/platform.cc index 9d10e0828..ed021e3b6 100644 --- a/src/dev/platform.cc +++ b/src/dev/platform.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ #include "dev/platform.hh" diff --git a/src/dev/platform.hh b/src/dev/platform.hh index f149ca2fb..bfc229748 100644 --- a/src/dev/platform.hh +++ b/src/dev/platform.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andrew Schultz */ /** diff --git a/src/dev/rtcreg.h b/src/dev/rtcreg.h index 5025a0e95..37255777b 100644 --- a/src/dev/rtcreg.h +++ b/src/dev/rtcreg.h @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Miguel Serrano + * Nathan Binkert */ #define RTC_SEC 0x00 diff --git a/src/dev/simconsole.cc b/src/dev/simconsole.cc index e33fa18b5..77aafd9fa 100644 --- a/src/dev/simconsole.cc +++ b/src/dev/simconsole.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ali Saidi */ /* @file diff --git a/src/dev/simconsole.hh b/src/dev/simconsole.hh index 64d1f6717..ec99c6028 100644 --- a/src/dev/simconsole.hh +++ b/src/dev/simconsole.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ali Saidi */ /* @file diff --git a/src/dev/simple_disk.cc b/src/dev/simple_disk.cc index 9eee4668c..e29bfa726 100644 --- a/src/dev/simple_disk.cc +++ b/src/dev/simple_disk.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/simple_disk.hh b/src/dev/simple_disk.hh index 19967f208..9f588cb95 100644 --- a/src/dev/simple_disk.hh +++ b/src/dev/simple_disk.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/sinic.cc b/src/dev/sinic.cc index 31fbc49aa..a0223733b 100644 --- a/src/dev/sinic.cc +++ b/src/dev/sinic.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <deque> @@ -31,7 +33,7 @@ #include <string> #include "base/inet.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/intr_control.hh" #include "dev/etherlink.hh" #include "dev/sinic.hh" @@ -322,7 +324,6 @@ Device::read(Packet *pkt) Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; - pkt->time += pioDelay; pkt->allocate(); if (!regValid(raddr)) @@ -410,8 +411,6 @@ Device::write(Packet *pkt) Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; - pkt->time += pioDelay; - if (!regValid(raddr)) panic("invalid register: cpu=%d, da=%#x pa=%#x size=%d", cpu, daddr, pkt->getAddr(), pkt->getSize()); diff --git a/src/dev/sinic.hh b/src/dev/sinic.hh index 1bb4c77e0..f6c229039 100644 --- a/src/dev/sinic.hh +++ b/src/dev/sinic.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __DEV_SINIC_HH__ diff --git a/src/dev/sinicreg.hh b/src/dev/sinicreg.hh index d41eb5b16..de4188145 100644 --- a/src/dev/sinicreg.hh +++ b/src/dev/sinicreg.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __DEV_SINICREG_HH__ diff --git a/src/dev/tsunami.cc b/src/dev/tsunami.cc index ed011531d..c9e15581d 100644 --- a/src/dev/tsunami.cc +++ b/src/dev/tsunami.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/tsunami.hh b/src/dev/tsunami.hh index 668c82674..13fc4417c 100644 --- a/src/dev/tsunami.hh +++ b/src/dev/tsunami.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** diff --git a/src/dev/tsunami_cchip.cc b/src/dev/tsunami_cchip.cc index 146321b9f..3feb7439f 100644 --- a/src/dev/tsunami_cchip.cc +++ b/src/dev/tsunami_cchip.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Ron Dreslinski */ /** @file @@ -40,7 +43,7 @@ #include "dev/tsunamireg.h" #include "dev/tsunami.hh" #include "mem/port.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/intr_control.hh" #include "sim/builder.hh" #include "sim/system.hh" @@ -76,7 +79,6 @@ TsunamiCChip::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; Addr regnum = (pkt->getAddr() - pioAddr) >> 6; Addr daddr = (pkt->getAddr() - pioAddr); @@ -182,9 +184,6 @@ TsunamiCChip::read(Packet *pkt) Tick TsunamiCChip::write(Packet *pkt) { - pkt->time += pioDelay; - - assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; Addr regnum = (pkt->getAddr() - pioAddr) >> 6 ; @@ -369,7 +368,7 @@ TsunamiCChip::write(Packet *pkt) void TsunamiCChip::clearIPI(uint64_t ipintr) { - int numcpus = tsunami->intrctrl->cpu->system->execContexts.size(); + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(numcpus <= Tsunami::Max_CPUs); if (ipintr) { @@ -395,7 +394,7 @@ TsunamiCChip::clearIPI(uint64_t ipintr) void TsunamiCChip::clearITI(uint64_t itintr) { - int numcpus = tsunami->intrctrl->cpu->system->execContexts.size(); + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(numcpus <= Tsunami::Max_CPUs); if (itintr) { @@ -415,7 +414,7 @@ TsunamiCChip::clearITI(uint64_t itintr) void TsunamiCChip::reqIPI(uint64_t ipreq) { - int numcpus = tsunami->intrctrl->cpu->system->execContexts.size(); + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(numcpus <= Tsunami::Max_CPUs); if (ipreq) { @@ -442,7 +441,7 @@ TsunamiCChip::reqIPI(uint64_t ipreq) void TsunamiCChip::postRTC() { - int size = tsunami->intrctrl->cpu->system->execContexts.size(); + int size = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(size <= Tsunami::Max_CPUs); for (int i = 0; i < size; i++) { @@ -460,7 +459,7 @@ void TsunamiCChip::postDRIR(uint32_t interrupt) { uint64_t bitvector = ULL(1) << interrupt; - uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + uint64_t size = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(size <= Tsunami::Max_CPUs); drir |= bitvector; @@ -478,7 +477,7 @@ void TsunamiCChip::clearDRIR(uint32_t interrupt) { uint64_t bitvector = ULL(1) << interrupt; - uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + uint64_t size = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(size <= Tsunami::Max_CPUs); if (drir & bitvector) diff --git a/src/dev/tsunami_cchip.hh b/src/dev/tsunami_cchip.hh index 3dde0131e..ef43f621f 100644 --- a/src/dev/tsunami_cchip.hh +++ b/src/dev/tsunami_cchip.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/tsunami_io.cc b/src/dev/tsunami_io.cc index 729a61cf7..e3da10eb5 100644 --- a/src/dev/tsunami_io.cc +++ b/src/dev/tsunami_io.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano */ /** @file @@ -447,7 +451,6 @@ TsunamiIO::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; DPRINTF(Tsunami, "io read va=%#x size=%d IOPorrt=%#x\n", pkt->getAddr(), @@ -511,8 +514,6 @@ TsunamiIO::read(Packet *pkt) Tick TsunamiIO::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff --git a/src/dev/tsunami_io.hh b/src/dev/tsunami_io.hh index 71ca0d98f..ee25bbdfd 100644 --- a/src/dev/tsunami_io.hh +++ b/src/dev/tsunami_io.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano */ /** @file diff --git a/src/dev/tsunami_pchip.cc b/src/dev/tsunami_pchip.cc index c430ca0a0..a376b908d 100644 --- a/src/dev/tsunami_pchip.cc +++ b/src/dev/tsunami_pchip.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz */ /** @file @@ -70,8 +73,6 @@ TsunamiPChip::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - - pkt->time += pioDelay; pkt->allocate(); Addr daddr = (pkt->getAddr() - pioAddr) >> 6;; assert(pkt->getSize() == sizeof(uint64_t)); @@ -151,8 +152,6 @@ TsunamiPChip::read(Packet *pkt) Tick TsunamiPChip::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = (pkt->getAddr() - pioAddr) >> 6; diff --git a/src/dev/tsunami_pchip.hh b/src/dev/tsunami_pchip.hh index bb84339c9..9f80f7d68 100644 --- a/src/dev/tsunami_pchip.hh +++ b/src/dev/tsunami_pchip.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/tsunamireg.h b/src/dev/tsunamireg.h index a10259900..d603972be 100644 --- a/src/dev/tsunamireg.h +++ b/src/dev/tsunamireg.h @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/uart.cc b/src/dev/uart.cc index 4a9f2b505..f769b720b 100644 --- a/src/dev/uart.cc +++ b/src/dev/uart.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/uart.hh b/src/dev/uart.hh index 2dd15d9b8..f0ae0854c 100644 --- a/src/dev/uart.hh +++ b/src/dev/uart.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/uart8250.cc b/src/dev/uart8250.cc index 8a5a1f1cd..b92527b5a 100644 --- a/src/dev/uart8250.cc +++ b/src/dev/uart8250.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file @@ -114,7 +116,6 @@ Uart8250::read(Packet *pkt) assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == 1); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; pkt->allocate(); @@ -198,7 +199,6 @@ Uart8250::write(Packet *pkt) assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == 1); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; DPRINTF(Uart, " write register %#x value %#x\n", daddr, pkt->get<uint8_t>()); diff --git a/src/dev/uart8250.hh b/src/dev/uart8250.hh index 4b7c2f1e4..44e90132f 100644 --- a/src/dev/uart8250.hh +++ b/src/dev/uart8250.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/kern/kernel_stats.cc b/src/kern/kernel_stats.cc index b85d88145..f7868b50f 100644 --- a/src/kern/kernel_stats.cc +++ b/src/kern/kernel_stats.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + * Nathan Binkert */ #include <map> @@ -32,7 +35,7 @@ #include "arch/alpha/osfpal.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "kern/tru64/tru64_syscalls.hh" #include "sim/system.hh" @@ -42,13 +45,12 @@ using namespace Stats; namespace Kernel { -const char *modestr[] = { "kernel", "user", "idle", "interrupt" }; +const char *modestr[] = { "kernel", "user", "idle" }; Statistics::Statistics(System *system) : idleProcess((Addr)-1), themode(kernel), lastModeTick(0), iplLast(0), iplLastTick(0) { - bin_int = system->params()->bin_int; } void @@ -181,16 +183,16 @@ Statistics::regStats(const string &_name) } void -Statistics::setIdleProcess(Addr idlepcbb, ExecContext *xc) +Statistics::setIdleProcess(Addr idlepcbb, ThreadContext *tc) { - assert(themode == kernel || themode == interrupt); + assert(themode == kernel); idleProcess = idlepcbb; themode = idle; - changeMode(themode, xc); + changeMode(themode, tc); } void -Statistics::changeMode(cpu_mode newmode, ExecContext *xc) +Statistics::changeMode(cpu_mode newmode, ThreadContext *tc) { _mode[newmode]++; @@ -203,8 +205,6 @@ Statistics::changeMode(cpu_mode newmode, ExecContext *xc) _modeGood[newmode]++; _modeTicks[themode] += curTick - lastModeTick; - xc->getSystemPtr()->kernelBinning->changeMode(newmode); - lastModeTick = curTick; themode = newmode; } @@ -226,31 +226,27 @@ Statistics::swpipl(int ipl) } void -Statistics::mode(cpu_mode newmode, ExecContext *xc) +Statistics::mode(cpu_mode newmode, ThreadContext *tc) { - Addr pcbb = xc->readMiscReg(AlphaISA::IPR_PALtemp23); + Addr pcbb = tc->readMiscReg(AlphaISA::IPR_PALtemp23); - if ((newmode == kernel || newmode == interrupt) && - pcbb == idleProcess) + if (newmode == kernel && pcbb == idleProcess) newmode = idle; - if (bin_int == false && newmode == interrupt) - newmode = kernel; - - changeMode(newmode, xc); + changeMode(newmode, tc); } void -Statistics::context(Addr oldpcbb, Addr newpcbb, ExecContext *xc) +Statistics::context(Addr oldpcbb, Addr newpcbb, ThreadContext *tc) { assert(themode != user); _swap_context++; - changeMode(newpcbb == idleProcess ? idle : kernel, xc); + changeMode(newpcbb == idleProcess ? idle : kernel, tc); } void -Statistics::callpal(int code, ExecContext *xc) +Statistics::callpal(int code, ThreadContext *tc) { if (!PAL::name(code)) return; @@ -259,17 +255,12 @@ Statistics::callpal(int code, ExecContext *xc) switch (code) { case PAL::callsys: { - int number = xc->readIntReg(0); + int number = tc->readIntReg(0); if (SystemCalls<Tru64>::validSyscallNumber(number)) { int cvtnum = SystemCalls<Tru64>::convert(number); _syscall[cvtnum]++; } } break; - - case PAL::swpctx: - if (xc->getSystemPtr()->kernelBinning) - xc->getSystemPtr()->kernelBinning->palSwapContext(xc); - break; } } diff --git a/src/kern/kernel_stats.hh b/src/kern/kernel_stats.hh index 16ec721d0..c691ad8cf 100644 --- a/src/kern/kernel_stats.hh +++ b/src/kern/kernel_stats.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + * Nathan Binkert */ #ifndef __KERNEL_STATS_HH__ @@ -37,104 +40,26 @@ #include "cpu/static_inst.hh" class BaseCPU; -class ExecContext; +class ThreadContext; class FnEvent; // What does kernel stats expect is included? class System; namespace Kernel { -enum cpu_mode { kernel, user, idle, interrupt, cpu_mode_num }; +enum cpu_mode { kernel, user, idle, cpu_mode_num }; extern const char *modestr[]; -class Binning -{ - private: - std::string myname; - System *system; - - private: - // lisa's binning stuff - struct fnCall - { - Stats::MainBin *myBin; - std::string name; - }; - - struct SWContext - { - Counter calls; - std::stack<fnCall *> callStack; - }; - - std::map<const std::string, Stats::MainBin *> fnBins; - std::map<const Addr, SWContext *> swCtxMap; - - std::multimap<const std::string, std::string> callerMap; - void populateMap(std::string caller, std::string callee); - - std::vector<FnEvent *> fnEvents; - - Stats::Scalar<> fnCalls; - - Stats::MainBin *getBin(const std::string &name); - bool findCaller(std::string, std::string) const; - - SWContext *findContext(Addr pcb); - bool addContext(Addr pcb, SWContext *ctx) - { - return (swCtxMap.insert(std::make_pair(pcb, ctx))).second; - } - - void remContext(Addr pcb) - { - swCtxMap.erase(pcb); - } - - void dumpState() const; - - SWContext *swctx; - std::vector<std::string> binned_fns; - - private: - Stats::MainBin *modeBin[cpu_mode_num]; - - public: - const bool bin; - const bool fnbin; - - cpu_mode themode; - void palSwapContext(ExecContext *xc); - void execute(ExecContext *xc, StaticInstPtr inst); - void call(ExecContext *xc, Stats::MainBin *myBin); - void changeMode(cpu_mode mode); - - public: - Binning(System *sys); - virtual ~Binning(); - - const std::string name() const { return myname; } - void regStats(const std::string &name); - - public: - virtual void serialize(std::ostream &os); - virtual void unserialize(Checkpoint *cp, const std::string §ion); -}; - class Statistics : public Serializable { private: - friend class Binning; - - private: std::string myname; Addr idleProcess; cpu_mode themode; Tick lastModeTick; - bool bin_int; - void changeMode(cpu_mode newmode, ExecContext *xc); + void changeMode(cpu_mode newmode, ThreadContext *tc); private: Stats::Scalar<> _arm; @@ -176,11 +101,11 @@ class Statistics : public Serializable void ivle() { _ivle++; } void hwrei() { _hwrei++; } void swpipl(int ipl); - void mode(cpu_mode newmode, ExecContext *xc); - void context(Addr oldpcbb, Addr newpcbb, ExecContext *xc); - void callpal(int code, ExecContext *xc); + void mode(cpu_mode newmode, ThreadContext *tc); + void context(Addr oldpcbb, Addr newpcbb, ThreadContext *tc); + void callpal(int code, ThreadContext *tc); - void setIdleProcess(Addr idle, ExecContext *xc); + void setIdleProcess(Addr idle, ThreadContext *tc); public: virtual void serialize(std::ostream &os); diff --git a/src/kern/linux/events.cc b/src/kern/linux/events.cc index b688e9dd0..5ff7e26db 100644 --- a/src/kern/linux/events.cc +++ b/src/kern/linux/events.cc @@ -24,11 +24,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include "arch/arguments.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/events.hh" #include "kern/linux/printk.hh" #include "kern/system_events.hh" @@ -38,17 +40,17 @@ namespace Linux { void -DebugPrintkEvent::process(ExecContext *xc) +DebugPrintkEvent::process(ThreadContext *tc) { if (DTRACE(DebugPrintf)) { if (!raw) { - StringWrap name(xc->getSystemPtr()->name() + ".dprintk"); + StringWrap name(tc->getSystemPtr()->name() + ".dprintk"); DPRINTFN(""); } - AlphaISA::AlphaArguments args(xc); + AlphaISA::AlphaArguments args(tc); Printk(args); - SkipFuncEvent::process(xc); + SkipFuncEvent::process(tc); } } diff --git a/src/kern/linux/events.hh b/src/kern/linux/events.hh index 95c268976..65f794a9c 100644 --- a/src/kern/linux/events.hh +++ b/src/kern/linux/events.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __KERN_LINUX_EVENTS_HH__ @@ -42,7 +44,7 @@ class DebugPrintkEvent : public SkipFuncEvent DebugPrintkEvent(PCEventQueue *q, const std::string &desc, Addr addr, bool r = false) : SkipFuncEvent(q, desc, addr), raw(r) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *xc); }; } diff --git a/src/kern/linux/linux.hh b/src/kern/linux/linux.hh index 63e0dd5ca..af5e23b95 100644 --- a/src/kern/linux/linux.hh +++ b/src/kern/linux/linux.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __LINUX_HH__ diff --git a/src/kern/linux/linux_syscalls.cc b/src/kern/linux/linux_syscalls.cc index c85b6d28f..7c2821dec 100644 --- a/src/kern/linux/linux_syscalls.cc +++ b/src/kern/linux/linux_syscalls.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include "kern/linux/linux_syscalls.hh" diff --git a/src/kern/linux/linux_syscalls.hh b/src/kern/linux/linux_syscalls.hh index 7f2a08ea9..65616e862 100644 --- a/src/kern/linux/linux_syscalls.hh +++ b/src/kern/linux/linux_syscalls.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __KERN_LINUX_LINUX_SYSCALLS_HH__ diff --git a/src/kern/linux/printk.cc b/src/kern/linux/printk.cc index 918b8dabe..e39a15982 100644 --- a/src/kern/linux/printk.cc +++ b/src/kern/linux/printk.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #include <sys/types.h> diff --git a/src/kern/linux/printk.hh b/src/kern/linux/printk.hh index b88c40f5e..f9203717a 100644 --- a/src/kern/linux/printk.hh +++ b/src/kern/linux/printk.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __PRINTK_HH__ diff --git a/src/kern/linux/sched.hh b/src/kern/linux/sched.hh index a11fa590d..f849cee30 100644 --- a/src/kern/linux/sched.hh +++ b/src/kern/linux/sched.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __KERN_LINUX_SCHED_HH__ diff --git a/src/kern/solaris/solaris.hh b/src/kern/solaris/solaris.hh index e2b61d613..0fec0bcce 100644 --- a/src/kern/solaris/solaris.hh +++ b/src/kern/solaris/solaris.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ #ifndef __SOLARIS_HH__ diff --git a/src/kern/system_events.cc b/src/kern/system_events.cc index fd5c12e44..177ce96d1 100644 --- a/src/kern/system_events.cc +++ b/src/kern/system_events.cc @@ -24,10 +24,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + * Nathan Binkert */ #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "kern/system_events.hh" #include "sim/system.hh" @@ -35,57 +38,28 @@ using namespace TheISA; void -SkipFuncEvent::process(ExecContext *xc) +SkipFuncEvent::process(ThreadContext *tc) { - Addr newpc = xc->readIntReg(ReturnAddressReg); + Addr newpc = tc->readIntReg(ReturnAddressReg); DPRINTF(PCEvent, "skipping %s: pc=%x, newpc=%x\n", description, - xc->readPC(), newpc); + tc->readPC(), newpc); - xc->setPC(newpc); - xc->setNextPC(xc->readPC() + sizeof(TheISA::MachInst)); + tc->setPC(newpc); + tc->setNextPC(tc->readPC() + sizeof(TheISA::MachInst)); /* - BranchPred *bp = xc->getCpuPtr()->getBranchPred(); + BranchPred *bp = tc->getCpuPtr()->getBranchPred(); if (bp != NULL) { - bp->popRAS(xc->getThreadNum()); + bp->popRAS(tc->getThreadNum()); } */ } - -FnEvent::FnEvent(PCEventQueue *q, const std::string &desc, Addr addr, - Stats::MainBin *bin) - : PCEvent(q, desc, addr), _name(desc), mybin(bin) -{ -} - -void -FnEvent::process(ExecContext *xc) -{ - if (xc->misspeculating()) - return; - - xc->getSystemPtr()->kernelBinning->call(xc, mybin); -} - void -IdleStartEvent::process(ExecContext *xc) +IdleStartEvent::process(ThreadContext *tc) { - xc->getCpuPtr()->kernelStats->setIdleProcess( - xc->readMiscReg(AlphaISA::IPR_PALtemp23), xc); + if (tc->getKernelStats()) + tc->getKernelStats()->setIdleProcess( + tc->readMiscReg(AlphaISA::IPR_PALtemp23), tc); remove(); } - -void -InterruptStartEvent::process(ExecContext *xc) -{ - xc->getCpuPtr()->kernelStats->mode(Kernel::interrupt, xc); -} - -void -InterruptEndEvent::process(ExecContext *xc) -{ - // We go back to kernel, if we are user, inside the rti - // pal code we will get switched to user because of the ICM write - xc->getCpuPtr()->kernelStats->mode(Kernel::kernel, xc); -} diff --git a/src/kern/system_events.hh b/src/kern/system_events.hh index 246140a09..ccd6bd9a4 100644 --- a/src/kern/system_events.hh +++ b/src/kern/system_events.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + * Ali Saidi */ #ifndef __SYSTEM_EVENTS_HH__ @@ -39,20 +42,7 @@ class SkipFuncEvent : public PCEvent SkipFuncEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); -}; - -class FnEvent : public PCEvent -{ - public: - FnEvent(PCEventQueue *q, const std::string &desc, Addr addr, - Stats::MainBin *bin); - virtual void process(ExecContext *xc); - std::string myname() const { return _name; } - - private: - std::string _name; - Stats::MainBin *mybin; + virtual void process(ThreadContext *tc); }; class IdleStartEvent : public PCEvent @@ -61,26 +51,7 @@ class IdleStartEvent : public PCEvent IdleStartEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; -class InterruptStartEvent : public PCEvent -{ - public: - InterruptStartEvent(PCEventQueue *q, const std::string &desc, Addr addr) - : PCEvent(q, desc, addr) - {} - virtual void process(ExecContext *xc); -}; - -class InterruptEndEvent : public PCEvent -{ - public: - InterruptEndEvent(PCEventQueue *q, const std::string &desc, Addr addr) - : PCEvent(q, desc, addr) - {} - virtual void process(ExecContext *xc); -}; - - #endif // __SYSTEM_EVENTS_HH__ diff --git a/src/kern/tru64/dump_mbuf.cc b/src/kern/tru64/dump_mbuf.cc index c3c37531a..8f88f8904 100644 --- a/src/kern/tru64/dump_mbuf.cc +++ b/src/kern/tru64/dump_mbuf.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/types.h> @@ -32,7 +34,7 @@ #include "base/cprintf.hh" #include "base/trace.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/tru64/mbuf.hh" #include "sim/host.hh" #include "sim/system.hh" @@ -47,11 +49,11 @@ namespace tru64 { void DumpMbuf(AlphaArguments args) { - ExecContext *xc = args.getExecContext(); + ThreadContext *tc = args.getThreadContext(); Addr addr = (Addr)args; struct mbuf m; - CopyOut(xc, &m, addr, sizeof(m)); + CopyOut(tc, &m, addr, sizeof(m)); int count = m.m_pkthdr.len; @@ -62,8 +64,8 @@ DumpMbuf(AlphaArguments args) ccprintf(DebugOut(), "m=%#lx, m->m_data=%#lx, m->m_len=%d\n", addr, m.m_data, m.m_len); char *buffer = new char[m.m_len]; - CopyOut(xc, buffer, m.m_data, m.m_len); - Trace::dataDump(curTick, xc->getSystemPtr()->name(), (uint8_t *)buffer, + CopyOut(tc, buffer, m.m_data, m.m_len); + Trace::dataDump(curTick, tc->getSystemPtr()->name(), (uint8_t *)buffer, m.m_len); delete [] buffer; @@ -71,7 +73,7 @@ DumpMbuf(AlphaArguments args) if (!m.m_next) break; - CopyOut(xc, &m, m.m_next, sizeof(m)); + CopyOut(tc, &m, m.m_next, sizeof(m)); } } diff --git a/src/kern/tru64/dump_mbuf.hh b/src/kern/tru64/dump_mbuf.hh index 9e1698ff1..25c6fd31d 100644 --- a/src/kern/tru64/dump_mbuf.hh +++ b/src/kern/tru64/dump_mbuf.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __DUMP_MBUF_HH__ diff --git a/src/kern/tru64/mbuf.hh b/src/kern/tru64/mbuf.hh index 93424858f..cb5a84a7e 100644 --- a/src/kern/tru64/mbuf.hh +++ b/src/kern/tru64/mbuf.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __MBUF_HH__ diff --git a/src/kern/tru64/printf.cc b/src/kern/tru64/printf.cc index 319d36673..29dd443d2 100644 --- a/src/kern/tru64/printf.cc +++ b/src/kern/tru64/printf.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/kern/tru64/printf.hh b/src/kern/tru64/printf.hh index 61236e83a..f6a4544ad 100644 --- a/src/kern/tru64/printf.hh +++ b/src/kern/tru64/printf.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __PRINTF_HH__ diff --git a/src/kern/tru64/tru64.hh b/src/kern/tru64/tru64.hh index 91db5bb84..d2b7c862f 100644 --- a/src/kern/tru64/tru64.hh +++ b/src/kern/tru64/tru64.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __TRU64_HH__ @@ -576,23 +579,23 @@ class Tru64 { /// Target getdirentries() handler. static SyscallReturn getdirentriesFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; #ifdef __CYGWIN__ panic("getdirent not implemented on cygwin!"); #else - int fd = process->sim_fd(xc->getSyscallArg(0)); - Addr tgt_buf = xc->getSyscallArg(1); - int tgt_nbytes = xc->getSyscallArg(2); - Addr tgt_basep = xc->getSyscallArg(3); + int fd = process->sim_fd(tc->getSyscallArg(0)); + Addr tgt_buf = tc->getSyscallArg(1); + int tgt_nbytes = tc->getSyscallArg(2); + Addr tgt_basep = tc->getSyscallArg(3); char * const host_buf = new char[tgt_nbytes]; // just pass basep through uninterpreted. TypedBufferArg<int64_t> basep(tgt_basep); - basep.copyIn(xc->getMemPort()); + basep.copyIn(tc->getMemPort()); long host_basep = (off_t)htog((int64_t)*basep); int host_result = getdirentries(fd, host_buf, tgt_nbytes, &host_basep); @@ -619,7 +622,7 @@ class Tru64 { tgt_dp->d_reclen = tgt_bufsize; tgt_dp->d_namlen = namelen; strcpy(tgt_dp->d_name, host_dp->d_name); - tgt_dp.copyOut(xc->getMemPort()); + tgt_dp.copyOut(tc->getMemPort()); tgt_buf_ptr += tgt_bufsize; host_buf_ptr += host_dp->d_reclen; @@ -628,7 +631,7 @@ class Tru64 { delete [] host_buf; *basep = htog((int64_t)host_basep); - basep.copyOut(xc->getMemPort()); + basep.copyOut(tc->getMemPort()); return tgt_buf_ptr - tgt_buf; #endif @@ -637,27 +640,27 @@ class Tru64 { /// Target sigreturn() handler. static SyscallReturn sigreturnFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; using TheISA::RegFile; - TypedBufferArg<Tru64::sigcontext> sc(xc->getSyscallArg(0)); + TypedBufferArg<Tru64::sigcontext> sc(tc->getSyscallArg(0)); - sc.copyIn(xc->getMemPort()); + sc.copyIn(tc->getMemPort()); // Restore state from sigcontext structure. // Note that we'll advance PC <- NPC before the end of the cycle, // so we need to restore the desired PC into NPC. // The current regs->pc will get clobbered. - xc->setNextPC(htog(sc->sc_pc)); + tc->setNextPC(htog(sc->sc_pc)); for (int i = 0; i < 31; ++i) { - xc->setIntReg(i, htog(sc->sc_regs[i])); - xc->setFloatRegBits(i, htog(sc->sc_fpregs[i])); + tc->setIntReg(i, htog(sc->sc_regs[i])); + tc->setFloatRegBits(i, htog(sc->sc_fpregs[i])); } - xc->setMiscReg(TheISA::Fpcr_DepTag, htog(sc->sc_fpcr)); + tc->setMiscReg(TheISA::Fpcr_DepTag, htog(sc->sc_fpcr)); return 0; } @@ -670,13 +673,13 @@ class Tru64 { /// Create a stack region for a thread. static SyscallReturn stack_createFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; - TypedBufferArg<Tru64::vm_stack> argp(xc->getSyscallArg(0)); + TypedBufferArg<Tru64::vm_stack> argp(tc->getSyscallArg(0)); - argp.copyIn(xc->getMemPort()); + argp.copyIn(tc->getMemPort()); // if the user chose an address, just let them have it. Otherwise // pick one for them. @@ -685,7 +688,7 @@ class Tru64 { int stack_size = (htog(argp->rsize) + htog(argp->ysize) + htog(argp->gsize)); process->next_thread_stack_base -= stack_size; - argp.copyOut(xc->getMemPort()); + argp.copyOut(tc->getMemPort()); } return 0; @@ -700,15 +703,15 @@ class Tru64 { /// region has several structs, some global, some per-RAD, some per-VP. static SyscallReturn nxm_task_initFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; using namespace TheISA; - TypedBufferArg<Tru64::nxm_task_attr> attrp(xc->getSyscallArg(0)); - TypedBufferArg<Addr> configptr_ptr(xc->getSyscallArg(1)); + TypedBufferArg<Tru64::nxm_task_attr> attrp(tc->getSyscallArg(0)); + TypedBufferArg<Addr> configptr_ptr(tc->getSyscallArg(1)); - attrp.copyIn(xc->getMemPort()); + attrp.copyIn(tc->getMemPort()); if (gtoh(attrp->nxm_version) != NXM_LIB_VERSION) { cerr << "nxm_task_init: thread library version mismatch! " @@ -749,7 +752,7 @@ class Tru64 { config->nxm_slot_state = htog(slot_state_addr); config->nxm_rad[0] = htog(rad_state_addr); - config.copyOut(xc->getMemPort()); + config.copyOut(tc->getMemPort()); // initialize the slot_state array and copy it out TypedBufferArg<Tru64::nxm_slot_state_t> slot_state(slot_state_addr, @@ -762,7 +765,7 @@ class Tru64 { (i == 0) ? Tru64::NXM_SLOT_BOUND : Tru64::NXM_SLOT_AVAIL; } - slot_state.copyOut(xc->getMemPort()); + slot_state.copyOut(tc->getMemPort()); // same for the per-RAD "shared" struct. Note that we need to // allocate extra bytes for the per-VP array which is embedded at @@ -786,7 +789,7 @@ class Tru64 { ssp->nxm_sysevent = htog(0); if (i == 0) { - uint64_t uniq = xc->readMiscReg(TheISA::Uniq_DepTag); + uint64_t uniq = tc->readMiscReg(TheISA::Uniq_DepTag); ssp->nxm_u.pth_id = htog(uniq + gtoh(attrp->nxm_uniq_offset)); ssp->nxm_u.nxm_active = htog(uniq | 1); } @@ -796,13 +799,13 @@ class Tru64 { } } - rad_state.copyOut(xc->getMemPort()); + rad_state.copyOut(tc->getMemPort()); // // copy pointer to shared config area out to user // *configptr_ptr = htog(config_addr); - configptr_ptr.copyOut(xc->getMemPort()); + configptr_ptr.copyOut(tc->getMemPort()); // Register this as a valid address range with the process process->nxm_start = base_addr; @@ -811,40 +814,40 @@ class Tru64 { return 0; } - /// Initialize execution context. + /// Initialize thread context. static void - init_exec_context(ExecContext *ec, + init_thread_context(ThreadContext *tc, Tru64::nxm_thread_attr *attrp, uint64_t uniq_val) { using namespace TheISA; - ec->clearArchRegs(); + tc->clearArchRegs(); - ec->setIntReg(TheISA::ArgumentReg0, gtoh(attrp->registers.a0)); - ec->setIntReg(27/*t12*/, gtoh(attrp->registers.pc)); - ec->setIntReg(TheISA::StackPointerReg, gtoh(attrp->registers.sp)); - ec->setMiscReg(TheISA::Uniq_DepTag, uniq_val); + tc->setIntReg(TheISA::ArgumentReg0, gtoh(attrp->registers.a0)); + tc->setIntReg(27/*t12*/, gtoh(attrp->registers.pc)); + tc->setIntReg(TheISA::StackPointerReg, gtoh(attrp->registers.sp)); + tc->setMiscReg(TheISA::Uniq_DepTag, uniq_val); - ec->setPC(gtoh(attrp->registers.pc)); - ec->setNextPC(gtoh(attrp->registers.pc) + sizeof(TheISA::MachInst)); + tc->setPC(gtoh(attrp->registers.pc)); + tc->setNextPC(gtoh(attrp->registers.pc) + sizeof(TheISA::MachInst)); - ec->activate(); + tc->activate(); } /// Create thread. static SyscallReturn nxm_thread_createFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; using namespace TheISA; - TypedBufferArg<Tru64::nxm_thread_attr> attrp(xc->getSyscallArg(0)); - TypedBufferArg<uint64_t> kidp(xc->getSyscallArg(1)); - int thread_index = xc->getSyscallArg(2); + TypedBufferArg<Tru64::nxm_thread_attr> attrp(tc->getSyscallArg(0)); + TypedBufferArg<uint64_t> kidp(tc->getSyscallArg(1)); + int thread_index = tc->getSyscallArg(2); // get attribute args - attrp.copyIn(xc->getMemPort()); + attrp.copyIn(tc->getMemPort()); if (gtoh(attrp->version) != NXM_LIB_VERSION) { cerr << "nxm_thread_create: thread library version mismatch! " @@ -869,7 +872,7 @@ class Tru64 { TypedBufferArg<Tru64::nxm_shared> rad_state(0x14000, rad_state_size); - rad_state.copyIn(xc->getMemPort()); + rad_state.copyIn(tc->getMemPort()); uint64_t uniq_val = gtoh(attrp->pthid) - gtoh(rad_state->nxm_uniq_offset); @@ -880,7 +883,7 @@ class Tru64 { // This is supposed to be a port number. Make something up. *kidp = htog(99); - kidp.copyOut(xc->getMemPort()); + kidp.copyOut(tc->getMemPort()); return 0; } else if (gtoh(attrp->type) == Tru64::NXM_TYPE_VP) { @@ -894,7 +897,7 @@ class Tru64 { ssp->nxm_u.pth_id = attrp->pthid; ssp->nxm_u.nxm_active = htog(uniq_val | 1); - rad_state.copyOut(xc->getMemPort()); + rad_state.copyOut(tc->getMemPort()); Addr slot_state_addr = 0x12000 + sizeof(Tru64::nxm_config_info); int slot_state_size = @@ -904,7 +907,7 @@ class Tru64 { slot_state(slot_state_addr, slot_state_size); - slot_state.copyIn(xc->getMemPort()); + slot_state.copyIn(tc->getMemPort()); if (slot_state[thread_index] != Tru64::NXM_SLOT_AVAIL) { cerr << "nxm_thread_createFunc: requested VP slot " @@ -916,21 +919,21 @@ class Tru64 { // doesn't work anyway slot_state[thread_index] = Tru64::NXM_SLOT_BOUND; - slot_state.copyOut(xc->getMemPort()); + slot_state.copyOut(tc->getMemPort()); - // Find a free simulator execution context. + // Find a free simulator thread context. for (int i = 0; i < process->numCpus(); ++i) { - ExecContext *xc = process->execContexts[i]; + ThreadContext *tc = process->threadContexts[i]; - if (xc->status() == ExecContext::Unallocated) { + if (tc->status() == ThreadContext::Suspended) { // inactive context... grab it - init_exec_context(xc, attrp, uniq_val); + init_thread_context(tc, attrp, uniq_val); // This is supposed to be a port number, but we'll try // and get away with just sticking the thread index // here. *kidp = htog(thread_index); - kidp.copyOut(xc->getMemPort()); + kidp.copyOut(tc->getMemPort()); return 0; } @@ -951,7 +954,7 @@ class Tru64 { /// Thread idle call (like yield()). static SyscallReturn nxm_idleFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 0; } @@ -959,17 +962,17 @@ class Tru64 { /// Block thread. static SyscallReturn nxm_thread_blockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; - uint64_t tid = xc->getSyscallArg(0); - uint64_t secs = xc->getSyscallArg(1); - uint64_t flags = xc->getSyscallArg(2); - uint64_t action = xc->getSyscallArg(3); - uint64_t usecs = xc->getSyscallArg(4); + uint64_t tid = tc->getSyscallArg(0); + uint64_t secs = tc->getSyscallArg(1); + uint64_t flags = tc->getSyscallArg(2); + uint64_t action = tc->getSyscallArg(3); + uint64_t usecs = tc->getSyscallArg(4); - cout << xc->getCpuPtr()->name() << ": nxm_thread_block " << tid << " " + cout << tc->getCpuPtr()->name() << ": nxm_thread_block " << tid << " " << secs << " " << flags << " " << action << " " << usecs << endl; return 0; @@ -978,17 +981,17 @@ class Tru64 { /// block. static SyscallReturn nxm_blockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; - Addr uaddr = xc->getSyscallArg(0); - uint64_t val = xc->getSyscallArg(1); - uint64_t secs = xc->getSyscallArg(2); - uint64_t usecs = xc->getSyscallArg(3); - uint64_t flags = xc->getSyscallArg(4); + Addr uaddr = tc->getSyscallArg(0); + uint64_t val = tc->getSyscallArg(1); + uint64_t secs = tc->getSyscallArg(2); + uint64_t usecs = tc->getSyscallArg(3); + uint64_t flags = tc->getSyscallArg(4); - BaseCPU *cpu = xc->getCpuPtr(); + BaseCPU *cpu = tc->getCpuPtr(); cout << cpu->name() << ": nxm_block " << hex << uaddr << dec << " " << val @@ -1001,13 +1004,13 @@ class Tru64 { /// Unblock thread. static SyscallReturn nxm_unblockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); - cout << xc->getCpuPtr()->name() << ": nxm_unblock " + cout << tc->getCpuPtr()->name() << ": nxm_unblock " << hex << uaddr << dec << endl; return 0; @@ -1016,7 +1019,7 @@ class Tru64 { /// Switch thread priority. static SyscallReturn swtch_priFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Attempts to switch to another runnable thread (if there is // one). Returns false if there are no other threads to run @@ -1029,7 +1032,7 @@ class Tru64 { } - /// Activate exec context waiting on a channel. Just activate one + /// Activate thread context waiting on a channel. Just activate one /// by default. static int activate_waiting_context(Addr uaddr, Process *process, @@ -1045,8 +1048,8 @@ class Tru64 { while (i != end && (num_activated == 0 || activate_all)) { if (i->waitChan == uaddr) { // found waiting process: make it active - ExecContext *newCtx = i->waitingContext; - assert(newCtx->status() == ExecContext::Suspended); + ThreadContext *newCtx = i->waitingContext; + assert(newCtx->status() == ThreadContext::Suspended); newCtx->activate(); // get rid of this record @@ -1063,32 +1066,32 @@ class Tru64 { /// M5 hacked-up lock acquire. static void - m5_lock_mutex(Addr uaddr, Process *process, ExecContext *xc) + m5_lock_mutex(Addr uaddr, Process *process, ThreadContext *tc) { using namespace TheISA; TypedBufferArg<uint64_t> lockp(uaddr); - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); if (gtoh(*lockp) == 0) { // lock is free: grab it *lockp = htog(1); - lockp.copyOut(xc->getMemPort()); + lockp.copyOut(tc->getMemPort()); } else { // lock is busy: disable until free - process->waitList.push_back(Process::WaitRec(uaddr, xc)); - xc->suspend(); + process->waitList.push_back(Process::WaitRec(uaddr, tc)); + tc->suspend(); } } /// M5 unlock call. static void - m5_unlock_mutex(Addr uaddr, Process *process, ExecContext *xc) + m5_unlock_mutex(Addr uaddr, Process *process, ThreadContext *tc) { TypedBufferArg<uint64_t> lockp(uaddr); - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); assert(*lockp != 0); // Check for a process waiting on the lock. @@ -1097,18 +1100,18 @@ class Tru64 { // clear lock field if no waiting context is taking over the lock if (num_waiting == 0) { *lockp = 0; - lockp.copyOut(xc->getMemPort()); + lockp.copyOut(tc->getMemPort()); } } /// Lock acquire syscall handler. static SyscallReturn m5_mutex_lockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); - m5_lock_mutex(uaddr, process, xc); + m5_lock_mutex(uaddr, process, tc); // Return 0 since we will always return to the user with the lock // acquired. We will just keep the context inactive until that is @@ -1119,19 +1122,19 @@ class Tru64 { /// Try lock (non-blocking). static SyscallReturn m5_mutex_trylockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); TypedBufferArg<uint64_t> lockp(uaddr); - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); if (gtoh(*lockp) == 0) { // lock is free: grab it *lockp = htog(1); - lockp.copyOut(xc->getMemPort()); + lockp.copyOut(tc->getMemPort()); return 0; } else { return 1; @@ -1141,11 +1144,11 @@ class Tru64 { /// Unlock syscall handler. static SyscallReturn m5_mutex_unlockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); - m5_unlock_mutex(uaddr, process, xc); + m5_unlock_mutex(uaddr, process, tc); return 0; } @@ -1153,9 +1156,9 @@ class Tru64 { /// Signal ocndition. static SyscallReturn m5_cond_signalFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr cond_addr = xc->getSyscallArg(0); + Addr cond_addr = tc->getSyscallArg(0); // Wake up one process waiting on the condition variable. activate_waiting_context(cond_addr, process); @@ -1166,9 +1169,9 @@ class Tru64 { /// Wake up all processes waiting on the condition variable. static SyscallReturn m5_cond_broadcastFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr cond_addr = xc->getSyscallArg(0); + Addr cond_addr = tc->getSyscallArg(0); activate_waiting_context(cond_addr, process, true); @@ -1178,23 +1181,23 @@ class Tru64 { /// Wait on a condition. static SyscallReturn m5_cond_waitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; - Addr cond_addr = xc->getSyscallArg(0); - Addr lock_addr = xc->getSyscallArg(1); + Addr cond_addr = tc->getSyscallArg(0); + Addr lock_addr = tc->getSyscallArg(1); TypedBufferArg<uint64_t> condp(cond_addr); TypedBufferArg<uint64_t> lockp(lock_addr); // user is supposed to acquire lock before entering - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); assert(gtoh(*lockp) != 0); - m5_unlock_mutex(lock_addr, process, xc); + m5_unlock_mutex(lock_addr, process, tc); - process->waitList.push_back(Process::WaitRec(cond_addr, xc)); - xc->suspend(); + process->waitList.push_back(Process::WaitRec(cond_addr, tc)); + tc->suspend(); return 0; } @@ -1202,10 +1205,10 @@ class Tru64 { /// Thread exit. static SyscallReturn m5_thread_exitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - assert(xc->status() == ExecContext::Active); - xc->deallocate(); + assert(tc->status() == ThreadContext::Active); + tc->deallocate(); return 0; } @@ -1213,21 +1216,21 @@ class Tru64 { /// Indirect syscall invocation (call #0). static SyscallReturn indirectSyscallFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int new_callnum = xc->getSyscallArg(0); + int new_callnum = tc->getSyscallArg(0); LiveProcess *lp = dynamic_cast<LiveProcess*>(process); assert(lp); for (int i = 0; i < 5; ++i) - xc->setSyscallArg(i, xc->getSyscallArg(i+1)); + tc->setSyscallArg(i, tc->getSyscallArg(i+1)); SyscallDesc *new_desc = lp->getDesc(new_callnum); if (desc == NULL) fatal("Syscall %d out of range", callnum); - new_desc->doSyscall(new_callnum, process, xc); + new_desc->doSyscall(new_callnum, process, tc); return 0; } diff --git a/src/kern/tru64/tru64_events.cc b/src/kern/tru64/tru64_events.cc index 69fc5c55d..69638bde1 100644 --- a/src/kern/tru64/tru64_events.cc +++ b/src/kern/tru64/tru64_events.cc @@ -24,9 +24,12 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Lisa Hsu */ -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "kern/system_events.hh" #include "kern/tru64/tru64_events.hh" @@ -39,23 +42,23 @@ using namespace TheISA; -//void SkipFuncEvent::process(ExecContext *xc); +//void SkipFuncEvent::process(ExecContext *tc); void -BadAddrEvent::process(ExecContext *xc) +BadAddrEvent::process(ThreadContext *tc) { // The following gross hack is the equivalent function to the // annotation for vmunix::badaddr in: // simos/simulation/apps/tcl/osf/tlaser.tcl - uint64_t a0 = xc->readIntReg(ArgumentReg0); + uint64_t a0 = tc->readIntReg(ArgumentReg0); AddrRangeList resp; AddrRangeList snoop; AddrRangeIter iter; bool found = false; - xc->getPhysPort()->getPeerAddressRanges(resp, snoop); + tc->getPhysPort()->getPeerAddressRanges(resp, snoop); for(iter = resp.begin(); iter != resp.end(); iter++) { if (*iter == (TheISA::K0Seg2Phys(a0) & EV5::PAddrImplMask)) @@ -65,41 +68,41 @@ BadAddrEvent::process(ExecContext *xc) if (!TheISA::IsK0Seg(a0) || found ) { DPRINTF(BADADDR, "badaddr arg=%#x bad\n", a0); - xc->setIntReg(ReturnValueReg, 0x1); - SkipFuncEvent::process(xc); + tc->setIntReg(ReturnValueReg, 0x1); + SkipFuncEvent::process(tc); } else DPRINTF(BADADDR, "badaddr arg=%#x good\n", a0); } void -PrintfEvent::process(ExecContext *xc) +PrintfEvent::process(ThreadContext *tc) { if (DTRACE(Printf)) { - DebugOut() << curTick << ": " << xc->getCpuPtr()->name() << ": "; + DebugOut() << curTick << ": " << tc->getCpuPtr()->name() << ": "; - AlphaArguments args(xc); + AlphaArguments args(tc); tru64::Printf(args); } } void -DebugPrintfEvent::process(ExecContext *xc) +DebugPrintfEvent::process(ThreadContext *tc) { if (DTRACE(DebugPrintf)) { if (!raw) - DebugOut() << curTick << ": " << xc->getCpuPtr()->name() << ": "; + DebugOut() << curTick << ": " << tc->getCpuPtr()->name() << ": "; - AlphaArguments args(xc); + AlphaArguments args(tc); tru64::Printf(args); } } void -DumpMbufEvent::process(ExecContext *xc) +DumpMbufEvent::process(ThreadContext *tc) { if (DTRACE(DebugPrintf)) { - AlphaArguments args(xc); + AlphaArguments args(tc); tru64::DumpMbuf(args); } } diff --git a/src/kern/tru64/tru64_events.hh b/src/kern/tru64/tru64_events.hh index 9b5bcfea2..6a1ab2e51 100644 --- a/src/kern/tru64/tru64_events.hh +++ b/src/kern/tru64/tru64_events.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Lisa Hsu */ #ifndef __TRU64_EVENTS_HH__ @@ -34,14 +37,14 @@ #include "cpu/pc_event.hh" #include "kern/system_events.hh" -class ExecContext; +class ThreadContext; class BadAddrEvent : public SkipFuncEvent { public: BadAddrEvent(PCEventQueue *q, const std::string &desc, Addr addr) : SkipFuncEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class PrintfEvent : public PCEvent @@ -49,7 +52,7 @@ class PrintfEvent : public PCEvent public: PrintfEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class DebugPrintfEvent : public PCEvent @@ -61,7 +64,7 @@ class DebugPrintfEvent : public PCEvent DebugPrintfEvent(PCEventQueue *q, const std::string &desc, Addr addr, bool r = false) : PCEvent(q, desc, addr), raw(r) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class DebugPrintfrEvent : public DebugPrintfEvent @@ -77,7 +80,7 @@ class DumpMbufEvent : public PCEvent public: DumpMbufEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; #endif // __TRU64_EVENTS_HH__ diff --git a/src/kern/tru64/tru64_syscalls.cc b/src/kern/tru64/tru64_syscalls.cc index 8aa57dbaa..8051b9efb 100644 --- a/src/kern/tru64/tru64_syscalls.cc +++ b/src/kern/tru64/tru64_syscalls.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "kern/tru64/tru64_syscalls.hh" diff --git a/src/kern/tru64/tru64_syscalls.hh b/src/kern/tru64/tru64_syscalls.hh index 69fa9101c..66f5c2d39 100644 --- a/src/kern/tru64/tru64_syscalls.hh +++ b/src/kern/tru64/tru64_syscalls.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __KERN_TRU64_TRU64_SYSCALLS_HH__ diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc index 736e8dc81..3718cbaaf 100644 --- a/src/mem/bridge.cc +++ b/src/mem/bridge.cc @@ -25,6 +25,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Steve Reinhardt */ /** @@ -90,7 +93,17 @@ Bridge::BridgePort::recvTiming(Packet *pkt) DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr()); - if (pkt->isResponse()) { + return otherPort->queueForSendTiming(pkt); +} + + +bool +Bridge::BridgePort::queueForSendTiming(Packet *pkt) +{ + if (queueFull()) + return false; + + if (pkt->isResponse()) { // This is a response for a request we forwarded earlier. The // corresponding PacketBuffer should be stored in the packet's // senderState field. @@ -99,22 +112,16 @@ Bridge::BridgePort::recvTiming(Packet *pkt) // set up new packet dest & senderState based on values saved // from original request buf->fixResponse(pkt); + DPRINTF(BusBridge, "restoring sender state: %#X, from packet buffer: %#X\n", + pkt->senderState, buf); DPRINTF(BusBridge, " is response, new dest %d\n", pkt->getDest()); delete buf; } - return otherPort->queueForSendTiming(pkt); -} - - -bool -Bridge::BridgePort::queueForSendTiming(Packet *pkt) -{ - if (queueFull()) - return false; - Tick readyTime = curTick + delay; PacketBuffer *buf = new PacketBuffer(pkt, readyTime); + DPRINTF(BusBridge, "old sender state: %#X, new sender state: %#X\n", + buf->origSenderState, buf); // If we're about to put this packet at the head of the queue, we // need to schedule an event to do the transmit. Otherwise there @@ -126,43 +133,16 @@ Bridge::BridgePort::queueForSendTiming(Packet *pkt) sendQueue.push_back(buf); - // Did we just become blocked? If yes, let other side know. - if (queueFull()) - otherPort->sendStatusChange(Port::Blocked); - return true; } - -void -Bridge::BridgePort::finishSend(PacketBuffer *buf) -{ - if (buf->expectResponse) { - // Must wait for response. We just need to count outstanding - // responses (in case we want to cap them); PacketBuffer - // pointer will be recovered on response. - ++outstandingResponses; - DPRINTF(BusBridge, " successful: awaiting response (%d)\n", - outstandingResponses); - } else { - // no response expected... deallocate packet buffer now. - DPRINTF(BusBridge, " successful: no response expected\n"); - delete buf; - } - - // If there are more packets to send, schedule event to try again. - if (!sendQueue.empty()) { - buf = sendQueue.front(); - sendEvent.schedule(std::max(buf->ready, curTick + 1)); - } -} - - void Bridge::BridgePort::trySend() { assert(!sendQueue.empty()); + bool was_full = queueFull(); + PacketBuffer *buf = sendQueue.front(); assert(buf->ready <= curTick); @@ -176,20 +156,41 @@ Bridge::BridgePort::trySend() // send successful sendQueue.pop_front(); buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it - finishSend(buf); + + if (buf->expectResponse) { + // Must wait for response. We just need to count outstanding + // responses (in case we want to cap them); PacketBuffer + // pointer will be recovered on response. + ++outstandingResponses; + DPRINTF(BusBridge, " successful: awaiting response (%d)\n", + outstandingResponses); + } else { + // no response expected... deallocate packet buffer now. + DPRINTF(BusBridge, " successful: no response expected\n"); + delete buf; + } + + // If there are more packets to send, schedule event to try again. + if (!sendQueue.empty()) { + buf = sendQueue.front(); + sendEvent.schedule(std::max(buf->ready, curTick + 1)); + } + // Let things start sending again + if (was_full) { + DPRINTF(BusBridge, "Queue was full, sending retry\n"); + otherPort->sendRetry(); + } + } else { DPRINTF(BusBridge, " unsuccessful\n"); } } -Packet * +void Bridge::BridgePort::recvRetry() { - PacketBuffer *buf = sendQueue.front(); - Packet *pkt = buf->pkt; - finishSend(buf); - return pkt; + trySend(); } /** Function called by the port when the bus is receiving a Atomic @@ -223,9 +224,6 @@ Bridge::BridgePort::recvFunctional(Packet *pkt) void Bridge::BridgePort::recvStatusChange(Port::Status status) { - if (status == Port::Blocked || status == Port::Unblocked) - return; - otherPort->sendStatusChange(status); } diff --git a/src/mem/bridge.hh b/src/mem/bridge.hh index 8a5cbf92a..37fb92662 100644 --- a/src/mem/bridge.hh +++ b/src/mem/bridge.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Steve Reinhardt */ /** @@ -38,7 +41,6 @@ #include <inttypes.h> #include <queue> - #include "mem/mem_object.hh" #include "mem/packet.hh" #include "mem/port.hh" @@ -77,7 +79,8 @@ class Bridge : public MemObject origSenderState(_pkt->senderState), origSrc(_pkt->getSrc()), expectResponse(_pkt->needsResponse()) { - pkt->senderState = this; + if (!pkt->isResponse()) + pkt->senderState = this; } void fixResponse(Packet *pkt) @@ -146,7 +149,7 @@ class Bridge : public MemObject /** When receiving a retry request from the peer port, pass it to the bridge. */ - virtual Packet* recvRetry(); + virtual void recvRetry(); /** When receiving a Atomic requestfrom the peer port, pass it to the bridge. */ diff --git a/src/mem/bus.cc b/src/mem/bus.cc index cfc99a64f..919acd23c 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @@ -72,9 +74,35 @@ Bus::recvTiming(Packet *pkt) assert(dest != pkt->getSrc()); // catch infinite loops port = interfaces[dest]; } - return port->sendTiming(pkt); + if (port->sendTiming(pkt)) { + // packet was successfully sent, just return true. + return true; + } + + // packet not successfully sent + retryList.push_back(interfaces[pkt->getSrc()]); + return false; } +void +Bus::recvRetry(int id) +{ + // Go through all the elements on the list calling sendRetry on each + // This is not very efficient at all but it works. Ultimately we should end + // up with something that is more intelligent. + int initialSize = retryList.size(); + int i; + Port *p; + + for (i = 0; i < initialSize; i++) { + assert(retryList.size() > 0); + p = retryList.front(); + retryList.pop_front(); + p->sendRetry(); + } +} + + Port * Bus::findPort(Addr addr, int id) { diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 5eeb07904..50bfba6e4 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ /** @@ -67,6 +69,10 @@ class Bus : public MemObject transaction.*/ void recvFunctional(Packet *pkt); + /** Timing function called by port when it is once again able to process + * requests. */ + void recvRetry(int id); + /** Function called by the port when the bus is recieving a status change.*/ void recvStatusChange(Port::Status status, int id); @@ -126,6 +132,11 @@ class Bus : public MemObject virtual void recvStatusChange(Status status) { bus->recvStatusChange(status, id); } + /** When reciving a retry from the peer port (at id), + pass it to the bus. */ + virtual void recvRetry() + { bus->recvRetry(id); } + // This should return all the 'owned' addresses that are // downstream from this bus, yes? That is, the union of all // the 'owned' address ranges of all the other interfaces on @@ -143,6 +154,10 @@ class Bus : public MemObject connected to this bus.*/ std::vector<Port*> interfaces; + /** An array of pointers to ports that retry should be called on because the + * original send failed for whatever reason.*/ + std::list<Port*> retryList; + public: /** A function used to return the port associated with this bus object. */ diff --git a/src/mem/cache/prefetch/tagged_prefetcher_impl.hh b/src/mem/cache/prefetch/tagged_prefetcher_impl.hh index 6c27256a9..7bdabbe14 100644 --- a/src/mem/cache/prefetch/tagged_prefetcher_impl.hh +++ b/src/mem/cache/prefetch/tagged_prefetcher_impl.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ /** diff --git a/src/mem/config/prefetch.hh b/src/mem/config/prefetch.hh index 03eb570f0..d24db79da 100644 --- a/src/mem/config/prefetch.hh +++ b/src/mem/config/prefetch.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ /** diff --git a/src/mem/mem_object.cc b/src/mem/mem_object.cc index f579a0727..d4d3fd283 100644 --- a/src/mem/mem_object.cc +++ b/src/mem/mem_object.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include "mem/mem_object.hh" diff --git a/src/mem/mem_object.hh b/src/mem/mem_object.hh index 58930eccc..ac547619d 100644 --- a/src/mem/mem_object.hh +++ b/src/mem/mem_object.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ /** diff --git a/src/mem/packet.cc b/src/mem/packet.cc index 3b415d77f..56dd2bdfa 100644 --- a/src/mem/packet.cc +++ b/src/mem/packet.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Steve Reinhardt */ /** @@ -97,20 +100,6 @@ Packet::intersect(Packet *p) return false; } -/** Minimally reset a packet so something like simple cpu can reuse it. */ -void -Packet::reset() -{ - result = Unknown; - if (dynamicData) { - deleteData(); - dynamicData = false; - arrayData = false; - time = curTick; - } -} - - bool fixPacket(Packet *func, Packet *timing) { diff --git a/src/mem/packet.hh b/src/mem/packet.hh index 83f52ede5..403039d96 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -24,12 +24,15 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + * Ali Saidi */ /** * @file - * Declaration of the Packet Class, a packet is a transaction occuring - * between a single level of the memory heirarchy (ie L1->L2). + * Declaration of the Packet class. */ #ifndef __MEM_PACKET_HH__ @@ -44,81 +47,96 @@ typedef Packet* PacketPtr; typedef uint8_t* PacketDataPtr; /** - * A Packet is the structure to handle requests between two levels - * of the memory system. The Request is a global object that trancends - * all of the memory heirarchy, but at each levels interface a packet - * is created to transfer data/requests. For example, a request would - * be used to initiate a request to go to memory/IOdevices, as the request - * passes through the memory system several packets will be created. One - * will be created to go between the L1 and L2 caches and another to go to - * the next level and so forth. - * - * Packets are assumed to be returned in the case of a single response. If - * the transaction has no response, then the consumer will delete the packet. + * A Packet is used to encapsulate a transfer between two objects in + * the memory system (e.g., the L1 and L2 cache). (In contrast, a + * single Request travels all the way from the requester to the + * ultimate destination and back, possibly being conveyed by several + * different Packets along the way.) */ class Packet { private: - /** A pointer to the data being transfered. It can be differnt sizes - at each level of the heirarchy so it belongs in the packet, - not request. This may or may not be populated when a responder recieves - the packet. If not populated it memory should be allocated. + /** A pointer to the data being transfered. It can be differnt + * sizes at each level of the heirarchy so it belongs in the + * packet, not request. This may or may not be populated when a + * responder recieves the packet. If not populated it memory + * should be allocated. */ PacketDataPtr data; - /** Is the data pointer set to a value that shouldn't be freed when the - * packet is destroyed? */ + /** Is the data pointer set to a value that shouldn't be freed + * when the packet is destroyed? */ bool staticData; - /** The data pointer points to a value that should be freed when the packet - * is destroyed. */ + /** The data pointer points to a value that should be freed when + * the packet is destroyed. */ bool dynamicData; - /** the data pointer points to an array (thus delete [] ) needs to be called - * on it rather than simply delete.*/ + /** the data pointer points to an array (thus delete [] ) needs to + * be called on it rather than simply delete.*/ bool arrayData; - /** The address of the request, could be virtual or physical (depending on - cache configurations). */ + /** The address of the request. This address could be virtual or + * physical, depending on the system configuration. */ Addr addr; - /** Indicates the size of the request. */ + /** The size of the request or transfer. */ int size; - /** A index of the source of the transaction. */ + /** Device address (e.g., bus ID) of the source of the + * transaction. The source is not responsible for setting this + * field; it is set implicitly by the interconnect when the + * packet * is first sent. */ short src; - /** A index to the destination of the transaction. */ + /** Device address (e.g., bus ID) of the destination of the + * transaction. The special value Broadcast indicates that the + * packet should be routed based on its address. This field is + * initialized in the constructor and is thus always valid + * (unlike * addr, size, and src). */ short dest; - bool addrValid; - bool sizeValid; + /** Are the 'addr' and 'size' fields valid? */ + bool addrSizeValid; + /** Is the 'src' field valid? */ bool srcValid; public: + /** The special destination address indicating that the packet + * should be routed based on its address. */ static const short Broadcast = -1; - /** A pointer to the overall request. */ + /** A pointer to the original request. */ RequestPtr req; + /** A virtual base opaque structure used to hold coherence-related + * state. A specific subclass would be derived from this to + * carry state specific to a particular coherence protocol. */ class CoherenceState { public: virtual ~CoherenceState() {} }; - /** A virtual base opaque structure used to hold - coherence status messages. */ - CoherenceState *coherence; // virtual base opaque, - // assert(dynamic_cast<Foo>) etc. - + /** This packet's coherence state. Caches should use + * dynamic_cast<> to cast to the state appropriate for the + * system's coherence protocol. */ + CoherenceState *coherence; + + /** A virtual base opaque structure used to hold state associated + * with the packet but specific to the sending device (e.g., an + * MSHR). A pointer to this state is returned in the packet's + * response so that the sender can quickly look up the state + * needed to process it. A specific subclass would be derived + * from this to carry state specific to a particular sending + * device. */ class SenderState { public: virtual ~SenderState() {} }; - /** A virtual base opaque structure used to hold the senders state. */ - SenderState *senderState; // virtual base opaque, - // assert(dynamic_cast<Foo>) etc. + /** This packet's sender state. Devices should use dynamic_cast<> + * to cast to the state appropriate to the sender. */ + SenderState *senderState; private: /** List of command attributes. */ @@ -144,9 +162,11 @@ class Packet WriteResp = IsWrite | IsResponse }; + /** Return the string name of the cmd field (for debugging and + * tracing). */ const std::string &cmdString() const; - /** The command of the transaction. */ + /** The command field of the packet. */ Command cmd; bool isRead() { return (cmd & IsRead) != 0; } @@ -154,28 +174,16 @@ class Packet bool isResponse() { return (cmd & IsResponse) != 0; } bool needsResponse() { return (cmd & NeedsResponse) != 0; } - void makeTimingResponse() { - assert(needsResponse()); - int icmd = (int)cmd; - icmd &= ~(IsRequest | NeedsResponse); - icmd |= IsResponse; - cmd = (Command)icmd; - dest = src; - srcValid = false; - } - - /** The time this request was responded to. Used to calculate latencies. */ - Tick time; - - /** The result of a particular packets request. */ + /** Possible results of a packet's request. */ enum Result { Success, BadAddress, + Nacked, Unknown }; - /** The result of the packet transaction. */ + /** The result of this packet's request. */ Result result; /** Accessor function that returns the source index of the packet. */ @@ -187,35 +195,71 @@ class Packet short getDest() const { return dest; } void setDest(short _dest) { dest = _dest; } - Addr getAddr() const { assert(addrValid); return addr; } - void setAddr(Addr _addr) { addr = _addr; addrValid = true; } - - int getSize() const { assert(sizeValid); return size; } - void setSize(int _size) { size = _size; sizeValid = true; } - + Addr getAddr() const { assert(addrSizeValid); return addr; } + int getSize() const { assert(addrSizeValid); return size; } + /** Constructor. Note that a Request object must be constructed + * first, but the Requests's physical address and size fields + * need not be valid. The command and destination addresses + * must be supplied. */ Packet(Request *_req, Command _cmd, short _dest) : data(NULL), staticData(false), dynamicData(false), arrayData(false), addr(_req->paddr), size(_req->size), dest(_dest), - addrValid(_req->validPaddr), sizeValid(_req->validSize), + addrSizeValid(_req->validPaddr), srcValid(false), req(_req), coherence(NULL), senderState(NULL), cmd(_cmd), - time(curTick), result(Unknown) + result(Unknown) { } + /** Destructor. */ ~Packet() { deleteData(); } + /** Reinitialize packet address and size from the associated + * Request object, and reset other fields that may have been + * modified by a previous transaction. Typically called when a + * statically allocated Request/Packet pair is reused for + * multiple transactions. */ + void reinitFromRequest() { + assert(req->validPaddr); + addr = req->paddr; + size = req->size; + addrSizeValid = true; + result = Unknown; + if (dynamicData) { + deleteData(); + dynamicData = false; + arrayData = false; + } + } - /** Minimally reset a packet so something like simple cpu can reuse it. */ - void reset(); + /** Take a request packet and modify it in place to be suitable + * for returning as a response to that request. Used for timing + * accesses only. For atomic and functional accesses, the + * request packet is always implicitly passed back *without* + * modifying the command or destination fields, so this function + * should not be called. */ + void makeTimingResponse() { + assert(needsResponse()); + int icmd = (int)cmd; + icmd &= ~(IsRequest | NeedsResponse); + icmd |= IsResponse; + cmd = (Command)icmd; + dest = src; + srcValid = false; + } - void reinitFromRequest() { - if (req->validPaddr) setAddr(req->paddr); - if (req->validSize) setSize(req->size); + /** Take a request packet that has been returned as NACKED and modify it so + * that it can be sent out again. Only packets that need a response can be + * NACKED, so verify that that is true. */ + void reinitNacked() { + assert(needsResponse() && result == Nacked); + dest = Broadcast; + result = Unknown; } + /** Set the data pointer to the following value that should not be freed. */ template <typename T> void dataStatic(T *p); diff --git a/src/mem/page_table.cc b/src/mem/page_table.cc index c4e1ea193..b5cecc7da 100644 --- a/src/mem/page_table.cc +++ b/src/mem/page_table.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Ron Dreslinski */ /** diff --git a/src/mem/page_table.hh b/src/mem/page_table.hh index 26248261a..f7212d423 100644 --- a/src/mem/page_table.hh +++ b/src/mem/page_table.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ /** diff --git a/src/mem/physical.cc b/src/mem/physical.cc index 26dbef0cd..fb31fb4a3 100644 --- a/src/mem/physical.cc +++ b/src/mem/physical.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ #include <sys/types.h> @@ -139,8 +141,7 @@ Tick PhysicalMemory::doAtomicAccess(Packet *pkt) { doFunctionalAccess(pkt); - pkt->time = curTick + lat; - return curTick + lat; + return lat; } void diff --git a/src/mem/physical.hh b/src/mem/physical.hh index 2ced79045..88ea543da 100644 --- a/src/mem/physical.hh +++ b/src/mem/physical.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ /* @file diff --git a/src/mem/port.cc b/src/mem/port.cc index 651cb739a..bec9d2274 100644 --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ /** @@ -45,13 +47,12 @@ Port::setPeer(Port *port) void Port::blobHelper(Addr addr, uint8_t *p, int size, Packet::Command cmd) { - Request req(false); + Request req; Packet pkt(&req, cmd, Packet::Broadcast); for (ChunkGenerator gen(addr, size, peerBlockSize()); !gen.done(); gen.next()) { - req.setPaddr(gen.addr()); - req.setSize(gen.size()); + req.setPhys(gen.addr(), gen.size(), 0); pkt.reinitFromRequest(); pkt.dataStatic(p); sendFunctional(&pkt); diff --git a/src/mem/port.hh b/src/mem/port.hh index f9103865e..2edad095e 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski */ /** @@ -74,6 +76,11 @@ class Port /** Descriptive name (for DPRINTF output) */ const std::string portName; + /** A pointer to the peer port. Ports always come in pairs, that way they + can use a standardized interface to communicate between different + memory objects. */ + Port *peer; + public: /** @@ -83,7 +90,7 @@ class Port * of memory system object to which the port belongs. */ Port(const std::string &_name) - : portName(_name) + : portName(_name), peer(NULL) { } /** Return port name (for DPRINTF). */ @@ -92,23 +99,12 @@ class Port virtual ~Port() {}; // mey be better to use subclasses & RTTI? - /** Holds the ports status. Keeps track if it is blocked, or has - calculated a range change. */ + /** Holds the ports status. Currently just that a range recomputation needs + * to be done. */ enum Status { - Blocked, - Unblocked, RangeChange }; - private: - - /** A pointer to the peer port. Ports always come in pairs, that way they - can use a standardized interface to communicate between different - memory objects. */ - Port *peer; - - public: - /** Function to set the pointer for the peer port. @todo should be called by the configuration stuff (python). */ @@ -140,7 +136,7 @@ class Port wait. This shouldn't be valid for response paths (IO Devices). so it is set to panic if it isn't already defined. */ - virtual Packet *recvRetry() { panic("??"); } + virtual void recvRetry() { panic("??"); } /** Called by a peer port in order to determine the block size of the device connected to this port. It sometimes doesn't make sense for @@ -165,16 +161,17 @@ class Port port receive function. @return This function returns if the send was succesful in it's recieve. If it was a failure, then the port will wait for a recvRetry - at which point it can issue a successful sendTiming. This is used in + at which point it can possibly issue a successful sendTiming. This is used in case a cache has a higher priority request come in while waiting for the bus to arbitrate. */ bool sendTiming(Packet *pkt) { return peer->recvTiming(pkt); } - /** Function called by the associated device to send an atomic access, - an access in which the data is moved and the state is updated in one - cycle, without interleaving with other memory accesses. - */ + /** Function called by the associated device to send an atomic + * access, an access in which the data is moved and the state is + * updated in one cycle, without interleaving with other memory + * accesses. Returns estimated latency of access. + */ Tick sendAtomic(Packet *pkt) { return peer->recvAtomic(pkt); } @@ -194,7 +191,7 @@ class Port /** When a timing access doesn't return a success, some time later the Retry will be sent. */ - Packet *sendRetry() { return peer->recvRetry(); } + void sendRetry() { return peer->recvRetry(); } /** Called by the associated device if it wishes to find out the blocksize of the device on attached to the peer port. @@ -252,6 +249,14 @@ class FunctionalPort : public Port virtual void recvFunctional(Packet *pkt) { panic("FuncPort is UniDir"); } virtual void recvStatusChange(Status status) {} + /** a write function that also does an endian conversion. */ + template <typename T> + inline void writeHtoG(Addr addr, T d); + + /** a read function that also does an endian conversion. */ + template <typename T> + inline T readGtoH(Addr addr); + template <typename T> inline void write(Addr addr, T d) { diff --git a/src/mem/port_impl.hh b/src/mem/port_impl.hh new file mode 100644 index 000000000..e9a159293 --- /dev/null +++ b/src/mem/port_impl.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 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. + * + * Authors: Ali Saidi + */ + +#include "arch/isa_specific.hh" +#include "arch/isa_traits.hh" +#include "mem/port.hh" +#include "sim/byteswap.hh" + +template <typename T> +void +FunctionalPort::writeHtoG(Addr addr, T d) +{ + d = TheISA::htog(d); + writeBlob(addr, (uint8_t*)&d, sizeof(T)); +} + + +template <typename T> +T +FunctionalPort::readGtoH(Addr addr) +{ + T d; + readBlob(addr, (uint8_t*)&d, sizeof(T)); + return TheISA::gtoh(d); +} + diff --git a/src/mem/request.hh b/src/mem/request.hh index 10550e859..af1d6d8a8 100644 --- a/src/mem/request.hh +++ b/src/mem/request.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + * Ali Saidi */ /** @@ -61,139 +65,164 @@ const unsigned NO_ALIGN_FAULT = 0x400; class Request { - //@todo Make Accesor functions, make these private. - public: - /** Constructor, needs a bool to signify if it is/isn't Cpu Request. */ - Request(bool isCpu); - - /** reset the request to it's initial state so it can be reused.*/ - void resetAll(bool isCpu); - - /** reset the request's addrs times, etc, so but not everything to same - * time. */ - void resetMin(); - -//First non-cpu request fields private: - /** The physical address of the request. */ + /** + * The physical address of the request. Valid only if validPaddr + * is set. */ Addr paddr; - /** Wether or not paddr is valid (has been written yet). */ - bool validPaddr; - /** The size of the request. */ + /** + * The size of the request. This field must be set when vaddr or + * paddr is written via setVirt() or setPhys(), so it is always + * valid as long as one of the address fields is valid. */ int size; - /** Wether or not size is valid (has been written yet). */ - bool validSize; - - /** The time this request was started. Used to calculate latencies. */ - Tick time; - /** Wether or not time is valid (has been written yet). */ - bool validTime; - - /** Destination address if this is a block copy. */ - Addr copyDest; - /** Wether or not copyDest is valid (has been written yet). */ - bool validCopyDest; /** Flag structure for the request. */ uint32_t flags; -//Accsesors for non-cpu request fields - public: - /** Accesor for paddr. */ - Addr getPaddr(); - /** Accesor for paddr. */ - void setPaddr(Addr _paddr); - - /** Accesor for size. */ - int getSize(); - /** Accesor for size. */ - void setSize(int _size); - - /** Accesor for time. */ - Tick getTime(); - /** Accesor for time. */ - void setTime(Tick _time); - - /** Accesor for copy dest. */ - Addr getCopyDest(); - /** Accesor for copy dest. */ - void setCopyDest(Addr _copyDest); - - /** Accesor for flags. */ - uint32_t getFlags(); - /** Accesor for paddr. */ - void setFlags(uint32_t _flags); - -//Now cpu-request fields - private: - /** Bool to signify if this is a cpuRequest. */ - bool cpuReq; - - /** The virtual address of the request. */ - Addr vaddr; - /** Wether or not the vaddr is valid. */ - bool validVaddr; + /** + * The time this request was started. Used to calculate + * latencies. This field is set to curTick any time paddr or vaddr + * is written. */ + Tick time; /** The address space ID. */ int asid; - /** Wether or not the asid is valid. */ - bool validAsid; + /** The virtual address of the request. */ + Addr vaddr; /** The return value of store conditional. */ uint64_t scResult; - /** Wether or not the sc result is valid. */ - bool validScResult; - /** The cpu number for statistics. */ + /** The cpu number (for statistics, typically). */ int cpuNum; - /** Wether or not the cpu number is valid. */ - bool validCpuNum; - - /** The requesting thread id. */ + /** The requesting thread id (for statistics, typically). */ int threadNum; - /** Wether or not the thread id is valid. */ - bool validThreadNum; /** program counter of initiating access; for tracing/debugging */ Addr pc; - /** Wether or not the pc is valid. */ + + /** Whether or not paddr is valid (has been written yet). */ + bool validPaddr; + /** Whether or not the asid & vaddr are valid. */ + bool validAsidVaddr; + /** Whether or not the sc result is valid. */ + bool validScResult; + /** Whether or not the cpu number & thread ID are valid. */ + bool validCpuAndThreadNums; + /** Whether or not the pc is valid. */ bool validPC; -//Accessor Functions for cpu request fields public: - /** Accesor function to determine if this is a cpu request or not.*/ - bool isCpuRequest(); - - /** Accesor function for vaddr.*/ - Addr getVaddr(); - /** Accesor function for vaddr.*/ - void setVaddr(Addr _vaddr); - - /** Accesor function for asid.*/ - int getAsid(); - /** Accesor function for asid.*/ - void setAsid(int _asid); - - /** Accesor function for store conditional return value.*/ - uint64_t getScResult(); - /** Accesor function for store conditional return value.*/ - void setScResult(uint64_t _scResult); - - /** Accesor function for cpu number.*/ - int getCpuNum(); - /** Accesor function for cpu number.*/ - void setCpuNum(int _cpuNum); - - /** Accesor function for thread number.*/ - int getThreadNum(); - /** Accesor function for thread number.*/ - void setThreadNum(int _threadNum); - - /** Accesor function for pc.*/ - Addr getPC(); - /** Accesor function for pc.*/ - void setPC(Addr _pc); + /** Minimal constructor. No fields are initialized. */ + Request() + : validPaddr(false), validAsidVaddr(false), + validScResult(false), validCpuAndThreadNums(false), validPC(false) + {} + + /** + * Constructor for physical (e.g. device) requests. Initializes + * just physical address, size, flags, and timestamp (to curTick). + * These fields are adequate to perform a request. */ + Request(Addr _paddr, int _size, int _flags) + : validCpuAndThreadNums(false) + { setPhys(_paddr, _size, _flags); } + + Request(int _asid, Addr _vaddr, int _size, int _flags, Addr _pc, + int _cpuNum, int _threadNum) + { + setThreadContext(_cpuNum, _threadNum); + setVirt(_asid, _vaddr, _size, _flags, _pc); + } + + /** + * Set up CPU and thread numbers. */ + void setThreadContext(int _cpuNum, int _threadNum) + { + cpuNum = _cpuNum; + threadNum = _threadNum; + validCpuAndThreadNums = true; + } + + /** + * Set up a physical (e.g. device) request in a previously + * allocated Request object. */ + void setPhys(Addr _paddr, int _size, int _flags) + { + paddr = _paddr; + size = _size; + flags = _flags; + time = curTick; + validPaddr = true; + validAsidVaddr = false; + validPC = false; + validScResult = false; + } + + /** + * Set up a virtual (e.g., CPU) request in a previously + * allocated Request object. */ + void setVirt(int _asid, Addr _vaddr, int _size, int _flags, Addr _pc) + { + asid = _asid; + vaddr = _vaddr; + size = _size; + flags = _flags; + pc = _pc; + time = curTick; + validPaddr = false; + validAsidVaddr = true; + validPC = true; + validScResult = false; + } + + /** Set just the physical address. This should only be used to + * record the result of a translation, and thus the vaddr must be + * valid before this method is called. Otherwise, use setPhys() + * to guarantee that the size and flags are also set. + */ + void setPaddr(Addr _paddr) + { + assert(validAsidVaddr); + paddr = _paddr; + validPaddr = true; + } + + /** Accessor for paddr. */ + Addr getPaddr() { assert(validPaddr); return paddr; } + + /** Accessor for size. */ + int getSize() { assert(validPaddr || validAsidVaddr); return size; } + /** Accessor for time. */ + Tick getTime() { assert(validPaddr || validAsidVaddr); return time; } + + /** Accessor for flags. */ + uint32_t getFlags() { assert(validPaddr || validAsidVaddr); return flags; } + /** Accessor for paddr. */ + void setFlags(uint32_t _flags) + { assert(validPaddr || validAsidVaddr); flags = _flags; } + + /** Accessor function for vaddr.*/ + Addr getVaddr() { assert(validAsidVaddr); return vaddr; } + + /** Accessor function for asid.*/ + int getAsid() { assert(validAsidVaddr); return asid; } + + /** Accessor function to check if sc result is valid. */ + bool scResultValid() { return validScResult; } + /** Accessor function for store conditional return value.*/ + uint64_t getScResult() { assert(validScResult); return scResult; } + /** Accessor function for store conditional return value.*/ + void setScResult(uint64_t _scResult) + { scResult = _scResult; validScResult = true; } + + /** Accessor function for cpu number.*/ + int getCpuNum() { assert(validCpuAndThreadNums); return cpuNum; } + /** Accessor function for thread number.*/ + int getThreadNum() { assert(validCpuAndThreadNums); return threadNum; } + + /** Accessor function for pc.*/ + Addr getPC() { assert(validPC); return pc; } friend class Packet; }; diff --git a/src/mem/translating_port.cc b/src/mem/translating_port.cc index ee4d277b6..d2c854086 100644 --- a/src/mem/translating_port.cc +++ b/src/mem/translating_port.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + * Steve Reinhardt */ #include <string> diff --git a/src/mem/translating_port.hh b/src/mem/translating_port.hh index d078158a3..7354278ba 100644 --- a/src/mem/translating_port.hh +++ b/src/mem/translating_port.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + * Ali Saidi */ #ifndef __MEM_TRANSLATING_PROT_HH__ diff --git a/src/mem/vport.cc b/src/mem/vport.cc index cc569acf3..cd297bb8e 100644 --- a/src/mem/vport.cc +++ b/src/mem/vport.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @@ -40,8 +42,8 @@ VirtualPort::readBlob(Addr addr, uint8_t *p, int size) for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); gen.next()) { - if (xc) - paddr = TheISA::vtophys(xc,gen.addr()); + if (tc) + paddr = TheISA::vtophys(tc,gen.addr()); else paddr = TheISA::vtophys(gen.addr()); @@ -57,8 +59,8 @@ VirtualPort::writeBlob(Addr addr, uint8_t *p, int size) for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); gen.next()) { - if (xc) - paddr = TheISA::vtophys(xc,gen.addr()); + if (tc) + paddr = TheISA::vtophys(tc,gen.addr()); else paddr = TheISA::vtophys(gen.addr()); diff --git a/src/mem/vport.hh b/src/mem/vport.hh index 0f3b1f09e..697c8e5f3 100644 --- a/src/mem/vport.hh +++ b/src/mem/vport.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi */ /** @@ -36,13 +38,13 @@ #ifndef __MEM_VPORT_HH__ #define __MEM_VPORT_HH__ -#include "mem/port.hh" +#include "mem/port_impl.hh" #include "config/full_system.hh" #include "arch/vtophys.hh" /** A class that translates a virtual address to a physical address and then - * calls the above read/write functions. If an execution context is provided the + * calls the above read/write functions. If a thread context is provided the * address can alway be translated, If not it can only be translated if it is a * simple address masking operation (such as alpha super page accesses). */ @@ -50,18 +52,19 @@ class VirtualPort : public FunctionalPort { private: - ExecContext *xc; + ThreadContext *tc; public: - VirtualPort(const std::string &_name, ExecContext *_xc = NULL) - : FunctionalPort(_name), xc(_xc) + VirtualPort(const std::string &_name, ThreadContext *_tc = NULL) + : FunctionalPort(_name), tc(_tc) {} - /** Return true if we have an exec context. This is used to prevent someone - * from accidently deleting the cpus statically allocated vport. - * @return true if an execution context isn't valid + /** Return true if we have an thread context. This is used to + * prevent someone from accidently deleting the cpus statically + * allocated vport. + * @return true if a thread context isn't valid */ - bool nullExecContext() { return xc != NULL; } + bool nullThreadContext() { return tc != NULL; } /** Version of readblob that translates virt->phys and deals * with page boundries. */ diff --git a/src/python/SConscript b/src/python/SConscript index 4407e403d..7b0f591eb 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -25,183 +25,82 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt +# Nathan Binkert import os, os.path, re, sys +from zipfile import PyZipFile -Import('env') +# handy function for path joins +def join(*args): + return os.path.normpath(os.path.join(*args)) -import scons_helper - -def WriteEmbeddedPyFile(target, source, path, name, ext, filename): - if isinstance(source, str): - source = file(source, 'r') - - if isinstance(target, str): - target = file(target, 'w') - - print >>target, "AddModule(%s, %s, %s, %s, '''\\" % \ - (`path`, `name`, `ext`, `filename`) - - for line in source: - line = line - # escape existing backslashes - line = line.replace('\\', '\\\\') - # escape existing triple quotes - line = line.replace("'''", r"\'\'\'") - - print >>target, line, - - print >>target, "''')" - print >>target - -def WriteCFile(target, source, name): - if isinstance(source, str): - source = file(source, 'r') - - if isinstance(target, str): - target = file(target, 'w') - - print >>target, 'const char %s_string[] = {' % name - - count = 0 - from array import array - try: - while True: - foo = array('B') - foo.fromfile(source, 10000) - l = [ str(i) for i in foo.tolist() ] - count += len(l) - for i in xrange(0,9999,20): - print >>target, ','.join(l[i:i+20]) + ',' - except EOFError: - l = [ str(i) for i in foo.tolist() ] - count += len(l) - for i in xrange(0,len(l),20): - print >>target, ','.join(l[i:i+20]) + ',' - print >>target, ','.join(l[i:]) + ',' - - print >>target, '};' - print >>target, 'const int %s_length = %d;' % (name, count) - print >>target - -def splitpath(path): - dir,file = os.path.split(path) - path = [] - assert(file) - while dir: - dir,base = os.path.split(dir) - path.insert(0, base) - return path, file - -def MakeEmbeddedPyFile(target, source, env): - target = file(str(target[0]), 'w') - - tree = {} - for src in source: - src = str(src) - path,pyfile = splitpath(src) - node = tree - for dir in path: - if not node.has_key(dir): - node[dir] = { } - node = node[dir] - - name,ext = pyfile.split('.') - if name == '__init__': - node['.hasinit'] = True - node[pyfile] = (src,name,ext,src) - - done = False - while not done: - done = True - for name,entry in tree.items(): - if not isinstance(entry, dict): continue - if entry.has_key('.hasinit'): continue - - done = False - del tree[name] - for key,val in entry.iteritems(): - if tree.has_key(key): - raise NameError, \ - "dir already has %s can't add it again" % key - tree[key] = val - - files = [] - def populate(node, path = []): - names = node.keys() - names.sort() - for name in names: - if name == '.hasinit': - continue - - entry = node[name] - if isinstance(entry, dict): - if not entry.has_key('.hasinit'): - raise NameError, 'package directory missing __init__.py' - populate(entry, path + [ name ]) - else: - pyfile,name,ext,filename = entry - files.append((pyfile, path, name, ext, filename)) - populate(tree) - - for pyfile, path, name, ext, filename in files: - WriteEmbeddedPyFile(target, pyfile, path, name, ext, filename) +Import('env') +# This SConscript is in charge of collecting .py files and generating +# a zip archive that is appended to the m5 binary. + +# List of files & directories to include in the zip file. To include +# a package, list only the root directory of the package, not any +# internal .py files (else they will get the path stripped off when +# they are imported into the zip file). +pyzip_files = [] + +# List of additional files on which the zip archive depends, but which +# are not included in pyzip_files... i.e. individual .py files within +# a package. +pyzip_dep_files = [] + +# Add the specified package to the zip archive. Adds the directory to +# pyzip_files and all included .py files to pyzip_dep_files. +def addPkg(pkgdir): + pyzip_files.append(pkgdir) + origdir = os.getcwd() + srcdir = join(Dir('.').srcnode().abspath, pkgdir) + os.chdir(srcdir) + for path, dirs, files in os.walk('.'): + for i,dir in enumerate(dirs): + if dir == 'SCCS': + del dirs[i] + break + + for f in files: + if f.endswith('.py'): + pyzip_dep_files.append(join(pkgdir, path, f)) + + os.chdir(origdir) + +# Generate Python file that contains a dict specifying the current +# build_env flags. def MakeDefinesPyFile(target, source, env): f = file(str(target[0]), 'w') - print >>f, "import __main__" - print >>f, "__main__.m5_build_env = ", + print >>f, "m5_build_env = ", print >>f, source[0] f.close() -CFileCounter = 0 -def MakePythonCFile(target, source, env): - global CFileCounter - target = file(str(target[0]), 'w') - - print >>target, '''\ -#include "base/embedfile.hh" - -namespace { -''' - for src in source: - src = str(src) - fname = os.path.basename(src) - name = 'embedded_file%d' % CFileCounter - CFileCounter += 1 - WriteCFile(target, src, name) - print >>target, '''\ -EmbedMap %(name)s("%(fname)s", - %(name)s_string, %(name)s_length); - -''' % locals() - print >>target, '''\ - -/* namespace */ } -''' - -# base list of .py files to embed -embedded_py_files = [ os.path.join(env['ROOT'], 'util/pbs/jobfile.py') ] -# add all .py files in python/m5 -objpath = os.path.join(env['SRCDIR'], 'python', 'm5') -for root, dirs, files in os.walk(objpath, topdown=True): - for i,dir in enumerate(dirs): - if dir == 'SCCS': - del dirs[i] - break - - assert(root.startswith(objpath)) - for f in files: - if f.endswith('.py'): - embedded_py_files.append(os.path.join(root, f)) - -embedfile_hh = os.path.join(env['SRCDIR'], 'base/embedfile.hh') - optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions]) -env.Command('defines.py', Value(optionDict), MakeDefinesPyFile) - -env.Command('embedded_py.py', embedded_py_files, MakeEmbeddedPyFile) -env.Depends('embedded_py.cc', embedfile_hh) -env.Command('embedded_py.cc', - ['string_importer.py', 'defines.py', 'embedded_py.py'], - MakePythonCFile) +env.Command('m5/defines.py', Value(optionDict), MakeDefinesPyFile) + +# Now specify the packages & files for the zip archive. +addPkg('m5') +pyzip_files.append('m5/defines.py') +pyzip_files.append(join(env['ROOT'], 'util/pbs/jobfile.py')) + +env.Command(['swig/main_wrap.cc', 'm5/main.py'], + 'swig/main.i', + '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} ' + '-o ${TARGETS[0]} $SOURCES') + +pyzip_dep_files.append('m5/main.py') + +# Action function to build the zip archive. Uses the PyZipFile module +# included in the standard Python library. +def buildPyZip(target, source, env): + pzf = PyZipFile(str(target[0]), 'w') + for s in source: + pzf.writepy(str(s)) + +# Add the zip file target to the environment. +env.Command('m5py.zip', pyzip_files, buildPyZip) +env.Depends('m5py.zip', pyzip_dep_files) diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index 9bb68a090..60a61d66e 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -23,24 +23,30 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert +# Steve Reinhardt + +import sys, os, time, atexit, optparse -import sys, os +# import the SWIG-wrapped main C++ functions +import main +# import a few SWIG-wrapped items (those that are likely to be used +# directly by user scripts) completely into this module for +# convenience +from main import simulate, SimLoopExitEvent + +# import the m5 compile options +import defines # define this here so we can use it right away if necessary def panic(string): print >>sys.stderr, 'panic:', string sys.exit(1) -def m5execfile(f, global_dict): - # copy current sys.path - oldpath = sys.path[:] - # push file's directory onto front of path - sys.path.insert(0, os.path.abspath(os.path.dirname(f))) - execfile(f, global_dict) - # restore original path - sys.path = oldpath - -# Prepend given directory to system module search path. +# Prepend given directory to system module search path. We may not +# need this anymore if we can structure our config library more like a +# Python package. def AddToPath(path): # if it's a relative path and we know what directory the current # python script is in, make the path relative to that directory. @@ -51,24 +57,58 @@ def AddToPath(path): # so place the new dir right after that. sys.path.insert(1, path) -# find the m5 compile options: must be specified as a dict in -# __main__.m5_build_env. -import __main__ -if not hasattr(__main__, 'm5_build_env'): - panic("__main__ must define m5_build_env") + +# Callback to set trace flags. Not necessarily the best way to do +# things in the long run (particularly if we change how these global +# options are handled). +def setTraceFlags(option, opt_str, value, parser): + objects.Trace.flags = value + +# Standard optparse options. Need to be explicitly included by the +# user script when it calls optparse.OptionParser(). +standardOptions = [ + optparse.make_option("--traceflags", type="string", action="callback", + callback=setTraceFlags) + ] # make a SmartDict out of the build options for our local use import smartdict build_env = smartdict.SmartDict() -build_env.update(__main__.m5_build_env) +build_env.update(defines.m5_build_env) # make a SmartDict out of the OS environment too env = smartdict.SmartDict() env.update(os.environ) -# import the main m5 config code -from config import * +# The final hook to generate .ini files. Called from the user script +# once the config is built. +def instantiate(root): + config.ticks_per_sec = float(root.clock.frequency) + # ugly temporary hack to get output to config.ini + sys.stdout = file('config.ini', 'w') + root.print_ini() + sys.stdout.close() # close config.ini + sys.stdout = sys.__stdout__ # restore to original + main.initialize() # load config.ini into C++ and process it + noDot = True # temporary until we fix dot + if not noDot: + dot = pydot.Dot() + instance.outputDot(dot) + dot.orientation = "portrait" + dot.size = "8.5,11" + dot.ranksep="equally" + dot.rank="samerank" + dot.write("config.dot") + dot.write_ps("config.ps") + +# Export curTick to user script. +def curTick(): + return main.cvar.curTick -# import the built-in object definitions -from objects import * +# register our C++ exit callback function with Python +atexit.register(main.doExitCleanup) +# This import allows user scripts to reference 'm5.objects.Foo' after +# just doing an 'import m5' (without an 'import m5.objects'). May not +# matter since most scripts will probably 'from m5.objects import *'. +import objects diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 1e25e0d09..97e13c900 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -23,12 +23,14 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Steve Reinhardt +# Nathan Binkert -from __future__ import generators import os, re, sys, types, inspect import m5 -panic = m5.panic +from m5 import panic from convert import * from multidict import multidict @@ -134,8 +136,15 @@ class Singleton(type): def isSimObject(value): return isinstance(value, SimObject) -def isSimObjSequence(value): - if not isinstance(value, (list, tuple)): +def isSimObjectClass(value): + try: + return issubclass(value, SimObject) + except TypeError: + # happens if value is not a class at all + return False + +def isSimObjectSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: return False for val in value: @@ -144,9 +153,41 @@ def isSimObjSequence(value): return True +def isSimObjectClassSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: + return False + + for val in value: + if not isNullPointer(val) and not isSimObjectClass(val): + return False + + return True + +def isSimObjectOrSequence(value): + return isSimObject(value) or isSimObjectSequence(value) + +def isSimObjectClassOrSequence(value): + return isSimObjectClass(value) or isSimObjectClassSequence(value) + def isNullPointer(value): return isinstance(value, NullSimObject) +# Apply method to object. +# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>) +def applyMethod(obj, meth, *args, **kwargs): + return getattr(obj, meth)(*args, **kwargs) + +# If the first argument is an (non-sequence) object, apply the named +# method with the given arguments. If the first argument is a +# sequence, apply the method to each element of the sequence (a la +# 'map'). +def applyOrMap(objOrSeq, meth, *args, **kwargs): + if not isinstance(objOrSeq, (list, tuple)): + return applyMethod(objOrSeq, meth, *args, **kwargs) + else: + return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq] + + # The metaclass for ConfigNode (and thus for everything that derives # from ConfigNode, including SimObject). This class controls how new # classes that derive from ConfigNode are instantiated, and provides @@ -167,24 +208,32 @@ class MetaSimObject(type): # and only allow "private" attributes to be passed to the base # __new__ (starting with underscore). def __new__(mcls, name, bases, dict): - # Copy "private" attributes (including special methods such as __new__) - # to the official dict. Everything else goes in _init_dict to be - # filtered in __init__. - cls_dict = {} - for key,val in dict.items(): - if key.startswith('_'): - cls_dict[key] = val - del dict[key] - cls_dict['_init_dict'] = dict + if dict.has_key('_init_dict'): + # must have been called from makeSubclass() rather than + # via Python class declaration; bypass filtering process. + cls_dict = dict + else: + # Copy "private" attributes (including special methods + # such as __new__) to the official dict. Everything else + # goes in _init_dict to be filtered in __init__. + cls_dict = {} + for key,val in dict.items(): + if key.startswith('_'): + cls_dict[key] = val + del dict[key] + cls_dict['_init_dict'] = dict return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) - # initialization + # subclass initialization def __init__(cls, name, bases, dict): + # calls type.__init__()... I think that's a no-op, but leave + # it here just in case it's not. super(MetaSimObject, cls).__init__(name, bases, dict) # initialize required attributes cls._params = multidict() cls._values = multidict() + cls._instantiated = False # really instantiated or subclassed cls._anon_subclass_counter = 0 # We don't support multiple inheritance. If you want to, you @@ -194,27 +243,14 @@ class MetaSimObject(type): base = bases[0] + # the only time the following is not true is when we define + # the SimObject class itself if isinstance(base, MetaSimObject): cls._params.parent = base._params cls._values.parent = base._values + base._instantiated = True - # If your parent has a value in it that's a config node, clone - # it. Do this now so if we update any of the values' - # attributes we are updating the clone and not the original. - for key,val in base._values.iteritems(): - - # don't clone if (1) we're about to overwrite it with - # a local setting or (2) we've already cloned a copy - # from an earlier (more derived) base - if cls._init_dict.has_key(key) or cls._values.has_key(key): - continue - - if isSimObject(val): - cls._values[key] = val() - elif isSimObjSequence(val) and len(val): - cls._values[key] = [ v() for v in val ] - - # now process remaining _init_dict items + # now process the _init_dict items for key,val in cls._init_dict.items(): if isinstance(val, (types.FunctionType, types.TypeType)): type.__setattr__(cls, key, val) @@ -231,6 +267,27 @@ class MetaSimObject(type): else: setattr(cls, key, val) + # Pull the deep-copy memoization dict out of the class dict if + # it's there... + memo = cls.__dict__.get('_memo', {}) + + # Handle SimObject values + for key,val in cls._values.iteritems(): + # SimObject instances need to be promoted to classes. + # Existing classes should not have any instance values, so + # these can only occur at the lowest level dict (the + # parameters just being set in this class definition). + if isSimObjectOrSequence(val): + assert(val == cls._values.local[key]) + cls._values[key] = applyOrMap(val, 'makeClass', memo) + # SimObject classes need to be subclassed so that + # parameters that get set at this level only affect this + # level and derivatives. + elif isSimObjectClassOrSequence(val): + assert(not cls._values.local.has_key(key)) + cls._values[key] = applyOrMap(val, 'makeSubclass', {}, memo) + + def _set_keyword(cls, keyword, val, kwtype): if not isinstance(val, kwtype): raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ @@ -260,6 +317,11 @@ class MetaSimObject(type): param = cls._params.get(attr, None) if param: # It's ok: set attribute by delegating to 'object' class. + if isSimObjectOrSequence(value) and cls._instantiated: + raise AttributeError, \ + "Cannot set SimObject parameter '%s' after\n" \ + " class %s has been instantiated or subclassed" \ + % (attr, cls.__name__) try: cls._values[attr] = param.convert(value) except Exception, e: @@ -268,7 +330,7 @@ class MetaSimObject(type): e.args = (msg, ) raise # I would love to get rid of this - elif isSimObject(value) or isSimObjSequence(value): + elif isSimObjectOrSequence(value): cls._values[attr] = value else: raise AttributeError, \ @@ -281,6 +343,22 @@ class MetaSimObject(type): raise AttributeError, \ "object '%s' has no attribute '%s'" % (cls.__name__, attr) + # Create a subclass of this class. Basically a function interface + # to the standard Python class definition mechanism, primarily for + # internal use. 'memo' dict param supports "deep copy" (really + # "deep subclass") operations... within a given operation, + # multiple references to a class should result in a single + # subclass object with multiple references to it (as opposed to + # mutiple unique subclasses). + def makeSubclass(cls, init_dict, memo = {}): + subcls = memo.get(cls) + if not subcls: + name = cls.__name__ + '_' + str(cls._anon_subclass_counter) + cls._anon_subclass_counter += 1 + subcls = MetaSimObject(name, (cls,), + { '_init_dict': init_dict, '_memo': memo }) + return subcls + # The ConfigNode class is the root of the special hierarchy. Most of # the code in this class deals with the configuration hierarchy itself # (parent/child node relationships). @@ -289,27 +367,80 @@ class SimObject(object): # get this metaclass. __metaclass__ = MetaSimObject - def __init__(self, _value_parent = None, **kwargs): + # __new__ operator allocates new instances of the class. We + # override it here just to support "deep instantiation" operation + # via the _memo dict. When recursively instantiating an object + # hierarchy we want to make sure that each class is instantiated + # only once, and that if there are multiple references to the same + # original class, we end up with the corresponding instantiated + # references all pointing to the same instance. + def __new__(cls, _memo = None, **kwargs): + if _memo is not None and _memo.has_key(cls): + # return previously instantiated object + assert(len(kwargs) == 0) + return _memo[cls] + else: + # Need a new one... if it needs to be memoized, this will + # happen in __init__. We defer the insertion until then + # so __init__ can use the memo dict to tell whether or not + # to perform the initialization. + return super(SimObject, cls).__new__(cls, **kwargs) + + # Initialize new instance previously allocated by __new__. For + # objects with SimObject-valued params, we need to recursively + # instantiate the classes represented by those param values as + # well (in a consistent "deep copy"-style fashion; see comment + # above). + def __init__(self, _memo = None, **kwargs): + if _memo is not None: + # We're inside a "deep instantiation" + assert(isinstance(_memo, dict)) + assert(len(kwargs) == 0) + if _memo.has_key(self.__class__): + # __new__ returned an existing, already initialized + # instance, so there's nothing to do here + assert(_memo[self.__class__] == self) + return + # no pre-existing object, so remember this one here + _memo[self.__class__] = self + else: + # This is a new top-level instantiation... don't memoize + # this objcet, but prepare to memoize any recursively + # instantiated objects. + _memo = {} + + self.__class__._instantiated = True + self._children = {} - if _value_parent and type(_value_parent) != type(self): - # this was called as a type conversion rather than a clone - raise TypeError, "Cannot convert %s to %s" % \ - (_value_parent.__class__.__name__, self.__class__.__name__) - if not _value_parent: - _value_parent = self.__class__ - # clone values - self._values = multidict(_value_parent._values) - for key,val in _value_parent._values.iteritems(): - if isSimObject(val): - setattr(self, key, val()) - elif isSimObjSequence(val) and len(val): - setattr(self, key, [ v() for v in val ]) + # Inherit parameter values from class using multidict so + # individual value settings can be overridden. + self._values = multidict(self.__class__._values) + # For SimObject-valued parameters, the class should have + # classes (not instances) for the values. We need to + # instantiate these classes rather than just inheriting the + # class object. + for key,val in self.__class__._values.iteritems(): + if isSimObjectClass(val): + setattr(self, key, val(_memo)) + elif isSimObjectClassSequence(val) and len(val): + setattr(self, key, [ v(_memo) for v in val ]) # apply attribute assignments from keyword args, if any for key,val in kwargs.iteritems(): setattr(self, key, val) + # Use this instance as a template to create a new class. + def makeClass(self, memo = {}): + cls = memo.get(self) + if not cls: + cls = self.__class__.makeSubclass(self._values.local) + memo[self] = cls + return cls + + # Direct instantiation of instances (cloning) is no longer + # allowed; must generate class from instance first. def __call__(self, **kwargs): - return self.__class__(_value_parent = self, **kwargs) + raise TypeError, "cannot instantiate SimObject; "\ + "use makeClass() to make class first" def __getattr__(self, attr): if self._values.has_key(attr): @@ -338,7 +469,7 @@ class SimObject(object): e.args = (msg, ) raise # I would love to get rid of this - elif isSimObject(value) or isSimObjSequence(value): + elif isSimObjectOrSequence(value): pass else: raise AttributeError, "Class %s has no parameter %s" \ @@ -349,7 +480,7 @@ class SimObject(object): if isSimObject(value): value.set_path(self, attr) - elif isSimObjSequence(value): + elif isSimObjectSequence(value): value = SimObjVector(value) [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)] @@ -725,7 +856,7 @@ class ParamDesc(object): def __getattr__(self, attr): if attr == 'ptype': try: - ptype = eval(self.ptype_str, m5.__dict__) + ptype = eval(self.ptype_str, m5.objects.__dict__) if not isinstance(ptype, type): panic("Param qualifier is not a type: %s" % self.ptype) self.ptype = ptype @@ -772,7 +903,7 @@ class VectorParamDesc(ParamDesc): if isinstance(value, (list, tuple)): # list: coerce each element into new list tmp_list = [ ParamDesc.convert(self, v) for v in value ] - if isSimObjSequence(tmp_list): + if isSimObjectSequence(tmp_list): return SimObjVector(tmp_list) else: return VectorParamValue(tmp_list) @@ -794,7 +925,7 @@ class ParamFactory(object): # E.g., Param.Int(5, "number of widgets") def __call__(self, *args, **kwargs): - caller_frame = inspect.stack()[1][0] + caller_frame = inspect.currentframe().f_back ptype = None try: ptype = eval(self.ptype_str, @@ -1066,7 +1197,10 @@ class EthernetAddr(ParamValue): def __str__(self): if self.value == NextEthernetAddr: - return self.addr + if hasattr(self, 'addr'): + return self.addr + else: + return "NextEthernetAddr (unresolved)" else: return self.value @@ -1287,23 +1421,6 @@ AllMemory = AddrRange(0, MaxAddr) ##################################################################### -# The final hook to generate .ini files. Called from configuration -# script once config is built. -def instantiate(root): - global ticks_per_sec - ticks_per_sec = float(root.clock.frequency) - root.print_ini() - noDot = True # temporary until we fix dot - if not noDot: - dot = pydot.Dot() - instance.outputDot(dot) - dot.orientation = "portrait" - dot.size = "8.5,11" - dot.ranksep="equally" - dot.rank="samerank" - dot.write("config.dot") - dot.write_ps("config.ps") - # __all__ defines the list of symbols that get exported when # 'from config import *' is invoked. Try to keep this reasonably # short to avoid polluting other namespaces. @@ -1319,5 +1436,5 @@ __all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 'NetworkBandwidth', 'MemoryBandwidth', 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Null', 'NULL', - 'NextEthernetAddr', 'instantiate'] + 'NextEthernetAddr'] diff --git a/src/python/m5/convert.py b/src/python/m5/convert.py index 73181e985..580a579bc 100644 --- a/src/python/m5/convert.py +++ b/src/python/m5/convert.py @@ -23,6 +23,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert # metric prefixes exa = 1.0e18 diff --git a/src/python/m5/multidict.py b/src/python/m5/multidict.py index fd40ebbbd..34fc3139b 100644 --- a/src/python/m5/multidict.py +++ b/src/python/m5/multidict.py @@ -23,13 +23,15 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert __all__ = [ 'multidict' ] class multidict(object): __nodefault = object() def __init__(self, parent = {}, **kwargs): - self.dict = dict(**kwargs) + self.local = dict(**kwargs) self.parent = parent self.deleted = {} @@ -40,11 +42,11 @@ class multidict(object): return `dict(self.items())` def __contains__(self, key): - return self.dict.has_key(key) or self.parent.has_key(key) + return self.local.has_key(key) or self.parent.has_key(key) def __delitem__(self, key): try: - del self.dict[key] + del self.local[key] except KeyError, e: if key in self.parent: self.deleted[key] = True @@ -53,11 +55,11 @@ class multidict(object): def __setitem__(self, key, value): self.deleted.pop(key, False) - self.dict[key] = value + self.local[key] = value def __getitem__(self, key): try: - return self.dict[key] + return self.local[key] except KeyError, e: if not self.deleted.get(key, False) and key in self.parent: return self.parent[key] @@ -65,15 +67,15 @@ class multidict(object): raise KeyError, e def __len__(self): - return len(self.dict) + len(self.parent) + return len(self.local) + len(self.parent) def next(self): - for key,value in self.dict.items(): + for key,value in self.local.items(): yield key,value if self.parent: for key,value in self.parent.next(): - if key not in self.dict and key not in self.deleted: + if key not in self.local and key not in self.deleted: yield key,value def has_key(self, key): @@ -114,22 +116,22 @@ class multidict(object): return self[key] except KeyError: self.deleted.pop(key, False) - self.dict[key] = default + self.local[key] = default return default def _dump(self): print 'multidict dump' node = self while isinstance(node, multidict): - print ' ', node.dict + print ' ', node.local node = node.parent def _dumpkey(self, key): values = [] node = self while isinstance(node, multidict): - if key in node.dict: - values.append(node.dict[key]) + if key in node.local: + values.append(node.local[key]) node = node.parent print key, values diff --git a/src/python/m5/objects/AlphaConsole.py b/src/python/m5/objects/AlphaConsole.py index 68e6089ab..329b8c5bd 100644 --- a/src/python/m5/objects/AlphaConsole.py +++ b/src/python/m5/objects/AlphaConsole.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice class AlphaConsole(BasicPioDevice): diff --git a/src/python/m5/objects/AlphaFullCPU.py b/src/python/m5/objects/AlphaFullCPU.py index 48989d057..2988305d3 100644 --- a/src/python/m5/objects/AlphaFullCPU.py +++ b/src/python/m5/objects/AlphaFullCPU.py @@ -1,13 +1,15 @@ -from m5 import * +from m5 import build_env +from m5.config import * from BaseCPU import BaseCPU class DerivAlphaFullCPU(BaseCPU): type = 'DerivAlphaFullCPU' - + activity = Param.Unsigned("Initial count") numThreads = Param.Unsigned("number of HW thread contexts") - if not build_env['FULL_SYSTEM']: - mem = Param.FunctionalMemory(NULL, "memory") + checker = Param.BaseCPU(NULL, "checker") + + cachePorts = Param.Unsigned("Cache Ports") decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") renameToFetchDelay = Param.Unsigned("Rename to fetch delay") @@ -41,22 +43,26 @@ class DerivAlphaFullCPU(BaseCPU): executeFloatWidth = Param.Unsigned("Floating point execute width") executeBranchWidth = Param.Unsigned("Branch execute width") executeMemoryWidth = Param.Unsigned("Memory execute width") + fuPool = Param.FUPool(NULL, "Functional Unit pool") iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " "delay") renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") commitWidth = Param.Unsigned("Commit width") squashWidth = Param.Unsigned("Squash width") + trapLatency = Param.Tick("Trap latency") + fetchTrapLatency = Param.Tick("Fetch trap latency") - local_predictor_size = Param.Unsigned("Size of local predictor") - local_ctr_bits = Param.Unsigned("Bits per counter") - local_history_table_size = Param.Unsigned("Size of local history table") - local_history_bits = Param.Unsigned("Bits for the local history") - global_predictor_size = Param.Unsigned("Size of global predictor") - global_ctr_bits = Param.Unsigned("Bits per counter") - global_history_bits = Param.Unsigned("Bits of history") - choice_predictor_size = Param.Unsigned("Size of choice predictor") - choice_ctr_bits = Param.Unsigned("Bits of choice counters") + predType = Param.String("Branch predictor type ('local', 'tournament')") + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") BTBEntries = Param.Unsigned("Number of BTB entries") BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") @@ -68,6 +74,8 @@ class DerivAlphaFullCPU(BaseCPU): LFSTSize = Param.Unsigned("Last fetched store table size") SSITSize = Param.Unsigned("Store set ID table size") + numRobs = Param.Unsigned("Number of Reorder Buffers"); + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") numPhysFloatRegs = Param.Unsigned("Number of physical floating point " "registers") @@ -78,3 +86,13 @@ class DerivAlphaFullCPU(BaseCPU): function_trace = Param.Bool(False, "Enable function trace") function_trace_start = Param.Tick(0, "Cycle to start function trace") + + smtNumFetchingThreads = Param.Unsigned("SMT Number of Fetching Threads") + smtFetchPolicy = Param.String("SMT Fetch policy") + smtLSQPolicy = Param.String("SMT LSQ Sharing Policy") + smtLSQThreshold = Param.String("SMT LSQ Threshold Sharing Parameter") + smtIQPolicy = Param.String("SMT IQ Sharing Policy") + smtIQThreshold = Param.String("SMT IQ Threshold Sharing Parameter") + smtROBPolicy = Param.String("SMT ROB Sharing Policy") + smtROBThreshold = Param.String("SMT ROB Threshold Sharing Parameter") + smtCommitPolicy = Param.String("SMT Commit Policy") diff --git a/src/python/m5/objects/AlphaTLB.py b/src/python/m5/objects/AlphaTLB.py index 5edf8e13d..11c1792ee 100644 --- a/src/python/m5/objects/AlphaTLB.py +++ b/src/python/m5/objects/AlphaTLB.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class AlphaTLB(SimObject): type = 'AlphaTLB' abstract = True diff --git a/src/python/m5/objects/BadDevice.py b/src/python/m5/objects/BadDevice.py index 9cb9a8f03..186b733fa 100644 --- a/src/python/m5/objects/BadDevice.py +++ b/src/python/m5/objects/BadDevice.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice class BadDevice(BasicPioDevice): diff --git a/src/python/m5/objects/BaseCPU.py b/src/python/m5/objects/BaseCPU.py index 49cb2a8f3..2e78578df 100644 --- a/src/python/m5/objects/BaseCPU.py +++ b/src/python/m5/objects/BaseCPU.py @@ -1,4 +1,6 @@ -from m5 import * +from m5 import build_env +from m5.config import * + class BaseCPU(SimObject): type = 'BaseCPU' abstract = True diff --git a/src/python/m5/objects/BaseCache.py b/src/python/m5/objects/BaseCache.py index 79d21572a..33f44759b 100644 --- a/src/python/m5/objects/BaseCache.py +++ b/src/python/m5/objects/BaseCache.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from BaseMem import BaseMem class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb'] diff --git a/src/python/m5/objects/Bridge.py b/src/python/m5/objects/Bridge.py index ada715ce9..880535755 100644 --- a/src/python/m5/objects/Bridge.py +++ b/src/python/m5/objects/Bridge.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class Bridge(MemObject): diff --git a/src/python/m5/objects/Bus.py b/src/python/m5/objects/Bus.py index 8c5397281..c37dab438 100644 --- a/src/python/m5/objects/Bus.py +++ b/src/python/m5/objects/Bus.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class Bus(MemObject): diff --git a/src/python/m5/objects/CoherenceProtocol.py b/src/python/m5/objects/CoherenceProtocol.py index 7013000d6..64b6cbacf 100644 --- a/src/python/m5/objects/CoherenceProtocol.py +++ b/src/python/m5/objects/CoherenceProtocol.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi'] class CoherenceProtocol(SimObject): diff --git a/src/python/m5/objects/Device.py b/src/python/m5/objects/Device.py index 2a71bbc65..7798f5f04 100644 --- a/src/python/m5/objects/Device.py +++ b/src/python/m5/objects/Device.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class PioDevice(MemObject): diff --git a/src/python/m5/objects/DiskImage.py b/src/python/m5/objects/DiskImage.py index 0d55e9329..70d8b2e45 100644 --- a/src/python/m5/objects/DiskImage.py +++ b/src/python/m5/objects/DiskImage.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class DiskImage(SimObject): type = 'DiskImage' abstract = True diff --git a/src/python/m5/objects/Ethernet.py b/src/python/m5/objects/Ethernet.py index 4286c71c8..418670592 100644 --- a/src/python/m5/objects/Ethernet.py +++ b/src/python/m5/objects/Ethernet.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from Device import DmaDevice from Pci import PciDevice diff --git a/src/python/m5/objects/FUPool.py b/src/python/m5/objects/FUPool.py new file mode 100644 index 000000000..cbf1089cf --- /dev/null +++ b/src/python/m5/objects/FUPool.py @@ -0,0 +1,5 @@ +from m5.config import * + +class FUPool(SimObject): + type = 'FUPool' + FUList = VectorParam.FUDesc("list of FU's for this pool") diff --git a/src/python/m5/objects/Ide.py b/src/python/m5/objects/Ide.py index 2403e6d36..9ee578177 100644 --- a/src/python/m5/objects/Ide.py +++ b/src/python/m5/objects/Ide.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Pci import PciDevice class IdeID(Enum): vals = ['master', 'slave'] diff --git a/src/python/m5/objects/IntrControl.py b/src/python/m5/objects/IntrControl.py index 66c82c182..514c3fc62 100644 --- a/src/python/m5/objects/IntrControl.py +++ b/src/python/m5/objects/IntrControl.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class IntrControl(SimObject): type = 'IntrControl' cpu = Param.BaseCPU(Parent.any, "the cpu") diff --git a/src/python/m5/objects/MemObject.py b/src/python/m5/objects/MemObject.py index 4d68243e6..d957dae17 100644 --- a/src/python/m5/objects/MemObject.py +++ b/src/python/m5/objects/MemObject.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class MemObject(SimObject): type = 'MemObject' diff --git a/src/python/m5/objects/MemTest.py b/src/python/m5/objects/MemTest.py index 34299faf0..9916d7cb4 100644 --- a/src/python/m5/objects/MemTest.py +++ b/src/python/m5/objects/MemTest.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class MemTest(SimObject): type = 'MemTest' cache = Param.BaseCache("L1 cache") diff --git a/src/python/m5/objects/OzoneCPU.py b/src/python/m5/objects/OzoneCPU.py new file mode 100644 index 000000000..f2d9aea84 --- /dev/null +++ b/src/python/m5/objects/OzoneCPU.py @@ -0,0 +1,91 @@ +from m5 import build_env +from m5.config import * +from BaseCPU import BaseCPU + +class DerivOzoneCPU(BaseCPU): + type = 'DerivOzoneCPU' + + numThreads = Param.Unsigned("number of HW thread contexts") + + if not build_env['FULL_SYSTEM']: + mem = Param.FunctionalMemory(NULL, "memory") + + checker = Param.BaseCPU("Checker CPU") + + width = Param.Unsigned("Width") + frontEndWidth = Param.Unsigned("Front end width") + backEndWidth = Param.Unsigned("Back end width") + backEndSquashLatency = Param.Unsigned("Back end squash latency") + backEndLatency = Param.Unsigned("Back end latency") + maxInstBufferSize = Param.Unsigned("Maximum instruction buffer size") + maxOutstandingMemOps = Param.Unsigned("Maximum number of outstanding memory operations") + decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") + renameToFetchDelay = Param.Unsigned("Rename to fetch delay") + iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned("Commit to fetch delay") + fetchWidth = Param.Unsigned("Fetch width") + + renameToDecodeDelay = Param.Unsigned("Rename to decode delay") + iewToDecodeDelay = Param.Unsigned("Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned("Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned("Fetch to decode delay") + decodeWidth = Param.Unsigned("Decode width") + + iewToRenameDelay = Param.Unsigned("Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned("Commit to rename delay") + decodeToRenameDelay = Param.Unsigned("Decode to rename delay") + renameWidth = Param.Unsigned("Rename width") + + commitToIEWDelay = Param.Unsigned("Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned("Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned("Issue to execute delay (internal " + "to the IEW stage)") + issueWidth = Param.Unsigned("Issue width") + executeWidth = Param.Unsigned("Execute width") + executeIntWidth = Param.Unsigned("Integer execute width") + executeFloatWidth = Param.Unsigned("Floating point execute width") + executeBranchWidth = Param.Unsigned("Branch execute width") + executeMemoryWidth = Param.Unsigned("Memory execute width") + + iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") + commitWidth = Param.Unsigned("Commit width") + squashWidth = Param.Unsigned("Squash width") + + predType = Param.String("Type of branch predictor ('local', 'tournament')") + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") + + BTBEntries = Param.Unsigned("Number of BTB entries") + BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") + + RASSize = Param.Unsigned("RAS size") + + LQEntries = Param.Unsigned("Number of load queue entries") + SQEntries = Param.Unsigned("Number of store queue entries") + LFSTSize = Param.Unsigned("Last fetched store table size") + SSITSize = Param.Unsigned("Store set ID table size") + + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned("Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned("Number of instruction queue entries") + numROBEntries = Param.Unsigned("Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned("Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/python/m5/objects/Pci.py b/src/python/m5/objects/Pci.py index 85cefcd44..9e1e91b13 100644 --- a/src/python/m5/objects/Pci.py +++ b/src/python/m5/objects/Pci.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice, DmaDevice class PciConfigData(SimObject): diff --git a/src/python/m5/objects/PhysicalMemory.py b/src/python/m5/objects/PhysicalMemory.py index c59910093..bed90d555 100644 --- a/src/python/m5/objects/PhysicalMemory.py +++ b/src/python/m5/objects/PhysicalMemory.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from MemObject import * class PhysicalMemory(MemObject): diff --git a/src/python/m5/objects/Platform.py b/src/python/m5/objects/Platform.py index 4da0ffab4..89fee9991 100644 --- a/src/python/m5/objects/Platform.py +++ b/src/python/m5/objects/Platform.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Platform(SimObject): type = 'Platform' abstract = True diff --git a/src/python/m5/objects/Process.py b/src/python/m5/objects/Process.py index 60b00229e..0091d8654 100644 --- a/src/python/m5/objects/Process.py +++ b/src/python/m5/objects/Process.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Process(SimObject): type = 'Process' abstract = True diff --git a/src/python/m5/objects/Repl.py b/src/python/m5/objects/Repl.py index afd256082..8e9f1094f 100644 --- a/src/python/m5/objects/Repl.py +++ b/src/python/m5/objects/Repl.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Repl(SimObject): type = 'Repl' abstract = True diff --git a/src/python/m5/objects/Root.py b/src/python/m5/objects/Root.py index 205a93c76..373475a7a 100644 --- a/src/python/m5/objects/Root.py +++ b/src/python/m5/objects/Root.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Serialize import Serialize from Statistics import Statistics from Trace import Trace diff --git a/src/python/m5/objects/SimConsole.py b/src/python/m5/objects/SimConsole.py index df3061908..9e1452c6d 100644 --- a/src/python/m5/objects/SimConsole.py +++ b/src/python/m5/objects/SimConsole.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class ConsoleListener(SimObject): type = 'ConsoleListener' port = Param.TcpPort(3456, "listen port") diff --git a/src/python/m5/objects/SimpleDisk.py b/src/python/m5/objects/SimpleDisk.py index e34155ace..44ef709af 100644 --- a/src/python/m5/objects/SimpleDisk.py +++ b/src/python/m5/objects/SimpleDisk.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class SimpleDisk(SimObject): type = 'SimpleDisk' disk = Param.DiskImage("Disk Image") diff --git a/src/python/m5/objects/SimpleOzoneCPU.py b/src/python/m5/objects/SimpleOzoneCPU.py new file mode 100644 index 000000000..5d968cab0 --- /dev/null +++ b/src/python/m5/objects/SimpleOzoneCPU.py @@ -0,0 +1,87 @@ +from m5 import build_env +from m5.config import * +from BaseCPU import BaseCPU + +class SimpleOzoneCPU(BaseCPU): + type = 'SimpleOzoneCPU' + + numThreads = Param.Unsigned("number of HW thread contexts") + + if not build_env['FULL_SYSTEM']: + mem = Param.FunctionalMemory(NULL, "memory") + + width = Param.Unsigned("Width") + frontEndWidth = Param.Unsigned("Front end width") + backEndWidth = Param.Unsigned("Back end width") + backEndSquashLatency = Param.Unsigned("Back end squash latency") + backEndLatency = Param.Unsigned("Back end latency") + maxInstBufferSize = Param.Unsigned("Maximum instruction buffer size") + decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") + renameToFetchDelay = Param.Unsigned("Rename to fetch delay") + iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned("Commit to fetch delay") + fetchWidth = Param.Unsigned("Fetch width") + + renameToDecodeDelay = Param.Unsigned("Rename to decode delay") + iewToDecodeDelay = Param.Unsigned("Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned("Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned("Fetch to decode delay") + decodeWidth = Param.Unsigned("Decode width") + + iewToRenameDelay = Param.Unsigned("Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned("Commit to rename delay") + decodeToRenameDelay = Param.Unsigned("Decode to rename delay") + renameWidth = Param.Unsigned("Rename width") + + commitToIEWDelay = Param.Unsigned("Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned("Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned("Issue to execute delay (internal " + "to the IEW stage)") + issueWidth = Param.Unsigned("Issue width") + executeWidth = Param.Unsigned("Execute width") + executeIntWidth = Param.Unsigned("Integer execute width") + executeFloatWidth = Param.Unsigned("Floating point execute width") + executeBranchWidth = Param.Unsigned("Branch execute width") + executeMemoryWidth = Param.Unsigned("Memory execute width") + + iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") + commitWidth = Param.Unsigned("Commit width") + squashWidth = Param.Unsigned("Squash width") + + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") + + BTBEntries = Param.Unsigned("Number of BTB entries") + BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") + + RASSize = Param.Unsigned("RAS size") + + LQEntries = Param.Unsigned("Number of load queue entries") + SQEntries = Param.Unsigned("Number of store queue entries") + LFSTSize = Param.Unsigned("Last fetched store table size") + SSITSize = Param.Unsigned("Store set ID table size") + + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned("Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned("Number of instruction queue entries") + numROBEntries = Param.Unsigned("Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned("Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/python/m5/objects/System.py b/src/python/m5/objects/System.py index 622b5a870..9a1e1d690 100644 --- a/src/python/m5/objects/System.py +++ b/src/python/m5/objects/System.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * class System(SimObject): type = 'System' @@ -7,8 +8,6 @@ class System(SimObject): boot_cpu_frequency = Param.Frequency(Self.cpu[0].clock.frequency, "boot processor frequency") init_param = Param.UInt64(0, "numerical value to pass into simulator") - bin = Param.Bool(False, "is this system binned") - binned_fns = VectorParam.String([], "functions broken down and binned") boot_osflags = Param.String("a", "boot flags to pass to the kernel") kernel = Param.String("file that contains the kernel code") readfile = Param.String("", "file to read startup script from") diff --git a/src/python/m5/objects/Tsunami.py b/src/python/m5/objects/Tsunami.py index 27ea0bce8..4613571d8 100644 --- a/src/python/m5/objects/Tsunami.py +++ b/src/python/m5/objects/Tsunami.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice from Platform import Platform diff --git a/src/python/m5/objects/Uart.py b/src/python/m5/objects/Uart.py index 54754aeb9..8e1fd1a37 100644 --- a/src/python/m5/objects/Uart.py +++ b/src/python/m5/objects/Uart.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from Device import BasicPioDevice class Uart(BasicPioDevice): diff --git a/src/python/m5/smartdict.py b/src/python/m5/smartdict.py index cd38d7326..d85dbd517 100644 --- a/src/python/m5/smartdict.py +++ b/src/python/m5/smartdict.py @@ -23,6 +23,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert # The SmartDict class fixes a couple of issues with using the content # of os.environ or similar dicts of strings as Python variables: diff --git a/src/sim/async.hh b/src/sim/async.hh index e0190a133..50ae73040 100644 --- a/src/sim/async.hh +++ b/src/sim/async.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ASYNC_HH__ diff --git a/src/sim/builder.cc b/src/sim/builder.cc index 7dc118985..121275c83 100644 --- a/src/sim/builder.cc +++ b/src/sim/builder.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <assert.h> diff --git a/src/sim/builder.hh b/src/sim/builder.hh index b32e00e76..8d0846155 100644 --- a/src/sim/builder.hh +++ b/src/sim/builder.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __BUILDER_HH__ diff --git a/src/sim/byteswap.hh b/src/sim/byteswap.hh index a8c5da9d7..a3138a25e 100644 --- a/src/sim/byteswap.hh +++ b/src/sim/byteswap.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black */ //The purpose of this file is to provide endainness conversion utility diff --git a/src/sim/debug.cc b/src/sim/debug.cc index 09e5346a8..b82219f7d 100644 --- a/src/sim/debug.cc +++ b/src/sim/debug.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <sys/types.h> diff --git a/src/sim/debug.hh b/src/sim/debug.hh index 75b261d80..79792234b 100644 --- a/src/sim/debug.hh +++ b/src/sim/debug.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __DEBUG_HH__ diff --git a/src/sim/eventq.cc b/src/sim/eventq.cc index 4bfd6face..6ae838897 100644 --- a/src/sim/eventq.cc +++ b/src/sim/eventq.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Steve Raasch */ #include <assert.h> @@ -98,7 +102,7 @@ EventQueue::remove(Event *event) prev->next = curr->next; } -void +Event * EventQueue::serviceOne() { Event *event = head; @@ -106,13 +110,20 @@ EventQueue::serviceOne() head = event->next; // handle action - if (!event->squashed()) + if (!event->squashed()) { event->process(); - else + if (event->isExitEvent()) { + assert(!event->getFlags(Event::AutoDelete)); // would be silly + return event; + } + } else { event->clearFlags(Event::Squashed); + } if (event->getFlags(Event::AutoDelete) && !event->scheduled()) delete event; + + return NULL; } diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh index 5fc73bb53..430473df3 100644 --- a/src/sim/eventq.hh +++ b/src/sim/eventq.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ /* @file @@ -87,7 +90,8 @@ class Event : public Serializable, public FastAlloc Squashed = 0x1, Scheduled = 0x2, AutoDelete = 0x4, - AutoSerialize = 0x8 + AutoSerialize = 0x8, + IsExitEvent = 0x10 }; bool getFlags(Flags f) const { return (_flags & f) == f; } @@ -211,6 +215,9 @@ class Event : public Serializable, public FastAlloc /// Check whether the event is squashed bool squashed() { return getFlags(Squashed); } + /// See if this is a SimExitEvent (without resorting to RTTI) + bool isExitEvent() { return getFlags(IsExitEvent); } + /// Get the time that the event is scheduled Tick when() const { return _when; } @@ -295,7 +302,7 @@ class EventQueue : public Serializable void reschedule(Event *ev); Tick nextTick() { return head->when(); } - void serviceOne(); + Event *serviceOne(); // process all events up to the given timestamp. we inline a // quick test to see if there are any events to process; if so, diff --git a/src/sim/faults.cc b/src/sim/faults.cc index cb095f852..650b728f7 100644 --- a/src/sim/faults.cc +++ b/src/sim/faults.cc @@ -24,29 +24,32 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Gabe Black */ #include "base/misc.hh" #include "sim/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #if !FULL_SYSTEM -void FaultBase::invoke(ExecContext * xc) +void FaultBase::invoke(ThreadContext * tc) { - fatal("fault (%s) detected @ PC 0x%08p", name(), xc->readPC()); + fatal("fault (%s) detected @ PC 0x%08p", name(), tc->readPC()); } #else -void FaultBase::invoke(ExecContext * xc) +void FaultBase::invoke(ThreadContext * tc) { - DPRINTF(Fault, "Fault %s at PC: %#x\n", name(), xc->readPC()); - xc->getCpuPtr()->recordEvent(csprintf("Fault %s", name())); + DPRINTF(Fault, "Fault %s at PC: %#x\n", name(), tc->readPC()); + tc->getCpuPtr()->recordEvent(csprintf("Fault %s", name())); - assert(!xc->misspeculating()); + assert(!tc->misspeculating()); } #endif -void UnimpFault::invoke(ExecContext * xc) +void UnimpFault::invoke(ThreadContext * tc) { panic("Unimpfault: %s\n", panicStr.c_str()); } diff --git a/src/sim/faults.hh b/src/sim/faults.hh index 9b3bc9103..23385c649 100644 --- a/src/sim/faults.hh +++ b/src/sim/faults.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Gabe Black */ #ifndef __FAULTS_HH__ @@ -33,7 +36,7 @@ #include "sim/stats.hh" #include "config/full_system.hh" -class ExecContext; +class ThreadContext; class FaultBase; typedef RefCountingPtr<FaultBase> Fault; @@ -52,9 +55,9 @@ class FaultBase : public RefCounted public: virtual FaultName name() = 0; #if FULL_SYSTEM - virtual void invoke(ExecContext * xc); + virtual void invoke(ThreadContext * tc); #else - virtual void invoke(ExecContext * xc); + virtual void invoke(ThreadContext * tc); #endif // template<typename T> // bool isA() {return dynamic_cast<T *>(this);} @@ -74,7 +77,7 @@ class UnimpFault : public FaultBase { } FaultName name() {return "Unimplemented simulator feature";} - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); }; #endif // __FAULTS_HH__ diff --git a/src/sim/host.hh b/src/sim/host.hh index 48c977331..9c79580b1 100644 --- a/src/sim/host.hh +++ b/src/sim/host.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ /** @@ -54,6 +56,8 @@ typedef int64_t Counter; */ typedef int64_t Tick; +const Tick MaxTick = (1LL << 62); + /** * Address type * This will probably be moved somewhere else in the near future. diff --git a/src/sim/main.cc b/src/sim/main.cc index aecc171ed..741926056 100644 --- a/src/sim/main.cc +++ b/src/sim/main.cc @@ -24,24 +24,30 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + * Nathan Binkert + * Steve Reinhardt */ /// /// @file sim/main.cc /// +#include <Python.h> // must be before system headers... see Python docs + #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <libgen.h> #include <stdlib.h> #include <signal.h> +#include <unistd.h> #include <list> #include <string> #include <vector> -#include "base/copyright.hh" -#include "base/embedfile.hh" +#include "base/callback.hh" #include "base/inifile.hh" #include "base/misc.hh" #include "base/output.hh" @@ -51,7 +57,6 @@ #include "base/time.hh" #include "cpu/base.hh" #include "cpu/smt.hh" -#include "python/pyconfig.hh" #include "sim/async.hh" #include "sim/builder.hh" #include "sim/configfile.hh" @@ -108,42 +113,23 @@ abortHandler(int sigtype) #endif } -/// Simulator executable name -char *myProgName = ""; -/// Show brief help message. -void -showBriefHelp(ostream &out) -{ - char *prog = basename(myProgName); - - ccprintf(out, "Usage:\n"); - ccprintf(out, -"%s [-d <dir>] [-E <var>[=<val>]] [-I <dir>] [-P <python>]\n" -" [--<var>=<val>] <config file>\n" -"\n" -" -d set the output directory to <dir>\n" -" -E set the environment variable <var> to <val> (or 'True')\n" -" -I add the directory <dir> to python's path\n" -" -P execute <python> directly in the configuration\n" -" --var=val set the python variable <var> to '<val>'\n" -" <configfile> config file name (ends in .py)\n\n", - prog); - - ccprintf(out, "%s -X\n -X extract embedded files\n\n", prog); - ccprintf(out, "%s -h\n -h print short help\n\n", prog); -} +const char *briefCopyright = +"Copyright (c) 2001-2006\n" +"The Regents of The University of Michigan\n" +"All Rights Reserved\n"; /// Print welcome message. void sayHello(ostream &out) { - extern const char *compileDate; // from date.cc + extern const char *compileDate; // from date.cc ccprintf(out, "M5 Simulator System\n"); // display copyright ccprintf(out, "%s\n", briefCopyright); - ccprintf(out, "M5 compiled on %d\n", compileDate); + ccprintf(out, "M5 compiled %d\n", compileDate); + ccprintf(out, "M5 started %s\n", Time::start); char *host = getenv("HOSTNAME"); if (!host) @@ -151,65 +137,15 @@ sayHello(ostream &out) if (host) ccprintf(out, "M5 executing on %s\n", host); - - ccprintf(out, "M5 simulation started %s\n", Time::start); -} - -/// -/// Echo the command line for posterity in such a way that it can be -/// used to rerun the same simulation (given the same .ini files). -/// -void -echoCommandLine(int argc, char **argv, ostream &out) -{ - out << "command line: " << argv[0]; - for (int i = 1; i < argc; i++) { - string arg(argv[i]); - - out << ' '; - - // If the arg contains spaces, we need to quote it. - // The rest of this is overkill to make it look purty. - - // print dashes first outside quotes - int non_dash_pos = arg.find_first_not_of("-"); - out << arg.substr(0, non_dash_pos); // print dashes - string body = arg.substr(non_dash_pos); // the rest - - // if it's an assignment, handle the lhs & rhs separately - int eq_pos = body.find("="); - if (eq_pos == string::npos) { - out << quote(body); - } - else { - string lhs(body.substr(0, eq_pos)); - string rhs(body.substr(eq_pos + 1)); - - out << quote(lhs) << "=" << quote(rhs); - } - } - out << endl << endl; } -char * -getOptionString(int &index, int argc, char **argv) -{ - char *option = argv[index] + 2; - if (*option != '\0') - return option; - - // We didn't find an argument, it must be in the next variable. - if (++index >= argc) - panic("option string for option '%s' not found", argv[index - 1]); - return argv[index]; -} +extern "C" { void init_main(); } int main(int argc, char **argv) { - // Save off program name - myProgName = argv[0]; + sayHello(cerr); signal(SIGFPE, SIG_IGN); // may occur on misspeculated paths signal(SIGTRAP, SIG_IGN); @@ -218,121 +154,113 @@ main(int argc, char **argv) signal(SIGINT, exitNowHandler); // dump final stats and exit signal(SIGABRT, abortHandler); - bool configfile_found = false; - PythonConfig pyconfig; - string outdir; + Py_SetProgramName(argv[0]); + + // default path to m5 python code is the currently executing + // file... Python ZipImporter will find embedded zip archive + char *pythonpath = argv[0]; + + bool interactive = false; + bool getopt_done = false; + do { + switch (getopt(argc, argv, "+p:i")) { + // -p <path> prepends <path> to PYTHONPATH instead of + // using built-in zip archive. Useful when + // developing/debugging changes to built-in Python + // libraries, as the new Python can be tested without + // building a new m5 binary. + case 'p': + pythonpath = optarg; + break; + + // -i forces entry into interactive mode after the + // supplied script is executed (just like the -i option to + // the Python interpreter). + case 'i': + interactive = true; + break; + + case -1: + getopt_done = true; + break; + + default: + fatal("Unrecognized option %c\n", optopt); + } + } while (!getopt_done); - if (argc < 2) { - showBriefHelp(cerr); - exit(1); + // Fix up argc & argv to hide arguments we just processed. + // getopt() sets optind to the index of the first non-processed + // argv element. + argc -= optind; + argv += optind; + + // Set up PYTHONPATH to make sure the m5 module is found + string newpath(pythonpath); + + char *oldpath = getenv("PYTHONPATH"); + if (oldpath != NULL) { + newpath += ":"; + newpath += oldpath; } - sayHello(cerr); + if (setenv("PYTHONPATH", newpath.c_str(), true) == -1) + fatal("setenv: %s\n", strerror(errno)); - // Parse command-line options. - // Since most of the complex options are handled through the - // config database, we don't mess with getopts, and just parse - // manually. - for (int i = 1; i < argc; ++i) { - char *arg_str = argv[i]; - - // if arg starts with '--', parse as a special python option - // of the format --<python var>=<string value>, if the arg - // starts with '-', it should be a simulator option with a - // format similar to getopt. In any other case, treat the - // option as a configuration file name and load it. - if (arg_str[0] == '-' && arg_str[1] == '-') { - string str = &arg_str[2]; - string var, val; - - if (!split_first(str, var, val, '=')) - panic("Could not parse configuration argument '%s'\n" - "Expecting --<variable>=<value>\n", arg_str); - - pyconfig.setVariable(var, val); - } else if (arg_str[0] == '-') { - char *option; - string var, val; - - // switch on second char - switch (arg_str[1]) { - case 'd': - outdir = getOptionString(i, argc, argv); - break; - - case 'h': - showBriefHelp(cerr); - exit(1); - - case 'E': - option = getOptionString(i, argc, argv); - if (!split_first(option, var, val, '=')) - val = "True"; - - if (setenv(var.c_str(), val.c_str(), true) == -1) - panic("setenv: %s\n", strerror(errno)); - break; - - case 'I': - option = getOptionString(i, argc, argv); - pyconfig.addPath(option); - break; - - case 'P': - option = getOptionString(i, argc, argv); - pyconfig.writeLine(option); - break; - - case 'X': { - list<EmbedFile> lst; - EmbedMap::all(lst); - list<EmbedFile>::iterator i = lst.begin(); - list<EmbedFile>::iterator end = lst.end(); - - while (i != end) { - cprintf("Embedded File: %s\n", i->name); - cout.write(i->data, i->length); - ++i; - } - - return 0; - } - - default: - showBriefHelp(cerr); - panic("invalid argument '%s'\n", arg_str); - } - } else { - string file(arg_str); - string base, ext; + // initialize embedded Python interpreter + Py_Initialize(); + PySys_SetArgv(argc, argv); - if (!split_last(file, base, ext, '.') || ext != "py") - panic("Config file '%s' must end in '.py'\n", file); + // initialize SWIG 'main' module + init_main(); - pyconfig.load(file); - configfile_found = true; + if (argc > 0) { + // extra arg(s): first is script file, remaining ones are args + // to script file + char *filename = argv[0]; + FILE *fp = fopen(filename, "r"); + if (!fp) { + fatal("cannot open file '%s'\n", filename); } + + PyRun_AnyFile(fp, filename); + } else { + // no script file argument... force interactive prompt + interactive = true; } - if (outdir.empty()) { - char *env = getenv("OUTPUT_DIR"); - outdir = env ? env : "."; + if (interactive) { + // The following code to import readline was copied from Python + // 2.4.3's Modules/main.c. + // Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 + // Python Software Foundation; All Rights Reserved + // We should only enable this if we're actually using an + // interactive prompt. + PyObject *v; + v = PyImport_ImportModule("readline"); + if (v == NULL) + PyErr_Clear(); + else + Py_DECREF(v); + + PyRun_InteractiveLoop(stdin, "stdin"); } - simout.setDirectory(outdir); + // clean up Python intepreter. + Py_Finalize(); +} - char *env = getenv("CONFIG_OUTPUT"); - if (!env) - env = "config.out"; - configStream = simout.find(env); - if (!configfile_found) - panic("no configuration file specified!"); +/// Initialize C++ configuration. Exported to Python via SWIG; invoked +/// from m5.instantiate(). +void +initialize() +{ + configStream = simout.find("config.out"); // The configuration database is now complete; start processing it. IniFile inifile; - if (!pyconfig.output(inifile)) - panic("Error processing python code"); + inifile.load("config.ini"); // Initialize statistics database Stats::InitSimStats(); @@ -346,13 +274,7 @@ main(int argc, char **argv) ParamContext::parseAllContexts(inifile); ParamContext::checkAllContexts(); - // Print hello message to stats file if it's actually a file. If - // it's not (i.e. it's cout or cerr) then we already did it above. - if (simout.isFile(*outputStream)) - sayHello(*outputStream); - - // Echo command line and all parameter settings to stats file as well. - echoCommandLine(argc, argv, *outputStream); + // Echo all parameter settings to stats file as well. ParamContext::showAllContexts(*configStream); // Any objects that can't connect themselves until after construction should @@ -383,16 +305,61 @@ main(int argc, char **argv) // Reset to put the stats in a consistent state. Stats::reset(); - warn("Entering event queue. Starting simulation...\n"); SimStartup(); - while (!mainEventQueue.empty()) { +} + + +/** Simulate for num_cycles additional cycles. If num_cycles is -1 + * (the default), do not limit simulation; some other event must + * terminate the loop. Exported to Python via SWIG. + * @return The SimLoopExitEvent that caused the loop to exit. + */ +SimLoopExitEvent * +simulate(Tick num_cycles = -1) +{ + warn("Entering event queue @ %d. Starting simulation...\n", curTick); + + // Fix up num_cycles. Special default value -1 means simulate + // "forever"... schedule event at MaxTick just to be safe. + // Otherwise it's a delta for additional cycles to simulate past + // curTick, and thus must be non-negative. + if (num_cycles == -1) + num_cycles = MaxTick; + else if (num_cycles < 0) + fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles); + else + num_cycles = curTick + num_cycles; + + Event *limit_event = new SimLoopExitEvent(num_cycles, + "simulate() limit reached"); + + while (1) { + // there should always be at least one event (the SimLoopExitEvent + // we just scheduled) in the queue + assert(!mainEventQueue.empty()); assert(curTick <= mainEventQueue.nextTick() && "event scheduled in the past"); // forward current cycle to the time of the first event on the // queue curTick = mainEventQueue.nextTick(); - mainEventQueue.serviceOne(); + Event *exit_event = mainEventQueue.serviceOne(); + if (exit_event != NULL) { + // hit some kind of exit event; return to Python + // event must be subclass of SimLoopExitEvent... + SimLoopExitEvent *se_event = dynamic_cast<SimLoopExitEvent *>(exit_event); + if (se_event == NULL) + panic("Bogus exit event class!"); + + // if we didn't hit limit_event, delete it + if (se_event != limit_event) { + assert(limit_event->scheduled()); + limit_event->deschedule(); + delete limit_event; + } + + return se_event; + } if (async_event) { async_event = false; @@ -412,7 +379,7 @@ main(int argc, char **argv) if (async_exit) { async_exit = false; - new SimExitEvent("User requested STOP"); + exitSimLoop("user interrupt received"); } if (async_io || async_alarm) { @@ -423,11 +390,37 @@ main(int argc, char **argv) } } - // This should never happen... every conceivable way for the - // simulation to terminate (hit max cycles/insts, signal, - // simulated system halts/exits) generates an exit event, so we - // should never run out of events on the queue. - exitNow("no events on event loop! All CPUs must be idle.", 1); + // not reached... only exit is return on SimLoopExitEvent +} + +/** + * Queue of C++ callbacks to invoke on simulator exit. + */ +CallbackQueue exitCallbacks; + +/** + * Register an exit callback. + */ +void +registerExitCallback(Callback *callback) +{ + exitCallbacks.add(callback); +} + +/** + * Do C++ simulator exit processing. Exported to SWIG to be invoked + * when simulator terminates via Python's atexit mechanism. + */ +void +doExitCleanup() +{ + exitCallbacks.process(); + exitCallbacks.clear(); + + cout.flush(); + + ParamContext::cleanupAllContexts(); - return 0; + // print simulation stats + Stats::DumpNow(); } diff --git a/src/sim/param.cc b/src/sim/param.cc index 8998d7d77..7f648b8e1 100644 --- a/src/sim/param.cc +++ b/src/sim/param.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #include <algorithm> diff --git a/src/sim/param.hh b/src/sim/param.hh index 4a1b8bda1..49db17df9 100644 --- a/src/sim/param.hh +++ b/src/sim/param.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt */ #ifndef __SIM_PARAM_HH__ diff --git a/src/sim/process.cc b/src/sim/process.cc index 1da525093..5080c3ac1 100644 --- a/src/sim/process.cc +++ b/src/sim/process.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi */ #include <unistd.h> @@ -36,7 +40,7 @@ #include "base/loader/symtab.hh" #include "base/statistics.hh" #include "config/full_system.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/page_table.hh" #include "mem/physical.hh" #include "mem/translating_port.hh" @@ -46,6 +50,20 @@ #include "sim/syscall_emul.hh" #include "sim/system.hh" +#include "arch/isa_specific.hh" +#if THE_ISA == ALPHA_ISA +#include "arch/alpha/linux/process.hh" +#include "arch/alpha/tru64/process.hh" +#elif THE_ISA == SPARC_ISA +#include "arch/sparc/linux/process.hh" +#include "arch/sparc/solaris/process.hh" +#elif THE_ISA == MIPS_ISA +#include "arch/mips/linux/process.hh" +#else +#error "THE_ISA not set" +#endif + + using namespace std; using namespace TheISA; @@ -130,11 +148,11 @@ Process::openOutputFile(const string &filename) int -Process::registerExecContext(ExecContext *xc) +Process::registerThreadContext(ThreadContext *tc) { // add to list - int myIndex = execContexts.size(); - execContexts.push_back(xc); + int myIndex = threadContexts.size(); + threadContexts.push_back(tc); // return CPU number to caller return myIndex; @@ -143,14 +161,14 @@ Process::registerExecContext(ExecContext *xc) void Process::startup() { - if (execContexts.empty()) + if (threadContexts.empty()) fatal("Process %s is not associated with any CPUs!\n", name()); - // first exec context for this process... initialize & enable - ExecContext *xc = execContexts[0]; + // first thread context for this process... initialize & enable + ThreadContext *tc = threadContexts[0]; // mark this context as active so it will start ticking. - xc->activate(0); + tc->activate(0); Port *mem_port; mem_port = system->physmem->getPort("functional"); @@ -160,14 +178,14 @@ Process::startup() } void -Process::replaceExecContext(ExecContext *xc, int xcIndex) +Process::replaceThreadContext(ThreadContext *tc, int tcIndex) { - if (xcIndex >= execContexts.size()) { - panic("replaceExecContext: bad xcIndex, %d >= %d\n", - xcIndex, execContexts.size()); + if (tcIndex >= threadContexts.size()) { + panic("replaceThreadContext: bad tcIndex, %d >= %d\n", + tcIndex, threadContexts.size()); } - execContexts[xcIndex] = xc; + threadContexts[tcIndex] = tc; } // map simulator fd sim_fd to target fd tgt_fd @@ -334,20 +352,20 @@ LiveProcess::argsInit(int intSize, int pageSize) copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem); copyStringArray(envp, envp_array_base, env_data_base, initVirtMem); - execContexts[0]->setIntReg(ArgumentReg0, argc); - execContexts[0]->setIntReg(ArgumentReg1, argv_array_base); - execContexts[0]->setIntReg(StackPointerReg, stack_min); + threadContexts[0]->setIntReg(ArgumentReg0, argc); + threadContexts[0]->setIntReg(ArgumentReg1, argv_array_base); + threadContexts[0]->setIntReg(StackPointerReg, stack_min); Addr prog_entry = objFile->entryPoint(); - execContexts[0]->setPC(prog_entry); - execContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); - execContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); + threadContexts[0]->setPC(prog_entry); + threadContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); + threadContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); num_processes++; } void -LiveProcess::syscall(int64_t callnum, ExecContext *xc) +LiveProcess::syscall(int64_t callnum, ThreadContext *tc) { num_syscalls++; @@ -355,7 +373,135 @@ LiveProcess::syscall(int64_t callnum, ExecContext *xc) if (desc == NULL) fatal("Syscall %d out of range", callnum); - desc->doSyscall(callnum, this, xc); + desc->doSyscall(callnum, this, tc); +} + +LiveProcess * +LiveProcess::create(const std::string &nm, System *system, int stdin_fd, + int stdout_fd, int stderr_fd, std::string executable, + std::vector<std::string> &argv, + std::vector<std::string> &envp) +{ + LiveProcess *process = NULL; + + ObjectFile *objFile = createObjectFile(executable); + if (objFile == NULL) { + fatal("Can't load object file %s", executable); + } + +#if THE_ISA == ALPHA_ISA + if (objFile->getArch() != ObjectFile::Alpha) + fatal("Object file architecture does not match compiled ISA (Alpha)."); + switch (objFile->getOpSys()) { + case ObjectFile::Tru64: + process = new AlphaTru64Process(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + case ObjectFile::Linux: + process = new AlphaLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + default: + fatal("Unknown/unsupported operating system."); + } +#elif THE_ISA == SPARC_ISA + if (objFile->getArch() != ObjectFile::SPARC) + fatal("Object file architecture does not match compiled ISA (SPARC)."); + switch (objFile->getOpSys()) { + case ObjectFile::Linux: + process = new SparcLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + + case ObjectFile::Solaris: + process = new SparcSolarisProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + default: + fatal("Unknown/unsupported operating system."); + } +#elif THE_ISA == MIPS_ISA + if (objFile->getArch() != ObjectFile::Mips) + fatal("Object file architecture does not match compiled ISA (MIPS)."); + switch (objFile->getOpSys()) { + case ObjectFile::Linux: + process = new MipsLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + default: + fatal("Unknown/unsupported operating system."); + } +#else +#error "THE_ISA not set" +#endif + + + if (process == NULL) + fatal("Unknown error creating process object."); + return process; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(LiveProcess) + + VectorParam<string> cmd; + Param<string> executable; + Param<string> input; + Param<string> output; + VectorParam<string> env; + SimObjectParam<System *> system; + +END_DECLARE_SIM_OBJECT_PARAMS(LiveProcess) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(LiveProcess) + + INIT_PARAM(cmd, "command line (executable plus arguments)"), + INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), + INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), + INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), + INIT_PARAM(env, "environment settings"), + INIT_PARAM(system, "system") + +END_INIT_SIM_OBJECT_PARAMS(LiveProcess) + + +CREATE_SIM_OBJECT(LiveProcess) +{ + string in = input; + string out = output; + + // initialize file descriptors to default: same as simulator + int stdin_fd, stdout_fd, stderr_fd; + + if (in == "stdin" || in == "cin") + stdin_fd = STDIN_FILENO; + else + stdin_fd = Process::openInputFile(input); + + if (out == "stdout" || out == "cout") + stdout_fd = STDOUT_FILENO; + else if (out == "stderr" || out == "cerr") + stdout_fd = STDERR_FILENO; + else + stdout_fd = Process::openOutputFile(out); + + stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; + + return LiveProcess::create(getInstanceName(), system, + stdin_fd, stdout_fd, stderr_fd, + (string)executable == "" ? cmd[0] : executable, + cmd, env); } -DEFINE_SIM_OBJECT_CLASS_NAME("LiveProcess", LiveProcess); + +REGISTER_SIM_OBJECT("LiveProcess", LiveProcess) diff --git a/src/sim/process.hh b/src/sim/process.hh index 807bf330f..763deb100 100644 --- a/src/sim/process.hh +++ b/src/sim/process.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __PROCESS_HH__ @@ -43,8 +46,7 @@ #include "base/statistics.hh" #include "sim/sim_object.hh" -class CPUExecContext; -class ExecContext; +class ThreadContext; class SyscallDesc; class PageTable; class TranslatingPort; @@ -62,24 +64,24 @@ class Process : public SimObject /// running on. System *system; - // have we initialized an execution context from this process? If + // have we initialized a thread context from this process? If // yes, subsequent contexts are assumed to be for dynamically // created threads and are not initialized. bool initialContextLoaded; - // execution contexts associated with this process - std::vector<ExecContext *> execContexts; + // thread contexts associated with this process + std::vector<ThreadContext *> threadContexts; // number of CPUs (esxec contexts, really) assigned to this process. - unsigned int numCpus() { return execContexts.size(); } + unsigned int numCpus() { return threadContexts.size(); } // record of blocked context struct WaitRec { Addr waitChan; - ExecContext *waitingContext; + ThreadContext *waitingContext; - WaitRec(Addr chan, ExecContext *ctx) + WaitRec(Addr chan, ThreadContext *ctx) : waitChan(chan), waitingContext(ctx) { } }; @@ -140,12 +142,12 @@ class Process : public SimObject // override of virtual SimObject method: register statistics virtual void regStats(); - // register an execution context for this process. - // returns xc's cpu number (index into execContexts[]) - int registerExecContext(ExecContext *xc); + // register a thread context for this process. + // returns tc's cpu number (index into threadContexts[]) + int registerThreadContext(ThreadContext *tc); - void replaceExecContext(ExecContext *xc, int xcIndex); + void replaceThreadContext(ThreadContext *tc, int tcIndex); // map simulator fd sim_fd to target fd tgt_fd void dup_fd(int sim_fd, int tgt_fd); @@ -159,7 +161,7 @@ class Process : public SimObject // look up simulator fd for given target fd int sim_fd(int tgt_fd); - virtual void syscall(int64_t callnum, ExecContext *xc) = 0; + virtual void syscall(int64_t callnum, ThreadContext *tc) = 0; }; // @@ -181,9 +183,19 @@ class LiveProcess : public Process virtual void argsInit(int intSize, int pageSize); public: - virtual void syscall(int64_t callnum, ExecContext *xc); + virtual void syscall(int64_t callnum, ThreadContext *tc); virtual SyscallDesc* getDesc(int callnum) = 0; + + // this function is used to create the LiveProcess object, since + // we can't tell which subclass of LiveProcess to use until we + // open and look at the object file. + static LiveProcess *create(const std::string &nm, + System *_system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::string executable, + std::vector<std::string> &argv, + std::vector<std::string> &envp); }; diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc index 7897b5c8b..ae52cdd41 100644 --- a/src/sim/pseudo_inst.cc +++ b/src/sim/pseudo_inst.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <errno.h> @@ -36,7 +38,8 @@ #include "arch/vtophys.hh" #include "cpu/base.hh" #include "cpu/sampler/sampler.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" +#include "cpu/quiesce_event.hh" #include "kern/kernel_stats.hh" #include "sim/param.hh" #include "sim/serialize.hh" @@ -61,89 +64,94 @@ namespace AlphaPseudo bool doQuiesce; void - arm(ExecContext *xc) + arm(ThreadContext *tc) { - xc->getCpuPtr()->kernelStats->arm(); + if (tc->getKernelStats()) + tc->getKernelStats()->arm(); } void - quiesce(ExecContext *xc) + quiesce(ThreadContext *tc) { if (!doQuiesce) return; - xc->suspend(); - xc->getCpuPtr()->kernelStats->quiesce(); + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); } void - quiesceNs(ExecContext *xc, uint64_t ns) + quiesceNs(ThreadContext *tc, uint64_t ns) { if (!doQuiesce || ns == 0) return; - Event *quiesceEvent = xc->getQuiesceEvent(); + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); if (quiesceEvent->scheduled()) quiesceEvent->reschedule(curTick + Clock::Int::ns * ns); else quiesceEvent->schedule(curTick + Clock::Int::ns * ns); - xc->suspend(); - xc->getCpuPtr()->kernelStats->quiesce(); + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); } void - quiesceCycles(ExecContext *xc, uint64_t cycles) + quiesceCycles(ThreadContext *tc, uint64_t cycles) { if (!doQuiesce || cycles == 0) return; - Event *quiesceEvent = xc->getQuiesceEvent(); + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); if (quiesceEvent->scheduled()) quiesceEvent->reschedule(curTick + - xc->getCpuPtr()->cycles(cycles)); + tc->getCpuPtr()->cycles(cycles)); else quiesceEvent->schedule(curTick + - xc->getCpuPtr()->cycles(cycles)); + tc->getCpuPtr()->cycles(cycles)); - xc->suspend(); - xc->getCpuPtr()->kernelStats->quiesce(); + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); } uint64_t - quiesceTime(ExecContext *xc) + quiesceTime(ThreadContext *tc) { - return (xc->readLastActivate() - xc->readLastSuspend()) / Clock::Int::ns; + return (tc->readLastActivate() - tc->readLastSuspend()) / Clock::Int::ns; } void - ivlb(ExecContext *xc) + ivlb(ThreadContext *tc) { - xc->getCpuPtr()->kernelStats->ivlb(); + if (tc->getKernelStats()) + tc->getKernelStats()->ivlb(); } void - ivle(ExecContext *xc) + ivle(ThreadContext *tc) { } void - m5exit_old(ExecContext *xc) + m5exit_old(ThreadContext *tc) { - SimExit(curTick, "m5_exit_old instruction encountered"); + exitSimLoop(curTick, "m5_exit_old instruction encountered"); } void - m5exit(ExecContext *xc, Tick delay) + m5exit(ThreadContext *tc, Tick delay) { Tick when = curTick + delay * Clock::Int::ns; - SimExit(when, "m5_exit instruction encountered"); + exitSimLoop(when, "m5_exit instruction encountered"); } void - resetstats(ExecContext *xc, Tick delay, Tick period) + resetstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) return; @@ -157,7 +165,7 @@ namespace AlphaPseudo } void - dumpstats(ExecContext *xc, Tick delay, Tick period) + dumpstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) return; @@ -171,19 +179,19 @@ namespace AlphaPseudo } void - addsymbol(ExecContext *xc, Addr addr, Addr symbolAddr) + addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr) { char symb[100]; - CopyStringOut(xc, symb, symbolAddr, 100); + CopyStringOut(tc, symb, symbolAddr, 100); std::string symbol(symb); DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr); - xc->getSystemPtr()->kernelSymtab->insert(addr,symbol); + tc->getSystemPtr()->kernelSymtab->insert(addr,symbol); } void - dumpresetstats(ExecContext *xc, Tick delay, Tick period) + dumpresetstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) return; @@ -197,7 +205,7 @@ namespace AlphaPseudo } void - m5checkpoint(ExecContext *xc, Tick delay, Tick period) + m5checkpoint(ThreadContext *tc, Tick delay, Tick period) { if (!doCheckpointInsts) return; @@ -210,9 +218,9 @@ namespace AlphaPseudo } uint64_t - readfile(ExecContext *xc, Addr vaddr, uint64_t len, uint64_t offset) + readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset) { - const string &file = xc->getCpuPtr()->system->params()->readfile; + const string &file = tc->getCpuPtr()->system->params()->readfile; if (file.empty()) { return ULL(0); } @@ -239,7 +247,7 @@ namespace AlphaPseudo } close(fd); - CopyIn(xc, vaddr, buf, result); + CopyIn(tc, vaddr, buf, result); delete [] buf; return result; } @@ -271,12 +279,12 @@ namespace AlphaPseudo doCheckpointInsts = __checkpoint; } - void debugbreak(ExecContext *xc) + void debugbreak(ThreadContext *tc) { debug_break(); } - void switchcpu(ExecContext *xc) + void switchcpu(ThreadContext *tc) { if (SampCPU) SampCPU->switchCPUs(); diff --git a/src/sim/pseudo_inst.hh b/src/sim/pseudo_inst.hh index 4dd427c99..5e5b7d95f 100644 --- a/src/sim/pseudo_inst.hh +++ b/src/sim/pseudo_inst.hh @@ -24,9 +24,11 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ -class ExecContext; +class ThreadContext; //We need the "Tick" data type from here #include "sim/host.hh" @@ -42,21 +44,21 @@ namespace AlphaPseudo extern bool doCheckpointInsts; extern bool doQuiesce; - void arm(ExecContext *xc); - void quiesce(ExecContext *xc); - void quiesceNs(ExecContext *xc, uint64_t ns); - void quiesceCycles(ExecContext *xc, uint64_t cycles); - uint64_t quiesceTime(ExecContext *xc); - void ivlb(ExecContext *xc); - void ivle(ExecContext *xc); - void m5exit(ExecContext *xc, Tick delay); - void m5exit_old(ExecContext *xc); - void resetstats(ExecContext *xc, Tick delay, Tick period); - void dumpstats(ExecContext *xc, Tick delay, Tick period); - void dumpresetstats(ExecContext *xc, Tick delay, Tick period); - void m5checkpoint(ExecContext *xc, Tick delay, Tick period); - uint64_t readfile(ExecContext *xc, Addr vaddr, uint64_t len, uint64_t offset); - void debugbreak(ExecContext *xc); - void switchcpu(ExecContext *xc); - void addsymbol(ExecContext *xc, Addr addr, Addr symbolAddr); + void arm(ThreadContext *tc); + void quiesce(ThreadContext *tc); + void quiesceNs(ThreadContext *tc, uint64_t ns); + void quiesceCycles(ThreadContext *tc, uint64_t cycles); + uint64_t quiesceTime(ThreadContext *tc); + void ivlb(ThreadContext *tc); + void ivle(ThreadContext *tc); + void m5exit(ThreadContext *tc, Tick delay); + void m5exit_old(ThreadContext *tc); + void resetstats(ThreadContext *tc, Tick delay, Tick period); + void dumpstats(ThreadContext *tc, Tick delay, Tick period); + void dumpresetstats(ThreadContext *tc, Tick delay, Tick period); + void m5checkpoint(ThreadContext *tc, Tick delay, Tick period); + uint64_t readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset); + void debugbreak(ThreadContext *tc); + void switchcpu(ThreadContext *tc); + void addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr); } diff --git a/src/sim/root.cc b/src/sim/root.cc index 6348ec104..ec5e2f7e2 100644 --- a/src/sim/root.cc +++ b/src/sim/root.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <cstring> @@ -37,6 +40,7 @@ #include "sim/builder.hh" #include "sim/host.hh" #include "sim/sim_events.hh" +#include "sim/sim_exit.hh" #include "sim/sim_object.hh" #include "sim/root.hh" @@ -96,7 +100,7 @@ void Root::startup() { if (max_tick != 0) - new SimExitEvent(curTick + max_tick, "reached maximum cycle count"); + exitSimLoop(curTick + max_tick, "reached maximum cycle count"); if (progress_interval != 0) new ProgressEvent(&mainEventQueue, progress_interval); diff --git a/src/sim/serialize.cc b/src/sim/serialize.cc index c4ef124bb..5270802d1 100644 --- a/src/sim/serialize.cc +++ b/src/sim/serialize.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt */ #include <sys/time.h> @@ -245,7 +248,7 @@ Serializable::serializeAll() assert(Serializable::ckptPrevCount + 1 == Serializable::ckptCount); Serializable::ckptPrevCount++; if (ckptMaxCount && ++ckptCount >= ckptMaxCount) - SimExit(curTick + 1, "Maximum number of checkpoints dropped"); + exitSimLoop(curTick + 1, "Maximum number of checkpoints dropped"); } diff --git a/src/sim/serialize.hh b/src/sim/serialize.hh index d8f5f8fc5..1eb721cf4 100644 --- a/src/sim/serialize.hh +++ b/src/sim/serialize.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt */ /* @file diff --git a/src/sim/sim_events.cc b/src/sim/sim_events.cc index c2bdca9df..b7901832d 100644 --- a/src/sim/sim_events.cc +++ b/src/sim/sim_events.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <string> @@ -43,26 +45,37 @@ using namespace std; // handle termination event // void -SimExitEvent::process() +SimLoopExitEvent::process() { - // This event does not autodelete because exitNow may be called, - // and the function will never be allowed to finish. - if (theQueue() == &mainEventQueue) { - string _cause = cause; - int _code = code; - delete this; - exitNow(_cause, _code); - } else { - new SimExitEvent(cause, code); + // if this got scheduled on a different queue (e.g. the committed + // instruction queue) then make a corresponding event on the main + // queue. + if (theQueue() != &mainEventQueue) { + exitSimLoop(cause, code); delete this; } + + // otherwise do nothing... the IsExitEvent flag takes care of + // exiting the simulation loop and returning this object to Python } const char * -SimExitEvent::description() +SimLoopExitEvent::description() +{ + return "simulation loop exit"; +} + +void +exitSimLoop(Tick when, const std::string &message, int exit_code) +{ + new SimLoopExitEvent(when, message, exit_code); +} + +void +exitSimLoop(const std::string &message, int exit_code) { - return "simulation termination"; + exitSimLoop(curTick, message, exit_code); } // @@ -88,7 +101,7 @@ void CountedExitEvent::process() { if (--downCounter == 0) { - new SimExitEvent(cause, 0); + exitSimLoop(cause, 0); } } @@ -117,7 +130,7 @@ CheckSwapEvent::process() if (swap < 100) { cerr << "\a\aAborting Simulation! Inadequate swap space!\n\n"; - new SimExitEvent("Lack of swap space"); + exitSimLoop("Lack of swap space"); } schedule(curTick + interval); diff --git a/src/sim/sim_events.hh b/src/sim/sim_events.hh index c93914457..4f305ad38 100644 --- a/src/sim/sim_events.hh +++ b/src/sim/sim_events.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __SIM_SIM_EVENTS_HH__ @@ -34,7 +36,7 @@ // // Event to terminate simulation at a particular cycle/instruction // -class SimExitEvent : public Event +class SimLoopExitEvent : public Event { private: // string explaining why we're terminating @@ -42,24 +44,18 @@ class SimExitEvent : public Event int code; public: - SimExitEvent(const std::string &_cause, int c = 0) + SimLoopExitEvent(Tick _when, const std::string &_cause, int c = 0) : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(curTick); } + { setFlags(IsExitEvent); schedule(_when); } - SimExitEvent(Tick _when, const std::string &_cause, int c = 0) - : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), - code(c) - { schedule(_when); } - - SimExitEvent(EventQueue *q, const std::string &_cause, int c = 0) + SimLoopExitEvent(EventQueue *q, + Tick _when, const std::string &_cause, int c = 0) : Event(q, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(curTick); } + { setFlags(IsExitEvent); schedule(_when); } - SimExitEvent(EventQueue *q, Tick _when, const std::string &_cause, - int c = 0) - : Event(q, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(_when); } + std::string getCause() { return cause; } + int getCode() { return code; } void process(); // process event diff --git a/src/sim/sim_exit.hh b/src/sim/sim_exit.hh index f14256933..545bf4ae0 100644 --- a/src/sim/sim_exit.hh +++ b/src/sim/sim_exit.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __SIM_EXIT_HH__ @@ -33,12 +36,23 @@ #include "sim/host.hh" +// forward declaration class Callback; +/// Register a callback to be called when Python exits. Defined in +/// sim/main.cc. void registerExitCallback(Callback *); -void exitNow(const std::string &cause, int exit_code); -void exitNow(const char *cause, int exit_code); -void SimExit(Tick when, const char *message); +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the indicated tick. The message and exit_code +/// parameters are saved in the SimLoopExitEvent to indicate why the +/// exit occurred. +void exitSimLoop(Tick when, const std::string &message, int exit_code = 0); + +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the end of the current cycle (curTick). The message +/// and exit_code parameters are saved in the SimLoopExitEvent to +/// indicate why the exit occurred. +void exitSimLoop(const std::string &cause, int exit_code = 0); #endif // __SIM_EXIT_HH__ diff --git a/src/sim/sim_object.cc b/src/sim/sim_object.cc index 17d58ba4f..117ca9325 100644 --- a/src/sim/sim_object.cc +++ b/src/sim/sim_object.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include <assert.h> diff --git a/src/sim/sim_object.hh b/src/sim/sim_object.hh index 76aba7ea1..84e9376a0 100644 --- a/src/sim/sim_object.hh +++ b/src/sim/sim_object.hh @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ /* @file diff --git a/src/sim/startup.cc b/src/sim/startup.cc index 683e6c746..838352107 100644 --- a/src/sim/startup.cc +++ b/src/sim/startup.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Kevin Lim */ #include <list> diff --git a/src/sim/startup.hh b/src/sim/startup.hh index 3c9b654f1..cfc4450f4 100644 --- a/src/sim/startup.hh +++ b/src/sim/startup.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __SIM_STARTUP_HH__ diff --git a/src/sim/stat_control.cc b/src/sim/stat_control.cc index 85c405b7f..f7fc03d74 100644 --- a/src/sim/stat_control.cc +++ b/src/sim/stat_control.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ // This file will contain default statistics for the simulator that diff --git a/src/sim/stat_control.hh b/src/sim/stat_control.hh index a22ce76af..fb369f640 100644 --- a/src/sim/stat_control.hh +++ b/src/sim/stat_control.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __SIM_STAT_CONTROL_HH__ diff --git a/src/sim/stats.hh b/src/sim/stats.hh index 8e97d041f..97251283d 100644 --- a/src/sim/stats.hh +++ b/src/sim/stats.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __SIM_STATS_HH__ diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index ed0da628e..848b6f869 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Ali Saidi + * Korey Sewell */ #include <fcntl.h> @@ -35,37 +39,37 @@ #include "sim/syscall_emul.hh" #include "base/chunk_generator.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "mem/page_table.hh" #include "sim/process.hh" -#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" using namespace std; using namespace TheISA; void -SyscallDesc::doSyscall(int callnum, Process *process, ExecContext *xc) +SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc) { DPRINTFR(SyscallVerbose, "%d: %s: syscall %s called w/arguments %d,%d,%d,%d\n", - curTick,xc->getCpuPtr()->name(), name, - xc->getSyscallArg(0),xc->getSyscallArg(1), - xc->getSyscallArg(2),xc->getSyscallArg(3)); + curTick,tc->getCpuPtr()->name(), name, + tc->getSyscallArg(0),tc->getSyscallArg(1), + tc->getSyscallArg(2),tc->getSyscallArg(3)); - SyscallReturn retval = (*funcPtr)(this, callnum, process, xc); + SyscallReturn retval = (*funcPtr)(this, callnum, process, tc); DPRINTFR(SyscallVerbose, "%d: %s: syscall %s returns %d\n", - curTick,xc->getCpuPtr()->name(), name, retval.value()); + curTick,tc->getCpuPtr()->name(), name, retval.value()); if (!(flags & SyscallDesc::SuppressReturnValue)) - xc->setSyscallReturn(retval); + tc->setSyscallReturn(retval); } SyscallReturn unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { fatal("syscall %s (#%d) unimplemented.", desc->name, callnum); @@ -75,10 +79,10 @@ unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, SyscallReturn ignoreFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { warn("ignoring syscall %s(%d, %d, ...)", desc->name, - xc->getSyscallArg(0), xc->getSyscallArg(1)); + tc->getSyscallArg(0), tc->getSyscallArg(1)); return 0; } @@ -86,28 +90,28 @@ ignoreFunc(SyscallDesc *desc, int callnum, Process *process, SyscallReturn exitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - new SimExitEvent("target called exit()", xc->getSyscallArg(0) & 0xff); + exitSimLoop("target called exit()", tc->getSyscallArg(0) & 0xff); return 1; } SyscallReturn -getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { return (int)VMPageSize; } SyscallReturn -obreakFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +obreakFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { Addr junk; // change brk addr to first arg - Addr new_brk = xc->getSyscallArg(0); + Addr new_brk = tc->getSyscallArg(0); if (new_brk != 0) { for (ChunkGenerator gen(p->brk_point, new_brk - p->brk_point, VMPageSize); !gen.done(); gen.next()) { @@ -123,9 +127,9 @@ obreakFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -closeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int target_fd = xc->getSyscallArg(0); + int target_fd = tc->getSyscallArg(0); int status = close(p->sim_fd(target_fd)); if (status >= 0) p->free_fd(target_fd); @@ -134,28 +138,28 @@ closeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -readFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +readFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int fd = p->sim_fd(xc->getSyscallArg(0)); - int nbytes = xc->getSyscallArg(2); - BufferArg bufArg(xc->getSyscallArg(1), nbytes); + int fd = p->sim_fd(tc->getSyscallArg(0)); + int nbytes = tc->getSyscallArg(2); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); int bytes_read = read(fd, bufArg.bufferPtr(), nbytes); if (bytes_read != -1) - bufArg.copyOut(xc->getMemPort()); + bufArg.copyOut(tc->getMemPort()); return bytes_read; } SyscallReturn -writeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +writeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int fd = p->sim_fd(xc->getSyscallArg(0)); - int nbytes = xc->getSyscallArg(2); - BufferArg bufArg(xc->getSyscallArg(1), nbytes); + int fd = p->sim_fd(tc->getSyscallArg(0)); + int nbytes = tc->getSyscallArg(2); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); - bufArg.copyIn(xc->getMemPort()); + bufArg.copyIn(tc->getMemPort()); int bytes_written = write(fd, bufArg.bufferPtr(), nbytes); @@ -166,11 +170,11 @@ writeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -lseekFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +lseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int fd = p->sim_fd(xc->getSyscallArg(0)); - uint64_t offs = xc->getSyscallArg(1); - int whence = xc->getSyscallArg(2); + int fd = p->sim_fd(tc->getSyscallArg(0)); + uint64_t offs = tc->getSyscallArg(1); + int whence = tc->getSyscallArg(2); off_t result = lseek(fd, offs, whence); @@ -179,7 +183,7 @@ lseekFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -munmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +munmapFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { // given that we don't really implement mmap, munmap is really easy return 0; @@ -189,24 +193,24 @@ munmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) const char *hostname = "m5.eecs.umich.edu"; SyscallReturn -gethostnameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +gethostnameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int name_len = xc->getSyscallArg(1); - BufferArg name(xc->getSyscallArg(0), name_len); + int name_len = tc->getSyscallArg(1); + BufferArg name(tc->getSyscallArg(0), name_len); strncpy((char *)name.bufferPtr(), hostname, name_len); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } SyscallReturn -unlinkFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +unlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return (TheISA::IntReg)-EFAULT; int result = unlink(path.c_str()); @@ -214,16 +218,16 @@ unlinkFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) } SyscallReturn -renameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +renameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string old_name; - if (!xc->getMemPort()->tryReadString(old_name, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(old_name, tc->getSyscallArg(0))) return -EFAULT; string new_name; - if (!xc->getMemPort()->tryReadString(new_name, xc->getSyscallArg(1))) + if (!tc->getMemPort()->tryReadString(new_name, tc->getSyscallArg(1))) return -EFAULT; int64_t result = rename(old_name.c_str(), new_name.c_str()); @@ -231,45 +235,45 @@ renameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) } SyscallReturn -truncateFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +truncateFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; - off_t length = xc->getSyscallArg(1); + off_t length = tc->getSyscallArg(1); int result = truncate(path.c_str(), length); return (result == -1) ? -errno : result; } SyscallReturn -ftruncateFunc(SyscallDesc *desc, int num, Process *process, ExecContext *xc) +ftruncateFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); if (fd < 0) return -EBADF; - off_t length = xc->getSyscallArg(1); + off_t length = tc->getSyscallArg(1); int result = ftruncate(fd, length); return (result == -1) ? -errno : result; } SyscallReturn -chownFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +chownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; /* XXX endianess */ - uint32_t owner = xc->getSyscallArg(1); + uint32_t owner = tc->getSyscallArg(1); uid_t hostOwner = owner; - uint32_t group = xc->getSyscallArg(2); + uint32_t group = tc->getSyscallArg(2); gid_t hostGroup = group; int result = chown(path.c_str(), hostOwner, hostGroup); @@ -277,17 +281,17 @@ chownFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) } SyscallReturn -fchownFunc(SyscallDesc *desc, int num, Process *process, ExecContext *xc) +fchownFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); if (fd < 0) return -EBADF; /* XXX endianess */ - uint32_t owner = xc->getSyscallArg(1); + uint32_t owner = tc->getSyscallArg(1); uid_t hostOwner = owner; - uint32_t group = xc->getSyscallArg(2); + uint32_t group = tc->getSyscallArg(2); gid_t hostGroup = group; int result = fchown(fd, hostOwner, hostGroup); @@ -297,14 +301,14 @@ fchownFunc(SyscallDesc *desc, int num, Process *process, ExecContext *xc) SyscallReturn fcntlFunc(SyscallDesc *desc, int num, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) return -EBADF; - int cmd = xc->getSyscallArg(1); + int cmd = tc->getSyscallArg(1); switch (cmd) { case 0: // F_DUPFD // if we really wanted to support this, we'd need to do it @@ -338,8 +342,37 @@ fcntlFunc(SyscallDesc *desc, int num, Process *process, } SyscallReturn +fcntl64Func(SyscallDesc *desc, int num, Process *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + + if (fd < 0 || process->sim_fd(fd) < 0) + return -EBADF; + + int cmd = tc->getSyscallArg(1); + switch (cmd) { + case 33: //F_GETLK64 + warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", fd); + return -EMFILE; + + case 34: // F_SETLK64 + case 35: // F_SETLKW64 + warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", fd); + return -EMFILE; + + default: + // not sure if this is totally valid, but we'll pass it through + // to the underlying OS + warn("fcntl64(%d, %d) passed through to host\n", fd, cmd); + return fcntl(process->sim_fd(fd), cmd); + // return 0; + } +} + +SyscallReturn pipePseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { int fds[2], sim_fds[2]; int pipe_retval = pipe(fds); @@ -354,99 +387,99 @@ pipePseudoFunc(SyscallDesc *desc, int callnum, Process *process, // Alpha Linux convention for pipe() is that fd[0] is returned as // the return value of the function, and fd[1] is returned in r20. - xc->setIntReg(SyscallPseudoReturnReg, sim_fds[1]); + tc->setIntReg(SyscallPseudoReturnReg, sim_fds[1]); return sim_fds[0]; } SyscallReturn getpidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Make up a PID. There's no interprocess communication in // fake_syscall mode, so there's no way for a process to know it's // not getting a unique value. - xc->setIntReg(SyscallPseudoReturnReg, 99); + tc->setIntReg(SyscallPseudoReturnReg, 99); return 100; } SyscallReturn getuidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Make up a UID and EUID... it shouldn't matter, and we want the // simulation to be deterministic. // EUID goes in r20. - xc->setIntReg(SyscallPseudoReturnReg, 100); //EUID + tc->setIntReg(SyscallPseudoReturnReg, 100); //EUID return 100; // UID } SyscallReturn getgidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Get current group ID. EGID goes in r20. - xc->setIntReg(SyscallPseudoReturnReg, 100); //EGID + tc->setIntReg(SyscallPseudoReturnReg, 100); //EGID return 100; } SyscallReturn setuidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // can't fathom why a benchmark would call this. - warn("Ignoring call to setuid(%d)\n", xc->getSyscallArg(0)); + warn("Ignoring call to setuid(%d)\n", tc->getSyscallArg(0)); return 0; } SyscallReturn getpidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Make up a PID. There's no interprocess communication in // fake_syscall mode, so there's no way for a process to know it's // not getting a unique value. - xc->setIntReg(SyscallPseudoReturnReg, 99); //PID + tc->setIntReg(SyscallPseudoReturnReg, 99); //PID return 100; } SyscallReturn getppidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 99; } SyscallReturn getuidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; // UID } SyscallReturn geteuidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; // UID } SyscallReturn getgidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; } SyscallReturn getegidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; } diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index 00f016410..f027dbf24 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Kevin Lim + * Korey Sewell */ #ifndef __SIM_SYSCALL_EMUL_HH__ @@ -51,7 +55,7 @@ #include "base/misc.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/translating_port.hh" #include "mem/page_table.hh" #include "sim/process.hh" @@ -65,7 +69,7 @@ class SyscallDesc { /// Typedef for target syscall handler functions. typedef SyscallReturn (*FuncPtr)(SyscallDesc *, int num, - Process *, ExecContext *); + Process *, ThreadContext *); const char *name; //!< Syscall name (e.g., "open"). FuncPtr funcPtr; //!< Pointer to emulation function. @@ -75,7 +79,7 @@ class SyscallDesc { enum Flags { /// Don't set return regs according to funcPtr return value. /// Used for syscalls with non-standard return conventions - /// that explicitly set the ExecContext regs (e.g., + /// that explicitly set the ThreadContext regs (e.g., /// sigreturn). SuppressReturnValue = 1 }; @@ -87,7 +91,7 @@ class SyscallDesc { } /// Emulate the syscall. Public interface for calling through funcPtr. - void doSyscall(int callnum, Process *proc, ExecContext *xc); + void doSyscall(int callnum, Process *proc, ThreadContext *tc); }; @@ -169,129 +173,133 @@ class TypedBufferArg : public BaseBufferArg /// Handler for unimplemented syscalls that we haven't thought about. SyscallReturn unimplementedFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Handler for unimplemented syscalls that we never intend to /// implement (signal handling, etc.) and should not affect the correct /// behavior of the program. Print a warning only if the appropriate /// trace flag is enabled. Return success to the target program. SyscallReturn ignoreFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target exit() handler: terminate simulation. SyscallReturn exitFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getpagesize() handler. SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target obreak() handler: set brk address. SyscallReturn obreakFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target close() handler. SyscallReturn closeFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target read() handler. SyscallReturn readFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target write() handler. SyscallReturn writeFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target lseek() handler. SyscallReturn lseekFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target munmap() handler. SyscallReturn munmapFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target gethostname() handler. SyscallReturn gethostnameFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target unlink() handler. SyscallReturn unlinkFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target rename() handler. SyscallReturn renameFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target truncate() handler. SyscallReturn truncateFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target ftruncate() handler. SyscallReturn ftruncateFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target chown() handler. SyscallReturn chownFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target fchown() handler. SyscallReturn fchownFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target fnctl() handler. SyscallReturn fcntlFunc(SyscallDesc *desc, int num, - Process *process, ExecContext *xc); + Process *process, ThreadContext *tc); + +/// Target fcntl64() handler. +SyscallReturn fcntl64Func(SyscallDesc *desc, int num, + Process *process, ThreadContext *tc); /// Target setuid() handler. SyscallReturn setuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getpid() handler. SyscallReturn getpidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getuid() handler. SyscallReturn getuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getgid() handler. SyscallReturn getgidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getppid() handler. SyscallReturn getppidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target geteuid() handler. SyscallReturn geteuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getegid() handler. SyscallReturn getegidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Pseudo Funcs - These functions use a different return convension, /// returning a second value in a register other than the normal return register SyscallReturn pipePseudoFunc(SyscallDesc *desc, int num, - Process *process, ExecContext *xc); + Process *process, ThreadContext *tc); /// Target getpidPseudo() handler. SyscallReturn getpidPseudoFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getuidPseudo() handler. SyscallReturn getuidPseudoFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getgidPseudo() handler. SyscallReturn getgidPseudoFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// This struct is used to build an target-OS-dependent table that @@ -335,10 +343,10 @@ getElapsedTime(T1 &sec, T2 &usec) template <class OS> SyscallReturn ioctlFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); - unsigned req = xc->getSyscallArg(1); + int fd = tc->getSyscallArg(0); + unsigned req = tc->getSyscallArg(1); DPRINTF(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req); @@ -360,7 +368,7 @@ ioctlFunc(SyscallDesc *desc, int callnum, Process *process, default: fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n", - fd, req, xc->readPC()); + fd, req, tc->readPC()); } } @@ -368,11 +376,11 @@ ioctlFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn openFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; if (path == "/dev/sysdev0") { @@ -382,8 +390,8 @@ openFunc(SyscallDesc *desc, int callnum, Process *process, return -ENOENT; } - int tgtFlags = xc->getSyscallArg(1); - int mode = xc->getSyscallArg(2); + int tgtFlags = tc->getSyscallArg(1); + int mode = tc->getSyscallArg(2); int hostFlags = 0; // translate open flags @@ -415,14 +423,14 @@ openFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn chmodFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; - uint32_t mode = xc->getSyscallArg(1); + uint32_t mode = tc->getSyscallArg(1); mode_t hostMode = 0; // XXX translate mode flags via OS::something??? @@ -441,15 +449,15 @@ chmodFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fchmodFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; } - uint32_t mode = xc->getSyscallArg(1); + uint32_t mode = tc->getSyscallArg(1); mode_t hostMode = 0; // XXX translate mode flags via OS::someting??? @@ -468,11 +476,11 @@ fchmodFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn statFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; struct stat hostBuf; @@ -481,7 +489,7 @@ statFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -491,9 +499,9 @@ statFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fstat64Func(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; @@ -510,7 +518,7 @@ fstat64Func(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStat64Buf(xc->getMemPort(), fd, xc->getSyscallArg(1), &hostBuf); + OS::copyOutStat64Buf(tc->getMemPort(), fd, tc->getSyscallArg(1), &hostBuf); return 0; } @@ -520,11 +528,11 @@ fstat64Func(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn lstatFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; struct stat hostBuf; @@ -533,7 +541,7 @@ lstatFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -542,11 +550,11 @@ lstatFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn lstat64Func(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; #if BSD_HOST @@ -560,7 +568,7 @@ lstat64Func(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStat64Buf(xc->getMemPort(), -1, xc->getSyscallArg(1), &hostBuf); + OS::copyOutStat64Buf(tc->getMemPort(), -1, tc->getSyscallArg(1), &hostBuf); return 0; } @@ -569,9 +577,9 @@ lstat64Func(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fstatFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); DPRINTF(SyscallVerbose, "fstat(%d, ...)\n", fd); @@ -584,7 +592,7 @@ fstatFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -594,11 +602,11 @@ fstatFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn statfsFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; struct statfs hostBuf; @@ -607,7 +615,7 @@ statfsFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatfsBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatfsBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -617,9 +625,9 @@ statfsFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fstatfsFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); if (fd < 0) return -EBADF; @@ -630,7 +638,7 @@ fstatfsFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatfsBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatfsBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -640,17 +648,17 @@ fstatfsFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn writevFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; } - TranslatingPort *p = xc->getMemPort(); - uint64_t tiov_base = xc->getSyscallArg(1); - size_t count = xc->getSyscallArg(2); + TranslatingPort *p = tc->getMemPort(); + uint64_t tiov_base = tc->getSyscallArg(1); + size_t count = tc->getSyscallArg(2); struct iovec hiov[count]; for (int i = 0; i < count; ++i) { @@ -692,14 +700,14 @@ writevFunc(SyscallDesc *desc, int callnum, Process *process, /// anything else. template <class OS> SyscallReturn -mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +mmapFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - Addr start = xc->getSyscallArg(0); - uint64_t length = xc->getSyscallArg(1); - // int prot = xc->getSyscallArg(2); - int flags = xc->getSyscallArg(3); - // int fd = p->sim_fd(xc->getSyscallArg(4)); - // int offset = xc->getSyscallArg(5); + Addr start = tc->getSyscallArg(0); + uint64_t length = tc->getSyscallArg(1); + // int prot = tc->getSyscallArg(2); + int flags = tc->getSyscallArg(3); + // int fd = p->sim_fd(tc->getSyscallArg(4)); + // int offset = tc->getSyscallArg(5); if ((start % TheISA::VMPageSize) != 0 || (length % TheISA::VMPageSize) != 0) { @@ -721,7 +729,7 @@ mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) if (!(flags & OS::TGT_MAP_ANONYMOUS)) { warn("allowing mmap of file @ fd %d. " - "This will break if not /dev/zero.", xc->getSyscallArg(4)); + "This will break if not /dev/zero.", tc->getSyscallArg(4)); } return start; @@ -731,10 +739,10 @@ mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) template <class OS> SyscallReturn getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned resource = xc->getSyscallArg(0); - TypedBufferArg<typename OS::rlimit> rlp(xc->getSyscallArg(1)); + unsigned resource = tc->getSyscallArg(0); + TypedBufferArg<typename OS::rlimit> rlp(tc->getSyscallArg(1)); switch (resource) { case OS::TGT_RLIMIT_STACK: @@ -751,7 +759,7 @@ getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, break; } - rlp.copyOut(xc->getMemPort()); + rlp.copyOut(tc->getMemPort()); return 0; } @@ -759,16 +767,16 @@ getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<typename OS::timeval> tp(xc->getSyscallArg(0)); + TypedBufferArg<typename OS::timeval> tp(tc->getSyscallArg(0)); getElapsedTime(tp->tv_sec, tp->tv_usec); tp->tv_sec += seconds_since_epoch; tp->tv_sec = htog(tp->tv_sec); tp->tv_usec = htog(tp->tv_usec); - tp.copyOut(xc->getMemPort()); + tp.copyOut(tc->getMemPort()); return 0; } @@ -778,15 +786,15 @@ gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn utimesFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; - TypedBufferArg<typename OS::timeval [2]> tp(xc->getSyscallArg(1)); - tp.copyIn(xc->getMemPort()); + TypedBufferArg<typename OS::timeval [2]> tp(tc->getSyscallArg(1)); + tp.copyIn(tc->getMemPort()); struct timeval hostTimeval[2]; for (int i = 0; i < 2; ++i) @@ -805,10 +813,10 @@ utimesFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn getrusageFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int who = xc->getSyscallArg(0); // THREAD, SELF, or CHILDREN - TypedBufferArg<typename OS::rusage> rup(xc->getSyscallArg(1)); + int who = tc->getSyscallArg(0); // THREAD, SELF, or CHILDREN + TypedBufferArg<typename OS::rusage> rup(tc->getSyscallArg(1)); if (who != OS::TGT_RUSAGE_SELF) { // don't really handle THREAD or CHILDREN, but just warn and @@ -838,7 +846,7 @@ getrusageFunc(SyscallDesc *desc, int callnum, Process *process, rup->ru_nvcsw = 0; rup->ru_nivcsw = 0; - rup.copyOut(xc->getMemPort()); + rup.copyOut(tc->getMemPort()); return 0; } diff --git a/src/sim/system.cc b/src/sim/system.cc index 89f39491e..b3c7870fd 100644 --- a/src/sim/system.cc +++ b/src/sim/system.cc @@ -1,8 +1,41 @@ +/* + * Copyright (c) 2003-2006 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + * Ali Saidi + */ + #include "arch/isa_traits.hh" #include "base/loader/object_file.hh" #include "base/loader/symtab.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/mem_object.hh" #include "mem/physical.hh" #include "sim/builder.hh" @@ -99,8 +132,6 @@ System::~System() #if FULL_SYSTEM delete kernelSymtab; delete kernel; - - delete kernelBinning; #else panic("System::fixFuncEventAddr needs to be rewritten " "to work with syscall emulation"); @@ -115,26 +146,26 @@ int rgdb_wait = -1; #endif // FULL_SYSTEM int -System::registerExecContext(ExecContext *xc, int id) +System::registerThreadContext(ThreadContext *tc, int id) { if (id == -1) { - for (id = 0; id < execContexts.size(); id++) { - if (!execContexts[id]) + for (id = 0; id < threadContexts.size(); id++) { + if (!threadContexts[id]) break; } } - if (execContexts.size() <= id) - execContexts.resize(id + 1); + if (threadContexts.size() <= id) + threadContexts.resize(id + 1); - if (execContexts[id]) + if (threadContexts[id]) panic("Cannot have two CPUs with the same id (%d)\n", id); - execContexts[id] = xc; + threadContexts[id] = tc; numcpus++; #if FULL_SYSTEM - RemoteGDB *rgdb = new RemoteGDB(this, xc); + RemoteGDB *rgdb = new RemoteGDB(this, tc); GDBListener *gdbl = new GDBListener(rgdb, 7000 + id); gdbl->listen(); /** @@ -158,21 +189,21 @@ void System::startup() { int i; - for (i = 0; i < execContexts.size(); i++) - execContexts[i]->activate(0); + for (i = 0; i < threadContexts.size(); i++) + threadContexts[i]->activate(0); } void -System::replaceExecContext(ExecContext *xc, int id) +System::replaceThreadContext(ThreadContext *tc, int id) { - if (id >= execContexts.size()) { - panic("replaceExecContext: bad id, %d >= %d\n", - id, execContexts.size()); + if (id >= threadContexts.size()) { + panic("replaceThreadContext: bad id, %d >= %d\n", + id, threadContexts.size()); } - execContexts[id] = xc; + threadContexts[id] = tc; #if FULL_SYSTEM - remoteGDB[id]->replaceExecContext(xc); + remoteGDB[id]->replaceThreadContext(tc); #endif // FULL_SYSTEM } @@ -187,19 +218,9 @@ System::new_page() #endif void -System::regStats() -{ -#if FULL_SYSTEM - kernelBinning->regStats(name() + ".kern"); -#endif // FULL_SYSTEM -} - -void System::serialize(ostream &os) { #if FULL_SYSTEM - kernelBinning->serialize(os); - kernelSymtab->serialize("kernel_symtab", os); #endif // FULL_SYSTEM } @@ -209,8 +230,6 @@ void System::unserialize(Checkpoint *cp, const string §ion) { #if FULL_SYSTEM - kernelBinning->unserialize(cp, section); - kernelSymtab->unserialize("kernel_symtab", cp, section); #endif // FULL_SYSTEM } diff --git a/src/sim/system.hh b/src/sim/system.hh index 7e21bd587..059dc92dc 100644 --- a/src/sim/system.hh +++ b/src/sim/system.hh @@ -24,6 +24,10 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert */ #ifndef __SYSTEM_HH__ @@ -44,7 +48,7 @@ #endif class BaseCPU; -class ExecContext; +class ThreadContext; class ObjectFile; class PhysicalMemory; @@ -52,7 +56,6 @@ class PhysicalMemory; class Platform; class GDBListener; class RemoteGDB; -namespace Kernel { class Binning; } #endif class System : public SimObject @@ -61,12 +64,12 @@ class System : public SimObject PhysicalMemory *physmem; PCEventQueue pcEventQueue; - std::vector<ExecContext *> execContexts; + std::vector<ThreadContext *> threadContexts; int numcpus; int getNumCPUs() { - if (numcpus != execContexts.size()) + if (numcpus != threadContexts.size()) panic("cpu array not fully populated!"); return numcpus; @@ -96,8 +99,6 @@ class System : public SimObject /** Entry point in the kernel to start at */ Addr kernelEntry; - Kernel::Binning *kernelBinning; - #else int page_ptr; @@ -157,9 +158,6 @@ class System : public SimObject Tick boot_cpu_frequency; std::string boot_osflags; uint64_t init_param; - bool bin; - std::vector<std::string> binned_fns; - bool bin_int; std::string kernel_path; std::string readfile; @@ -204,10 +202,9 @@ class System : public SimObject #endif // FULL_SYSTEM - int registerExecContext(ExecContext *xc, int xcIndex); - void replaceExecContext(ExecContext *xc, int xcIndex); + int registerThreadContext(ThreadContext *tc, int tcIndex); + void replaceThreadContext(ThreadContext *tc, int tcIndex); - void regStats(); void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); diff --git a/src/sim/vptr.hh b/src/sim/vptr.hh index cc57e63f0..bcc22f0ca 100644 --- a/src/sim/vptr.hh +++ b/src/sim/vptr.hh @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_VPTR_HH__ @@ -32,7 +34,7 @@ #include "arch/vtophys.hh" #include "arch/isa_traits.hh" -class ExecContext; +class ThreadContext; template <class T> class VPtr @@ -41,17 +43,17 @@ class VPtr typedef T Type; private: - ExecContext *xc; + ThreadContext *tc; Addr ptr; public: - ExecContext *GetXC() const { return xc; } + ThreadContext *GetTC() const { return tc; } Addr GetPointer() const { return ptr; } public: - explicit VPtr(ExecContext *_xc, Addr p = 0) : xc(_xc), ptr(p) { } + explicit VPtr(ThreadContext *_tc, Addr p = 0) : tc(_tc), ptr(p) { } template <class U> - VPtr(const VPtr<U> &vp) : xc(vp.GetXC()), ptr(vp.GetPointer()) {} + VPtr(const VPtr<U> &vp) : tc(vp.GetTC()), ptr(vp.GetPointer()) {} ~VPtr() {} bool operator!() const @@ -88,7 +90,7 @@ class VPtr template <class U> const VPtr<T> &operator=(const VPtr<U> &vp) { - xc = vp.GetXC(); + tc = vp.GetTC(); ptr = vp.GetPointer(); return *this; @@ -97,7 +99,7 @@ class VPtr operator T *() { panic("Needs to be rewritten\n"); -/* void *addr = vtomem(xc, ptr, sizeof(T)); +/* void *addr = vtomem(tc, ptr, sizeof(T)); return (T *)addr; */ } @@ -105,7 +107,7 @@ class VPtr T *operator->() { panic("Needs to be rewritten\n"); -/* void *addr = vtomem(xc, ptr, sizeof(T)); +/* void *addr = vtomem(tc, ptr, sizeof(T)); return (T *)addr; */ } @@ -113,7 +115,7 @@ class VPtr T &operator*() { panic("Needs to be rewritten\n"); -/* void *addr = vtomem(xc, ptr, sizeof(T)); +/* void *addr = vtomem(tc, ptr, sizeof(T)); return *(T *)addr; */ } diff --git a/src/unittest/bitvectest.cc b/src/unittest/bitvectest.cc index 1b8c332f5..440a150a3 100644 --- a/src/unittest/bitvectest.cc +++ b/src/unittest/bitvectest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream.h> diff --git a/src/unittest/circletest.cc b/src/unittest/circletest.cc index bb15f8c64..f072cf044 100644 --- a/src/unittest/circletest.cc +++ b/src/unittest/circletest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <fcntl.h> diff --git a/src/unittest/cprintftest.cc b/src/unittest/cprintftest.cc index 361f84028..9c3eb0cd6 100644 --- a/src/unittest/cprintftest.cc +++ b/src/unittest/cprintftest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/genini.py b/src/unittest/genini.py index 2af81fe2b..a969a6ee4 100755 --- a/src/unittest/genini.py +++ b/src/unittest/genini.py @@ -24,6 +24,8 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert import getopt, os, os.path, sys from os.path import join as joinpath, realpath diff --git a/src/unittest/initest.cc b/src/unittest/initest.cc index 0c5ac2343..8f53fce5c 100644 --- a/src/unittest/initest.cc +++ b/src/unittest/initest.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <iostream> diff --git a/src/unittest/lru_test.cc b/src/unittest/lru_test.cc index 2829163de..d10eb1dd0 100644 --- a/src/unittest/lru_test.cc +++ b/src/unittest/lru_test.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Dave Greene + * Nathan Binkert */ #include <iostream> diff --git a/src/unittest/nmtest.cc b/src/unittest/nmtest.cc index e9c20d19d..b6b74e08d 100644 --- a/src/unittest/nmtest.cc +++ b/src/unittest/nmtest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/offtest.cc b/src/unittest/offtest.cc index d3f035b73..ebfb2515c 100644 --- a/src/unittest/offtest.cc +++ b/src/unittest/offtest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/unittest/paramtest.cc b/src/unittest/paramtest.cc index cb31c49d5..e513ab981 100644 --- a/src/unittest/paramtest.cc +++ b/src/unittest/paramtest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ // diff --git a/src/unittest/rangetest.cc b/src/unittest/rangetest.cc index 41d438f48..b7a68ab44 100644 --- a/src/unittest/rangetest.cc +++ b/src/unittest/rangetest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/sized_test.cc b/src/unittest/sized_test.cc index 86cd13e5b..f1bf7528f 100644 --- a/src/unittest/sized_test.cc +++ b/src/unittest/sized_test.cc @@ -24,6 +24,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Dave Greene + * Nathan Binkert */ #include <iostream> diff --git a/src/unittest/stattest.cc b/src/unittest/stattest.cc index b944eff45..4e504fde9 100644 --- a/src/unittest/stattest.cc +++ b/src/unittest/stattest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iomanip> @@ -48,10 +50,10 @@ Tick ticksPerSecond = ULL(2000000000); Scalar<> s1; Scalar<> s2; Average<> s3; -Scalar<MainBin> s4; -Vector<MainBin> s5; -Distribution<MainBin> s6; -Vector<MainBin> s7; +Scalar<> s4; +Vector<> s5; +Distribution<> s6; +Vector<> s7; AverageVector<> s8; StandardDeviation<> s9; AverageDeviation<> s10; @@ -70,9 +72,6 @@ Value f5; Formula f6; Formula f7; -MainBin bin1("bin1"); -MainBin bin2("bin2"); - ostream *outputStream = &cout; double @@ -301,8 +300,6 @@ main(int argc, char *argv[]) check(); reset(); - bin1.activate(); - s16[1][0] = 1; s16[0][1] = 3; s16[0][0] = 2; @@ -493,7 +490,6 @@ main(int argc, char *argv[]) s6.sample(8); s6.sample(9); - bin2.activate(); s6.sample(10); s6.sample(10); s6.sample(10); diff --git a/src/unittest/strnumtest.cc b/src/unittest/strnumtest.cc index a80dd7c36..ea28e35df 100644 --- a/src/unittest/strnumtest.cc +++ b/src/unittest/strnumtest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream.h> diff --git a/src/unittest/symtest.cc b/src/unittest/symtest.cc index 5fba71736..f0142b923 100644 --- a/src/unittest/symtest.cc +++ b/src/unittest/symtest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream.h> diff --git a/src/unittest/tokentest.cc b/src/unittest/tokentest.cc index 7f27d58fe..128346b5b 100644 --- a/src/unittest/tokentest.cc +++ b/src/unittest/tokentest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/tracetest.cc b/src/unittest/tracetest.cc index 866b67d9b..b1343aac3 100644 --- a/src/unittest/tracetest.cc +++ b/src/unittest/tracetest.cc @@ -24,6 +24,8 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert */ #include "sim/host.hh" |