diff -r 6548721032fa -r 042f38a2e360 configs/common/CacheConfig.py --- a/configs/common/CacheConfig.py Fri Feb 11 18:29:36 2011 -0600 +++ b/configs/common/CacheConfig.py Fri Aug 05 18:14:06 2011 +0100 @@ -35,8 +35,9 @@ def config_cache(options, system): if options.l2cache: - system.l2 = L2Cache(size='2MB') - system.tol2bus = Bus() + system.l2 = L2Cache(size=options.l2_size, assoc=options.l2_assoc, + block_size=options.cacheline_size) + system.tol2bus = CoherentBus() system.l2.cpu_side = system.tol2bus.port system.l2.mem_side = system.membus.port system.l2.num_cpus = options.num_cpus @@ -44,13 +45,18 @@ for i in xrange(options.num_cpus): if options.caches: if buildEnv['TARGET_ISA'] == 'x86': - system.cpu[i].addPrivateSplitL1Caches(L1Cache(size = '32kB'), - L1Cache(size = '64kB'), - PageTableWalkerCache(), - PageTableWalkerCache()) + system.cpu[i].addPrivateSplitL1Caches( + L1Cache(size=options.l1i_size, assoc=options.l1i_assoc, + block_size=options.cacheline_size), + L1Cache(size=options.l1d_size, assoc=options.l1d_assoc, + block_size=options.cacheline_size), + PageTableWalkerCache(), PageTableWalkerCache()) else: - system.cpu[i].addPrivateSplitL1Caches(L1Cache(size = '32kB'), - L1Cache(size = '64kB')) + system.cpu[i].addPrivateSplitL1Caches( + L1Cache(size=options.l1i_size, assoc=options.l1i_assoc, + block_size=options.cacheline_size), + L1Cache(size=options.l1d_size, assoc=options.l1d_assoc, + block_size=options.cacheline_size)) if options.l2cache: system.cpu[i].connectAllPorts(system.tol2bus, system.membus) else: diff -r 6548721032fa -r 042f38a2e360 configs/common/FSConfig.py --- a/configs/common/FSConfig.py Fri Feb 11 18:29:36 2011 -0600 +++ b/configs/common/FSConfig.py Fri Aug 05 18:14:06 2011 +0100 @@ -49,9 +49,9 @@ def childImage(self, ci): self.image.child.image_file = ci -class MemBus(Bus): +class MemBus(CoherentBus): badaddr_responder = BadAddr() - default = Self.badaddr_responder.pio + default_mem_master = Self.badaddr_responder.pio def makeLinuxAlphaSystem(mem_mode, mdesc = None): @@ -61,25 +61,44 @@ pci_func=0, pci_dev=0, pci_bus=0) self = LinuxAlphaSystem() + if not mdesc: # generic system mdesc = SysConfig() self.readfile = mdesc.script() - self.iobus = Bus(bus_id=0) - self.membus = MemBus(bus_id=1) - self.bridge = Bridge(delay='50ns', nack_delay='4ns') + + # the non-coherent IO bus to which all peripherals are attached + self.iobus = NonCoherentBus() + + # the coherent memory bus to which the main memories are attached + # including the last level caches and the bridge to/from IO + self.membus = MemBus() + + # bridge from the masters on the memory bus (CPUs) to access the + # IO bus either a bridge or a cache is added in the opposite + # direction + self.mem2ioBridge = Bridge(delay = '50ns') + self.membus.mem_master = self.mem2ioBridge.slave + self.mem2ioBridge.master = self.iobus.mem_slave + + # the main memory self.physmem = PhysicalMemory(range = AddrRange(mdesc.mem())) - self.bridge.side_a = self.iobus.port - self.bridge.side_b = self.membus.port - self.physmem.port = self.membus.port + self.memsize = mdesc.mem() + + self.membus.mem_master = self.physmem.port + self.disk0 = CowIdeDisk(driveID='master') self.disk2 = CowIdeDisk(driveID='master') self.disk0.childImage(mdesc.disk()) self.disk2.childImage(disk('linux-bigswap2.img')) self.tsunami = BaseTsunami() self.tsunami.attachIO(self.iobus) - self.tsunami.ide.pio = self.iobus.port - self.tsunami.ethernet.pio = self.iobus.port + self.iobus.mem_master = self.tsunami.ide.pio + self.iobus.mem_master = self.tsunami.ide.config + self.iobus.mem_slave = self.tsunami.ide.dma + self.iobus.mem_master = self.tsunami.ethernet.pio + self.iobus.mem_master = self.tsunami.ethernet.config + self.iobus.mem_slave = self.tsunami.ethernet.dma self.simple_disk = SimpleDisk(disk=RawDiskImage(image_file = mdesc.disk(), read_only = True)) self.intrctrl = IntrControl() @@ -90,46 +109,45 @@ self.console = binary('console') self.boot_osflags = 'root=/dev/hda1 console=ttyS0' + self.system_port = self.membus.mem_slave return self -def makeLinuxAlphaRubySystem(mem_mode, mdesc = None): + + +def makeSimpleLinuxAlphaSystem(mem_mode, mdesc = None): class BaseTsunami(Tsunami): ethernet = NSGigE(pci_bus=0, pci_dev=1, pci_func=0) ide = IdeController(disks=[Parent.disk0, Parent.disk2], pci_func=0, pci_dev=0, pci_bus=0) - - physmem = PhysicalMemory(range = AddrRange(mdesc.mem())) - self = LinuxAlphaSystem(physmem = physmem) + + self = LinuxAlphaSystem() + if not mdesc: # generic system mdesc = SysConfig() self.readfile = mdesc.script() - # Create pio bus to connect all device pio ports to rubymem's pio port - self.piobus = Bus(bus_id=0) + # the non-coherent bus to which all components are attached + self.bus = NonCoherentBus() - # - # Pio functional accesses from devices need direct access to memory - # RubyPort currently does support functional accesses. Therefore provide - # the piobus a direct connection to physical memory - # - self.piobus.port = physmem.port + # the main memory + self.physmem = PhysicalMemory(range = AddrRange(mdesc.mem())) + self.memsize = mdesc.mem() + + self.bus.mem_master = self.physmem.port self.disk0 = CowIdeDisk(driveID='master') self.disk2 = CowIdeDisk(driveID='master') self.disk0.childImage(mdesc.disk()) self.disk2.childImage(disk('linux-bigswap2.img')) self.tsunami = BaseTsunami() - self.tsunami.attachIO(self.piobus) - self.tsunami.ide.pio = self.piobus.port - self.tsunami.ethernet.pio = self.piobus.port - - # - # Store the dma devices for later connection to dma ruby ports. - # Append an underscore to dma_devices to avoid the SimObjectVector check. - # - self._dma_devices = [self.tsunami.ide, self.tsunami.ethernet] - + self.tsunami.attachIO(self.bus) + self.bus.mem_master = self.tsunami.ide.pio + self.bus.mem_master = self.tsunami.ide.config + self.bus.mem_slave = self.tsunami.ide.dma + self.bus.mem_master = self.tsunami.ethernet.pio + self.bus.mem_master = self.tsunami.ethernet.config + self.bus.mem_slave = self.tsunami.ethernet.dma self.simple_disk = SimpleDisk(disk=RawDiskImage(image_file = mdesc.disk(), read_only = True)) self.intrctrl = IntrControl() @@ -467,73 +485,3 @@ self.etherlink.dump = Parent.etherdump return self - -def setMipsOptions(TestCPUClass): - #CP0 Configuration - TestCPUClass.CoreParams.CP0_PRId_CompanyOptions = 0 - TestCPUClass.CoreParams.CP0_PRId_CompanyID = 1 - TestCPUClass.CoreParams.CP0_PRId_ProcessorID = 147 - TestCPUClass.CoreParams.CP0_PRId_Revision = 0 - - #CP0 Interrupt Control - TestCPUClass.CoreParams.CP0_IntCtl_IPTI = 7 - TestCPUClass.CoreParams.CP0_IntCtl_IPPCI = 7 - - # Config Register - #TestCPUClass.CoreParams.CP0_Config_K23 = 0 # Since TLB - #TestCPUClass.CoreParams.CP0_Config_KU = 0 # Since TLB - TestCPUClass.CoreParams.CP0_Config_BE = 0 # Little Endian - TestCPUClass.CoreParams.CP0_Config_AR = 1 # Architecture Revision 2 - TestCPUClass.CoreParams.CP0_Config_AT = 0 # MIPS32 - TestCPUClass.CoreParams.CP0_Config_MT = 1 # TLB MMU - #TestCPUClass.CoreParams.CP0_Config_K0 = 2 # Uncached - - #Config 1 Register - TestCPUClass.CoreParams.CP0_Config1_M = 1 # Config2 Implemented - TestCPUClass.CoreParams.CP0_Config1_MMU = 63 # TLB Size - # ***VERY IMPORTANT*** - # Remember to modify CP0_Config1 according to cache specs - # Examine file ../common/Cache.py - TestCPUClass.CoreParams.CP0_Config1_IS = 1 # I-Cache Sets Per Way, 16KB cache, i.e., 1 (128) - TestCPUClass.CoreParams.CP0_Config1_IL = 5 # I-Cache Line Size, default in Cache.py is 64, i.e 5 - TestCPUClass.CoreParams.CP0_Config1_IA = 1 # I-Cache Associativity, default in Cache.py is 2, i.e, a value of 1 - TestCPUClass.CoreParams.CP0_Config1_DS = 2 # D-Cache Sets Per Way (see below), 32KB cache, i.e., 2 - TestCPUClass.CoreParams.CP0_Config1_DL = 5 # D-Cache Line Size, default is 64, i.e., 5 - TestCPUClass.CoreParams.CP0_Config1_DA = 1 # D-Cache Associativity, default is 2, i.e. 1 - TestCPUClass.CoreParams.CP0_Config1_C2 = 0 # Coprocessor 2 not implemented(?) - TestCPUClass.CoreParams.CP0_Config1_MD = 0 # MDMX ASE not implemented in Mips32 - TestCPUClass.CoreParams.CP0_Config1_PC = 1 # Performance Counters Implemented - TestCPUClass.CoreParams.CP0_Config1_WR = 0 # Watch Registers Implemented - TestCPUClass.CoreParams.CP0_Config1_CA = 0 # Mips16e NOT implemented - TestCPUClass.CoreParams.CP0_Config1_EP = 0 # EJTag Not Implemented - TestCPUClass.CoreParams.CP0_Config1_FP = 0 # FPU Implemented - - #Config 2 Register - TestCPUClass.CoreParams.CP0_Config2_M = 1 # Config3 Implemented - TestCPUClass.CoreParams.CP0_Config2_TU = 0 # Tertiary Cache Control - TestCPUClass.CoreParams.CP0_Config2_TS = 0 # Tertiary Cache Sets Per Way - TestCPUClass.CoreParams.CP0_Config2_TL = 0 # Tertiary Cache Line Size - TestCPUClass.CoreParams.CP0_Config2_TA = 0 # Tertiary Cache Associativity - TestCPUClass.CoreParams.CP0_Config2_SU = 0 # Secondary Cache Control - TestCPUClass.CoreParams.CP0_Config2_SS = 0 # Secondary Cache Sets Per Way - TestCPUClass.CoreParams.CP0_Config2_SL = 0 # Secondary Cache Line Size - TestCPUClass.CoreParams.CP0_Config2_SA = 0 # Secondary Cache Associativity - - - #Config 3 Register - TestCPUClass.CoreParams.CP0_Config3_M = 0 # Config4 Not Implemented - TestCPUClass.CoreParams.CP0_Config3_DSPP = 1 # DSP ASE Present - TestCPUClass.CoreParams.CP0_Config3_LPA = 0 # Large Physical Addresses Not supported in Mips32 - TestCPUClass.CoreParams.CP0_Config3_VEIC = 0 # EIC Supported - TestCPUClass.CoreParams.CP0_Config3_VInt = 0 # Vectored Interrupts Implemented - TestCPUClass.CoreParams.CP0_Config3_SP = 0 # Small Pages Supported (PageGrain reg. exists) - TestCPUClass.CoreParams.CP0_Config3_MT = 0 # MT Not present - TestCPUClass.CoreParams.CP0_Config3_SM = 0 # SmartMIPS ASE Not implemented - TestCPUClass.CoreParams.CP0_Config3_TL = 0 # TraceLogic Not implemented - - #SRS Ctl - HSS - TestCPUClass.CoreParams.CP0_SrsCtl_HSS = 3 # Four shadow register sets implemented - - - #TestCPUClass.CoreParams.tlb = TLB() - #TestCPUClass.CoreParams.UnifiedTLB = 1 diff -r 6548721032fa -r 042f38a2e360 configs/common/Options.py --- a/configs/common/Options.py Fri Feb 11 18:29:36 2011 -0600 +++ b/configs/common/Options.py Fri Aug 05 18:14:06 2011 +0100 @@ -38,14 +38,16 @@ parser.add_option("--num-dirs", type="int", default=1) parser.add_option("--num-l2caches", type="int", default=1) parser.add_option("--num-l3caches", type="int", default=1) -parser.add_option("--l1d_size", type="string", default="32kB") +parser.add_option("--l1d_size", type="string", default="64kB") parser.add_option("--l1i_size", type="string", default="32kB") parser.add_option("--l2_size", type="string", default="2MB") parser.add_option("--l3_size", type="string", default="16MB") parser.add_option("--l1d_assoc", type="int", default=2) parser.add_option("--l1i_assoc", type="int", default=2) -parser.add_option("--l2_assoc", type="int", default=16) +parser.add_option("--l2_assoc", type="int", default=8) parser.add_option("--l3_assoc", type="int", default=16) +parser.add_option("--cacheline_size", type="int", default=64) + # Run duration options parser.add_option("-m", "--maxtick", type="int", default=m5.MaxTick, diff -r 6548721032fa -r 042f38a2e360 src/cpu/base.hh --- a/src/cpu/base.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/base.hh Fri Aug 05 18:14:06 2011 +0100 @@ -54,7 +54,6 @@ class CheckerCPU; class ThreadContext; class System; -class Port; namespace TheISA { @@ -82,9 +81,13 @@ virtual const char *description() const; }; -class BaseCPU : public MemObject +class BaseCPU : public MemObject, public MemMapMasterInterface { protected: + + // enumerate the master ports + enum MasterId {DATA_MASTER_ID, INST_MASTER_ID, N_MASTER_ID}; + // CPU's clock period in terms of the number of ticks of curTime. Tick clock; // @todo remove me after debugging with legion done @@ -95,10 +98,33 @@ // therefore no setCpuId() method is provided int _cpuId; + // a memory-mapped master port for instruction and data respectively + MemMapMasterPort instPort; + MemMapMasterPort dataPort; + + // the block size as perceived through the instruction port + unsigned int instBlockSize; + + // the block size as perceived through the data port + unsigned int dataBlockSize; + + // non of the cpus care about range changes + virtual void recvRangeChange(int master_id) {} + public: /** Reads this CPU's ID. */ int cpuId() { return _cpuId; } + /** Get the instruction port. */ + MemMapMasterPort* getInstPort() { return &instPort; } + + /** Get the data port */ + MemMapMasterPort* getDataPort() { return &dataPort; } + + unsigned int getInstBlockSize() { return instBlockSize; } + + unsigned int getDataBlockSize() { return dataBlockSize; } + // Tick currentTick; inline Tick frequency() const { return SimClock::Frequency / clock; } inline Tick ticks(int numCycles) const { return clock * numCycles; } @@ -231,7 +257,8 @@ /// Take over execution from the given CPU. Used for warm-up and /// sampling. - virtual void takeOverFrom(BaseCPU *, Port *ic, Port *dc); + virtual void takeOverFrom(BaseCPU *, MemMapMasterPort *ic, + MemMapMasterPort *dc); /** * Number of threads we're actually simulating (<= SMT_MAX_THREADS). diff -r 6548721032fa -r 042f38a2e360 src/cpu/base.cc --- a/src/cpu/base.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/base.cc Fri Aug 05 18:14:06 2011 +0100 @@ -102,12 +102,18 @@ #if FULL_SYSTEM BaseCPU::BaseCPU(Params *p) : MemObject(p), clock(p->clock), instCnt(0), _cpuId(p->cpu_id), + instPort(*this, this, "inst_port", INST_MASTER_ID), + dataPort(*this, this, "data_port", DATA_MASTER_ID), + instBlockSize(1), dataBlockSize(1), interrupts(p->interrupts), numThreads(p->numThreads), system(p->system), phase(p->phase) #else BaseCPU::BaseCPU(Params *p) : MemObject(p), clock(p->clock), _cpuId(p->cpu_id), + instPort(*this, this, "inst_port", INST_MASTER_ID), + dataPort(*this, this, "data_port", DATA_MASTER_ID), + instBlockSize(1), dataBlockSize(1), numThreads(p->numThreads), system(p->system), phase(p->phase) #endif @@ -209,6 +215,9 @@ profileEvent = new ProfileEvent(this, params()->profile); #endif tracer = params()->tracer; + + regPort(&instPort); + regPort(&dataPort); } void @@ -226,6 +235,12 @@ { if (!params()->defer_registration) registerThreadContexts(); + + if (!dataPort.isConnected() || !instPort.isConnected()) + fatal("Both instruction and data port of CPU must be connected.\n"); + + instBlockSize = instPort->getBlockSize(); + dataBlockSize = dataPort->getBlockSize(); } void @@ -347,7 +362,7 @@ } void -BaseCPU::takeOverFrom(BaseCPU *oldCPU, Port *ic, Port *dc) +BaseCPU::takeOverFrom(BaseCPU *oldCPU, MemMapMasterPort *ic, MemMapMasterPort *dc) { assert(threadContexts.size() == oldCPU->threadContexts.size()); @@ -373,7 +388,9 @@ ThreadContext::compare(oldTC, newTC); */ - Port *old_itb_port, *old_dtb_port, *new_itb_port, *new_dtb_port; + MemMapMasterPort *old_itb_port, *old_dtb_port, *new_itb_port, + *new_dtb_port; + old_itb_port = oldTC->getITBPtr()->getPort(); old_dtb_port = oldTC->getDTBPtr()->getPort(); new_itb_port = newTC->getITBPtr()->getPort(); @@ -382,15 +399,13 @@ // Move over any table walker ports if they exist if (new_itb_port && !new_itb_port->isConnected()) { assert(old_itb_port); - Port *peer = old_itb_port->getPeer();; - new_itb_port->setPeer(peer); - peer->setPeer(new_itb_port); + BaseSlavePort *peer = old_itb_port->getSlavePort();; + new_itb_port->bind(peer); } if (new_dtb_port && !new_dtb_port->isConnected()) { assert(old_dtb_port); - Port *peer = old_dtb_port->getPeer();; - new_dtb_port->setPeer(peer); - peer->setPeer(new_dtb_port); + BaseSlavePort *peer = old_dtb_port->getSlavePort();; + new_dtb_port->bind(peer); } } @@ -409,15 +424,13 @@ // connected to anything. Also connect old CPU's memory to new // CPU. if (!ic->isConnected()) { - Port *peer = oldCPU->getPort("icache_port")->getPeer(); - ic->setPeer(peer); - peer->setPeer(ic); + BaseSlavePort *peer = oldCPU->instPort.getSlavePort(); + ic->bind(peer); } if (!dc->isConnected()) { - Port *peer = oldCPU->getPort("dcache_port")->getPeer(); - dc->setPeer(peer); - peer->setPeer(dc); + BaseSlavePort *peer = oldCPU->dataPort.getSlavePort(); + dc->bind(peer); } } diff -r 6548721032fa -r 042f38a2e360 src/cpu/base_dyn_inst.hh --- a/src/cpu/base_dyn_inst.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/base_dyn_inst.hh Fri Aug 05 18:14:06 2011 +0100 @@ -987,7 +987,7 @@ RequestPtr &sreqHigh) { // Check to see if the request crosses the next level block boundary. - unsigned block_size = cpu->getDcachePort()->peerBlockSize(); + unsigned block_size = cpu->getDataBlockSize(); Addr addr = req->getVaddr(); Addr split_addr = roundDown(addr + req->getSize() - 1, block_size); assert(split_addr <= addr || split_addr - addr < block_size); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/O3CPU.py --- a/src/cpu/o3/O3CPU.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/O3CPU.py Fri Aug 05 18:14:06 2011 +0100 @@ -32,30 +32,11 @@ from BaseCPU import BaseCPU from FUPool import * -if buildEnv['USE_CHECKER']: - from O3Checker import O3Checker - class DerivO3CPU(BaseCPU): type = 'DerivO3CPU' activity = Param.Unsigned(0, "Initial count") - if buildEnv['USE_CHECKER']: - if not buildEnv['FULL_SYSTEM']: - checker = Param.BaseCPU(O3Checker(workload=Parent.workload, - exitOnError=False, - updateOnError=True, - warnOnlyOnLoadError=False), - "checker") - else: - checker = Param.BaseCPU(O3Checker(exitOnError=False, updateOnError=True, - warnOnlyOnLoadError=False), "checker") - checker.itb = Parent.itb - checker.dtb = Parent.dtb - cachePorts = Param.Unsigned(200, "Cache Ports") - icache_port = Port("Instruction Port") - dcache_port = Port("Data Port") - _cached_ports = BaseCPU._cached_ports + ['icache_port', 'dcache_port'] decodeToFetchDelay = Param.Unsigned(1, "Decode to fetch delay") renameToFetchDelay = Param.Unsigned(1 ,"Rename to fetch delay") diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/bpred_unit_impl.hh --- a/src/cpu/o3/bpred_unit_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/bpred_unit_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -41,7 +41,7 @@ template BPredUnit::BPredUnit(DerivO3CPUParams *params) - : _name(params->name + ".BPredUnit"), + : _name(params->path + ".BPredUnit"), BTB(params->BTBEntries, params->BTBTagSize, params->instShiftAmt) diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/comm.hh --- a/src/cpu/o3/comm.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/comm.hh Fri Aug 05 18:14:06 2011 +0100 @@ -123,7 +123,6 @@ bool branchTaken; Addr mispredPC; TheISA::PCState nextPC; - unsigned branchCount; }; @@ -172,6 +171,9 @@ //Just in case we want to do a commit/squash on a cycle //(necessary for multiple ROBs?) bool commitInsts; + // Instruction that caused the squash (incase we need something + // from it) + DynInstPtr squashInst; InstSeqNum squashSeqNum; // Communication specifically to the IQ to tell the IQ that it can diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/commit.hh --- a/src/cpu/o3/commit.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/commit.hh Fri Aug 05 18:14:06 2011 +0100 @@ -262,7 +262,8 @@ * instructions instead of the current instruction and doesn't * clean up various status bits about traps/tc writes pending. */ - void squashAfter(ThreadID tid, uint64_t squash_after_seq_num); + void squashAfter(ThreadID tid, DynInstPtr &head_inst, + uint64_t squash_after_seq_num); #if FULL_SYSTEM /** Handles processing an interrupt. */ diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/commit_impl.hh --- a/src/cpu/o3/commit_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/commit_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -50,16 +50,11 @@ #include "cpu/timebuf.hh" #include "config/full_system.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" #include "cpu/exetrace.hh" #include "cpu/o3/commit.hh" #include "cpu/o3/thread_state.hh" #include "params/DerivO3CPU.hh" -#if USE_CHECKER -#include "cpu/checker/cpu.hh" -#endif - using namespace std; template @@ -543,6 +538,7 @@ toIEW->commitInfo[tid].branchMispredict = false; toIEW->commitInfo[tid].mispredictInst = NULL; + toIEW->commitInfo[tid].squashInst = NULL; toIEW->commitInfo[tid].pc = pc[tid]; } @@ -584,7 +580,8 @@ template void -DefaultCommit::squashAfter(ThreadID tid, uint64_t squash_after_seq_num) +DefaultCommit::squashAfter(ThreadID tid, DynInstPtr &head_inst, + uint64_t squash_after_seq_num) { youngestSeqNum[tid] = squash_after_seq_num; @@ -594,6 +591,7 @@ // Send back the sequence number of the squashed instruction. toIEW->commitInfo[tid].doneSeqNum = squash_after_seq_num; + toIEW->commitInfo[tid].squashInst = head_inst; // Send back the squash signal to tell stages that they should squash. toIEW->commitInfo[tid].squash = true; @@ -841,6 +839,7 @@ fromIEW->mispredictInst[tid]; toIEW->commitInfo[tid].branchTaken = fromIEW->branchTaken[tid]; + toIEW->commitInfo[tid].squashInst = NULL; toIEW->commitInfo[tid].pc = fromIEW->pc[tid]; @@ -988,7 +987,7 @@ // If this is an instruction that doesn't play nicely with // others squash everything and restart fetch if (head_inst->isSquashAfter()) - squashAfter(tid, head_inst->seqNum); + squashAfter(tid, head_inst, head_inst->seqNum); int count = 0; Addr oldpc; @@ -1098,14 +1097,6 @@ head_inst->setCompleted(); } -#if USE_CHECKER - // Use checker prior to updating anything due to traps or PC - // based events. - if (cpu->checker) { - cpu->checker->verify(head_inst); - } -#endif - if (inst_fault != NoFault) { DPRINTF(Commit, "Inst [sn:%lli] PC %s has a fault\n", head_inst->seqNum, head_inst->pcState()); @@ -1117,12 +1108,6 @@ head_inst->setCompleted(); -#if USE_CHECKER - if (cpu->checker && head_inst->isStore()) { - cpu->checker->verify(head_inst); - } -#endif - assert(!thread[tid]->inSyscall); // Mark that we're in state update mode so that the trap's @@ -1142,6 +1127,8 @@ commitStatus[tid] = TrapPending; + DPRINTF(Commit, "Committing instruction with fault [sn:%lli]\n", + head_inst->seqNum); if (head_inst->traceData) { if (DTRACE(ExecFaulting)) { head_inst->traceData->setFetchSeq(head_inst->seqNum); @@ -1175,7 +1162,8 @@ } } #endif - + DPRINTF(Commit, "Committing instruction with [sn:%lli]\n", + head_inst->seqNum); if (head_inst->traceData) { head_inst->traceData->setFetchSeq(head_inst->seqNum); head_inst->traceData->setCPSeq(thread[tid]->numInst); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/cpu.hh --- a/src/cpu/o3/cpu.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/cpu.hh Fri Aug 05 18:14:06 2011 +0100 @@ -45,7 +45,6 @@ #include "cpu/timebuf.hh" #include "config/full_system.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" #include "cpu/activity.hh" #include "cpu/base.hh" #include "cpu/simple_thread.hh" @@ -119,6 +118,7 @@ Status _threadStatus[Impl::MaxThreads]; private: + class TickEvent : public Event { private: @@ -154,6 +154,87 @@ tickEvent.squash(); } + class ProcessCacheCompletionEvent : public Event + { + private: + /** Pointer to the CPU. */ + FullO3CPU *cpu; + + public: + /** Constructs the event. */ + ProcessCacheCompletionEvent(FullO3CPU *c); + + /** Stores the returned packet. */ + MemMapPacketPtr pkt; + + /** Processes the event. */ + void process(); + /** Returns the description of the event. */ + const char *description() const; + }; + + ProcessCacheCompletionEvent processCacheCompletionEvent; + + class ProcessCompleteDataAccessEvent : public Event + { + private: + /** Pointer to the CPU. */ + FullO3CPU *cpu; + + public: + /** Constructs the event. */ + ProcessCompleteDataAccessEvent(FullO3CPU *c); + + /** Stores the returned packet. */ + MemMapPacketPtr pkt; + + /** Processes the event. */ + void process(); + /** Returns the description of the event. */ + const char *description() const; + }; + + ProcessCompleteDataAccessEvent processCompleteDataAccessEvent; + + class ProcessEndReqISideEvent : public Event + { + private: + /** Pointer to the CPU. */ + FullO3CPU *cpu; + + public: + /** Constructs the event. */ + ProcessEndReqISideEvent(FullO3CPU *c); + + /** Stores the returned packet. */ + MemMapPacketPtr pkt; + + /** Processes the event. */ + void process(); + /** Returns the description of the event. */ + const char *description() const; + }; + + ProcessEndReqISideEvent processEndReqISideEvent; + + class ProcessEndReqDSideEvent : public Event + { + private: + /** Pointer to the CPU. */ + FullO3CPU *cpu; + + public: + /** Constructs the event. */ + ProcessEndReqDSideEvent(FullO3CPU *c); + + /** Processes the event. */ + void process(); + /** Returns the description of the event. */ + const char *description() const; + }; + + ProcessEndReqDSideEvent processEndReqDSideEvent; + class ActivateThreadEvent : public Event { private: @@ -279,9 +360,6 @@ this->dtb->demapPage(vaddr, asn); } - /** Returns a specific port. */ - Port *getPort(const std::string &if_name, int idx); - /** Ticks CPU, calling tick() on each stage, and checking the overall * activity to see if the CPU should deschedule itself. */ @@ -312,6 +390,9 @@ /** Add Thread to Active Threads List. */ void activateContext(ThreadID tid, int delay); + /** Fix to wake up from untake quiesce instruction. */ + void wakeFromQuiesce(ThreadID tid); + /** Remove Thread from Active Threads List */ void suspendContext(ThreadID tid); @@ -564,6 +645,10 @@ TheISA::ISA isa[Impl::MaxThreads]; + virtual void endReq(MemMapPacketPtr req_pkt, int master_id); + + virtual void beginResp(MemMapPacketPtr resp_pkt, int master_id); + public: /** Enum to give each stage a specific index, so when calling * activateStage() or deactivateStage(), they can specify which stage @@ -645,14 +730,6 @@ /** The global sequence number counter. */ InstSeqNum globalSeqNum;//[Impl::MaxThreads]; -#if USE_CHECKER - /** 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 *checker; -#endif - #if FULL_SYSTEM /** Pointer to the system. */ System *system; @@ -704,9 +781,6 @@ data, store_idx); } - /** Get the dcache port (used to find block size for translations). */ - Port *getDcachePort() { return this->iew.ldstQueue.getDcachePort(); } - Addr lockAddr; /** Temporary fix for the lock flag, works in the UP case. */ diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/cpu.cc --- a/src/cpu/o3/cpu.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/cpu.cc Fri Aug 05 18:14:06 2011 +0100 @@ -33,7 +33,6 @@ #include "config/full_system.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" #include "cpu/activity.hh" #include "cpu/simple_thread.hh" #include "cpu/thread_context.hh" @@ -51,10 +50,6 @@ #include "sim/process.hh" #endif -#if USE_CHECKER -#include "cpu/checker/cpu.hh" -#endif - #if THE_ISA == ALPHA_ISA #include "arch/alpha/osfpal.hh" #endif @@ -76,6 +71,31 @@ } template +void +FullO3CPU::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + if (master_id == DATA_MASTER_ID) { + processCompleteDataAccessEvent.pkt = resp_pkt; + schedule(processCompleteDataAccessEvent, curTick()); + } else { + processCacheCompletionEvent.pkt = resp_pkt; + schedule(processCacheCompletionEvent, curTick()); + } +} + +template +void +FullO3CPU::endReq(MemMapPacketPtr resp_pkt, int master_id) +{ + if (master_id == DATA_MASTER_ID) { + schedule(processEndReqDSideEvent, curTick()); + } else { + processEndReqISideEvent.pkt = resp_pkt; + schedule(processEndReqISideEvent, curTick()); + } +} + +template FullO3CPU::TickEvent::TickEvent(FullO3CPU *c) : Event(CPU_Tick_Pri), cpu(c) { @@ -95,6 +115,97 @@ return "FullO3CPU tick"; } +// ProcessCacheCompletionEvent +template +FullO3CPU::ProcessCacheCompletionEvent::ProcessCacheCompletionEvent( + FullO3CPU *c) + : cpu(c) +{ +} + +template +void +FullO3CPU::ProcessCacheCompletionEvent::process() +{ + (*(cpu->getInstPort()))->endResp(pkt); + cpu->fetch.processCacheCompletion(pkt); +} + +template +const char * +FullO3CPU::ProcessCacheCompletionEvent::description() const +{ + return "processCacheCompletionEvent"; +} + +// ProcessCompleteDataAccessEvent +template +FullO3CPU::ProcessCompleteDataAccessEvent::ProcessCompleteDataAccessEvent( + FullO3CPU *c) + : cpu(c) +{ +} + +template +void +FullO3CPU::ProcessCompleteDataAccessEvent::process() +{ + (*(cpu->getDataPort()))->endResp(pkt); + cpu->iew.ldstQueue.completeDataAccess(pkt->req->threadId(), pkt); +} + +template +const char * +FullO3CPU::ProcessCompleteDataAccessEvent::description() const +{ + return "processCompleteDataAccessEvent"; +} + +// ProcessEndReqISideEvent +template +FullO3CPU::ProcessEndReqISideEvent::ProcessEndReqISideEvent( + FullO3CPU *c) + : cpu(c) +{ +} + +template +void +FullO3CPU::ProcessEndReqISideEvent::process() +{ + cpu->fetch.processEndReq(pkt); +} + +template +const char * +FullO3CPU::ProcessEndReqISideEvent::description() const +{ + return "processEndReqISideEvent"; +} + +// ProcessEndReqDSideEvent +template +FullO3CPU::ProcessEndReqDSideEvent::ProcessEndReqDSideEvent( + FullO3CPU *c) + : cpu(c) +{ +} + +template +void +FullO3CPU::ProcessEndReqDSideEvent::process() +{ + cpu->iew.ldstQueue.processEndReq(); +} + +template +const char * +FullO3CPU::ProcessEndReqDSideEvent::description() const +{ + return "processEndReqDSideEvent"; +} + +// ActivateThreadEvent template FullO3CPU::ActivateThreadEvent::ActivateThreadEvent() : Event(CPU_Switch_Pri) @@ -162,6 +273,11 @@ itb(params->itb), dtb(params->dtb), tickEvent(this), + processCacheCompletionEvent(this), + processCompleteDataAccessEvent(this), + processEndReqISideEvent(this), + processEndReqDSideEvent(this), + #ifndef NDEBUG instcount(0), #endif @@ -212,18 +328,6 @@ _status = Idle; } -#if USE_CHECKER - if (params->checker) { - BaseCPU *temp_checker = params->checker; - checker = dynamic_cast *>(temp_checker); -#if FULL_SYSTEM - checker->setSystem(params->system); -#endif - } else { - checker = NULL; - } -#endif // USE_CHECKER - #if !FULL_SYSTEM thread.resize(numThreads); tids.resize(numThreads); @@ -385,15 +489,6 @@ tc = o3_tc; - // If we're using a checker, then the TC should be the - // CheckerThreadContext. -#if USE_CHECKER - if (params->checker) { - tc = new CheckerThreadContext >( - o3_tc, this->checker); - } -#endif - o3_tc->cpu = (typename Impl::O3CPU *)(this); assert(o3_tc->cpu); o3_tc->thread = this->thread[tid]; @@ -516,18 +611,6 @@ } template -Port * -FullO3CPU::getPort(const std::string &if_name, int idx) -{ - if (if_name == "dcache_port") - return iew.getDcachePort(); - else if (if_name == "icache_port") - return fetch.getIcachePort(); - else - panic("No Such Port\n"); -} - -template void FullO3CPU::tick() { @@ -598,11 +681,18 @@ for (ThreadID tid = 0; tid < numThreads; ++tid) thread[tid]->inSyscall = true; + fetch.setIcache(); + #if FULL_SYSTEM for (ThreadID tid = 0; tid < numThreads; tid++) { ThreadContext *src_tc = threadContexts[tid]; TheISA::initCPU(src_tc, src_tc->contextId()); } + + // Update the ThreadContext's memory ports (Functional/Virtual + // Ports) + iew.ldstQueue.updateMemPorts(); + #endif // Clear inSyscall. @@ -693,6 +783,15 @@ } template +void +FullO3CPU::wakeFromQuiesce(ThreadID tid) +{ + activityRec.activity(); + fetch.wakeFromQuiesce(); + _status = Running; +} + +template bool FullO3CPU::deallocateContext(ThreadID tid, bool remove, int delay) { @@ -809,7 +908,7 @@ // Squash Throughout Pipeline InstSeqNum squash_seq_num = commit.rob->readHeadInst(tid)->seqNum; - fetch.squash(0, squash_seq_num, tid); + //fetch.squash(0, squash_seq_num, NULL, tid); decode.squash(tid); rename.squash(squash_seq_num, tid); iew.squash(tid); @@ -1143,10 +1242,6 @@ } _status = SwitchedOut; -#if USE_CHECKER - if (checker) - checker->switchOut(); -#endif if (tickEvent.scheduled()) tickEvent.squash(); } @@ -1166,7 +1261,7 @@ activityRec.reset(); - BaseCPU::takeOverFrom(oldCPU, fetch.getIcachePort(), iew.getDcachePort()); + BaseCPU::takeOverFrom(oldCPU, &instPort, &dataPort); fetch.takeOverFrom(); decode.takeOverFrom(); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/cpu_builder.cc --- a/src/cpu/o3/cpu_builder.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/cpu_builder.cc Fri Aug 05 18:14:06 2011 +0100 @@ -31,7 +31,6 @@ #include #include "config/full_system.hh" -#include "config/use_checker.hh" #include "cpu/o3/cpu.hh" #include "cpu/o3/impl.hh" #include "params/DerivO3CPU.hh" diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/dyn_inst.hh --- a/src/cpu/o3/dyn_inst.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/dyn_inst.hh Fri Aug 05 18:14:06 2011 +0100 @@ -105,7 +105,7 @@ Fault initiateAcc(); /** Completes the access. Only valid for memory operations. */ - Fault completeAcc(PacketPtr pkt); + Fault completeAcc(Packet* pkt); private: /** Initializes variables. */ @@ -254,18 +254,18 @@ BaseDynInst::setFloatRegOperandBits(si, idx, val); } -#if THE_ISA == MIPS_ISA - uint64_t readRegOtherThread(int misc_reg) - { - panic("MIPS MT not defined for O3 CPU.\n"); - return 0; - } - - void setRegOtherThread(int misc_reg, const TheISA::MiscReg &val) - { - panic("MIPS MT not defined for O3 CPU.\n"); - } -#endif +//#if THE_ISA == MIPS_ISA +// uint64_t readRegOtherThread(int misc_reg) +// { +// panic("MIPS MT not defined for O3 CPU.\n"); +// return 0; +// } +// +// void setRegOtherThread(int misc_reg, const TheISA::MiscReg &val) +// { +// panic("MIPS MT not defined for O3 CPU.\n"); +// } +//#endif public: /** Calculates EA part of a memory instruction. Currently unused, diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/dyn_inst_impl.hh --- a/src/cpu/o3/dyn_inst_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/dyn_inst_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -125,7 +125,7 @@ template Fault -BaseO3DynInst::completeAcc(PacketPtr pkt) +BaseO3DynInst::completeAcc(Packet* pkt) { // @todo: Pretty convoluted way to avoid squashing from happening // when using the TC during an instruction's execution diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/fetch.hh --- a/src/cpu/o3/fetch.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/fetch.hh Fri Aug 05 18:14:06 2011 +0100 @@ -84,48 +84,6 @@ 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 *fetch; - - public: - /** Default constructor. */ - IcachePort(DefaultFetch *_fetch) - : Port(_fetch->name() + "-iport", _fetch->cpu), fetch(_fetch) - { } - - bool snoopRangeSent; - - virtual void setPeer(Port *port); - - 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, - bool &snoop) - { resp.clear(); snoop = true; } - - /** 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(); - }; - class FetchTranslation : public BaseTLB::Translation { protected: @@ -170,8 +128,8 @@ QuiescePending, SwitchOut, ItlbWait, + IcacheWaitEndReq, IcacheWaitResponse, - IcacheWaitRetry, IcacheAccessComplete }; @@ -207,9 +165,6 @@ /** Registers statistics. */ void regStats(); - /** Returns the icache port. */ - Port *getIcachePort() { return icachePort; } - /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer *time_buffer); @@ -226,7 +181,10 @@ void setIcache(); /** Processes cache completion event. */ - void processCacheCompletion(PacketPtr pkt); + void processCacheCompletion(Packet* pkt); + + /** Process endReq */ + void processEndReq(MemMapPacketPtr resp_pkt); /** Begins the drain of the fetch stage. */ bool drain(); @@ -312,8 +270,8 @@ * remove any instructions that are not in the ROB. The source of this * squash should be the commit stage. */ - void squash(const TheISA::PCState &newPC, - const InstSeqNum &seq_num, ThreadID tid); + void squash(const TheISA::PCState &newPC, const InstSeqNum &seq_num, + DynInstPtr &squashInst, ThreadID tid); /** Ticks the fetch stage, processing all inputs signals and fetching * as many instructions as possible. @@ -338,14 +296,16 @@ return (addr & ~(cacheBlkMask)); } + + /** Handles retrying the fetch access. */ + void recvRetry(); + + private: DynInstPtr buildInst(ThreadID tid, StaticInstPtr staticInst, StaticInstPtr curMacroop, TheISA::PCState thisPC, TheISA::PCState nextPC, bool trace); - /** Handles retrying the fetch access. */ - void recvRetry(); - /** Returns the appropriate thread to fetch, given the fetch policy. */ ThreadID getFetchingThread(FetchPriority &fetch_priority); @@ -388,9 +348,6 @@ /** Wire used to write any information heading to decode. */ typename TimeBuffer::wire toDecode; - /** Icache interface. */ - IcachePort *icachePort; - /** BPredUnit. */ BPredUnit branchPred; @@ -444,7 +401,7 @@ bool cacheBlocked; /** The packet that is waiting to be retried. */ - PacketPtr retryPkt; + MemMapPacketPtr retryPkt; /** The thread that is waiting on the cache to tell fetch to retry. */ ThreadID retryTid; @@ -522,6 +479,10 @@ * due to a squash. */ Stats::Scalar fetchIcacheSquashes; + /** Total number of outstanding tlb accesses that were dropped + * due to a squash. + */ + Stats::Scalar fetchTlbSquashes; /** Distribution of number of instructions fetched each cycle. */ Stats::Distribution fetchNisnDist; /** Rate of how often fetch was idle. */ diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/fetch_impl.hh --- a/src/cpu/o3/fetch_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/fetch_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -48,8 +48,6 @@ #include "arch/utility.hh" #include "base/types.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" -#include "cpu/checker/cpu.hh" #include "cpu/exetrace.hh" #include "cpu/o3/fetch.hh" #include "mem/packet.hh" @@ -67,67 +65,9 @@ using namespace std; template -void -DefaultFetch::IcachePort::setPeer(Port *port) -{ - Port::setPeer(port); - - fetch->setIcache(); -} - -template -Tick -DefaultFetch::IcachePort::recvAtomic(PacketPtr pkt) -{ - panic("DefaultFetch doesn't expect recvAtomic callback!"); - return curTick(); -} - -template -void -DefaultFetch::IcachePort::recvFunctional(PacketPtr pkt) -{ - DPRINTF(Fetch, "DefaultFetch doesn't update its state from a " - "functional call."); -} - -template -void -DefaultFetch::IcachePort::recvStatusChange(Status status) -{ - if (status == RangeChange) { - if (!snoopRangeSent) { - snoopRangeSent = true; - sendStatusChange(Port::RangeChange); - } - return; - } - - panic("DefaultFetch doesn't expect recvStatusChange callback!"); -} - -template -bool -DefaultFetch::IcachePort::recvTiming(PacketPtr pkt) -{ - DPRINTF(Fetch, "Received timing\n"); - if (pkt->isResponse()) { - fetch->processCacheCompletion(pkt); - } - //else Snooped a coherence request, just return - return true; -} - -template -void -DefaultFetch::IcachePort::recvRetry() -{ - fetch->recvRetry(); -} - -template DefaultFetch::DefaultFetch(O3CPU *_cpu, DerivO3CPUParams *params) : cpu(_cpu), + branchPred(params), predecoder(NULL), decodeToFetchDelay(params->decodeToFetchDelay), @@ -182,17 +122,6 @@ // Get the size of an instruction. instSize = sizeof(TheISA::MachInst); - - // Name is finally available, so create the port. - icachePort = new IcachePort(this); - - icachePort->snoopRangeSent = false; - -#if USE_CHECKER - if (cpu->checker) { - cpu->checker->setIcachePort(icachePort); - } -#endif } template @@ -268,6 +197,11 @@ .desc("Number of outstanding Icache misses that were squashed") .prereq(fetchIcacheSquashes); + fetchTlbSquashes + .name(name() + ".ItlbSquashes") + .desc("Number of outstanding ITLB misses that were squashed") + .prereq(fetchTlbSquashes); + fetchNisnDist .init(/* base value */ 0, /* last value */ fetchWidth, @@ -365,7 +299,7 @@ DefaultFetch::setIcache() { // Size of cache block. - cacheBlkSize = icachePort->peerBlockSize(); + cacheBlkSize = cpu->getInstBlockSize(); // Create mask to get rid of offset bits. cacheBlkMask = (cacheBlkSize - 1); @@ -380,14 +314,12 @@ template void -DefaultFetch::processCacheCompletion(PacketPtr pkt) +DefaultFetch::processCacheCompletion(Packet* pkt) { ThreadID tid = pkt->req->threadId(); DPRINTF(Fetch, "[tid:%u] Waking up from cache miss.\n", tid); - assert(!pkt->wasNacked()); - // Only change the status if it's still waiting on the icache access // to return. if (fetchStatus[tid] != IcacheWaitResponse || @@ -580,6 +512,9 @@ // Align the fetch address so it's at the start of a cache block. Addr block_PC = icacheBlockAlignPC(vaddr); + DPRINTF(Fetch, "[tid:%i] Fetching cache line %#x for addr %#x\n", + tid, block_PC, vaddr); + // Setup the memReq to do a read of the first instruction's address. // Set the appropriate read size and flags as well. // Build request here. @@ -607,11 +542,20 @@ // Wake up CPU if it was idle cpu->wakeCPU(); + if (fetchStatus[tid] != ItlbWait || mem_req != memReq[tid] || + mem_req->getVaddr() != memReq[tid]->getVaddr() || isSwitchedOut()) { + DPRINTF(Fetch, "[tid:%i] Ignoring itlb completed after squash\n", + tid); + ++fetchTlbSquashes; + delete mem_req; + return; + } + + // If translation was successful, attempt to read the icache block. if (fault == NoFault) { // Build packet here. - PacketPtr data_pkt = new Packet(mem_req, - MemCmd::ReadReq, Packet::Broadcast); + MemMapPacketPtr data_pkt = new MemMapPacket(mem_req, MemCmd::ReadReq); data_pkt->dataDynamicArray(new uint8_t[cacheBlkSize]); cacheDataPC[tid] = block_PC; @@ -621,16 +565,17 @@ fetchedCacheLines++; // Access the cache. - if (!icachePort->sendTiming(data_pkt)) { + if ((*cpu->getInstPort())->reqLinkState() == PortInterface::BUSY) { assert(retryPkt == NULL); assert(retryTid == InvalidThreadID); DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid); - fetchStatus[tid] = IcacheWaitRetry; + fetchStatus[tid] = IcacheWaitEndReq; retryPkt = data_pkt; retryTid = tid; cacheBlocked = true; } else { + (*cpu->getInstPort())->beginReq(data_pkt); DPRINTF(Fetch, "[tid:%i]: Doing Icache access.\n", tid); DPRINTF(Activity, "[tid:%i]: Activity: Waiting on I-cache " "response.\n", tid); @@ -638,7 +583,11 @@ lastIcacheStall[tid] = curTick(); fetchStatus[tid] = IcacheWaitResponse; } + + } else { + DPRINTF(Fetch, "[tid:%i] Got back req with addr %#x but expected %#x\n", + mem_req->getVaddr(), memReq[tid]->getVaddr()); // Translation faulted, icache request won't be sent. delete mem_req; memReq[tid] = NULL; @@ -648,6 +597,7 @@ // wake up is if a squash comes along and changes the PC. TheISA::PCState fetchPC = pc[tid]; + DPRINTF(Fetch, "[tid:%i]: Translation faulted, building noop.\n", tid); // We will use a nop in ordier to carry the fault. DynInstPtr instruction = buildInst(tid, StaticInstPtr(TheISA::NoopMachInst, fetchPC.instAddr()), @@ -669,6 +619,30 @@ _status = updateFetchStatus(); } + +template +inline void +DefaultFetch::processEndReq(MemMapPacketPtr resp_pkt) +{ + if (retryPkt != NULL) { + assert(cacheBlocked); + assert(retryTid != InvalidThreadID); + assert(fetchStatus[retryTid] == IcacheWaitEndReq); + + (*cpu->getInstPort())->beginReq(retryPkt); + + fetchStatus[retryTid] = IcacheWaitResponse; + retryPkt = NULL; + retryTid = InvalidThreadID; + cacheBlocked = false; + } else { + assert(retryTid == InvalidThreadID); + // Access has been squashed since it was sent out. Just clear + // the cache being blocked. + cacheBlocked = false; + } +} + template inline void DefaultFetch::doSquash(const TheISA::PCState &newPC, ThreadID tid) @@ -686,6 +660,10 @@ DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n", tid); memReq[tid] = NULL; + } else if (fetchStatus[tid] == ItlbWait) { + DPRINTF(Fetch, "[tid:%i]: Squashing outstanding ITLB miss.\n", + tid); + memReq[tid] = NULL; } // Get rid of the retrying packet if it was from this thread. @@ -787,11 +765,14 @@ template void DefaultFetch::squash(const TheISA::PCState &newPC, - const InstSeqNum &seq_num, ThreadID tid) + const InstSeqNum &seq_num, DynInstPtr &squashInst, + ThreadID tid) { DPRINTF(Fetch, "[tid:%u]: Squash from commit.\n", tid); doSquash(newPC, tid); + if (squashInst) + predecoder.reset(squashInst->staticInst->machInst); // Tell the CPU to remove any instructions that are not in the ROB. cpu->removeInstsNotInROB(tid); @@ -906,7 +887,7 @@ // In any case, squash. squash(fromCommit->commitInfo[tid].pc, fromCommit->commitInfo[tid].doneSeqNum, - tid); + fromCommit->commitInfo[tid].squashInst, tid); // If it was a branch mispredict on a control instruction, update the // branch predictor with that instruction, otherwise just kill the @@ -972,8 +953,8 @@ } if (checkStall(tid) && - fetchStatus[tid] != IcacheWaitResponse && - fetchStatus[tid] != IcacheWaitRetry) { + fetchStatus[tid] != IcacheWaitEndReq && + fetchStatus[tid] != IcacheWaitResponse) { DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid); fetchStatus[tid] = Blocked; @@ -1118,8 +1099,12 @@ DPRINTF(Fetch, "[tid:%i]: Fetch is waiting ITLB walk to " "finish! \n", tid); ++fetchTlbCycles; + } else if (fetchStatus[tid] == TrapPending) { + DPRINTF(Fetch, "[tid:%i]: Fetch is waiting for a pending trap\n", + tid); } + // Status is Idle, Squashing, Blocked, ItlbWait or IcacheWaitResponse // so fetch should do nothing. return; @@ -1262,29 +1247,6 @@ pc[tid] = thisPC; } -template -void -DefaultFetch::recvRetry() -{ - if (retryPkt != NULL) { - assert(cacheBlocked); - assert(retryTid != InvalidThreadID); - assert(fetchStatus[retryTid] == IcacheWaitRetry); - - if (icachePort->sendTiming(retryPkt)) { - fetchStatus[retryTid] = IcacheWaitResponse; - retryPkt = NULL; - retryTid = InvalidThreadID; - cacheBlocked = false; - } - } else { - assert(retryTid == InvalidThreadID); - // Access has been squashed since it was sent out. Just clear - // the cache being blocked. - cacheBlocked = false; - } -} - /////////////////////////////////////// // // // SMT FETCH POLICY MAINTAINED HERE // diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/iew.hh --- a/src/cpu/o3/iew.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/iew.hh Fri Aug 05 18:14:06 2011 +0100 @@ -137,9 +137,6 @@ /** Initializes stage; sends back the number of free IQ and LSQ entries. */ void initStage(); - /** Returns the dcache port. */ - Port *getDcachePort() { return ldstQueue.getDcachePort(); } - /** Sets main time buffer used for backwards communication. */ void setTimeBuffer(TimeBuffer *tb_ptr); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/iew_impl.hh --- a/src/cpu/o3/iew_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/iew_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -1253,7 +1253,7 @@ } if (inst->isDataPrefetch() || inst->isInstPrefetch()) { - fault = NoFault; + inst->fault = NoFault; } } else if (inst->isStore()) { fault = ldstQueue.executeStore(inst); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/inst_queue_impl.hh --- a/src/cpu/o3/inst_queue_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/inst_queue_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -880,7 +880,7 @@ iqInstsIssued+= total_issued; // If we issued any instructions, tell the CPU we had activity. - if (total_issued || total_deferred_mem_issued) { + if (total_issued || total_deferred_mem_issued || deferredMemInsts.size()) { cpu->activityThisCycle(); } else { DPRINTF(IQ, "Not able to schedule any instructions.\n"); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/lsq.hh --- a/src/cpu/o3/lsq.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/lsq.hh Fri Aug 05 18:14:06 2011 +0100 @@ -66,13 +66,6 @@ /** Registers statistics of each LSQ unit. */ void regStats(); - /** Returns dcache port. - * @todo: Dcache port needs to be moved up to this level for SMT - * to work. For now it just returns the port from one of the - * threads. - */ - Port *getDcachePort() { return &dcachePort; } - /** Sets the pointer to the list of active threads. */ void setActiveThreads(std::list *at_ptr); /** Switches out the LSQ. */ @@ -176,6 +169,11 @@ return thread[tid].getStoreHeadSeqNum(); } + void completeDataAccess(ThreadID tid, MemMapPacketPtr pkt) + { + thread[tid].completeDataAccess(pkt); + } + /** Returns the number of instructions in all of the queues. */ int getCount(); /** Returns the number of instructions in the queues of one thread. */ @@ -259,6 +257,34 @@ bool cacheBlocked() { return retryTid != InvalidThreadID; } + void processEndReq() + { + if (!cacheBlocked()) + { + //Squashed, so drop it + return; + } + int curr_retry_tid = retryTid; + // Speculatively clear the retry Tid. This will get set again if + // the LSQUnit was unable to complete its access. + retryTid = -1; + thread[curr_retry_tid].processEndReq(); + } + + void recvRetry() + { + if (!cacheBlocked()) + { + //Squashed, so drop it + return; + } + int curr_retry_tid = retryTid; + // Speculatively clear the retry Tid. This will get set again if + // the LSQUnit was unable to complete its access. + retryTid = -1; + thread[curr_retry_tid].recvRetry(); + } + /** Sets the retry thread id, indicating that one of the LSQUnits * tried to access the cache but the cache was blocked. */ void setRetryTid(ThreadID tid) @@ -288,52 +314,6 @@ /** The IEW stage pointer. */ IEW *iewStage; - /** DcachePort class for this LSQ. Handles doing the - * communication with the cache/memory. - */ - class DcachePort : public Port - { - protected: - /** Pointer to LSQ. */ - LSQ *lsq; - - public: - /** Default constructor. */ - DcachePort(LSQ *_lsq) - : Port(_lsq->name() + "-dport", _lsq->cpu), lsq(_lsq) - { } - - bool snoopRangeSent; - - virtual void setPeer(Port *port); - - 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, - bool &snoop) - { resp.clear(); snoop = true; } - - /** 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(); - }; - - /** D-cache port. */ - DcachePort dcachePort; - #if FULL_SYSTEM /** Tell the CPU to update the Phys and Virt ports. */ void updateMemPorts() { cpu->updateMemPorts(); } diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/lsq_impl.hh --- a/src/cpu/o3/lsq_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/lsq_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -37,92 +37,14 @@ using namespace std; -template -void -LSQ::DcachePort::setPeer(Port *port) -{ - Port::setPeer(port); - -#if FULL_SYSTEM - // Update the ThreadContext's memory ports (Functional/Virtual - // Ports) - lsq->updateMemPorts(); -#endif -} - -template -Tick -LSQ::DcachePort::recvAtomic(PacketPtr pkt) -{ - panic("O3CPU model does not work with atomic mode!"); - return curTick(); -} - -template -void -LSQ::DcachePort::recvFunctional(PacketPtr pkt) -{ - DPRINTF(LSQ, "LSQ doesn't update things on a recvFunctional."); -} - -template -void -LSQ::DcachePort::recvStatusChange(Status status) -{ - if (status == RangeChange) { - if (!snoopRangeSent) { - snoopRangeSent = true; - sendStatusChange(Port::RangeChange); - } - return; - } - panic("O3CPU doesn't expect recvStatusChange callback!"); -} - -template -bool -LSQ::DcachePort::recvTiming(PacketPtr pkt) -{ - if (pkt->isError()) - DPRINTF(LSQ, "Got error packet back for address: %#X\n", pkt->getAddr()); - if (pkt->isResponse()) { - lsq->thread[pkt->req->threadId()].completeDataAccess(pkt); - } - else { - // must be a snoop - - // @TODO someday may need to process invalidations in LSQ here - // to provide stronger consistency model - } - return true; -} - -template -void -LSQ::DcachePort::recvRetry() -{ - if (lsq->retryTid == -1) - { - //Squashed, so drop it - return; - } - int curr_retry_tid = lsq->retryTid; - // Speculatively clear the retry Tid. This will get set again if - // the LSQUnit was unable to complete its access. - lsq->retryTid = -1; - lsq->thread[curr_retry_tid].recvRetry(); -} - template LSQ::LSQ(O3CPU *cpu_ptr, IEW *iew_ptr, DerivO3CPUParams *params) - : cpu(cpu_ptr), iewStage(iew_ptr), dcachePort(this), + : cpu(cpu_ptr), iewStage(iew_ptr), LQEntries(params->LQEntries), SQEntries(params->SQEntries), numThreads(params->numThreads), - retryTid(-1) + retryTid(InvalidThreadID) { - dcachePort.snoopRangeSent = false; - //**********************************************/ //************ Handle SMT Parameters ***********/ //**********************************************/ @@ -174,11 +96,10 @@ for (ThreadID tid = 0; tid < numThreads; tid++) { thread[tid].init(cpu, iew_ptr, params, this, maxLQEntries, maxSQEntries, tid); - thread[tid].setDcachePort(&dcachePort); + thread[tid].setDcachePort(cpu->getDataPort()); } } - template std::string LSQ::name() const diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/lsq_unit.hh --- a/src/cpu/o3/lsq_unit.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/lsq_unit.hh Fri Aug 05 18:14:06 2011 +0100 @@ -86,7 +86,7 @@ void regStats(); /** Sets the pointer to the dcache port. */ - void setDcachePort(Port *dcache_port); + void setDcachePort(MemMapMasterPort *dcache_port); /** Switches out LSQ unit. */ void switchOut(); @@ -131,7 +131,7 @@ /** Completes the data access that has been returned from the * memory system. */ - void completeDataAccess(PacketPtr pkt); + void completeDataAccess(MemMapPacketPtr pkt); /** Clears all the entries in the LQ. */ void clearLQ(); @@ -207,24 +207,27 @@ !storeQueue[storeWBIdx].completed && !isStoreBlocked; } + /** Handles endReq */ + void processEndReq(); + /** Handles doing the retry. */ void recvRetry(); private: /** Writes back the instruction, sending it to IEW. */ - void writeback(DynInstPtr &inst, PacketPtr pkt); + void writeback(DynInstPtr &inst, MemMapPacketPtr pkt); /** Writes back a store that couldn't be completed the previous cycle. */ void writebackPendingStore(); /** Handles completing the send of a store to memory. */ - void storePostSend(PacketPtr pkt); + void storePostSend(MemMapPacketPtr pkt); /** Completes the store at the specified index. */ void completeStore(int store_idx); /** Attempts to send a store to the cache. */ - bool sendStore(PacketPtr data_pkt); + bool sendStore(MemMapPacketPtr data_pkt); /** Increments the given store index (circular queue). */ inline void incrStIdx(int &store_idx); @@ -250,7 +253,7 @@ LSQ *lsq; /** Pointer to the dcache port. Used only for sending. */ - Port *dcachePort; + MemMapMasterPort *dcachePort; /** Derived class to hold any sender state the LSQ needs. */ class LSQSenderState : public Packet::SenderState, public FastAlloc @@ -277,9 +280,9 @@ /** Number of outstanding packets to complete. */ int outstanding; /** The main packet from a split load, used during writeback. */ - PacketPtr mainPkt; + MemMapPacketPtr mainPkt; /** A second packet from a split store that needs sending. */ - PacketPtr pendingPacket; + MemMapPacketPtr pendingPacket; /** Completes a packet and returns whether the access is finished. */ inline bool complete() { return --outstanding == 0; } @@ -289,7 +292,8 @@ class WritebackEvent : public Event { public: /** Constructs a writeback event. */ - WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, LSQUnit *lsq_ptr); + WritebackEvent(DynInstPtr &_inst, MemMapPacketPtr pkt, + LSQUnit *lsq_ptr); /** Processes the writeback event. */ void process(); @@ -302,7 +306,7 @@ DynInstPtr inst; /** The packet that would have been sent to memory. */ - PacketPtr pkt; + MemMapPacketPtr pkt; /** The pointer to the LSQ unit that issued the store. */ LSQUnit *lsqPtr; @@ -412,7 +416,7 @@ int stallingLoadIdx; /** The packet that needs to be retried. */ - PacketPtr retryPkt; + MemMapPacketPtr retryPkt; /** Whehter or not a store is blocked due to the memory system. */ bool isStoreBlocked; @@ -434,7 +438,7 @@ bool hasPendingPkt; /** The packet that is pending free cache ports. */ - PacketPtr pendingPkt; + MemMapPacketPtr pendingPkt; // 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 @@ -615,8 +619,7 @@ "addr %#x, data %#x\n", store_idx, req->getVaddr(), data); - PacketPtr data_pkt = new Packet(req, MemCmd::ReadReq, - Packet::Broadcast); + MemMapPacketPtr data_pkt = new MemMapPacket(req, MemCmd::ReadReq); data_pkt->dataStatic(load_inst->memData); WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, this); @@ -698,9 +701,9 @@ if (!lsq->cacheBlocked()) { MemCmd command = req->isLLSC() ? MemCmd::LoadLockedReq : MemCmd::ReadReq; - PacketPtr data_pkt = new Packet(req, command, Packet::Broadcast); - PacketPtr fst_data_pkt = NULL; - PacketPtr snd_data_pkt = NULL; + MemMapPacketPtr data_pkt = new MemMapPacket(req, command); + MemMapPacketPtr fst_data_pkt = NULL; + MemMapPacketPtr snd_data_pkt = NULL; data_pkt->dataStatic(load_inst->memData); @@ -717,8 +720,8 @@ } else { // Create the split packets. - fst_data_pkt = new Packet(sreqLow, command, Packet::Broadcast); - snd_data_pkt = new Packet(sreqHigh, command, Packet::Broadcast); + fst_data_pkt = new MemMapPacket(sreqLow, command); + snd_data_pkt = new MemMapPacket(sreqHigh, command); fst_data_pkt->dataStatic(load_inst->memData); snd_data_pkt->dataStatic(load_inst->memData + sreqLow->getSize()); @@ -731,7 +734,7 @@ state->mainPkt = data_pkt; } - if (!dcachePort->sendTiming(fst_data_pkt)) { + if ((*dcachePort)->reqLinkState() == PortInterface::BUSY) { // Delete state and data packet because a load retry // initiates a pipeline restart; it does not retry. delete state; @@ -751,29 +754,34 @@ // If the access didn't succeed, tell the LSQ by setting // the retry thread id. lsq->setRetryTid(lsqID); - } else if (TheISA::HasUnalignedMemAcc && sreqLow) { - completedFirst = true; + } else { + (*dcachePort)->beginReq(fst_data_pkt); + if (TheISA::HasUnalignedMemAcc && sreqLow) { + panic("2 simultaneous requests are not supported yet!\n"); + completedFirst = true; - // The first packet was sent without problems, so send this one - // too. If there is a problem with this packet then the whole - // load will be squashed, so indicate this to the state object. - // The first packet will return in completeDataAccess and be - // handled there. - ++usedPorts; - if (!dcachePort->sendTiming(snd_data_pkt)) { + // The first packet was sent without problems, so send this one + // too. If there is a problem with this packet then the whole + // load will be squashed, so indicate this to the state object. + // The first packet will return in completeDataAccess and be + // handled there. + ++usedPorts; + if (!(*dcachePort)->sendTimingReq(snd_data_pkt)) { - // The main packet will be deleted in completeDataAccess. - delete snd_data_pkt->req; - delete snd_data_pkt; + // The main packet will be deleted in completeDataAccess. + delete snd_data_pkt->req; + delete snd_data_pkt; - state->complete(); + state->complete(); - req = NULL; - sreqHigh = NULL; + req = NULL; + sreqHigh = NULL; - lsq->setRetryTid(lsqID); + lsq->setRetryTid(lsqID); + } } } + } // If the cache was blocked, or has become blocked due to the access, diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/lsq_unit_impl.hh --- a/src/cpu/o3/lsq_unit_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/lsq_unit_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -43,19 +43,15 @@ #include "arch/locked_mem.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" #include "cpu/o3/lsq.hh" #include "cpu/o3/lsq_unit.hh" #include "base/str.hh" #include "mem/packet.hh" #include "mem/request.hh" -#if USE_CHECKER -#include "cpu/checker/cpu.hh" -#endif - template -LSQUnit::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt, +LSQUnit::WritebackEvent::WritebackEvent(DynInstPtr &_inst, + MemMapPacketPtr _pkt, LSQUnit *lsq_ptr) : inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr) { @@ -86,7 +82,7 @@ template void -LSQUnit::completeDataAccess(PacketPtr pkt) +LSQUnit::completeDataAccess(MemMapPacketPtr pkt) { LSQSenderState *state = dynamic_cast(pkt->senderState); DynInstPtr inst = state->inst; @@ -95,8 +91,6 @@ //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); - assert(!pkt->wasNacked()); - // If this is a split access, wait until all packets are received. if (TheISA::HasUnalignedMemAcc && !state->complete()) { delete pkt->req; @@ -233,15 +227,9 @@ template void -LSQUnit::setDcachePort(Port *dcache_port) +LSQUnit::setDcachePort(MemMapMasterPort* dcache_port) { dcachePort = dcache_port; - -#if USE_CHECKER - if (cpu->checker) { - cpu->checker->setDcachePort(dcachePort); - } -#endif } template @@ -738,8 +726,8 @@ MemCmd command = req->isSwap() ? MemCmd::SwapReq : (req->isLLSC() ? MemCmd::StoreCondReq : MemCmd::WriteReq); - PacketPtr data_pkt; - PacketPtr snd_data_pkt = NULL; + MemMapPacketPtr data_pkt; + MemMapPacketPtr snd_data_pkt = NULL; LSQSenderState *state = new LSQSenderState; state->isLoad = false; @@ -749,7 +737,7 @@ if (!TheISA::HasUnalignedMemAcc || !storeQueue[storeWBIdx].isSplit) { // Build a single data packet if the store isn't split. - data_pkt = new Packet(req, command, Packet::Broadcast); + data_pkt = new MemMapPacket(req, command); data_pkt->dataStatic(inst->memData); data_pkt->senderState = state; } else { @@ -757,8 +745,8 @@ RequestPtr sreqHigh = storeQueue[storeWBIdx].sreqHigh; // Create two packets if the store is split in two. - data_pkt = new Packet(sreqLow, command, Packet::Broadcast); - snd_data_pkt = new Packet(sreqHigh, command, Packet::Broadcast); + data_pkt = new MemMapPacket(sreqLow, command); + snd_data_pkt = new MemMapPacket(sreqHigh, command); data_pkt->dataStatic(inst->memData); snd_data_pkt->dataStatic(inst->memData + sreqLow->getSize()); @@ -967,7 +955,7 @@ template void -LSQUnit::storePostSend(PacketPtr pkt) +LSQUnit::storePostSend(MemMapPacketPtr pkt) { if (isStalled() && storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { @@ -984,11 +972,6 @@ // only works so long as the checker doesn't try to // verify the value in memory for stores. storeQueue[storeWBIdx].inst->setCompleted(); -#if USE_CHECKER - if (cpu->checker) { - cpu->checker->verify(storeQueue[storeWBIdx].inst); - } -#endif } incrStIdx(storeWBIdx); @@ -996,7 +979,7 @@ template void -LSQUnit::writeback(DynInstPtr &inst, PacketPtr pkt) +LSQUnit::writeback(DynInstPtr &inst, MemMapPacketPtr pkt) { iewStage->wakeCPU(); @@ -1067,18 +1050,13 @@ // 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 USE_CHECKER - if (cpu->checker) { - cpu->checker->verify(storeQueue[store_idx].inst); - } -#endif } template bool -LSQUnit::sendStore(PacketPtr data_pkt) +LSQUnit::sendStore(MemMapPacketPtr data_pkt) { - if (!dcachePort->sendTiming(data_pkt)) { + if ((*dcachePort)->reqLinkState() == PortInterface::BUSY) { // Need to handle becoming blocked on a store. isStoreBlocked = true; ++lsqCacheBlocked; @@ -1086,24 +1064,65 @@ retryPkt = data_pkt; lsq->setRetryTid(lsqID); return false; + } else { + (*dcachePort)->beginReq(data_pkt); } return true; } template void +LSQUnit::processEndReq() +{ + if (isStoreBlocked) { + DPRINTF(LSQUnit, "Receiving endReq: store blocked\n"); + assert(retryPkt != NULL); + + (*dcachePort)->beginReq(retryPkt); + LSQSenderState *state = + dynamic_cast(retryPkt->senderState); + + // Don't finish the store unless this is the last packet. + if (!TheISA::HasUnalignedMemAcc || !state->pktToSend || + state->pendingPacket == retryPkt) { + state->pktToSend = false; + storePostSend(retryPkt); + } + retryPkt = NULL; + isStoreBlocked = false; + lsq->setRetryTid(InvalidThreadID); + + // Send any outstanding packet. + if (TheISA::HasUnalignedMemAcc && state->pktToSend) { + assert(state->pendingPacket); + if (sendStore(state->pendingPacket)) { + storePostSend(state->pendingPacket); + } + } + } else if (isLoadBlocked) { + DPRINTF(LSQUnit, "Loads squash themselves and all younger insts, " + "no need to send packet.\n"); + } else { + DPRINTF(LSQUnit, "endReq received but LSQ is no longer blocked.\n"); + } +} + +template +void LSQUnit::recvRetry() { - if (isStoreBlocked) { +/* if (isStoreBlocked) { DPRINTF(LSQUnit, "Receiving retry: store blocked\n"); assert(retryPkt != NULL); - if (dcachePort->sendTiming(retryPkt)) { + if ((*dcachePort)->sendTimingReq(retryPkt)) { LSQSenderState *state = dynamic_cast(retryPkt->senderState); // Don't finish the store unless this is the last packet. - if (!TheISA::HasUnalignedMemAcc || !state->pktToSend) { + if (!TheISA::HasUnalignedMemAcc || !state->pktToSend || + state->pendingPacket == retryPkt) { + state->pktToSend = false; storePostSend(retryPkt); } retryPkt = NULL; @@ -1127,7 +1146,7 @@ "no need to resend packet.\n"); } else { DPRINTF(LSQUnit, "Retry received but LSQ is no longer blocked.\n"); - } + }*/ } template diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/mem_dep_unit_impl.hh --- a/src/cpu/o3/mem_dep_unit_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/mem_dep_unit_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -43,7 +43,7 @@ template MemDepUnit::MemDepUnit(DerivO3CPUParams *params) - : _name(params->name + ".memdepunit"), + : _name(params->path + ".memdepunit"), depPred(params->SSITSize, params->LFSTSize), loadBarrier(false), loadBarrierSN(0), storeBarrier(false), storeBarrierSN(0), iqPtr(NULL) { @@ -81,7 +81,7 @@ { DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid); - _name = csprintf("%s.memDep%d", params->name, tid); + _name = csprintf("%s.memDep%d", params->path, tid); id = tid; depPred.init(params->SSITSize, params->LFSTSize); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/thread_context.hh --- a/src/cpu/o3/thread_context.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/thread_context.hh Fri Aug 05 18:14:06 2011 +0100 @@ -40,7 +40,9 @@ class Statistics; }; -class TranslatingPort; +class SETranslatingProxy; +class PortProxy; +class FSTranslatingProxy; /** * Derived ThreadContext class for use with the O3CPU. It @@ -95,13 +97,13 @@ virtual TheISA::Kernel::Statistics *getKernelStats() { return thread->kernelStats; } - virtual FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + virtual PortProxy *getPhysProxy() { return thread->getPhysProxy(); } - virtual VirtualPort *getVirtPort(); + virtual FSTranslatingProxy *getVirtProxy(); virtual void connectMemPorts(ThreadContext *tc) { thread->connectMemPorts(tc); } #else - virtual TranslatingPort *getMemPort() { return thread->getMemPort(); } + virtual SETranslatingProxy *getMemProxy() { return thread->getMemProxy(); } /** Returns a pointer to this thread's process. */ virtual Process *getProcessPtr() { return thread->getProcessPtr(); } @@ -120,6 +122,9 @@ /** Set the status to Suspended. */ virtual void suspend(int delay = 0); + /** Fix to wake up from untaken quiesce instruction. */ + virtual void wakeFromQuiesce(); + /** Set the status to Halted. */ virtual void halt(int delay = 0); diff -r 6548721032fa -r 042f38a2e360 src/cpu/o3/thread_context_impl.hh --- a/src/cpu/o3/thread_context_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/o3/thread_context_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -48,10 +48,10 @@ #if FULL_SYSTEM template -VirtualPort * -O3ThreadContext::getVirtPort() +FSTranslatingProxy * +O3ThreadContext::getVirtProxy() { - return thread->getVirtPort(); + return thread->getVirtProxy(); } template @@ -126,6 +126,15 @@ template void +O3ThreadContext::wakeFromQuiesce() +{ + DPRINTF(O3CPU, "Waking from an untake Quiesce instruction" + " thread context %d\n", threadId()); + cpu->wakeFromQuiesce(thread->threadId()); +} + +template +void O3ThreadContext::suspend(int delay) { DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n", diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/AtomicSimpleCPU.py --- a/src/cpu/simple/AtomicSimpleCPU.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/AtomicSimpleCPU.py Fri Aug 05 18:14:06 2011 +0100 @@ -34,8 +34,3 @@ width = Param.Int(1, "CPU width") simulate_data_stalls = Param.Bool(False, "Simulate dcache stall cycles") simulate_inst_stalls = Param.Bool(False, "Simulate icache stall cycles") - icache_port = Port("Instruction Port") - dcache_port = Port("Data Port") - physmem_port = Port("Physical Memory Port") - _cached_ports = BaseSimpleCPU._cached_ports + \ - ['icache_port', 'dcache_port'] diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/TimingSimpleCPU.py --- a/src/cpu/simple/TimingSimpleCPU.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/TimingSimpleCPU.py Fri Aug 05 18:14:06 2011 +0100 @@ -31,6 +31,3 @@ class TimingSimpleCPU(BaseSimpleCPU): type = 'TimingSimpleCPU' - icache_port = Port("Instruction Port") - dcache_port = Port("Data Port") - _cached_ports = BaseSimpleCPU._cached_ports + ['icache_port', 'dcache_port'] diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/atomic.hh --- a/src/cpu/simple/atomic.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/atomic.hh Fri Aug 05 18:14:06 2011 +0100 @@ -64,50 +64,6 @@ // main simulation loop (one cycle) void tick(); - class CpuPort : public Port - { - public: - - CpuPort(const std::string &_name, AtomicSimpleCPU *_cpu) - : Port(_name, _cpu), cpu(_cpu) - { } - - bool snoopRangeSent; - - protected: - - AtomicSimpleCPU *cpu; - - virtual bool recvTiming(PacketPtr pkt); - - virtual Tick recvAtomic(PacketPtr pkt); - - virtual void recvFunctional(PacketPtr pkt); - - virtual void recvStatusChange(Status status); - - virtual void recvRetry(); - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { resp.clear(); snoop = true; } - - }; - CpuPort icachePort; - - class DcachePort : public CpuPort - { - public: - DcachePort(const std::string &_name, AtomicSimpleCPU *_cpu) - : CpuPort(_name, _cpu) - { } - - virtual void setPeer(Port *port); - }; - DcachePort dcachePort; - - CpuPort physmemPort; - bool hasPhysMemPort; Request ifetch_req; Request data_read_req; Request data_write_req; @@ -119,7 +75,13 @@ public: - virtual Port *getPort(const std::string &if_name, int idx = -1); + virtual void endReq(MemMapPacketPtr req_pkt, int master_id) { + panic("Atomic should never be involved in timing transactions"); + } + + virtual void beginResp(MemMapPacketPtr respPkt, int master_id) { + panic("Atomic should never be involved in timing transactions"); + } virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); @@ -141,12 +103,6 @@ Fault writeBytes(uint8_t *data, unsigned size, Addr addr, unsigned flags, uint64_t *res); - - /** - * Print state of address in memory system via PrintReq (for - * debugging). - */ - void printAddr(Addr a); }; #endif // __CPU_SIMPLE_ATOMIC_HH__ diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/atomic.cc --- a/src/cpu/simple/atomic.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/atomic.cc Fri Aug 05 18:14:06 2011 +0100 @@ -62,21 +62,6 @@ return "AtomicSimpleCPU tick"; } -Port * -AtomicSimpleCPU::getPort(const string &if_name, int idx) -{ - if (if_name == "dcache_port") - return &dcachePort; - else if (if_name == "icache_port") - return &icachePort; - else if (if_name == "physmem_port") { - hasPhysMemPort = true; - return &physmemPort; - } - else - panic("No Such Port\n"); -} - void AtomicSimpleCPU::init() { @@ -89,84 +74,23 @@ // initialize CPU, including PC TheISA::initCPU(tc, tc->contextId()); } + + // Update the ThreadContext's Functional memory port + tcBase()->connectMemPorts(tcBase()); + #endif - if (hasPhysMemPort) { - bool snoop = false; - AddrRangeList pmAddrList; - physmemPort.getPeerAddressRanges(pmAddrList, snoop); - physMemAddr = *pmAddrList.begin(); - } // Atomic doesn't do MT right now, so contextId == threadId ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too } -bool -AtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt) -{ - panic("AtomicSimpleCPU doesn't expect recvTiming callback!"); - return true; -} - -Tick -AtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt) -{ - //Snooping a coherence request, just return - return 0; -} - -void -AtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt) -{ - //No internal storage to update, just return - return; -} - -void -AtomicSimpleCPU::CpuPort::recvStatusChange(Status status) -{ - if (status == RangeChange) { - if (!snoopRangeSent) { - snoopRangeSent = true; - sendStatusChange(Port::RangeChange); - } - return; - } - - panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!"); -} - -void -AtomicSimpleCPU::CpuPort::recvRetry() -{ - panic("AtomicSimpleCPU doesn't expect recvRetry callback!"); -} - -void -AtomicSimpleCPU::DcachePort::setPeer(Port *port) -{ - Port::setPeer(port); - -#if FULL_SYSTEM - // Update the ThreadContext's memory ports (Functional/Virtual - // Ports) - cpu->tcBase()->connectMemPorts(cpu->tcBase()); -#endif -} - AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p) : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false), simulate_data_stalls(p->simulate_data_stalls), - simulate_inst_stalls(p->simulate_inst_stalls), - icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this), - physmemPort(name() + "-iport", this), hasPhysMemPort(false) + simulate_inst_stalls(p->simulate_inst_stalls) { _status = Idle; - - icachePort.snoopRangeSent = false; - dcachePort.snoopRangeSent = false; - } @@ -228,7 +152,7 @@ void AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) { - BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort); + BaseCPU::takeOverFrom(oldCPU, &instPort, &dataPort); assert(!tickEvent.scheduled()); @@ -295,7 +219,6 @@ _status = Idle; } - Fault AtomicSimpleCPU::readBytes(Addr addr, uint8_t * data, unsigned size, unsigned flags) @@ -308,7 +231,7 @@ } //The block size of our peer. - unsigned blockSize = dcachePort.peerBlockSize(); + unsigned blockSize = dataBlockSize; //The size of the data we're trying to read. int fullSize = size; @@ -329,21 +252,18 @@ // Now do the access. if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) { - Packet pkt = Packet(req, - req->isLLSC() ? MemCmd::LoadLockedReq : MemCmd::ReadReq, - Packet::Broadcast); + MemMapPacket pkt(req, + req->isLLSC() ? MemCmd::LoadLockedReq : + MemCmd::ReadReq); pkt.dataStatic(data); if (req->isMmapedIpr()) dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt); else { - if (hasPhysMemPort && pkt.getAddr() == physMemAddr) - dcache_latency += physmemPort.sendAtomic(&pkt); - else - dcache_latency += dcachePort.sendAtomic(&pkt); + dcache_latency += dataPort->sendAtomic(&pkt); } dcache_access = true; - + assert(!pkt.isError()); if (req->isLLSC()) { @@ -462,7 +382,7 @@ } //The block size of our peer. - unsigned blockSize = dcachePort.peerBlockSize(); + unsigned blockSize = dataBlockSize; //The size of the data we're trying to read. int fullSize = size; @@ -498,17 +418,14 @@ } if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) { - Packet pkt = Packet(req, cmd, Packet::Broadcast); + MemMapPacket pkt(req, cmd); pkt.dataStatic(data); if (req->isMmapedIpr()) { dcache_latency += TheISA::handleIprWrite(thread->getTC(), &pkt); } else { - if (hasPhysMemPort && pkt.getAddr() == physMemAddr) - dcache_latency += physmemPort.sendAtomic(&pkt); - else - dcache_latency += dcachePort.sendAtomic(&pkt); + dcache_latency += dataPort->sendAtomic(&pkt); } dcache_access = true; assert(!pkt.isError()); @@ -668,15 +585,11 @@ //if(predecoder.needMoreBytes()) //{ icache_access = true; - Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq, - Packet::Broadcast); + MemMapPacket ifetch_pkt(&ifetch_req, + MemCmd::ReadReq); ifetch_pkt.dataStatic(&inst); - - if (hasPhysMemPort && ifetch_pkt.getAddr() == physMemAddr) - icache_latency = physmemPort.sendAtomic(&ifetch_pkt); - else - icache_latency = icachePort.sendAtomic(&ifetch_pkt); - + icache_latency = instPort->sendAtomic(&ifetch_pkt); + assert(!ifetch_pkt.isError()); // ifetch_req is initialized to read the instruction directly @@ -735,14 +648,6 @@ schedule(tickEvent, curTick() + latency); } - -void -AtomicSimpleCPU::printAddr(Addr a) -{ - dcachePort.printAddr(a); -} - - //////////////////////////////////////////////////////////////////////// // // AtomicSimpleCPU Simulation Object diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/base.hh --- a/src/cpu/simple/base.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/base.hh Fri Aug 05 18:14:06 2011 +0100 @@ -125,13 +125,11 @@ Idle, Running, ITBWaitResponse, - IcacheRetry, + IcacheWaitEndReq, IcacheWaitResponse, - IcacheWaitSwitch, DTBWaitResponse, - DcacheRetry, + DcacheWaitEndReq, DcacheWaitResponse, - DcacheWaitSwitch, SwitchedOut }; diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/timing.hh --- a/src/cpu/simple/timing.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/timing.hh Fri Aug 05 18:14:06 2011 +0100 @@ -64,7 +64,7 @@ { public: int outstanding; - PacketPtr fragments[2]; + MemMapPacketPtr fragments[2]; int getPendingFragment() @@ -82,10 +82,10 @@ class SplitFragmentSenderState : public Packet::SenderState { public: - SplitFragmentSenderState(PacketPtr _bigPkt, int _index) : + SplitFragmentSenderState(MemMapPacketPtr _bigPkt, int _index) : bigPkt(_bigPkt), index(_index) {} - PacketPtr bigPkt; + MemMapPacketPtr bigPkt; int index; void @@ -129,121 +129,54 @@ void translationFault(Fault fault); - void buildPacket(PacketPtr &pkt, RequestPtr req, bool read); - void buildSplitPacket(PacketPtr &pkt1, PacketPtr &pkt2, + void buildPacket(MemMapPacketPtr& pkt, RequestPtr req, bool read); + void buildSplitPacket(MemMapPacketPtr& pkt1, MemMapPacketPtr& pkt2, RequestPtr req1, RequestPtr req2, RequestPtr req, uint8_t *data, bool read); - bool handleReadPacket(PacketPtr pkt); - // This function always implicitly uses dcache_pkt. + bool handleReadPacket(MemMapPacketPtr pkt); + // This function always implicitly uses dataReqPkt. bool handleWritePacket(); - class CpuPort : public Port - { - protected: - TimingSimpleCPU *cpu; - Tick lat; + /** + * The current request packet for the instruction port. + */ + MemMapPacketPtr instReqPkt; - public: + /** + * The current request packet for the data port. + */ + MemMapPacketPtr dataReqPkt; - CpuPort(const std::string &_name, TimingSimpleCPU *_cpu, Tick _lat) - : Port(_name, _cpu), cpu(_cpu), lat(_lat), retryEvent(this) - { } + /** + * The current response packet for the data port. + */ + MemMapPacketPtr dataRespPkt; - bool snoopRangeSent; - - protected: - - virtual Tick recvAtomic(PacketPtr pkt); - - virtual void recvFunctional(PacketPtr pkt); - - virtual void recvStatusChange(Status status); - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { resp.clear(); snoop = false; } - - struct TickEvent : public Event - { - PacketPtr pkt; - TimingSimpleCPU *cpu; - CpuPort *port; - - TickEvent(TimingSimpleCPU *_cpu) : cpu(_cpu) {} - const char *description() const { return "Timing CPU tick"; } - void schedule(PacketPtr _pkt, Tick t); - }; - - EventWrapper retryEvent; - }; - - class IcachePort : public CpuPort - { - public: - - IcachePort(TimingSimpleCPU *_cpu, Tick _lat) - : CpuPort(_cpu->name() + "-iport", _cpu, _lat), tickEvent(_cpu) - { } - - protected: - - virtual bool recvTiming(PacketPtr pkt); - - virtual void recvRetry(); - - struct ITickEvent : public TickEvent - { - - ITickEvent(TimingSimpleCPU *_cpu) - : TickEvent(_cpu) {} - void process(); - const char *description() const { return "Timing CPU icache tick"; } - }; - - ITickEvent tickEvent; - - }; - - class DcachePort : public CpuPort - { - public: - - DcachePort(TimingSimpleCPU *_cpu, Tick _lat) - : CpuPort(_cpu->name() + "-dport", _cpu, _lat), tickEvent(_cpu) - { } - - virtual void setPeer(Port *port); - - protected: - - virtual bool recvTiming(PacketPtr pkt); - - virtual void recvRetry(); - - struct DTickEvent : public TickEvent - { - DTickEvent(TimingSimpleCPU *_cpu) - : TickEvent(_cpu) {} - void process(); - const char *description() const { return "Timing CPU dcache tick"; } - }; - - DTickEvent tickEvent; - - }; - - IcachePort icachePort; - DcachePort dcachePort; - - PacketPtr ifetch_pkt; - PacketPtr dcache_pkt; + /** + * The patcket ptr received in beginResp + */ + MemMapPacketPtr beginRespPkt; Tick previousTick; public: - virtual Port *getPort(const std::string &if_name, int idx = -1); + /** + * Receive a timing response. + * + * @param resp_pkt the response packet + * @param master_id the id of the port the packet is received on + */ + virtual void beginResp(MemMapPacketPtr resp_pkt, int master_id); + + /** + * End a timing request. + * + * @param req_pkt the request packet + * @param master_id the id of the port the packet is ending on + */ + virtual void endReq(MemMapPacketPtr req_pkt, int master_id); virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); @@ -270,17 +203,11 @@ void fetch(); void sendFetch(Fault fault, RequestPtr req, ThreadContext *tc); - void completeIfetch(PacketPtr ); - void completeDataAccess(PacketPtr pkt); + void completeIfetch(); + void completeDataAccess(); void advanceInst(Fault fault); /** - * Print state of address in memory system via PrintReq (for - * debugging). - */ - void printAddr(Addr a); - - /** * Finish a DTB translation. * @param state The DTB translation state. */ @@ -293,17 +220,16 @@ Fault writeTheseBytes(uint8_t *data, unsigned size, Addr addr, unsigned flags, uint64_t *res); + EventWrapper completeIfetchEvent; + + EventWrapper completeDataAccessEvent; + + typedef EventWrapper FetchEvent; FetchEvent fetchEvent; - struct IprEvent : Event { - Packet *pkt; - TimingSimpleCPU *cpu; - IprEvent(Packet *_pkt, TimingSimpleCPU *_cpu, Tick t); - virtual void process(); - virtual const char *description() const; - }; - void completeDrain(); }; diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple/timing.cc --- a/src/cpu/simple/timing.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple/timing.cc Fri Aug 05 18:14:06 2011 +0100 @@ -56,17 +56,6 @@ using namespace std; using namespace TheISA; -Port * -TimingSimpleCPU::getPort(const std::string &if_name, int idx) -{ - if (if_name == "dcache_port") - return &dcachePort; - else if (if_name == "icache_port") - return &icachePort; - else - panic("No Such Port\n"); -} - void TimingSimpleCPU::init() { @@ -78,57 +67,24 @@ // initialize CPU, including PC TheISA::initCPU(tc, _cpuId); } + + // Update the ThreadContext's Functional memory port + tcBase()->connectMemPorts(tcBase()); + #endif } -Tick -TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt) -{ - panic("TimingSimpleCPU doesn't expect recvAtomic callback!"); - return curTick(); -} - -void -TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt) -{ - //No internal storage to update, jusst return - return; -} - -void -TimingSimpleCPU::CpuPort::recvStatusChange(Status status) -{ - if (status == RangeChange) { - if (!snoopRangeSent) { - snoopRangeSent = true; - sendStatusChange(Port::RangeChange); - } - return; - } - - panic("TimingSimpleCPU doesn't expect recvStatusChange callback!"); -} - - -void -TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t) -{ - pkt = _pkt; - cpu->schedule(this, t); -} - TimingSimpleCPU::TimingSimpleCPU(TimingSimpleCPUParams *p) - : BaseSimpleCPU(p), fetchTranslation(this), icachePort(this, p->clock), - dcachePort(this, p->clock), fetchEvent(this) + : BaseSimpleCPU(p), fetchTranslation(this), + instReqPkt(NULL), dataReqPkt(NULL), dataRespPkt(NULL), + beginRespPkt(NULL), + previousTick(0), + completeIfetchEvent(this), completeDataAccessEvent(this), + fetchEvent(this) { _status = Idle; - icachePort.snoopRangeSent = false; - dcachePort.snoopRangeSent = false; - - ifetch_pkt = dcache_pkt = NULL; drainEvent = NULL; - previousTick = 0; changeState(SimObject::Running); system->totalNumInsts = 0; } @@ -202,7 +158,7 @@ void TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) { - BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort); + BaseCPU::takeOverFrom(oldCPU, &instPort, &dataPort); // if any of this CPU's ThreadContexts are active, mark the CPU as // running and schedule its tick event. @@ -261,37 +217,39 @@ } bool -TimingSimpleCPU::handleReadPacket(PacketPtr pkt) +TimingSimpleCPU::handleReadPacket(MemMapPacketPtr pkt) { RequestPtr req = pkt->req; if (req->isMmapedIpr()) { Tick delay; delay = TheISA::handleIprRead(thread->getTC(), pkt); - new IprEvent(pkt, this, nextCycle(curTick() + delay)); + dataReqPkt = NULL; _status = DcacheWaitResponse; - dcache_pkt = NULL; - } else if (!dcachePort.sendTiming(pkt)) { - _status = DcacheRetry; - dcache_pkt = pkt; + dataRespPkt = pkt; + schedule(completeDataAccessEvent, nextCycle(curTick() + delay)); } else { - _status = DcacheWaitResponse; + dataPort->beginReq(pkt); + // memory system takes ownership of packet - dcache_pkt = NULL; + dataReqPkt = NULL; + + _status = DcacheWaitEndReq; } - return dcache_pkt == NULL; + return dataReqPkt == NULL; } void TimingSimpleCPU::sendData(RequestPtr req, uint8_t *data, uint64_t *res, bool read) { - PacketPtr pkt; + MemMapPacketPtr pkt; buildPacket(pkt, req, read); pkt->dataDynamicArray(data); if (req->getFlags().isSet(Request::NO_ACCESS)) { - assert(!dcache_pkt); + assert(!dataReqPkt); pkt->makeResponse(); - completeDataAccess(pkt); + dataRespPkt = pkt; + completeDataAccess(); } else if (read) { handleReadPacket(pkt); } else { @@ -305,11 +263,12 @@ } if (do_access) { - dcache_pkt = pkt; + dataReqPkt = pkt; handleWritePacket(); } else { _status = DcacheWaitResponse; - completeDataAccess(pkt); + dataRespPkt = pkt; + completeDataAccess(); } } } @@ -318,13 +277,16 @@ TimingSimpleCPU::sendSplitData(RequestPtr req1, RequestPtr req2, RequestPtr req, uint8_t *data, bool read) { - PacketPtr pkt1, pkt2; + MemMapPacketPtr pkt1, pkt2; buildSplitPacket(pkt1, pkt2, req1, req2, req, data, read); if (req->getFlags().isSet(Request::NO_ACCESS)) { - assert(!dcache_pkt); + assert(!dataReqPkt); pkt1->makeResponse(); - completeDataAccess(pkt1); + dataRespPkt = pkt1; + completeDataAccess(); } else if (read) { + panic("Split read is not currently supported with the 4-phase ports"); + SplitFragmentSenderState * send_state = dynamic_cast(pkt1->senderState); if (handleReadPacket(pkt1)) { @@ -336,12 +298,14 @@ } } } else { - dcache_pkt = pkt1; + panic("Split write is not currently supported with the 4-phase ports"); + + dataReqPkt = pkt1; SplitFragmentSenderState * send_state = dynamic_cast(pkt1->senderState); if (handleWritePacket()) { send_state->clearFromParent(); - dcache_pkt = pkt2; + dataReqPkt = pkt2; send_state = dynamic_cast( pkt2->senderState); if (handleWritePacket()) { @@ -376,7 +340,7 @@ } void -TimingSimpleCPU::buildPacket(PacketPtr &pkt, RequestPtr req, bool read) +TimingSimpleCPU::buildPacket(MemMapPacketPtr& pkt, RequestPtr req, bool read) { MemCmd cmd; if (read) { @@ -391,11 +355,11 @@ cmd = MemCmd::SwapReq; } } - pkt = new Packet(req, cmd, Packet::Broadcast); + pkt = new MemMapPacket(req, cmd); } void -TimingSimpleCPU::buildSplitPacket(PacketPtr &pkt1, PacketPtr &pkt2, +TimingSimpleCPU::buildSplitPacket(MemMapPacketPtr& pkt1, MemMapPacketPtr& pkt2, RequestPtr req1, RequestPtr req2, RequestPtr req, uint8_t *data, bool read) { @@ -412,8 +376,7 @@ buildPacket(pkt2, req2, read); req->setPhys(req1->getPaddr(), req->getSize(), req1->getFlags()); - PacketPtr pkt = new Packet(req, pkt1->cmd.responseCommand(), - Packet::Broadcast); + MemMapPacketPtr pkt = new MemMapPacket(req, pkt1->cmd.responseCommand()); pkt->dataDynamicArray(data); pkt1->dataStatic(data); @@ -436,7 +399,7 @@ const int asid = 0; const ThreadID tid = 0; const Addr pc = thread->instAddr(); - unsigned block_size = dcachePort.peerBlockSize(); + unsigned block_size = dataBlockSize; BaseTLB::Mode mode = BaseTLB::Read; if (traceData) { @@ -535,21 +498,23 @@ bool TimingSimpleCPU::handleWritePacket() { - RequestPtr req = dcache_pkt->req; + RequestPtr req = dataReqPkt->req; if (req->isMmapedIpr()) { Tick delay; - delay = TheISA::handleIprWrite(thread->getTC(), dcache_pkt); - new IprEvent(dcache_pkt, this, nextCycle(curTick() + delay)); + delay = TheISA::handleIprWrite(thread->getTC(), dataReqPkt); + dataRespPkt = dataReqPkt; + dataReqPkt = NULL; _status = DcacheWaitResponse; - dcache_pkt = NULL; - } else if (!dcachePort.sendTiming(dcache_pkt)) { - _status = DcacheRetry; + schedule(completeDataAccessEvent, nextCycle(curTick() + delay)); } else { - _status = DcacheWaitResponse; + dataPort->beginReq(dataReqPkt); + // memory system takes ownership of packet - dcache_pkt = NULL; + dataReqPkt = NULL; + + _status = DcacheWaitEndReq; } - return dcache_pkt == NULL; + return dataReqPkt == NULL; } Fault @@ -559,7 +524,7 @@ const int asid = 0; const ThreadID tid = 0; const Addr pc = thread->instAddr(); - unsigned block_size = dcachePort.peerBlockSize(); + unsigned block_size = dataBlockSize; BaseTLB::Mode mode = BaseTLB::Write; if (traceData) { @@ -725,7 +690,7 @@ BaseTLB::Execute); } else { _status = IcacheWaitResponse; - completeIfetch(NULL); + completeIfetch(); numCycles += tickToCycles(curTick() - previousTick); previousTick = curTick(); @@ -736,19 +701,17 @@ void TimingSimpleCPU::sendFetch(Fault fault, RequestPtr req, ThreadContext *tc) { + assert(instReqPkt == NULL); + if (fault == NoFault) { - ifetch_pkt = new Packet(req, MemCmd::ReadReq, Packet::Broadcast); - ifetch_pkt->dataStatic(&inst); + instReqPkt = new MemMapPacket(req, MemCmd::ReadReq); + instReqPkt->dataStatic(&inst); - if (!icachePort.sendTiming(ifetch_pkt)) { - // Need to wait for retry - _status = IcacheRetry; - } else { - // Need to wait for cache to respond - _status = IcacheWaitResponse; - // ownership of packet transferred to memory system - ifetch_pkt = NULL; - } + instPort->beginReq(instReqPkt); + + // Need to wait for endReq + _status = IcacheWaitEndReq; + } else { delete req; // fetch fault: advance directly to next instruction (fault handler) @@ -777,14 +740,20 @@ void -TimingSimpleCPU::completeIfetch(PacketPtr pkt) +TimingSimpleCPU::completeIfetch() { DPRINTF(SimpleCPU, "Complete ICache Fetch\n"); + instPort->endResp(beginRespPkt); + delete beginRespPkt->req; + delete beginRespPkt; + beginRespPkt = NULL; + + if (_status != IcacheWaitResponse) + panic("TimingCPU status %d", _status); + // received a response from the icache: execute the received // instruction - - assert(!pkt || !pkt->isError()); assert(_status == IcacheWaitResponse); _status = Running; @@ -793,11 +762,6 @@ previousTick = curTick(); if (getState() == SimObject::Draining) { - if (pkt) { - delete pkt->req; - delete pkt; - } - completeDrain(); return; } @@ -845,78 +809,76 @@ } else { advanceInst(NoFault); } +} - if (pkt) { - delete pkt->req; - delete pkt; +void +TimingSimpleCPU::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + assert(!resp_pkt->isError()); + + // delay processing of returned data until next CPU clock edge, + // note that it could be now + Tick next_tick = nextCycle(curTick()); + + if (master_id == INST_MASTER_ID) { + assert(beginRespPkt == NULL); + beginRespPkt = resp_pkt; + schedule(completeIfetchEvent, next_tick); + } else { + // essentially model a pipeline stage of a packet + assert(dataRespPkt == NULL); + dataRespPkt = resp_pkt; + schedule(completeDataAccessEvent, next_tick); } } void -TimingSimpleCPU::IcachePort::ITickEvent::process() +TimingSimpleCPU::endReq(MemMapPacketPtr req_pkt, int master_id) { - cpu->completeIfetch(pkt); -} + if (master_id == INST_MASTER_ID) { + assert(_status == IcacheWaitEndReq); -bool -TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt) -{ - if (pkt->isResponse() && !pkt->wasNacked()) { - // delay processing of returned data until next CPU clock edge - Tick next_tick = cpu->nextCycle(curTick()); + // ownership of packet transferred to memory system + instReqPkt = NULL; - if (next_tick == curTick()) - cpu->completeIfetch(pkt); - else - tickEvent.schedule(pkt, next_tick); + // Waiting for response now + _status = IcacheWaitResponse; + } else { + assert(_status == DcacheWaitEndReq); - return true; - } - else if (pkt->wasNacked()) { - assert(cpu->_status == IcacheWaitResponse); - pkt->reinitNacked(); - if (!sendTiming(pkt)) { - cpu->_status = IcacheRetry; - cpu->ifetch_pkt = pkt; - } - } - //Snooping a Coherence Request, do nothing - return true; -} - -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); - PacketPtr tmp = cpu->ifetch_pkt; - if (sendTiming(tmp)) { - cpu->_status = IcacheWaitResponse; - cpu->ifetch_pkt = NULL; + // Waiting for response now + _status = DcacheWaitResponse; } } void -TimingSimpleCPU::completeDataAccess(PacketPtr pkt) +TimingSimpleCPU::completeDataAccess() { // received a response from the dcache: complete the load or store // instruction - assert(!pkt->isError()); + assert(dataRespPkt != NULL && !dataRespPkt->isError()); + assert(_status == DcacheWaitResponse || _status == DTBWaitResponse || - pkt->req->getFlags().isSet(Request::NO_ACCESS)); + dataRespPkt->req->getFlags().isSet(Request::NO_ACCESS)); + + // this needs to be guarded as this method is used in multiple + // scenarios, even when it completes a data access not going to the caches + if (dataPort->respLinkState() == PortInterface::BUSY) + dataPort->endResp(dataRespPkt); numCycles += tickToCycles(curTick() - previousTick); previousTick = curTick(); - if (pkt->senderState) { + if (dataRespPkt->senderState) { SplitFragmentSenderState * send_state = - dynamic_cast(pkt->senderState); + dynamic_cast(dataRespPkt->senderState); assert(send_state); - delete pkt->req; - delete pkt; - PacketPtr big_pkt = send_state->bigPkt; + + delete dataRespPkt->req; + delete dataRespPkt; + dataRespPkt = NULL; + + MemMapPacketPtr big_pkt = send_state->bigPkt; delete send_state; SplitMainSenderState * main_send_state = @@ -931,13 +893,13 @@ } else { delete main_send_state; big_pkt->senderState = NULL; - pkt = big_pkt; + dataRespPkt = big_pkt; } } _status = Running; - Fault fault = curStaticInst->completeAcc(pkt, this, traceData); + Fault fault = curStaticInst->completeAcc(dataRespPkt, this, traceData); // keep an instruction count if (fault == NoFault) @@ -950,12 +912,13 @@ // the locked flag may be cleared on the response packet, so check // pkt->req and not pkt to see if it was a load-locked - if (pkt->isRead() && pkt->req->isLLSC()) { - TheISA::handleLockedRead(thread, pkt->req); + if (dataRespPkt->isRead() && dataRespPkt->req->isLLSC()) { + TheISA::handleLockedRead(thread, dataRespPkt->req); } - delete pkt->req; - delete pkt; + delete dataRespPkt->req; + delete dataRespPkt; + dataRespPkt = NULL; postExecute(); @@ -969,7 +932,6 @@ advanceInst(fault); } - void TimingSimpleCPU::completeDrain() { @@ -978,131 +940,6 @@ drainEvent->process(); } -void -TimingSimpleCPU::DcachePort::setPeer(Port *port) -{ - Port::setPeer(port); - -#if FULL_SYSTEM - // Update the ThreadContext's memory ports (Functional/Virtual - // Ports) - cpu->tcBase()->connectMemPorts(cpu->tcBase()); -#endif -} - -bool -TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt) -{ - if (pkt->isResponse() && !pkt->wasNacked()) { - // delay processing of returned data until next CPU clock edge - Tick next_tick = cpu->nextCycle(curTick()); - - if (next_tick == curTick()) { - cpu->completeDataAccess(pkt); - } else { - if (!tickEvent.scheduled()) { - tickEvent.schedule(pkt, next_tick); - } else { - // In the case of a split transaction and a cache that is - // faster than a CPU we could get two responses before - // next_tick expires - if (!retryEvent.scheduled()) - schedule(retryEvent, next_tick); - return false; - } - } - - return true; - } - else if (pkt->wasNacked()) { - assert(cpu->_status == DcacheWaitResponse); - pkt->reinitNacked(); - if (!sendTiming(pkt)) { - cpu->_status = DcacheRetry; - cpu->dcache_pkt = pkt; - } - } - //Snooping a Coherence Request, do nothing - return true; -} - -void -TimingSimpleCPU::DcachePort::DTickEvent::process() -{ - cpu->completeDataAccess(pkt); -} - -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); - PacketPtr tmp = cpu->dcache_pkt; - if (tmp->senderState) { - // This is a packet from a split access. - SplitFragmentSenderState * send_state = - dynamic_cast(tmp->senderState); - assert(send_state); - PacketPtr big_pkt = send_state->bigPkt; - - SplitMainSenderState * main_send_state = - dynamic_cast(big_pkt->senderState); - assert(main_send_state); - - if (sendTiming(tmp)) { - // If we were able to send without retrying, record that fact - // and try sending the other fragment. - send_state->clearFromParent(); - int other_index = main_send_state->getPendingFragment(); - if (other_index > 0) { - tmp = main_send_state->fragments[other_index]; - cpu->dcache_pkt = tmp; - if ((big_pkt->isRead() && cpu->handleReadPacket(tmp)) || - (big_pkt->isWrite() && cpu->handleWritePacket())) { - main_send_state->fragments[other_index] = NULL; - } - } else { - cpu->_status = DcacheWaitResponse; - // memory system takes ownership of packet - cpu->dcache_pkt = NULL; - } - } - } else if (sendTiming(tmp)) { - cpu->_status = DcacheWaitResponse; - // memory system takes ownership of packet - cpu->dcache_pkt = NULL; - } -} - -TimingSimpleCPU::IprEvent::IprEvent(Packet *_pkt, TimingSimpleCPU *_cpu, - Tick t) - : pkt(_pkt), cpu(_cpu) -{ - cpu->schedule(this, t); -} - -void -TimingSimpleCPU::IprEvent::process() -{ - cpu->completeDataAccess(pkt); -} - -const char * -TimingSimpleCPU::IprEvent::description() const -{ - return "Timing Simple CPU Delay IPR event"; -} - - -void -TimingSimpleCPU::printAddr(Addr a) -{ - dcachePort.printAddr(a); -} - - //////////////////////////////////////////////////////////////////////// // // TimingSimpleCPU Simulation Object diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple_thread.hh --- a/src/cpu/simple_thread.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple_thread.hh Fri Aug 05 18:14:06 2011 +0100 @@ -55,8 +55,8 @@ class FunctionProfile; class ProfileNode; -class FunctionalPort; -class PhysicalPort; +class PortProxy; +class FSTranslatingProxy; namespace TheISA { namespace Kernel { @@ -68,7 +68,6 @@ #include "sim/process.hh" #include "mem/page_table.hh" -class TranslatingPort; #endif // FULL_SYSTEM @@ -196,13 +195,13 @@ System *getSystemPtr() { return system; } #if FULL_SYSTEM - FunctionalPort *getPhysPort() { return physPort; } + PortProxy *getPhyProxy() { return physProxy; } /** Return a virtual port. This port cannot be cached locally in an object. * After a CPU switch it may point to the wrong memory object which could * mean stale data. */ - VirtualPort *getVirtPort() { return virtPort; } + FSTranslatingProxy *getVirtProxy() { return virtProxy; } #endif Status status() const { return _status; } @@ -213,6 +212,8 @@ /// cycles to wait before beginning execution. void activate(int delay = 1); + void wakeFromQuiesce() {} + /// Set the status to Suspended. void suspend(); diff -r 6548721032fa -r 042f38a2e360 src/cpu/simple_thread.cc --- a/src/cpu/simple_thread.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/simple_thread.cc Fri Aug 05 18:14:06 2011 +0100 @@ -50,11 +50,11 @@ #include "base/trace.hh" #include "cpu/profile.hh" #include "cpu/quiesce_event.hh" -#include "mem/vport.hh" #include "sim/serialize.hh" #include "sim/sim_exit.hh" +#include "mem/fs_translating_proxy.hh" #else -#include "mem/translating_port.hh" +#include "mem/se_translating_proxy.hh" #include "sim/process.hh" #include "sim/system.hh" #endif @@ -118,8 +118,8 @@ SimpleThread::~SimpleThread() { #if FULL_SYSTEM - delete physPort; - delete virtPort; + delete physProxy; + delete virtProxy; #endif delete tc; } diff -r 6548721032fa -r 042f38a2e360 src/cpu/thread_context.hh --- a/src/cpu/thread_context.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/thread_context.hh Fri Aug 05 18:14:06 2011 +0100 @@ -49,9 +49,9 @@ class BaseCPU; class Checkpoint; class EndQuiesceEvent; -class TranslatingPort; -class FunctionalPort; -class VirtualPort; +class SETranslatingProxy; +class PortProxy; +class FSTranslatingProxy; class Process; class System; namespace TheISA { @@ -125,13 +125,13 @@ #if FULL_SYSTEM virtual TheISA::Kernel::Statistics *getKernelStats() = 0; - virtual FunctionalPort *getPhysPort() = 0; + virtual PortProxy *getPhysProxy() = 0; - virtual VirtualPort *getVirtPort() = 0; + virtual FSTranslatingProxy *getVirtProxy() = 0; virtual void connectMemPorts(ThreadContext *tc) = 0; #else - virtual TranslatingPort *getMemPort() = 0; + virtual SETranslatingProxy *getMemProxy() = 0; virtual Process *getProcessPtr() = 0; #endif @@ -144,6 +144,8 @@ /// cycles to wait before beginning execution. virtual void activate(int delay = 1) = 0; + virtual void wakeFromQuiesce() = 0; + /// Set the status to Suspended. virtual void suspend(int delay = 0) = 0; @@ -293,13 +295,13 @@ TheISA::Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } - FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } + PortProxy *getPhysProxy() { return actualTC->getPhysProxy(); } - VirtualPort *getVirtPort() { return actualTC->getVirtPort(); } + FSTranslatingProxy *getVirtProxy() { return actualTC->getVirtProxy(); } void connectMemPorts(ThreadContext *tc) { actualTC->connectMemPorts(tc); } #else - TranslatingPort *getMemPort() { return actualTC->getMemPort(); } + SETranslatingProxy *getMemProxy() { return actualTC->getMemProxy(); } Process *getProcessPtr() { return actualTC->getProcessPtr(); } #endif @@ -312,6 +314,9 @@ /// cycles to wait before beginning execution. void activate(int delay = 1) { actualTC->activate(delay); } + /// Fix up for quiesce instruction not taken. + void wakeFromQuiesce() {} + /// Set the status to Suspended. void suspend(int delay = 0) { actualTC->suspend(); } diff -r 6548721032fa -r 042f38a2e360 src/cpu/thread_state.hh --- a/src/cpu/thread_state.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/thread_state.hh Fri Aug 05 18:14:06 2011 +0100 @@ -55,7 +55,9 @@ class Checkpoint; class Port; -class TranslatingPort; +class SETranslatingProxy; +class PortProxy; +class FSTranslatingProxy; /** * Struct for holding general thread state that is needed across CPU @@ -109,17 +111,13 @@ TheISA::Kernel::Statistics *getKernelStats() { return kernelStats; } - FunctionalPort *getPhysPort() { return physPort; } + PortProxy *getPhysProxy() { return physProxy; } - void setPhysPort(FunctionalPort *port) { physPort = port; } - - VirtualPort *getVirtPort() { return virtPort; } + FSTranslatingProxy *getVirtProxy() { return virtProxy; } #else Process *getProcessPtr() { return process; } - TranslatingPort *getMemPort(); - - void setMemPort(TranslatingPort *_port) { port = _port; } + SETranslatingProxy *getMemProxy(); #endif /** Reads the number of instructions functionally executed and @@ -141,7 +139,6 @@ public: /** Connects port to the functional port of the memory object * below the CPU. */ - void connectToMemFunc(Port *port); /** Number of instructions committed. */ Counter numInst; @@ -188,13 +185,13 @@ protected: /** A functional port outgoing only for functional accesses to physical * addresses.*/ - FunctionalPort *physPort; + PortProxy *physProxy; /** A functional port, outgoing only, for functional accesse to virtual * addresses. */ - VirtualPort *virtPort; + FSTranslatingProxy *virtProxy; #else - TranslatingPort *port; + SETranslatingProxy *memProxy; Process *process; #endif diff -r 6548721032fa -r 042f38a2e360 src/cpu/thread_state.cc --- a/src/cpu/thread_state.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/cpu/thread_state.cc Fri Aug 05 18:14:06 2011 +0100 @@ -33,13 +33,14 @@ #include "cpu/profile.hh" #include "cpu/thread_state.hh" #include "mem/port.hh" -#include "mem/translating_port.hh" #include "sim/serialize.hh" #if FULL_SYSTEM #include "arch/kernel_stats.hh" #include "cpu/quiesce_event.hh" -#include "mem/vport.hh" +#include "mem/fs_translating_proxy.hh" +#else +#include "mem/se_translating_proxy.hh" #endif #if FULL_SYSTEM @@ -51,9 +52,9 @@ baseCpu(cpu), _threadId(_tid), lastActivate(0), lastSuspend(0), #if FULL_SYSTEM profile(NULL), profileNode(NULL), profilePC(0), quiesceEvent(NULL), - kernelStats(NULL), physPort(NULL), virtPort(NULL), + kernelStats(NULL), physProxy(NULL), virtProxy(NULL), #else - port(NULL), process(_process), + memProxy(NULL), process(_process), #endif funcExeInst(0), storeCondFailures(0) { @@ -62,9 +63,8 @@ ThreadState::~ThreadState() { #if !FULL_SYSTEM - if (port) { - delete port->getPeer(); - delete port; + if (memProxy) { + delete memProxy; } #endif } @@ -118,12 +118,10 @@ // @todo: For now this disregards any older port that may have // already existed. Fix this memory leak once the bus port IDs // for functional ports is resolved. - if (physPort) - physPort->removeConn(); + if (physProxy) + panic("Already has physProxy"); else - physPort = new FunctionalPort(csprintf("%s-%d-funcport", - baseCpu->name(), _threadId)); - connectToMemFunc(physPort); + physProxy = new PortProxy(baseCpu->getDataPort()); } void @@ -132,12 +130,10 @@ // @todo: For now this disregards any older port that may have // already existed. Fix this memory leak once the bus port IDs // for functional ports is resolved. - if (virtPort) - virtPort->removeConn(); + if (virtProxy) + panic("Already has virtProxy"); else - virtPort = new VirtualPort(csprintf("%s-%d-vport", - baseCpu->name(), _threadId), tc); - connectToMemFunc(virtPort); + virtProxy = new FSTranslatingProxy(tc); } void @@ -155,36 +151,17 @@ } #else -TranslatingPort * -ThreadState::getMemPort() +SETranslatingProxy * +ThreadState::getMemProxy() { - if (port != NULL) - return port; + if (memProxy != NULL) + return memProxy; /* Use this port to for syscall emulation writes to memory. */ - port = new TranslatingPort(csprintf("%s-%d-funcport", baseCpu->name(), _threadId), - process, TranslatingPort::NextPage); + memProxy = new SETranslatingProxy(process, SETranslatingProxy::NextPage, + process->systemPort()); - connectToMemFunc(port); - - return port; + return memProxy; } #endif -void -ThreadState::connectToMemFunc(Port *port) -{ - Port *dcache_port, *func_mem_port; - - dcache_port = baseCpu->getPort("dcache_port"); - assert(dcache_port != NULL); - - MemObject *mem_object = dcache_port->getPeer()->getOwner(); - assert(mem_object != NULL); - - func_mem_port = mem_object->getPort("functional"); - assert(func_mem_port != NULL); - - func_mem_port->setPeer(port); - port->setPeer(func_mem_port); -} diff -r 6548721032fa -r 042f38a2e360 src/dev/Device.py --- a/src/dev/Device.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/Device.py Fri Aug 05 18:14:06 2011 +0100 @@ -33,7 +33,7 @@ class PioDevice(MemObject): type = 'PioDevice' abstract = True - pio = Port("Programmed I/O port") + pio = Port("MEM_MAP", "SLAVE", "Programmed I/O port") platform = Param.Platform(Parent.any, "Platform this device is part of") system = Param.System(Parent.any, "System this device is part of") @@ -46,13 +46,7 @@ class DmaDevice(PioDevice): type = 'DmaDevice' abstract = True - dma = Port(Self.pio.peerObj.port, "DMA port") - min_backoff_delay = Param.Latency('4ns', - "min time between a nack packet being received and the next request made by the device") - max_backoff_delay = Param.Latency('10us', - "max time between a nack packet being received and the next request made by the device") - - + dma = Port("MEM_MAP", "MASTER", "DMA port") class IsaFake(BasicPioDevice): type = 'IsaFake' diff -r 6548721032fa -r 042f38a2e360 src/dev/Ethernet.py --- a/src/dev/Ethernet.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/Ethernet.py Fri Aug 05 18:14:06 2011 +0100 @@ -37,8 +37,8 @@ class EtherLink(EtherObject): type = 'EtherLink' - int0 = Port("interface 0") - int1 = Port("interface 1") + int0 = Port("ETH", "SLAVE", "interface 0") + int1 = Port("ETH", "SLAVE", "interface 1") delay = Param.Latency('0us', "packet transmit delay") delay_var = Param.Latency('0ns', "packet transmit delay variability") speed = Param.NetworkBandwidth('1Gbps', "link speed") @@ -64,7 +64,7 @@ class EtherDevice(PciDevice): type = 'EtherDevice' abstract = True - interface = Port("Ethernet Interface") + interface = Port("ETH", "MASTER", "Ethernet Interface") class IGbE(EtherDevice): # Base class for two IGbE adapters listed above diff -r 6548721032fa -r 042f38a2e360 src/dev/Pci.py --- a/src/dev/Pci.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/Pci.py Fri Aug 05 18:14:06 2011 +0100 @@ -41,7 +41,7 @@ class PciDevice(DmaDevice): type = 'PciDevice' abstract = True - config = Port(Self.pio.peerObj.port, "PCI configuration space port") + config = Port("MEM_MAP", "SLAVE", "PCI configuration space port") pci_bus = Param.Int("PCI bus") pci_dev = Param.Int("PCI device number") pci_func = Param.Int("PCI function code") diff -r 6548721032fa -r 042f38a2e360 src/dev/SConscript --- a/src/dev/SConscript Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/SConscript Fri Aug 05 18:14:06 2011 +0100 @@ -36,7 +36,6 @@ if env['FULL_SYSTEM']: SimObject('BadDevice.py') - SimObject('CopyEngine.py') SimObject('Device.py') SimObject('DiskImage.py') SimObject('Ethernet.py') @@ -48,7 +47,6 @@ SimObject('Uart.py') Source('baddev.cc') - Source('copy_engine.cc') Source('disk_image.cc') Source('etherbus.cc') Source('etherdevice.cc') @@ -62,6 +60,7 @@ Source('ide_disk.cc') Source('intel_8254_timer.cc') Source('io_device.cc') + Source('dma_device.cc') Source('isa_fake.cc') Source('mc146818.cc') Source('ns_gige.cc') @@ -79,7 +78,6 @@ TraceFlag('DiskImageRead') TraceFlag('DiskImageWrite') TraceFlag('DMA') - TraceFlag('DMACopyEngine') TraceFlag('Ethernet') TraceFlag('EthernetCksum') TraceFlag('EthernetDMA') diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/Tsunami.py --- a/src/dev/alpha/Tsunami.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/Tsunami.py Fri Aug 05 18:14:06 2011 +0100 @@ -51,6 +51,7 @@ class TsunamiPChip(BasicPioDevice): type = 'TsunamiPChip' tsunami = Param.Tsunami(Parent.any, "Tsunami") + pte = Port("MEM_MAP", "MASTER", "PTE look-up port") class Tsunami(Platform): type = 'Tsunami' @@ -93,30 +94,31 @@ # earlier, since the bus object itself is typically defined at the # System level. def attachIO(self, bus): - self.cchip.pio = bus.port - self.pchip.pio = bus.port - self.pciconfig.pio = bus.default + bus.default_mem_master = self.pciconfig.pio bus.use_default_range = True - self.fake_sm_chip.pio = bus.port - self.fake_uart1.pio = bus.port - self.fake_uart2.pio = bus.port - self.fake_uart3.pio = bus.port - self.fake_uart4.pio = bus.port - self.fake_ppc.pio = bus.port - self.fake_OROM.pio = bus.port - self.fake_pnp_addr.pio = bus.port - self.fake_pnp_write.pio = bus.port - self.fake_pnp_read0.pio = bus.port - self.fake_pnp_read1.pio = bus.port - self.fake_pnp_read2.pio = bus.port - self.fake_pnp_read3.pio = bus.port - self.fake_pnp_read4.pio = bus.port - self.fake_pnp_read5.pio = bus.port - self.fake_pnp_read6.pio = bus.port - self.fake_pnp_read7.pio = bus.port - self.fake_ata0.pio = bus.port - self.fake_ata1.pio = bus.port - self.fb.pio = bus.port - self.io.pio = bus.port - self.uart.pio = bus.port - self.backdoor.pio = bus.port + bus.mem_master = self.fake_sm_chip.pio + bus.mem_master = self.fake_uart1.pio + bus.mem_master = self.fake_uart2.pio + bus.mem_master = self.fake_uart3.pio + bus.mem_master = self.fake_uart4.pio + bus.mem_master = self.fake_ppc.pio + bus.mem_master = self.fake_OROM.pio + bus.mem_master = self.fake_pnp_addr.pio + bus.mem_master = self.fake_pnp_write.pio + bus.mem_master = self.fake_pnp_read0.pio + bus.mem_master = self.fake_pnp_read1.pio + bus.mem_master = self.fake_pnp_read2.pio + bus.mem_master = self.fake_pnp_read3.pio + bus.mem_master = self.fake_pnp_read4.pio + bus.mem_master = self.fake_pnp_read5.pio + bus.mem_master = self.fake_pnp_read6.pio + bus.mem_master = self.fake_pnp_read7.pio + bus.mem_master = self.fake_ata0.pio + bus.mem_master = self.fake_ata1.pio + bus.mem_master = self.fb.pio + bus.mem_master = self.io.pio + bus.mem_master = self.uart.pio + bus.mem_master = self.backdoor.pio + bus.mem_master = self.cchip.pio + bus.mem_master = self.pchip.pio + bus.mem_slave = self.pchip.pte diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/backdoor.hh --- a/src/dev/alpha/backdoor.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/backdoor.hh Fri Aug 05 18:14:06 2011 +0100 @@ -113,8 +113,8 @@ /** * memory mapped reads and writes */ - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); /** * standard serialization routines for checkpointing diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/backdoor.cc --- a/src/dev/alpha/backdoor.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/backdoor.cc Fri Aug 05 18:14:06 2011 +0100 @@ -88,13 +88,13 @@ alphaAccess->kernStart = system->getKernelStart(); alphaAccess->kernEnd = system->getKernelEnd(); alphaAccess->entryPoint = system->getKernelEntry(); - alphaAccess->mem_size = system->physmem->size(); + alphaAccess->mem_size = system->memsize; alphaAccess->cpuClock = cpu->frequency() / 1000000; // In MHz alphaAccess->intrClockFrequency = params()->platform->intrFrequency(); } Tick -AlphaBackdoor::read(PacketPtr pkt) +AlphaBackdoor::read(MemMapPacketPtr pkt) { /** XXX Do we want to push the addr munging to a bus brige or something? So @@ -193,7 +193,7 @@ } Tick -AlphaBackdoor::write(PacketPtr pkt) +AlphaBackdoor::write(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami.cc --- a/src/dev/alpha/tsunami.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami.cc Fri Aug 05 18:14:06 2011 +0100 @@ -32,9 +32,7 @@ * Implementation of Tsunami platform. */ -#include #include -#include #include "config/the_isa.hh" #include "cpu/intr_control.hh" diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami_cchip.hh --- a/src/dev/alpha/tsunami_cchip.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami_cchip.hh Fri Aug 05 18:14:06 2011 +0100 @@ -93,9 +93,9 @@ return dynamic_cast(_params); } - virtual Tick read(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); /** * post an RTC interrupt to the CPU diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami_cchip.cc --- a/src/dev/alpha/tsunami_cchip.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami_cchip.cc Fri Aug 05 18:14:06 2011 +0100 @@ -75,7 +75,7 @@ } Tick -TsunamiCChip::read(PacketPtr pkt) +TsunamiCChip::read(MemMapPacketPtr pkt) { DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); @@ -192,7 +192,7 @@ } Tick -TsunamiCChip::write(PacketPtr pkt) +TsunamiCChip::write(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami_io.hh --- a/src/dev/alpha/tsunami_io.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami_io.hh Fri Aug 05 18:14:06 2011 +0100 @@ -125,8 +125,8 @@ return dynamic_cast(_params); } - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); /** * Post an PIC interrupt to the CPU via the CChip diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami_io.cc --- a/src/dev/alpha/tsunami_io.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami_io.cc Fri Aug 05 18:14:06 2011 +0100 @@ -84,7 +84,7 @@ } Tick -TsunamiIO::read(PacketPtr pkt) +TsunamiIO::read(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); @@ -149,7 +149,7 @@ } Tick -TsunamiIO::write(PacketPtr pkt) +TsunamiIO::write(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami_pchip.hh --- a/src/dev/alpha/tsunami_pchip.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami_pchip.hh Fri Aug 05 18:14:06 2011 +0100 @@ -43,7 +43,8 @@ /** * A very simple implementation of the Tsunami PCI interface chips. */ -class TsunamiPChip : public BasicPioDevice +class TsunamiPChip : public BasicPioDevice, + public MemMapMasterInterface { protected: @@ -87,8 +88,8 @@ Addr calcIOAddr(Addr addr); Addr calcMemAddr(Addr addr); - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); /** * Serialize this object to the given output stream. @@ -102,6 +103,24 @@ * @param section The section name of this object */ virtual void unserialize(Checkpoint *cp, const std::string §ion); + +private: + + /** + * A port used solely to read the pteEntries in memory. + */ + MemMapMasterPort ptePort; + +public: + + /** Master interface methods */ + void endReq(MemMapPacketPtr req_pkt, int master_id) {} + + void beginResp(MemMapPacketPtr resp_pkt, int master_id) {} + + void recvRangeChange(int master_id) {} + /** End of master interface */ + }; #endif // __TSUNAMI_PCHIP_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/alpha/tsunami_pchip.cc --- a/src/dev/alpha/tsunami_pchip.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/alpha/tsunami_pchip.cc Fri Aug 05 18:14:06 2011 +0100 @@ -38,6 +38,7 @@ #include #include "base/trace.hh" +#include "base/chunk_generator.hh" #include "config/the_isa.hh" #include "dev/alpha/tsunami_pchip.hh" #include "dev/alpha/tsunamireg.h" @@ -51,7 +52,8 @@ using namespace TheISA; TsunamiPChip::TsunamiPChip(const Params *p) -: BasicPioDevice(p) + : BasicPioDevice(p), + ptePort(*this, this, "pte") { pioSize = 0x1000; @@ -66,10 +68,12 @@ //Set back pointer in tsunami p->tsunami->pchip = this; + + regPort(&ptePort); } Tick -TsunamiPChip::read(PacketPtr pkt) +TsunamiPChip::read(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); @@ -150,7 +154,7 @@ } Tick -TsunamiPChip::write(PacketPtr pkt) +TsunamiPChip::write(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = (pkt->getAddr() - pioAddr) >> 6; @@ -283,7 +287,7 @@ baMask = (wsm[i] & (ULL(0xfff) << 20)) | (ULL(0x7f) << 13); pteAddr = (tba[i] & tbaMask) | ((busAddr & baMask) >> 10); - pioPort->readBlob(pteAddr, (uint8_t*)&pteEntry, sizeof(uint64_t)); + ptePort->readMem(pteAddr, (uint8_t*)&pteEntry, sizeof(uint64_t)); dmaAddr = ((pteEntry & ~ULL(0x1)) << 12) | (busAddr & ULL(0x1fff)); diff -r 6548721032fa -r 042f38a2e360 src/dev/baddev.hh --- a/src/dev/baddev.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/baddev.hh Fri Aug 05 18:14:06 2011 +0100 @@ -69,8 +69,8 @@ */ BadDevice(Params *p); - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); }; #endif // __DEV_BADDEV_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/baddev.cc --- a/src/dev/baddev.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/baddev.cc Fri Aug 05 18:14:06 2011 +0100 @@ -32,20 +32,8 @@ * BadDevice implemenation */ -#include -#include -#include - -#include "base/trace.hh" -#include "config/the_isa.hh" #include "dev/baddev.hh" -#include "dev/platform.hh" -#include "mem/port.hh" #include "params/BadDevice.hh" -#include "sim/system.hh" - -using namespace std; -using namespace TheISA; BadDevice::BadDevice(Params *p) : BasicPioDevice(p), devname(p->devicename) @@ -54,14 +42,14 @@ } Tick -BadDevice::read(PacketPtr pkt) +BadDevice::read(MemMapPacketPtr pkt) { panic("Device %s not imlpmented\n", devname); M5_DUMMY_RETURN } Tick -BadDevice::write(PacketPtr pkt) +BadDevice::write(MemMapPacketPtr pkt) { panic("Device %s not imlpmented\n", devname); M5_DUMMY_RETURN diff -r 6548721032fa -r 042f38a2e360 src/dev/dma_device.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dev/dma_device.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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 + * Nathan Binkert + * Andreas Hansson + */ + +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "dev/dma_device.hh" +#include "sim/system.hh" + + +DmaDevice::DmaDevice(const Params *p) + : PioDevice(p), masterPort(*this, this, "dma"), + pendingCount(0), + currRespPkt(NULL), acceptRespEvent(this), + dmaReqPktQueue(this, masterPort) +{ + // register our master port + regPort(&masterPort); +} + +DmaDevice::~DmaDevice() +{ +} + +void +DmaDevice::init() +{ + // check that the port is connected and let the base class do the rest + if (!masterPort.isConnected()) + panic("DMA port %s not connected!", masterPort.name()); + + PioDevice::init(); +} + +unsigned int +DmaDevice::drain(Event *de) +{ + unsigned int count = pioRespPktQueue.drain(de) + dmaReqPktQueue.drain(de); + + if (count) { + changeState(Draining); + } else { + changeState(Drained); + } + return count; +} + +void +DmaDevice::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + handleResponse(resp_pkt); + + assert(currRespPkt == NULL); + currRespPkt = resp_pkt; + schedule(acceptRespEvent, curTick()); +} + +void +DmaDevice::acceptResp() +{ + masterPort->endResp(currRespPkt); + currRespPkt = NULL; +} + +void +DmaDevice::endReq(MemMapPacketPtr req_pkt, int master_id) +{ + // tell the packet queue that the current packet is transfered and + // that it can go on to the next one + dmaReqPktQueue.endDeferred(); +} + +void +DmaDevice::handleResponse(MemMapPacketPtr resp_pkt, Tick latency) +{ + assert(resp_pkt->senderState); + DmaReqState *state = dynamic_cast(resp_pkt->senderState); + assert(state); + + DPRINTF(DMA, "Received response %s addr %#x size %#x\n", + resp_pkt->cmdString(), resp_pkt->getAddr(), + resp_pkt->req->getSize()); + + pendingCount--; + assert(pendingCount >= 0); + + state->numBytes += resp_pkt->req->getSize(); + assert(state->totBytes >= state->numBytes); + + if (state->totBytes == state->numBytes) { + if (state->completionEvent) { + schedule(state->completionEvent, + curTick() + latency + state->delay); + + } + delete state; + } + + delete resp_pkt->req; + delete resp_pkt; +} + +void +DmaDevice::recvRangeChange(int master_id) +{ +} + +void +DmaDevice::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, + uint8_t *data, Tick delay) +{ + assert(getState() == SimObject::Running); + + DmaReqState* reqState = new DmaReqState(event, size, delay); + + // no flags set + Request::Flags flag; + + DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", + addr, size, event ? event->scheduled() : -1 ); + for (ChunkGenerator gen(addr, size, cacheBlockSize()); + !gen.done(); gen.next()) { + Request *req = new Request(gen.addr(), gen.size(), flag); + MemMapPacketPtr pkt = new MemMapPacket(req, cmd); + + // Increment the data pointer on a write + if (data != NULL) + pkt->dataStatic(data + gen.complete()); + + // all the packets that constitute the action point to the same + // DMA request state + pkt->senderState = reqState; + + if (pkt->needsResponse()) { + assert(pendingCount >= 0); + pendingCount++; + } + + if (sys->getMemoryMode() == Enums::timing) { + DPRINTF(DMA, "--Queuing DMA for addr: %#x size: %d\n", gen.addr(), + gen.size()); + + // queue it for transmission straight away, even if this + // is unrealistic (separate by appropriate latency is the + // job of the port?) + dmaReqPktQueue.insert(pkt, curTick()); + } else { + // by-pass the packet queue and just send the requests out + // in zero time, deal with the results as if it was a + // response (even it if is not...) + DPRINTF(DMA, "--Sending DMA for addr: %#x size: %d\n", + pkt->req->getPaddr(), pkt->req->getSize()); + Tick latency = masterPort->sendAtomic(pkt); + + handleResponse(pkt, latency); + } + } +} diff -r 6548721032fa -r 042f38a2e360 src/dev/dma_device.cc~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dev/dma_device.cc~ Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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 + * Nathan Binkert + * Andreas Hansson + */ + +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "dev/dma_device.hh" +#include "sim/system.hh" + diff -r 6548721032fa -r 042f38a2e360 src/dev/dma_device.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dev/dma_device.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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: Ali Saidi + * Nathan Binkert + * Andreas Hansson + */ + +#ifndef __DEV_DMA_DEVICE_HH__ +#define __DEV_DMA_DEVICE_HH__ + +#include "dev/io_device.hh" +#include "params/DmaDevice.hh" + +/** + * A DMA device extends on the PIO device (which is a memory-mapped + * slave) and also adds the capability of being a memory-mapped + * master, supporting high-level DMA reads and writes. + */ +class DmaDevice : public PioDevice, + public MemMapMasterInterface +{ + + private: + + /** + * Our memory-mapped master port used for all DMA actions. + */ + MemMapMasterPort masterPort; + + /** + * Start a DMA action and queue the packets necessary to complete the + * action. + * + * @param cmd Command to issue, e.g. read/write + * @param addr Starting address + * @param size Size in bytes + * @param event Notification of action finished + * @param data Pointer to data for read/write + * @param delay Delay between finishing and notifying an action + */ + void dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, + uint8_t *data, Tick delay); + + void handleResponse(MemMapPacketPtr resp_pkt, Tick latency = 0); + + /** + * A DMA Request state is used to track the completion of DMA + * actions that are split into multiple packets. + */ + struct DmaReqState : public Packet::SenderState, public FastAlloc + { + /** Event to call on the device when this transaction (all packets) + * complete. */ + Event *completionEvent; + + /** Total number of bytes that this transaction involves. */ + Addr totBytes; + + /** Number of bytes that have been acked for this transaction. */ + Addr numBytes; + + /** Amount to delay completion of dma by */ + Tick delay; + + DmaReqState(Event *ce, Addr tb, Tick _delay) + : completionEvent(ce), totBytes(tb), numBytes(0), + delay(_delay) + {} + }; + + /** Number of outstanding response packets the dma port has. */ + int pendingCount; + + /** + * The current response packet between beginning and ending the + * response. + */ + MemMapPacketPtr currRespPkt; + + /** + * Accept and end the current response. + */ + void acceptResp(); + + EventWrapper acceptRespEvent; + + protected: + + /** + * The request queue is used by our children in their drain conditions. + */ + ReqPacketQueue4Phase dmaReqPktQueue; + + virtual void beginResp(MemMapPacketPtr resp_pkt, int master_id); + + virtual void endReq(MemMapPacketPtr req_pkt, int master_id); + + virtual void recvRangeChange(int master_id); + + public: + + bool dmaPending() { return pendingCount > 0; } + + unsigned cacheBlockSize() const { return masterPort->getBlockSize(); } + + typedef DmaDeviceParams Params; + DmaDevice(const Params *p); + virtual ~DmaDevice(); + + virtual void init(); + + const Params * + params() const + { + return dynamic_cast(_params); + } + + void dmaWrite(Addr addr, int size, Event *event, uint8_t *data, + Tick delay = 0) + { + dmaAction(MemCmd::WriteReq, addr, size, event, data, delay); + } + + void dmaRead(Addr addr, int size, Event *event, uint8_t *data, + Tick delay = 0) + { + dmaAction(MemCmd::ReadReq, addr, size, event, data, delay); + } + + virtual unsigned int drain(Event *de); + +}; + +#endif // __DEV_DMA_DEVICE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/dma_device.hh~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dev/dma_device.hh~ Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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: Ali Saidi + * Nathan Binkert + * Andreas Hansson + */ + + +class DmaMaster : public MemMapMasterInterface +{ + private: + + MemMapMasterPort masterPort; + + void handleResponse(MemMapPacketPtr resp_pkt, Tick latency = 0); + + protected: + + struct DmaReqState : public Packet::SenderState, public FastAlloc + { + /** Event to call on the device when this transaction (all packets) + * complete. */ + Event *completionEvent; + + /** Total number of bytes that this transaction involves. */ + Addr totBytes; + + /** Number of bytes that have been acked for this transaction. */ + Addr numBytes; + + /** Amount to delay completion of dma by */ + Tick delay; + + DmaReqState(Event *ce, Addr tb, Tick _delay) + : completionEvent(ce), totBytes(tb), numBytes(0), + delay(_delay) + {} + }; + + MemObject *device; + MemMapPacketList transmitList; + + /** The system that device/port are in. This is used to select which mode + * we are currently operating in. */ + System *sys; + + /** Number of outstanding packets the dma port has. */ + int pendingCount; + + /** If we need to drain, keep the drain event around until we're done + * here.*/ + Event *drainEvent; + + /** If the port is currently waiting for a retry before it can + * send whatever it is that it's sending. */ + bool inRetry; + + virtual bool recvTimingResp(MemMapPacketPtr respPkt, int master_id); + + virtual void recvRetryReq(int master_id); + + virtual void recvRangeChange(int master_id); + + void sendDma(); + + /** event to give us a kick every time we backoff time is reached. */ + EventWrapper sendDmaEvent; + + public: + + DmaMaster(MemObject *dev, System *s); + + MemMapMasterPort* getPort() { return &masterPort; } + + void dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, + uint8_t *data, Tick delay, Request::Flags flag = 0); + + bool dmaPending() { return pendingCount > 0; } + + unsigned cacheBlockSize() const { return masterPort->getBlockSize(); } + + unsigned int drain(Event *de); + + std::string _name; + + /** + * Return port name. + * @retval A string with the name also including the parent + */ + const std::string name() const; +}; + +class DmaDevice : public PioDevice +{ + protected: + + DmaMaster dmaMaster; + + public: + typedef DmaDeviceParams Params; + DmaDevice(const Params *p); + virtual ~DmaDevice(); + + virtual void init(); + + const Params * + params() const + { + return dynamic_cast(_params); + } + + void dmaWrite(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) + { + dmaMaster.dmaAction(MemCmd::WriteReq, addr, size, event, data, delay); + } + + void dmaRead(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) + { + dmaMaster.dmaAction(MemCmd::ReadReq, addr, size, event, data, delay); + } + + bool dmaPending() { return dmaMaster.dmaPending(); } + + virtual unsigned int drain(Event *de); + + unsigned cacheBlockSize() const { return dmaMaster.cacheBlockSize(); } + + friend class DmaMaster; +}; + +#endif // __DEV_DMA_DEVICE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/i8254xGBe.hh --- a/src/dev/i8254xGBe.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/i8254xGBe.hh Fri Aug 05 18:14:06 2011 +0100 @@ -525,10 +525,10 @@ Tick lastInterrupt; inline Tick ticks(int numCycles) const { return numCycles * clock; } - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); - virtual Tick writeConfig(PacketPtr pkt); + virtual Tick writeConfig(MemMapPacketPtr pkt); bool ethRxPkt(EthPacketPtr packet); void ethTxDone(); diff -r 6548721032fa -r 042f38a2e360 src/dev/i8254xGBe.cc --- a/src/dev/i8254xGBe.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/i8254xGBe.cc Fri Aug 05 18:14:06 2011 +0100 @@ -141,7 +141,7 @@ } Tick -IGbE::writeConfig(PacketPtr pkt) +IGbE::writeConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) @@ -160,7 +160,7 @@ #define IN_RANGE(val, base, len) (val >= base && val < (base + len)) Tick -IGbE::read(PacketPtr pkt) +IGbE::read(MemMapPacketPtr pkt) { int bar; Addr daddr; @@ -348,7 +348,7 @@ } Tick -IGbE::write(PacketPtr pkt) +IGbE::write(MemMapPacketPtr pkt) { int bar; Addr daddr; @@ -2046,7 +2046,7 @@ IGbE::drain(Event *de) { unsigned int count; - count = pioPort->drain(de) + dmaPort->drain(de); + count = pioRespPktQueue.drain(de) + dmaReqPktQueue.drain(de); if (rxDescCache.hasOutstandingEvents() || txDescCache.hasOutstandingEvents()) { count++; diff -r 6548721032fa -r 042f38a2e360 src/dev/ide_ctrl.hh --- a/src/dev/ide_ctrl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/ide_ctrl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -135,7 +135,7 @@ uint32_t ioShift, ctrlOffset; - void dispatchAccess(PacketPtr pkt, bool read); + void dispatchAccess(MemMapPacketPtr pkt, bool read); public: typedef IdeControllerParams Params; @@ -147,13 +147,13 @@ void intrPost(); - Tick writeConfig(PacketPtr pkt); - Tick readConfig(PacketPtr pkt); + Tick writeConfig(MemMapPacketPtr pkt); + Tick readConfig(MemMapPacketPtr pkt); void setDmaComplete(IdeDisk *disk); - Tick read(PacketPtr pkt); - Tick write(PacketPtr pkt); + Tick read(MemMapPacketPtr pkt); + Tick write(MemMapPacketPtr pkt); void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); diff -r 6548721032fa -r 042f38a2e360 src/dev/ide_ctrl.cc --- a/src/dev/ide_ctrl.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/ide_ctrl.cc Fri Aug 05 18:14:06 2011 +0100 @@ -107,11 +107,11 @@ primary.select(false); secondary.select(false); - if ((BARAddrs[0] & ~BAR_IO_MASK) && !legacyIO[0]){ + if ((BARAddrs[0] & ~BAR_IO_MASK) != 0){ primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0]; primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARAddrs[1]; } - if ((BARAddrs[2] & ~BAR_IO_MASK) && !legacyIO[2]){ + if ((BARAddrs[2] & ~BAR_IO_MASK) != 0){ secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2]; secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARAddrs[3]; } @@ -151,7 +151,7 @@ } Tick -IdeController::readConfig(PacketPtr pkt) +IdeController::readConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) { @@ -223,7 +223,7 @@ Tick -IdeController::writeConfig(PacketPtr pkt) +IdeController::writeConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) { @@ -432,7 +432,7 @@ } void -IdeController::dispatchAccess(PacketPtr pkt, bool read) +IdeController::dispatchAccess(MemMapPacketPtr pkt, bool read) { pkt->allocate(); if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) @@ -496,14 +496,14 @@ } Tick -IdeController::read(PacketPtr pkt) +IdeController::read(MemMapPacketPtr pkt) { dispatchAccess(pkt, true); return pioDelay; } Tick -IdeController::write(PacketPtr pkt) +IdeController::write(MemMapPacketPtr pkt) { dispatchAccess(pkt, false); return pioDelay; diff -r 6548721032fa -r 042f38a2e360 src/dev/io_device.hh --- a/src/dev/io_device.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/io_device.hh Fri Aug 05 18:14:06 2011 +0100 @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * @@ -27,6 +39,7 @@ * * Authors: Ali Saidi * Nathan Binkert + * Andreas Hansson */ #ifndef __DEV_IO_DEVICE_HH__ @@ -34,146 +47,63 @@ #include "base/fast_alloc.hh" #include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/tport.hh" +#include "mem/packet_queue.hh" +#include "params/PioDevice.hh" #include "params/BasicPioDevice.hh" -#include "params/DmaDevice.hh" -#include "params/PioDevice.hh" -#include "sim/sim_object.hh" class Event; class Platform; -class PioDevice; -class DmaDevice; class System; /** - * The PioPort class is a programmed i/o port that all devices that are - * sensitive to an address range use. The port takes all the memory - * access types and roles them into one read() and write() call that the device - * must respond to. The device must also provide the addressRanges() function - * with which it returns the address ranges it is interested in. + * This device is the base class which all devices senstive to an + * address range inherit from. There are three pure virtual functions + * which all devices must implement getAddrRanges(), read(), and + * write(). */ -class PioPort : public SimpleTimingPort +class PioDevice : public MemObject, public MemMapSlaveInterface { - protected: - /** The device that this port serves. */ - PioDevice *device; - - virtual Tick recvAtomic(PacketPtr pkt); - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); public: - PioPort(PioDevice *dev, System *s, std::string pname = "-pioport"); -}; + /** + * Create an enum for the slave id so that our subclasses can + * extend the list in a reasonable manner. + */ + enum SlaveId {PIO_SLAVE_ID, N_SLAVE_ID}; + protected: -class DmaPort : public Port -{ - protected: - struct DmaReqState : public Packet::SenderState, public FastAlloc + /** + * Create an event class to schedule the accepting of requests. + */ + class AcceptReqEvent : public Event { - /** Event to call on the device when this transaction (all packets) - * complete. */ - Event *completionEvent; - /** Where we came from for some sanity checking. */ - Port *outPort; + private: - /** Total number of bytes that this transaction involves. */ - Addr totBytes; + /** Device owning this event */ + PioDevice& device; - /** Number of bytes that have been acked for this transaction. */ - Addr numBytes; + /** One event per slave, so track the slave id. */ + int slaveId; - /** Amount to delay completion of dma by */ - Tick delay; + public: + /** For each packet, we need to keep track of it until accepted. */ + MemMapPacketPtr reqPkt; - DmaReqState(Event *ce, Port *p, Addr tb, Tick _delay) - : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0), - delay(_delay) - {} + /** Create a new accept request event */ + AcceptReqEvent(PioDevice& device, int slave_id) : device(device), + slaveId(slave_id), + reqPkt(NULL) { + } + + void process() { device.acceptReq(reqPkt, slaveId); } + + const char *description() const { return "Accept Request Event"; } }; - MemObject *device; - std::list transmitList; - - /** The system that device/port are in. This is used to select which mode - * we are currently operating in. */ - System *sys; - - /** Number of outstanding packets the dma port has. */ - int pendingCount; - - /** If a dmaAction is in progress. */ - int actionInProgress; - - /** If we need to drain, keep the drain event around until we're done - * here.*/ - Event *drainEvent; - - /** time to wait between sending another packet, increases as NACKs are - * recived, decreases as responses are recived. */ - Tick backoffTime; - - /** Minimum time that device should back off for after failed sendTiming */ - Tick minBackoffDelay; - - /** Maximum time that device should back off for after failed sendTiming */ - Tick maxBackoffDelay; - - /** If the port is currently waiting for a retry before it can send whatever - * it is that it's sending. */ - bool inRetry; - - virtual bool recvTiming(PacketPtr pkt); - virtual Tick recvAtomic(PacketPtr pkt) - { panic("dma port shouldn't be used for pio access."); M5_DUMMY_RETURN } - virtual void recvFunctional(PacketPtr pkt) - { panic("dma port shouldn't be used for pio access."); } - - virtual void recvStatusChange(Status status) - { ; } - - virtual void recvRetry() ; - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { resp.clear(); snoop = false; } - - void queueDma(PacketPtr pkt, bool front = false); - void sendDma(); - - /** event to give us a kick every time we backoff time is reached. */ - EventWrapper backoffEvent; - - public: - DmaPort(MemObject *dev, System *s, Tick min_backoff, Tick max_backoff); - - void dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, - uint8_t *data, Tick delay, Request::Flags flag = 0); - - bool dmaPending() { return pendingCount > 0; } - - unsigned cacheBlockSize() const { return peerBlockSize(); } - unsigned int drain(Event *de); -}; - -/** - * This device is the base class which all devices senstive to an address range - * inherit from. There are three pure virtual functions which all devices must - * implement addressRanges(), read(), and write(). The magic do choose which - * mode we are in, etc is handled by the PioPort so the device doesn't have to - * bother. - */ -class PioDevice : public MemObject -{ - protected: - /** The platform we are in. This is used to decide what type of memory * transaction we should perform. */ Platform *platform; @@ -182,25 +112,43 @@ /** The pioPort that handles the requests for us and provides us requests * that it sees. */ - PioPort *pioPort; + MemMapSlavePort pioPort; + RespPacketQueue4Phase pioRespPktQueue; - virtual void addressRanges(AddrRangeList &range_list) = 0; + std::vector* > respPktQueues; + + virtual AddrRangeList getAddrRanges(int slave_id) const = 0; + + virtual unsigned getBlockSize(int slave_id) const; + + virtual void recvDebug(MemMapPacketPtr pkt, int slave_id); + + virtual Tick recvAtomic(MemMapPacketPtr reqPkt, int slave_id); + + void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + void endResp(MemMapPacketPtr resp_pkt, int slave_id); /** Pure virtual function that the device must implement. Called * when a read command is recieved by the port. * @param pkt Packet describing this request * @return number of ticks it took to complete */ - virtual Tick read(PacketPtr pkt) = 0; + virtual Tick read(MemMapPacketPtr pkt) = 0; /** Pure virtual function that the device must implement. Called when a * write command is recieved by the port. * @param pkt Packet describing this request * @return number of ticks it took to complete */ - virtual Tick write(PacketPtr pkt) = 0; + virtual Tick write(MemMapPacketPtr pkt) = 0; + + std::vector acceptReqEvents; + + void acceptReq(MemMapPacketPtr req_pkt, int slave_id); public: + typedef PioDeviceParams Params; PioDevice(const Params *p); virtual ~PioDevice(); @@ -215,23 +163,22 @@ virtual unsigned int drain(Event *de); - virtual Port *getPort(const std::string &if_name, int idx = -1) - { - if (if_name == "pio") { - if (pioPort != NULL) - fatal("%s: pio port already connected to %s", - name(), pioPort->getPeer()->name()); - pioPort = new PioPort(this, sys); - return pioPort; - } else - return NULL; - } - friend class PioPort; + /** + * Get a device specific colour. + * + * @param r red in 0-255 + * @param g green in 0-255 + * @param b blue in 0-255 + */ + virtual void getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const; }; class BasicPioDevice : public PioDevice { + friend class PioDevice; + protected: /** Address that the device listens to. */ Addr pioAddr; @@ -252,66 +199,10 @@ return dynamic_cast(_params); } - /** return the address ranges that this device responds to. - * @param range_list range list to populate with ranges + /** return the address range that this device responds to. */ - void addressRanges(AddrRangeList &range_list); + AddrRangeList getAddrRanges(int slave_id) const; }; -class DmaDevice : public PioDevice -{ - protected: - DmaPort *dmaPort; - - public: - typedef DmaDeviceParams Params; - DmaDevice(const Params *p); - virtual ~DmaDevice(); - - const Params * - params() const - { - return dynamic_cast(_params); - } - - void dmaWrite(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) - { - dmaPort->dmaAction(MemCmd::WriteReq, addr, size, event, data, delay); - } - - void dmaRead(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) - { - dmaPort->dmaAction(MemCmd::ReadReq, addr, size, event, data, delay); - } - - bool dmaPending() { return dmaPort->dmaPending(); } - - virtual unsigned int drain(Event *de); - - unsigned cacheBlockSize() const { return dmaPort->cacheBlockSize(); } - - virtual Port *getPort(const std::string &if_name, int idx = -1) - { - if (if_name == "pio") { - if (pioPort != NULL) - fatal("%s: pio port already connected to %s", - name(), pioPort->getPeer()->name()); - pioPort = new PioPort(this, sys); - return pioPort; - } else if (if_name == "dma") { - if (dmaPort != NULL) - fatal("%s: dma port already connected to %s", - name(), dmaPort->getPeer()->name()); - dmaPort = new DmaPort(this, sys, params()->min_backoff_delay, - params()->max_backoff_delay); - return dmaPort; - } else - return NULL; - } - - friend class DmaPort; -}; - - #endif // __DEV_IO_DEVICE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/io_device.cc --- a/src/dev/io_device.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/io_device.cc Fri Aug 05 18:14:06 2011 +0100 @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -27,59 +39,109 @@ * * Authors: Ali Saidi * Nathan Binkert + * Andreas Hansson */ -#include "base/chunk_generator.hh" #include "base/trace.hh" #include "dev/io_device.hh" #include "sim/system.hh" - -PioPort::PioPort(PioDevice *dev, System *s, std::string pname) - : SimpleTimingPort(dev->name() + pname, dev), device(dev) -{ } - - -Tick -PioPort::recvAtomic(PacketPtr pkt) +PioDevice::PioDevice(const Params *p) + : MemObject(p), platform(p->platform), sys(p->system), + pioPort(*this, this, "pio", PIO_SLAVE_ID), + pioRespPktQueue(this, pioPort) { - return pkt->isRead() ? device->read(pkt) : device->write(pkt); + // create an accept event for the PIO port and also keep track of + // its response queue on index 0 + assert(PIO_SLAVE_ID == 0); + acceptReqEvents.push_back(new AcceptReqEvent(*this, PIO_SLAVE_ID)); + respPktQueues.push_back(&pioRespPktQueue); + regPort(&pioPort); } -void -PioPort::getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) -{ - snoop = false; - device->addressRanges(resp); - for (AddrRangeIter i = resp.begin(); i != resp.end(); i++) - DPRINTF(BusAddrRanges, "Adding Range %#x-%#x\n", i->start, i->end); -} - - -PioDevice::PioDevice(const Params *p) - : MemObject(p), platform(p->platform), sys(p->system), pioPort(NULL) -{} - PioDevice::~PioDevice() { - if (pioPort) - delete pioPort; } void PioDevice::init() { - if (!pioPort) - panic("Pio port not connected to anything!"); - pioPort->sendStatusChange(Port::RangeChange); + if (!pioPort.isConnected()) + panic("PIO port on %s is not connected!", name()); } +unsigned +PioDevice::getBlockSize(int slave_id) const +{ + // at the moment the bus only uses this to determine the maximum + // block size, so 0 is a safe default + return 0; +} + +void +PioDevice::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + // fall back on atomic and throw away the time + recvAtomic(pkt, slave_id); +} + +Tick +PioDevice::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + return pkt->isRead() ? read(pkt) : write(pkt); +} + +void +PioDevice::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + if (!req_pkt->memInhibitAsserted()) { + // perform an atomic access which also turns the packet around to + // go back to requester if response expected + Tick latency = recvAtomic(req_pkt, slave_id); + if (req_pkt->isResponse()) { + respPktQueues[slave_id]->insert(req_pkt, curTick() + latency); + } + } + + // even in the case of an inhibited request we have to + // accept and end the request + + // let the accept event keep track of the packet until we + // accept it, we should never get a new beginReq before the + // current one has ended, and so the reqPkt should be NULL + assert(acceptReqEvents[slave_id]->reqPkt == NULL); + acceptReqEvents[slave_id]->reqPkt = req_pkt; + + // schedule to accept after the request accept delay + schedule(acceptReqEvents[slave_id], curTick()); +} + +void +PioDevice::acceptReq(MemMapPacketPtr req_pkt, int slave_id) +{ + // consider the request to end here + (*memSlavePorts[slave_id])->endReq(req_pkt); + + // our responsibility to delete packet if inhibited or no response + if (req_pkt->memInhibitAsserted() || !req_pkt->isResponse()) + delete req_pkt; + + // ready for a new one + acceptReqEvents[slave_id]->reqPkt = NULL; +} + +void +PioDevice::endResp(MemMapPacketPtr resp_pkt, int slave_id) +{ + // tell the packet queue that the current packet is transfered and + // that it can go on to the next one + respPktQueues[slave_id]->endDeferred(); +} unsigned int PioDevice::drain(Event *de) { - unsigned int count; - count = pioPort->drain(de); + unsigned int count = pioRespPktQueue.drain(de); if (count) changeState(Draining); else @@ -87,261 +149,30 @@ return count; } +void +PioDevice::getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const +{ + r = 0x78; + g = 0xA4; + b = 0x93; +} + + BasicPioDevice::BasicPioDevice(const Params *p) : PioDevice(p), pioAddr(p->pio_addr), pioSize(0), pioDelay(p->pio_latency) {} -void -BasicPioDevice::addressRanges(AddrRangeList &range_list) +AddrRangeList +BasicPioDevice::getAddrRanges(int slave_id) const { - assert(pioSize != 0); - range_list.clear(); - DPRINTF(BusAddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize); - range_list.push_back(RangeSize(pioAddr, pioSize)); + assert(slave_id == PIO_SLAVE_ID); + if (pioSize == 0) { + panic("PIO device %s does not have a size", name()); + } + AddrRangeList ranges; + ranges.push_back(RangeSize(pioAddr, pioSize)); + return ranges; } - -DmaPort::DmaPort(MemObject *dev, System *s, Tick min_backoff, Tick max_backoff) - : Port(dev->name() + "-dmaport", dev), device(dev), sys(s), - pendingCount(0), actionInProgress(0), drainEvent(NULL), - backoffTime(0), minBackoffDelay(min_backoff), - maxBackoffDelay(max_backoff), inRetry(false), backoffEvent(this) -{ } - -bool -DmaPort::recvTiming(PacketPtr pkt) -{ - if (pkt->wasNacked()) { - DPRINTF(DMA, "Received nacked %s addr %#x\n", - pkt->cmdString(), pkt->getAddr()); - - if (backoffTime < minBackoffDelay) - backoffTime = minBackoffDelay; - else if (backoffTime < maxBackoffDelay) - backoffTime <<= 1; - - reschedule(backoffEvent, curTick() + backoffTime, true); - - DPRINTF(DMA, "Backoff time set to %d ticks\n", backoffTime); - - pkt->reinitNacked(); - queueDma(pkt, true); - } else if (pkt->senderState) { - DmaReqState *state; - backoffTime >>= 2; - - DPRINTF(DMA, "Received response %s addr %#x size %#x\n", - pkt->cmdString(), pkt->getAddr(), pkt->req->getSize()); - state = dynamic_cast(pkt->senderState); - pendingCount--; - - assert(pendingCount >= 0); - assert(state); - - state->numBytes += pkt->req->getSize(); - assert(state->totBytes >= state->numBytes); - if (state->totBytes == state->numBytes) { - if (state->completionEvent) { - if (state->delay) - schedule(state->completionEvent, curTick() + state->delay); - else - state->completionEvent->process(); - } - delete state; - } - delete pkt->req; - delete pkt; - - if (pendingCount == 0 && drainEvent) { - drainEvent->process(); - drainEvent = NULL; - } - } else { - panic("Got packet without sender state... huh?\n"); - } - - return true; -} - -DmaDevice::DmaDevice(const Params *p) - : PioDevice(p), dmaPort(NULL) -{ } - - -unsigned int -DmaDevice::drain(Event *de) -{ - unsigned int count; - count = pioPort->drain(de) + dmaPort->drain(de); - if (count) - changeState(Draining); - else - changeState(Drained); - return count; -} - -unsigned int -DmaPort::drain(Event *de) -{ - if (pendingCount == 0) - return 0; - drainEvent = de; - return 1; -} - - -void -DmaPort::recvRetry() -{ - assert(transmitList.size()); - bool result = true; - do { - PacketPtr pkt = transmitList.front(); - DPRINTF(DMA, "Retry on %s addr %#x\n", - pkt->cmdString(), pkt->getAddr()); - result = sendTiming(pkt); - if (result) { - DPRINTF(DMA, "-- Done\n"); - transmitList.pop_front(); - inRetry = false; - } else { - inRetry = true; - DPRINTF(DMA, "-- Failed, queued\n"); - } - } while (!backoffTime && result && transmitList.size()); - - if (transmitList.size() && backoffTime && !inRetry) { - DPRINTF(DMA, "Scheduling backoff for %d\n", curTick()+backoffTime); - if (!backoffEvent.scheduled()) - schedule(backoffEvent, backoffTime + curTick()); - } - DPRINTF(DMA, "TransmitList: %d, backoffTime: %d inRetry: %d es: %d\n", - transmitList.size(), backoffTime, inRetry, - backoffEvent.scheduled()); -} - - -void -DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, - uint8_t *data, Tick delay, Request::Flags flag) -{ - assert(device->getState() == SimObject::Running); - - DmaReqState *reqState = new DmaReqState(event, this, size, delay); - - - DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size, - event ? event->scheduled() : -1 ); - for (ChunkGenerator gen(addr, size, peerBlockSize()); - !gen.done(); gen.next()) { - Request *req = new Request(gen.addr(), gen.size(), flag); - PacketPtr pkt = new Packet(req, cmd, Packet::Broadcast); - - // Increment the data pointer on a write - if (data) - pkt->dataStatic(data + gen.complete()); - - pkt->senderState = reqState; - - assert(pendingCount >= 0); - pendingCount++; - DPRINTF(DMA, "--Queuing DMA for addr: %#x size: %d\n", gen.addr(), - gen.size()); - queueDma(pkt); - } - -} - -void -DmaPort::queueDma(PacketPtr pkt, bool front) -{ - - if (front) - transmitList.push_front(pkt); - else - transmitList.push_back(pkt); - sendDma(); -} - - -void -DmaPort::sendDma() -{ - // some kind of selction between access methods - // more work is going to have to be done to make - // switching actually work - assert(transmitList.size()); - PacketPtr pkt = transmitList.front(); - - Enums::MemoryMode state = sys->getMemoryMode(); - if (state == Enums::timing) { - if (backoffEvent.scheduled() || inRetry) { - DPRINTF(DMA, "Can't send immediately, waiting for retry or backoff timer\n"); - return; - } - - DPRINTF(DMA, "Attempting to send %s addr %#x\n", - pkt->cmdString(), pkt->getAddr()); - - bool result; - do { - result = sendTiming(pkt); - if (result) { - transmitList.pop_front(); - DPRINTF(DMA, "-- Done\n"); - } else { - inRetry = true; - DPRINTF(DMA, "-- Failed: queued\n"); - } - } while (result && !backoffTime && transmitList.size()); - - if (transmitList.size() && backoffTime && !inRetry && - !backoffEvent.scheduled()) { - DPRINTF(DMA, "-- Scheduling backoff timer for %d\n", - backoffTime+curTick()); - schedule(backoffEvent, backoffTime + curTick()); - } - } else if (state == Enums::atomic) { - transmitList.pop_front(); - - Tick lat; - DPRINTF(DMA, "--Sending DMA for addr: %#x size: %d\n", - pkt->req->getPaddr(), pkt->req->getSize()); - lat = sendAtomic(pkt); - assert(pkt->senderState); - DmaReqState *state = dynamic_cast(pkt->senderState); - assert(state); - state->numBytes += pkt->req->getSize(); - - DPRINTF(DMA, "--Received response for DMA for addr: %#x size: %d nb: %d, tot: %d sched %d\n", - pkt->req->getPaddr(), pkt->req->getSize(), state->numBytes, - state->totBytes, - state->completionEvent ? state->completionEvent->scheduled() : 0 ); - - if (state->totBytes == state->numBytes) { - if (state->completionEvent) { - assert(!state->completionEvent->scheduled()); - schedule(state->completionEvent, curTick() + lat + state->delay); - } - delete state; - delete pkt->req; - } - pendingCount--; - assert(pendingCount >= 0); - delete pkt; - - if (pendingCount == 0 && drainEvent) { - drainEvent->process(); - drainEvent = NULL; - } - - } else - panic("Unknown memory command state."); -} - -DmaDevice::~DmaDevice() -{ - if (dmaPort) - delete dmaPort; -} diff -r 6548721032fa -r 042f38a2e360 src/dev/isa_fake.hh --- a/src/dev/isa_fake.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/isa_fake.hh Fri Aug 05 18:14:06 2011 +0100 @@ -76,14 +76,14 @@ * @param pkt The memory request. * @param data Where to put the data. */ - virtual Tick read(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); /** * All writes are simply ignored. * @param pkt The memory request. * @param data the data to not write. */ - virtual Tick write(PacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); }; #endif // __ISA_FAKE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/isa_fake.cc --- a/src/dev/isa_fake.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/isa_fake.cc Fri Aug 05 18:14:06 2011 +0100 @@ -53,7 +53,7 @@ } Tick -IsaFake::read(PacketPtr pkt) +IsaFake::read(MemMapPacketPtr pkt) { pkt->makeAtomicResponse(); @@ -89,7 +89,7 @@ } Tick -IsaFake::write(PacketPtr pkt) +IsaFake::write(MemMapPacketPtr pkt) { pkt->makeAtomicResponse(); if (params()->warn_access != "") { diff -r 6548721032fa -r 042f38a2e360 src/dev/ns_gige.hh --- a/src/dev/ns_gige.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/ns_gige.hh Fri Aug 05 18:14:06 2011 +0100 @@ -356,10 +356,10 @@ virtual EtherInt *getEthPort(const std::string &if_name, int idx); - virtual Tick writeConfig(PacketPtr pkt); + virtual Tick writeConfig(MemMapPacketPtr pkt); - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); bool cpuIntrPending() const; void cpuIntrAck() { cpuIntrClear(); } diff -r 6548721032fa -r 042f38a2e360 src/dev/ns_gige.cc --- a/src/dev/ns_gige.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/ns_gige.cc Fri Aug 05 18:14:06 2011 +0100 @@ -135,7 +135,7 @@ * This is to write to the PCI general configuration registers */ Tick -NSGigE::writeConfig(PacketPtr pkt) +NSGigE::writeConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) @@ -174,7 +174,7 @@ * spec sheet */ Tick -NSGigE::read(PacketPtr pkt) +NSGigE::read(MemMapPacketPtr pkt) { assert(ioEnable); @@ -398,7 +398,7 @@ } Tick -NSGigE::write(PacketPtr pkt) +NSGigE::write(MemMapPacketPtr pkt) { assert(ioEnable); diff -r 6548721032fa -r 042f38a2e360 src/dev/pciconfigall.hh --- a/src/dev/pciconfigall.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/pciconfigall.hh Fri Aug 05 18:14:06 2011 +0100 @@ -69,7 +69,7 @@ * @param pkt Contains information about the read operation * @return Amount of time to do the read */ - virtual Tick read(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); /** * Write to PCI config spcae. If the device does not exit the simulator @@ -79,9 +79,11 @@ * @return Amount of time to do the read */ - virtual Tick write(PacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); - void addressRanges(AddrRangeList &range_list); + AddrRangeList getAddrRanges(int slave_id) const; + + unsigned getBlockSize(int slave_id) const; private: Addr pioAddr; diff -r 6548721032fa -r 042f38a2e360 src/dev/pciconfigall.cc --- a/src/dev/pciconfigall.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/pciconfigall.cc Fri Aug 05 18:14:06 2011 +0100 @@ -52,7 +52,7 @@ Tick -PciConfigAll::read(PacketPtr pkt) +PciConfigAll::read(MemMapPacketPtr pkt) { pkt->allocate(); @@ -78,20 +78,29 @@ } Tick -PciConfigAll::write(PacketPtr pkt) +PciConfigAll::write(MemMapPacketPtr pkt) { panic("Attempting to write to config space on non-existant device\n"); M5_DUMMY_RETURN } -void -PciConfigAll::addressRanges(AddrRangeList &range_list) +AddrRangeList +PciConfigAll::getAddrRanges(int slave_id) const { - range_list.clear(); - range_list.push_back(RangeSize(pioAddr, params()->size)); + assert(slave_id == PIO_SLAVE_ID); + AddrRangeList ranges; + ranges.push_back(RangeSize(pioAddr, params()->size)); + return ranges; } +unsigned +PciConfigAll::getBlockSize(int slave_id) const +{ + // at the moment the bus only uses this to determine the maximum + // block size, so 0 is a safe default + return 0; +} #ifndef DOXYGEN_SHOULD_SKIP_THIS diff -r 6548721032fa -r 042f38a2e360 src/dev/pcidev.hh --- a/src/dev/pcidev.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/pcidev.hh Fri Aug 05 18:14:06 2011 +0100 @@ -39,7 +39,7 @@ #include -#include "dev/io_device.hh" +#include "dev/dma_device.hh" #include "dev/pcireg.h" #include "dev/platform.hh" #include "params/PciDevice.hh" @@ -51,35 +51,20 @@ #define BAR_IO_SPACE(x) ((x) & BAR_IO_SPACE_BIT) #define BAR_NUMBER(x) (((x) - PCI0_BASE_ADDR0) >> 0x2); - - /** * PCI device, base implementation is only config space. */ class PciDev : public DmaDevice { - class PciConfigPort : public SimpleTimingPort - { - protected: - PciDev *device; - virtual Tick recvAtomic(PacketPtr pkt); + private: - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); - - Platform *platform; - - int busId; - int deviceId; - int functionId; - - Addr configAddr; - - public: - PciConfigPort(PciDev *dev, int busid, int devid, int funcid, - Platform *p); - }; + /** + * Used to enumberate the slave ports as this device has both the + * normal PIO slave port and an additional config slave. + */ + enum SlaveId {CONFIG_SLAVE_ID = DmaDevice::N_SLAVE_ID, + N_SLAVE_ID}; public: typedef PciDeviceParams Params; @@ -150,9 +135,12 @@ protected: Platform *plat; + Tick pioDelay; Tick configDelay; - PciConfigPort *configPort; + Addr configAddr; + MemMapSlavePort configPort; + RespPacketQueue4Phase configRespPktQueue; /** * Write to the PCI config space data that is stored locally. This may be @@ -160,7 +148,7 @@ * for normal operations that it does not need to override. * @param pkt packet containing the write the offset into config space */ - virtual Tick writeConfig(PacketPtr pkt); + virtual Tick writeConfig(MemMapPacketPtr pkt); /** @@ -169,7 +157,7 @@ * for normal operations that it does not need to override. * @param pkt packet containing the write the offset into config space */ - virtual Tick readConfig(PacketPtr pkt); + virtual Tick readConfig(MemMapPacketPtr pkt); public: Addr pciToDma(Addr pciAddr) const @@ -187,11 +175,6 @@ interruptLine() { return letoh(config.interruptLine); } - /** return the address ranges that this device responds to. - * @params range_list range list to populate with ranges - */ - void addressRanges(AddrRangeList &range_list); - /** * Constructor for PCI Dev. This function copies data from the * config file object PCIConfigData and registers the device with @@ -217,18 +200,15 @@ virtual unsigned int drain(Event *de); - virtual Port *getPort(const std::string &if_name, int idx = -1) - { - if (if_name == "config") { - if (configPort != NULL) - panic("pciconfig port already connected to."); - configPort = new PciConfigPort(this, params()->pci_bus, - params()->pci_dev, params()->pci_func, - params()->platform); - return configPort; - } - return DmaDevice::getPort(if_name, idx); - } + /** + * Implementation of the slave interface members. + */ + AddrRangeList getAddrRanges(int slave_id) const; + + void recvDebug(MemMapPacketPtr pkt, int slave_id); + + Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + }; #endif // __DEV_PCIDEV_HH__ diff -r 6548721032fa -r 042f38a2e360 src/dev/pcidev.cc --- a/src/dev/pcidev.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/pcidev.cc Fri Aug 05 18:14:06 2011 +0100 @@ -53,38 +53,62 @@ using namespace std; - -PciDev::PciConfigPort::PciConfigPort(PciDev *dev, int busid, int devid, - int funcid, Platform *p) - : SimpleTimingPort(dev->name() + "-pciconf", dev), device(dev), - platform(p), busId(busid), deviceId(devid), functionId(funcid) +void +PciDev::recvDebug(MemMapPacketPtr reqPkt, int slave_id) { - configAddr = platform->calcPciConfigAddr(busId, deviceId, functionId); + if (slave_id == CONFIG_SLAVE_ID) { + if (!configRespPktQueue.checkFunctional(reqPkt)) + recvAtomic(reqPkt, slave_id); + } else { + // not for the config port, fall back on the one above us + DmaDevice::recvDebug(reqPkt, slave_id); + } } - Tick -PciDev::PciConfigPort::recvAtomic(PacketPtr pkt) +PciDev::recvAtomic(MemMapPacketPtr pkt, int slave_id) { - assert(pkt->getAddr() >= configAddr && - pkt->getAddr() < configAddr + PCI_CONFIG_SIZE); - return pkt->isRead() ? device->readConfig(pkt) : device->writeConfig(pkt); + if (slave_id == CONFIG_SLAVE_ID) { + assert(pkt->getAddr() >= configAddr && + pkt->getAddr() < configAddr + PCI_CONFIG_SIZE); + return pkt->isRead() ? readConfig(pkt) : writeConfig(pkt); + } else { + // not for the config port, fall back on the one above us + return DmaDevice::recvAtomic(pkt, slave_id); + } } -void -PciDev::PciConfigPort::getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) +AddrRangeList +PciDev::getAddrRanges(int slave_id) const { - snoop = false;; - if (configAddr != ULL(-1)) - resp.push_back(RangeSize(configAddr, PCI_CONFIG_SIZE+1)); + AddrRangeList ranges; + + if (slave_id == CONFIG_SLAVE_ID) { + if (configAddr != ULL(-1)) + ranges.push_back(RangeSize(configAddr, PCI_CONFIG_SIZE+1)); + } else if (slave_id == PIO_SLAVE_ID) { + // the pci device overrides the normal slave address ranges of + // the PIO port + int x = 0; + for (x = 0; x < 6; x++) + if (BARAddrs[x] != 0) + ranges.push_back(RangeSize(BARAddrs[x],BARSize[x])); + } else { + assert(false); + } + + return ranges; } - PciDev::PciDev(const Params *p) : DmaDevice(p), plat(p->platform), pioDelay(p->pio_latency), - configDelay(p->config_latency), configPort(NULL) + configDelay(p->config_latency), + configPort(*this, this, "config", CONFIG_SLAVE_ID), + configRespPktQueue(this, configPort) { + configAddr = p->platform->calcPciConfigAddr(p->pci_bus, p->pci_dev, + p->pci_func); + config.vendor = htole(p->VendorID); config.device = htole(p->DeviceID); config.command = htole(p->Command); @@ -144,14 +168,17 @@ plat->registerPciDevice(0, p->pci_dev, p->pci_func, letoh(config.interruptLine)); + + acceptReqEvents.push_back(new AcceptReqEvent(*this, CONFIG_SLAVE_ID)); + respPktQueues.push_back(&configRespPktQueue); + regPort(&configPort); } void PciDev::init() { - if (!configPort) - panic("pci config port not connected to anything!"); - configPort->sendStatusChange(Port::RangeChange); + if (!configPort.isConnected()) + panic("PCI config port on %s not connected!", name()); PioDevice::init(); } @@ -159,7 +186,8 @@ PciDev::drain(Event *de) { unsigned int count; - count = pioPort->drain(de) + dmaPort->drain(de) + configPort->drain(de); + count = pioRespPktQueue.drain(de) + dmaReqPktQueue.drain(de) + + configRespPktQueue.drain(de); if (count) changeState(Draining); else @@ -168,7 +196,7 @@ } Tick -PciDev::readConfig(PacketPtr pkt) +PciDev::readConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset >= PCI_DEVICE_SPECIFIC) @@ -206,18 +234,8 @@ } -void -PciDev::addressRanges(AddrRangeList &range_list) -{ - int x = 0; - range_list.clear(); - for (x = 0; x < 6; x++) - if (BARAddrs[x] != 0) - range_list.push_back(RangeSize(BARAddrs[x],BARSize[x])); -} - Tick -PciDev::writeConfig(PacketPtr pkt) +PciDev::writeConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset >= PCI_DEVICE_SPECIFIC) @@ -300,7 +318,9 @@ BARAddrs[barnum] = BAR_IO_SPACE(he_old_bar) ? platform->calcPciIOAddr(he_new_bar) : platform->calcPciMemAddr(he_new_bar); - pioPort->sendStatusChange(Port::RangeChange); + + // dynamically updated address range + pioPort->sendRangeChange(); } } config.baseAddr[barnum] = htole((he_new_bar & ~bar_mask) | @@ -353,7 +373,6 @@ UNSERIALIZE_ARRAY(BARAddrs, sizeof(BARAddrs) / sizeof(BARAddrs[0])); UNSERIALIZE_ARRAY(config.data, sizeof(config.data) / sizeof(config.data[0])); - pioPort->sendStatusChange(Port::RangeChange); } diff -r 6548721032fa -r 042f38a2e360 src/dev/platform.hh --- a/src/dev/platform.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/platform.hh Fri Aug 05 18:14:06 2011 +0100 @@ -41,7 +41,6 @@ #include #include "sim/sim_object.hh" -#include "arch/isa_traits.hh" #include "params/Platform.hh" class PciConfigAll; diff -r 6548721032fa -r 042f38a2e360 src/dev/platform.cc --- a/src/dev/platform.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/platform.cc Fri Aug 05 18:14:06 2011 +0100 @@ -35,7 +35,6 @@ #include "sim/sim_exit.hh" using namespace std; -using namespace TheISA; Platform::Platform(const Params *p) : SimObject(p), intrctrl(p->intrctrl) diff -r 6548721032fa -r 042f38a2e360 src/dev/simple_disk.hh --- a/src/dev/simple_disk.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/simple_disk.hh Fri Aug 05 18:14:06 2011 +0100 @@ -36,9 +36,10 @@ #define __DEV_SIMPLE_DISK_HH__ #include "sim/sim_object.hh" -#include "arch/isa_traits.hh" #include "params/SimpleDisk.hh" +#include "mem/port_proxy.hh" + class DiskImage; class System; @@ -53,12 +54,15 @@ protected: System *system; DiskImage *image; + PortProxy *memProxy; public: typedef SimpleDiskParams Params; SimpleDisk(const Params *p); ~SimpleDisk(); + virtual void init(); + void read(Addr addr, baddr_t block, int count) const; void write(Addr addr, baddr_t block, int count); }; diff -r 6548721032fa -r 042f38a2e360 src/dev/simple_disk.cc --- a/src/dev/simple_disk.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/simple_disk.cc Fri Aug 05 18:14:06 2011 +0100 @@ -50,11 +50,19 @@ using namespace std; SimpleDisk::SimpleDisk(const Params *p) - : SimObject(p), system(p->system), image(p->disk) + : SimObject(p), system(p->system), image(p->disk), memProxy(NULL) {} SimpleDisk::~SimpleDisk() -{} +{ + delete memProxy; +} + +void +SimpleDisk::init() +{ + memProxy = new PortProxy(*systemPort()); +} void @@ -68,7 +76,8 @@ for (int i = 0, j = 0; i < count; i += SectorSize, j++) image->read(data + i, block + j); - system->functionalPort->writeBlob(addr, data, count); + //system->functionalPort->writeBlob(addr, data, count); + memProxy->writeMem(addr, data, count); DPRINTF(SimpleDisk, "read block=%#x len=%d\n", (uint64_t)block, count); DDUMP(SimpleDiskData, data, count); diff -r 6548721032fa -r 042f38a2e360 src/dev/sinic.hh --- a/src/dev/sinic.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/sinic.hh Fri Aug 05 18:14:06 2011 +0100 @@ -270,8 +270,8 @@ * Memory Interface */ public: - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); virtual void resume(); void prepareIO(int cpu, int index); diff -r 6548721032fa -r 042f38a2e360 src/dev/sinic.cc --- a/src/dev/sinic.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/sinic.cc Fri Aug 05 18:14:06 2011 +0100 @@ -375,7 +375,7 @@ * I/O read of device register */ Tick -Device::read(PacketPtr pkt) +Device::read(MemMapPacketPtr pkt) { assert(config.command & PCI_CMD_MSE); assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); @@ -462,7 +462,7 @@ * I/O write of device register */ Tick -Device::write(PacketPtr pkt) +Device::write(MemMapPacketPtr pkt) { assert(config.command & PCI_CMD_MSE); assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); @@ -1709,9 +1709,6 @@ UNSERIALIZE_SCALAR(transmitTick); if (transmitTick) schedule(txEvent, curTick() + transmitTick); - - pioPort->sendStatusChange(Port::RangeChange); - } } // namespace Sinic diff -r 6548721032fa -r 042f38a2e360 src/dev/uart8250.hh --- a/src/dev/uart8250.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/uart8250.hh Fri Aug 05 18:14:06 2011 +0100 @@ -98,8 +98,8 @@ } Uart8250(const Params *p); - virtual Tick read(PacketPtr pkt); - virtual Tick write(PacketPtr pkt); + virtual Tick read(MemMapPacketPtr pkt); + virtual Tick write(MemMapPacketPtr pkt); virtual void addressRanges(AddrRangeList &range_list); /** diff -r 6548721032fa -r 042f38a2e360 src/dev/uart8250.cc --- a/src/dev/uart8250.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/dev/uart8250.cc Fri Aug 05 18:14:06 2011 +0100 @@ -108,7 +108,7 @@ } Tick -Uart8250::read(PacketPtr pkt) +Uart8250::read(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == 1); @@ -190,7 +190,7 @@ } Tick -Uart8250::write(PacketPtr pkt) +Uart8250::write(MemMapPacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); diff -r 6548721032fa -r 042f38a2e360 src/mem/Bridge.py --- a/src/mem/Bridge.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/Bridge.py Fri Aug 05 18:14:06 2011 +0100 @@ -1,3 +1,15 @@ +# Copyright (c) 2011 ARM Limited +# All rights reserved +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2006-2007 The Regents of The University of Michigan # All rights reserved. # @@ -25,22 +37,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Authors: Ali Saidi +# Andreas Hansson from m5.params import * from MemObject import MemObject class Bridge(MemObject): type = 'Bridge' - side_a = Port('Side A port') - side_b = Port('Side B port') - req_size_a = Param.Int(16, "The number of requests to buffer") - req_size_b = Param.Int(16, "The number of requests to buffer") - resp_size_a = Param.Int(16, "The number of requests to buffer") - resp_size_b = Param.Int(16, "The number of requests to buffer") + slave = Port("MEM_MAP", "SLAVE", 'Slave port') + master = Port("MEM_MAP", "MASTER", 'Master port') + req_size = Param.Int(16, "The number of requests to buffer") + resp_size = Param.Int(16, "The number of responses to buffer") delay = Param.Latency('0ns', "The latency of this bridge") - nack_delay = Param.Latency('0ns', "The latency of this bridge") - write_ack = Param.Bool(False, "Should this bridge ack writes") - filter_ranges_a = VectorParam.AddrRange([], - "What addresses shouldn't be passed through the side of the bridge") - filter_ranges_b = VectorParam.AddrRange([], - "What addresses shouldn't be passed through the side of the bridge") + ranges = VectorParam.AddrRange([], + "Address ranges to pass through the bridge") diff -r 6548721032fa -r 042f38a2e360 src/mem/Bridge4PhaseToClassic.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/Bridge4PhaseToClassic.py Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,44 @@ +# Copyright (c) 2011 ARM Limited +# All rights reserved +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Geza Lore + +from m5.params import * +from MemObject import MemObject + +class Bridge4PhaseToClassic(MemObject): + type = 'Bridge4PhaseToClassic' + slave = Port("MEM_MAP", "SLAVE", 'Slave port') + master = Port("MEM_MAP", "MASTER", 'Master port') diff -r 6548721032fa -r 042f38a2e360 src/mem/BridgeClassicTo4Phase.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/BridgeClassicTo4Phase.py Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,44 @@ +# Copyright (c) 2011 ARM Limited +# All rights reserved +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Geza Lore + +from m5.params import * +from MemObject import MemObject + +class BridgeClassicTo4Phase(MemObject): + type = 'BridgeClassicTo4Phase' + slave = Port("MEM_MAP", "SLAVE", 'Slave port') + master = Port("MEM_MAP", "MASTER", 'Master port') diff -r 6548721032fa -r 042f38a2e360 src/mem/CoherentBus.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/CoherentBus.py Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,50 @@ +# Copyright (c) 2011 ARM Limited +# All rights reserved +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Copyright (c) 2005-2008 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 +# Andreas Hansson + +from m5.params import * +from NonCoherentBus import NonCoherentBus + +class CoherentBus(NonCoherentBus): + type = 'CoherentBus' + cache_slave = VectorPort("CACHE_MNT", "SLAVE", + "cache maintenance slave port(s)") + cache_master = VectorPort("CACHE_MNT", "MASTER", + "cache maintenance master port(s)") diff -r 6548721032fa -r 042f38a2e360 src/mem/NonCoherentBus.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/NonCoherentBus.py Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,56 @@ +# Copyright (c) 2011 ARM Limited +# All rights reserved +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Copyright (c) 2005-2008 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: Andreas Hansson +# Geza Lore + +from m5.params import * +from MemObject import MemObject + +class NonCoherentBus(MemObject): + type = 'NonCoherentBus' + mem_slave = VectorPort("MEM_MAP", "SLAVE", "memory-mapped slave port(s)") + mem_master = VectorPort("MEM_MAP", "MASTER", "memory-mapped master port(s)") + clock = Param.Clock("1GHz", "bus clock speed") + header_cycles = Param.Int(1, "cycles of overhead per transaction") + width = Param.Int(64, "bus width in (bytes)") + block_size = Param.Int(64, "The default block size (in bytes) if one isn't set by any device attached to the bus.") + default_mem_master = \ + Port("MEM_MAP", "MASTER", "Default master port") + use_default_range = \ + Param.Bool(False, "Only use the default port if an address is in its range.") diff -r 6548721032fa -r 042f38a2e360 src/mem/PhysicalMemory.py --- a/src/mem/PhysicalMemory.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/PhysicalMemory.py Fri Aug 05 18:14:06 2011 +0100 @@ -32,28 +32,10 @@ class PhysicalMemory(MemObject): type = 'PhysicalMemory' - port = VectorPort("the access port") + port = Port("MEM_MAP", "SLAVE", "the access port") range = Param.AddrRange(AddrRange('128MB'), "Device Address") file = Param.String('', "memory mapped file") latency = Param.Latency('30ns', "latency of an access") latency_var = Param.Latency('0ns', "access variablity") zero = Param.Bool(False, "zero initialize memory") null = Param.Bool(False, "do not store data, always return zero") - -class DRAMMemory(PhysicalMemory): - type = 'DRAMMemory' - # Many of these should be observed from the configuration - cpu_ratio = Param.Int(5,"ratio between CPU speed and memory bus speed") - mem_type = Param.String("SDRAM", "Type of DRAM (DRDRAM, SDRAM)") - mem_actpolicy = Param.String("open", "Open/Close policy") - memctrladdr_type = Param.String("interleaved", "Mapping interleaved or direct") - bus_width = Param.Int(16, "") - act_lat = Param.Latency("2ns", "RAS to CAS delay") - cas_lat = Param.Latency("1ns", "CAS delay") - war_lat = Param.Latency("2ns", "write after read delay") - pre_lat = Param.Latency("2ns", "precharge delay") - dpl_lat = Param.Latency("2ns", "data in to precharge delay") - trc_lat = Param.Latency("6ns", "row cycle delay") - num_banks = Param.Int(4, "Number of Banks") - num_cpus = Param.Int(4, "Number of CPUs connected to DRAM") - diff -r 6548721032fa -r 042f38a2e360 src/mem/SConscript --- a/src/mem/SConscript Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/SConscript Fri Aug 05 18:14:06 2011 +0100 @@ -30,48 +30,41 @@ Import('*') +SimObject('MemObject.py') SimObject('Bridge.py') -SimObject('Bus.py') -SimObject('MemObject.py') +SimObject('NonCoherentBus.py') +SimObject('CoherentBus.py') +SimObject('BridgeClassicTo4Phase.py') +SimObject('Bridge4PhaseToClassic.py') -Source('bridge.cc') -Source('bus.cc') Source('mem_object.cc') Source('packet.cc') +Source('port_interface.cc') Source('port.cc') -Source('tport.cc') -Source('mport.cc') +Source('packet_queue.cc') +Source('bridge.cc') +Source('noncoherent_bus.cc') +Source('coherent_bus.cc') +Source('bridge_classic_to_4phase.cc') +Source('bridge_4phase_to_classic.cc') if env['TARGET_ISA'] != 'no': SimObject('PhysicalMemory.py') - Source('dram.cc') Source('physical.cc') + Source('port_proxy.cc') if env['FULL_SYSTEM']: - Source('vport.cc') + Source('fs_translating_proxy.cc') elif env['TARGET_ISA'] != 'no': Source('page_table.cc') - Source('translating_port.cc') + Source('se_translating_proxy.cc') TraceFlag('Bus') +TraceFlag('NCBus') TraceFlag('BusAddrRanges') TraceFlag('BusBridge') TraceFlag('LLSC') TraceFlag('MMU') TraceFlag('MemoryAccess') -TraceFlag('ProtocolTrace') -TraceFlag('RubyCache') -TraceFlag('RubyDma') -TraceFlag('RubyGenerated') -TraceFlag('RubyMemory') -TraceFlag('RubyNetwork') -TraceFlag('RubyQueue') -TraceFlag('RubyPort') -TraceFlag('RubySlicc') -TraceFlag('RubyStorebuffer') -TraceFlag('RubyTester') - -CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester', - 'RubyGenerated', 'RubySlicc', 'RubyStorebuffer', 'RubyCache', - 'RubyMemory', 'RubyDma']) +TraceFlag('Port4Phase') diff -r 6548721032fa -r 042f38a2e360 src/mem/bridge.hh --- a/src/mem/bridge.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/bridge.hh Fri Aug 05 18:14:06 2011 +0100 @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -27,187 +39,159 @@ * * Authors: Ali Saidi * Steve Reinhardt + * Andreas Hansson */ /** * @file - * Declaration of a simple bus bridge object with no buffering + * Declaration of a memory-mapped bridge that connects a master and a slave + * through a request and response queue. */ #ifndef __MEM_BRIDGE_HH__ #define __MEM_BRIDGE_HH__ -#include -#include -#include +#include "mem/mem_object.hh" +#include "mem/packet_queue.hh" +#include "params/Bridge.hh" -#include "base/fast_alloc.hh" -#include "base/types.hh" -#include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/port.hh" -#include "params/Bridge.hh" -#include "sim/eventq.hh" +/** + * A bridge is used to interface two different busses (or in general a + * memory-mapped master and slave), with buffering for requests and + * responses. + */ +class Bridge : public MemObject, + public MemMapSlaveInterface, + public MemMapMasterInterface +{ + public: -class Bridge : public MemObject -{ - protected: - /** Declaration of the buses port type, one will be instantiated for each - of the interfaces connecting to the bus. */ - class BridgePort : public Port - { - /** A pointer to the bridge to which this port belongs. */ - Bridge *bridge; - - /** - * Pointer to the port on the other side of the bridge - * (connected to the other bus). - */ - BridgePort *otherPort; - - /** Minimum delay though this bridge. */ - Tick delay; - - /** Min delay to respond to a nack. */ - Tick nackDelay; - - /** Pass ranges from one side of the bridge to the other? */ - std::vector > filterRanges; - - class PacketBuffer : public Packet::SenderState, public FastAlloc { - - public: - Tick ready; - PacketPtr pkt; - bool nackedHere; - Packet::SenderState *origSenderState; - short origSrc; - bool expectResponse; - - PacketBuffer(PacketPtr _pkt, Tick t, bool nack = false) - : ready(t), pkt(_pkt), nackedHere(nack), - origSenderState(_pkt->senderState), - origSrc(nack ? _pkt->getDest() : _pkt->getSrc() ), - expectResponse(_pkt->needsResponse() && !nack) - - { - if (!pkt->isResponse() && !nack) - pkt->senderState = this; - } - - void fixResponse(PacketPtr pkt) - { - assert(pkt->senderState == this); - pkt->setDest(origSrc); - pkt->senderState = origSenderState; - } - }; - - /** - * Outbound packet queue. Packets are held in this queue for a - * specified delay to model the processing delay of the - * bridge. - */ - std::list sendQueue; - - int outstandingResponses; - int queuedRequests; - - /** If we're waiting for a retry to happen.*/ - bool inRetry; - - /** Max queue size for outbound packets */ - int reqQueueLimit; - - /** Max queue size for reserved responses. */ - int respQueueLimit; - - /** - * Is this side blocked from accepting outbound packets? - */ - bool respQueueFull(); - bool reqQueueFull(); - - void queueForSendTiming(PacketPtr pkt); - - void finishSend(PacketBuffer *buf); - - void nackRequest(PacketPtr pkt); - - /** - * Handle send event, scheduled when the packet at the head of - * the outbound queue is ready to transmit (for timing - * accesses only). - */ - void trySend(); - - class SendEvent : public Event - { - BridgePort *port; - - public: - SendEvent(BridgePort *p) : port(p) {} - virtual void process() { port->trySend(); } - virtual const char *description() const { return "bridge send"; } - }; - - SendEvent sendEvent; - - public: - /** Constructor for the BusPort.*/ - BridgePort(const std::string &_name, Bridge *_bridge, - BridgePort *_otherPort, int _delay, int _nack_delay, - int _req_limit, int _resp_limit, - std::vector > filter_ranges); - - protected: - - /** When receiving a timing request from the peer port, - pass it to the bridge. */ - virtual bool recvTiming(PacketPtr pkt); - - /** When receiving a retry request from the peer port, - pass it to the bridge. */ - virtual void recvRetry(); - - /** When receiving a Atomic requestfrom the peer port, - pass it to the bridge. */ - virtual Tick recvAtomic(PacketPtr pkt); - - /** When receiving a Functional request from the peer port, - pass it to the bridge. */ - virtual void recvFunctional(PacketPtr pkt); - - /** When receiving a status changefrom the peer port, - pass it to the bridge. */ - virtual void recvStatusChange(Status status); - - /** When receiving a address range request the peer port, - pass it to the bridge. */ - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); - }; - - BridgePort portA, portB; - - /** If this bridge should acknowledge writes. */ - bool ackWrites; - - public: typedef BridgeParams Params; - protected: - Params *_params; - - public: - const Params *params() const { return _params; } - - /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1); + Bridge(Params *p); virtual void init(); - Bridge(Params *p); + unsigned int drain(Event *de); + + protected: + + /** Slave interface */ + unsigned int getBlockSize(int slave_id) const; + + AddrRangeList getAddrRanges(int slave_id) const; + + void recvDebug(MemMapPacketPtr pkt, int slave_id); + + Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + + void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + void endResp(MemMapPacketPtr resp_pkt, int slave_id); + /** slave interface ends here */ + + /** Master interface */ + void beginResp(MemMapPacketPtr resp_pkt, int master_id); + + void endReq(MemMapPacketPtr req_pkt, int master_id); + + void recvRangeChange(int master_id){}; + /** master interface ends here */ + + private: + + void releaseRespSpace(); + + void releaseReqSpace(); + + enum SlaveId {MEM_SLAVE_ID, N_SLAVE_ID}; + + enum MasterId {MEM_MASTER_ID, N_MASTER_ID}; + + /** The memory-mapped slave port and its associated packet queue. */ + MemMapSlavePort slavePort; + RespPacketQueue4Phase respPktQueue; + + /** The memory-mapped master port and its associated packet queue. */ + MemMapMasterPort masterPort; + ReqPacketQueue4Phase reqPktQueue; + + /** Minimum delay though this bridge. */ + Tick delay; + + /** Address ranges to pass through the bridge. */ + AddrRangeList ranges; + + /** + * Event used to have the response packet queue notify us to + * release response space on successful sends + */ + EventWrapper releaseRespSpaceEvent; + + /** + * Event used to have the request packet queue notify us to + * release request space on successful sends + */ + EventWrapper releaseReqSpaceEvent; + + /** + * The current request packet between beginning and ending the + * response. + */ + MemMapPacketPtr currReqPkt; + + /** + * Accept and end the current request. + */ + void acceptReq(); + + /** + * Event to schedule accepting of requests. + */ + EventWrapper acceptReqEvent; + + /** + * The current response packet between beginning and ending the + * response. + */ + MemMapPacketPtr currRespPkt; + + /** + * Accept and end the current response. + */ + void acceptResp(); + + /** + * Event to schedule accepting of responses. + */ + EventWrapper acceptRespEvent; + + /** + * Number of outstanding responses. + */ + int outstandingResponses; + + /** Max queue size for reserved responses. */ + int respQueueLimit; + + /** the number of queued requests */ + int queuedRequests; + + /** Max queue size for outbound packets */ + int reqQueueLimit; + + /** + * Is this side blocked from accepting outbound packets? + */ + bool reqQueueFull(); + + /** + * Is the response queue full. + */ + bool respQueueFull(); + }; -#endif //__MEM_BUS_HH__ +#endif //__MEM_BRIDGE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/bridge.cc --- a/src/mem/bridge.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/bridge.cc Fri Aug 05 18:14:06 2011 +0100 @@ -1,5 +1,17 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -28,330 +40,210 @@ * * Authors: Ali Saidi * Steve Reinhardt + * Andreas Hansson */ /** * @file - * Definition of a simple bus bridge without buffering. + * Definition of a simple bus bridge with buffering. */ -#include - -#include "base/range_ops.hh" #include "base/trace.hh" #include "mem/bridge.hh" -#include "params/Bridge.hh" - -Bridge::BridgePort::BridgePort(const std::string &_name, - Bridge *_bridge, BridgePort *_otherPort, - int _delay, int _nack_delay, int _req_limit, - int _resp_limit, - std::vector > filter_ranges) - : Port(_name, _bridge), bridge(_bridge), otherPort(_otherPort), - delay(_delay), nackDelay(_nack_delay), filterRanges(filter_ranges), - outstandingResponses(0), queuedRequests(0), inRetry(false), - reqQueueLimit(_req_limit), respQueueLimit(_resp_limit), sendEvent(this) -{ -} Bridge::Bridge(Params *p) : MemObject(p), - portA(p->name + "-portA", this, &portB, p->delay, p->nack_delay, - p->req_size_a, p->resp_size_a, p->filter_ranges_a), - portB(p->name + "-portB", this, &portA, p->delay, p->nack_delay, - p->req_size_b, p->resp_size_b, p->filter_ranges_b), - ackWrites(p->write_ack), _params(p) + slavePort(*this, this, "slave", MEM_SLAVE_ID), + respPktQueue(this, slavePort, &releaseRespSpaceEvent), + masterPort(*this, this, "master", MEM_MASTER_ID), + reqPktQueue(this, masterPort, &releaseReqSpaceEvent), + delay(p->delay), + ranges(p->ranges.begin(), p->ranges.end()), + releaseRespSpaceEvent(this), + releaseReqSpaceEvent(this), + currReqPkt(NULL), + acceptReqEvent(this), + currRespPkt(NULL), + acceptRespEvent(this), + outstandingResponses(0), respQueueLimit(p->resp_size), + queuedRequests(0), reqQueueLimit(p->req_size) { - if (ackWrites) - panic("No support for acknowledging writes\n"); + regPort(&masterPort); + regPort(&slavePort); } -Port * -Bridge::getPort(const std::string &if_name, int idx) -{ - BridgePort *port; - - if (if_name == "side_a") - port = &portA; - else if (if_name == "side_b") - port = &portB; - else - return NULL; - - if (port->getPeer() != NULL && !port->getPeer()->isDefaultPort()) - panic("bridge side %s already connected to %s.", - if_name, port->getPeer()->name()); - return port; -} - - void Bridge::init() { // Make sure that both sides are connected to. - if (!portA.isConnected() || !portB.isConnected()) - fatal("Both ports of bus bridge are not connected to a bus.\n"); - - if (portA.peerBlockSize() != portB.peerBlockSize()) - fatal("port A size %d, port B size %d \n " \ - "Busses don't have the same block size... Not supported.\n", - portA.peerBlockSize(), portB.peerBlockSize()); + if (!slavePort.isConnected() || !masterPort.isConnected()) + fatal("Both master and slave port of bridge must be connected.\n"); } bool -Bridge::BridgePort::respQueueFull() +Bridge::reqQueueFull() { - assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit); - return outstandingResponses >= respQueueLimit; + return queuedRequests == reqQueueLimit; } bool -Bridge::BridgePort::reqQueueFull() +Bridge::respQueueFull() { - assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit); - return queuedRequests >= reqQueueLimit; + return outstandingResponses == respQueueLimit; } -/** Function called by the port when the bus is receiving a Timing - * transaction.*/ -bool -Bridge::BridgePort::recvTiming(PacketPtr pkt) +unsigned int +Bridge::getBlockSize(int slave_id) const { - DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", - pkt->getSrc(), pkt->getDest(), pkt->getAddr()); + // Note that this is rather dangerous if there are cyclic + // structures created by e.g. two bridges in reverse direction + // connecting two buses (as in the case of I/O bus and main memory + // bus). Rely on the bus to break the cycle. + return masterPort->getBlockSize(); +} - DPRINTF(BusBridge, "Local queue size: %d outreq: %d outresp: %d\n", - sendQueue.size(), queuedRequests, outstandingResponses); - DPRINTF(BusBridge, "Remote queue size: %d outreq: %d outresp: %d\n", - otherPort->sendQueue.size(), otherPort->queuedRequests, - otherPort->outstandingResponses); - - if (pkt->isRequest() && otherPort->reqQueueFull()) { - DPRINTF(BusBridge, "Remote queue full, nacking\n"); - nackRequest(pkt); - return true; - } - - if (pkt->needsResponse()) { - if (respQueueFull()) { - DPRINTF(BusBridge, "Local queue full, no space for response, nacking\n"); - DPRINTF(BusBridge, "queue size: %d outreq: %d outstanding resp: %d\n", - sendQueue.size(), queuedRequests, outstandingResponses); - nackRequest(pkt); - return true; - } else { - DPRINTF(BusBridge, "Request Needs response, reserving space\n"); - ++outstandingResponses; - } - } - - otherPort->queueForSendTiming(pkt); - - return true; +AddrRangeList +Bridge::getAddrRanges(int slave_id) const +{ + return ranges; } void -Bridge::BridgePort::nackRequest(PacketPtr pkt) +Bridge::releaseRespSpace() { - // Nack the packet - pkt->makeTimingResponse(); - pkt->setNacked(); - - //put it on the list to send - Tick readyTime = curTick() + nackDelay; - PacketBuffer *buf = new PacketBuffer(pkt, readyTime, true); - - // nothing on the list, add it and we're done - if (sendQueue.empty()) { - assert(!sendEvent.scheduled()); - schedule(sendEvent, readyTime); - sendQueue.push_back(buf); - return; - } - - assert(sendEvent.scheduled() || inRetry); - - // does it go at the end? - if (readyTime >= sendQueue.back()->ready) { - sendQueue.push_back(buf); - return; - } - - // ok, somewhere in the middle, fun - std::list::iterator i = sendQueue.begin(); - std::list::iterator end = sendQueue.end(); - std::list::iterator begin = sendQueue.begin(); - bool done = false; - - while (i != end && !done) { - if (readyTime < (*i)->ready) { - if (i == begin) - reschedule(sendEvent, readyTime); - sendQueue.insert(i,buf); - done = true; - } - i++; - } - assert(done); -} - - -void -Bridge::BridgePort::queueForSendTiming(PacketPtr pkt) -{ - 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. - - PacketBuffer *buf = dynamic_cast(pkt->senderState); - assert(buf != NULL); - // set up new packet dest & senderState based on values saved - // from original request - buf->fixResponse(pkt); - - DPRINTF(BusBridge, "response, new dest %d\n", pkt->getDest()); - delete buf; - } - - - if (pkt->isRequest()) { - ++queuedRequests; - } - - - - Tick readyTime = curTick() + delay; - PacketBuffer *buf = new PacketBuffer(pkt, readyTime); - - // 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 - // should already be an event scheduled for sending the head - // packet. - if (sendQueue.empty()) { - schedule(sendEvent, readyTime); - } - sendQueue.push_back(buf); + assert(outstandingResponses > 0); + --outstandingResponses; + // we now have response space, if there is a request waiting and + // we have space for it, accept it now + if (currReqPkt != NULL && !reqQueueFull()) + schedule(acceptReqEvent, curTick() + 1); } void -Bridge::BridgePort::trySend() +Bridge::releaseReqSpace() { - assert(!sendQueue.empty()); - - PacketBuffer *buf = sendQueue.front(); - - assert(buf->ready <= curTick()); - - PacketPtr pkt = buf->pkt; - - DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n", - buf->origSrc, pkt->getDest(), pkt->getAddr()); - - bool wasReq = pkt->isRequest(); - bool was_nacked_here = buf->nackedHere; - - // If the send was successful, make sure sender state was set to NULL - // otherwise we could get a NACK back of a packet that didn't expect a - // response and we would try to use freed memory. - - Packet::SenderState *old_sender_state = pkt->senderState; - if (pkt->isRequest() && !buf->expectResponse) - pkt->senderState = NULL; - - if (sendTiming(pkt)) { - // send successful - sendQueue.pop_front(); - buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it - - if (buf->expectResponse) { - // Must wait for response - 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 (wasReq) - --queuedRequests; - else if (!was_nacked_here) - --outstandingResponses; - - // If there are more packets to send, schedule event to try again. - if (!sendQueue.empty()) { - buf = sendQueue.front(); - DPRINTF(BusBridge, "Scheduling next send\n"); - schedule(sendEvent, std::max(buf->ready, curTick() + 1)); - } - } else { - DPRINTF(BusBridge, " unsuccessful\n"); - pkt->senderState = old_sender_state; - inRetry = true; - } - - DPRINTF(BusBridge, "trySend: queue size: %d outreq: %d outstanding resp: %d\n", - sendQueue.size(), queuedRequests, outstandingResponses); -} - - -void -Bridge::BridgePort::recvRetry() -{ - inRetry = false; - Tick nextReady = sendQueue.front()->ready; - if (nextReady <= curTick()) - trySend(); - else - schedule(sendEvent, nextReady); -} - -/** Function called by the port when the bus is receiving a Atomic - * transaction.*/ -Tick -Bridge::BridgePort::recvAtomic(PacketPtr pkt) -{ - return delay + otherPort->sendAtomic(pkt); -} - -/** Function called by the port when the bus is receiving a Functional - * transaction.*/ -void -Bridge::BridgePort::recvFunctional(PacketPtr pkt) -{ - std::list::iterator i; - - pkt->pushLabel(name()); - - for (i = sendQueue.begin(); i != sendQueue.end(); ++i) { - if (pkt->checkFunctional((*i)->pkt)) { - pkt->makeResponse(); - return; - } - } - - pkt->popLabel(); - - // fall through if pkt still not satisfied - otherPort->sendFunctional(pkt); -} - -/** Function called by the port when the bus is receiving a status change.*/ -void -Bridge::BridgePort::recvStatusChange(Port::Status status) -{ - otherPort->sendStatusChange(status); + assert(queuedRequests > 0); + --queuedRequests; + // we now have space for requests, if there is one waiting and if + // there is space fo a response (if it needs one) then accept it + // now + if (currReqPkt != NULL && + (currReqPkt->needsResponse() && !respQueueFull())) + schedule(acceptReqEvent, curTick() + 1); } void -Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) +Bridge::recvDebug(MemMapPacketPtr pkt, int slave_id) { - otherPort->getPeerAddressRanges(resp, snoop); - FilterRangeList(filterRanges, resp); - // we don't allow snooping across bridges - snoop = false; + pkt->pushLabel(name()); + + if (!reqPktQueue.checkFunctional(pkt) && + !respPktQueue.checkFunctional(pkt)) + // fall through if pkt still not satisfied + masterPort->sendDebug(pkt); + + pkt->popLabel(); +} + +Tick Bridge::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + return delay + masterPort->sendAtomic(pkt); +} + +void +Bridge::endResp(MemMapPacketPtr resp_pkt, int slave_id) +{ + // tell the packet queue that the current packet is transfered and + // that it can go on to the next one + respPktQueue.endDeferred(); +} + +void +Bridge::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + DPRINTF(BusBridge, "beginReq: addr 0x%x\n", req_pkt->getAddr()); + + // should not have an ongoing packet, and keep track of the one + // we are presented with until we end (accept) the request + assert(currReqPkt == NULL); + currReqPkt = req_pkt; + + // check if we have space to queue the request and a potential + // response + if (reqQueueFull() || (req_pkt->needsResponse() && respQueueFull())) { + // not ready to accept, wait until space is freed up + DPRINTF(BusBridge, "Awaiting space before accepting request\n"); + } else { + // accept straight away + schedule(acceptReqEvent, curTick()); + } +} + +void +Bridge::acceptReq() +{ + DPRINTF(BusBridge, "Accepting request\n"); + // if we need a response reserve space + if (currReqPkt->needsResponse()) { + ++outstandingResponses; + assert(outstandingResponses <= respQueueLimit); + } + + // always reserve space for the request + ++queuedRequests; + assert(queuedRequests <= reqQueueLimit); + + // send it out on the master port after the right delay + reqPktQueue.insert(currReqPkt, curTick() + delay); + + slavePort->endReq(currReqPkt); + currReqPkt = NULL; +} + +void +Bridge::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + DPRINTF(BusBridge, "beginResp: addr 0x%x\n", resp_pkt->getAddr()); + + // should not have an ongoing packet, and keep track of the one we + // are presented with until we end (accept) the response + assert(currRespPkt == NULL); + currRespPkt = resp_pkt; + + // space is already reserved when we issued the request + + // send it out on the slave port after the right delay + respPktQueue.insert(resp_pkt, curTick() + delay); + + schedule(acceptRespEvent, curTick()); +} + +void +Bridge::acceptResp() +{ + DPRINTF(BusBridge, "Accepting response\n"); + masterPort->endResp(currRespPkt); + currRespPkt = NULL; +} + +void +Bridge::endReq(MemMapPacketPtr req_pkt, int master_id) +{ + // tell the packet queue that the current packet is transfered and + // that it can go on to the next one + reqPktQueue.endDeferred(); +} + +unsigned int +Bridge::drain(Event *de) +{ + int count = respPktQueue.drain(de) + reqPktQueue.drain(de); + + if (count) + changeState(Draining); + else + changeState(Drained); + return count; } Bridge * diff -r 6548721032fa -r 042f38a2e360 src/mem/bridge_4phase_to_classic.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/bridge_4phase_to_classic.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Geza Lore + * Andreas Hansson + */ + +/** + * @file + * Bridge that converts the new begin/end handshake to + * the classic sendTiming/recvTiming handshake + */ + +#ifndef __MEM_BRIDGE_4PHASE_TO_CLASSIC_HH__ +#define __MEM_BRIDGE_4PHASE_TO_CLASSIC_HH__ + +#include +#include +#include + +#include "base/fast_alloc.hh" +#include "base/types.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "params/Bridge4PhaseToClassic.hh" +#include "sim/eventq.hh" + +class Bridge4PhaseToClassic : public MemObject, + public MemMapSlaveInterface, + public MemMapMasterInterfaceClassic +{ + public: + + typedef Bridge4PhaseToClassicParams Params; + + /** + * Create a 4phase to classic bridge. + */ + Bridge4PhaseToClassic(Params *p); + + virtual ~Bridge4PhaseToClassic(){} + + /** Slave interface 4phase*/ + unsigned int getBlockSize(int slave_id) const; + + AddrRangeList getAddrRanges(int slave_id) const; + + void recvDebug(MemMapPacketPtr req_pkt, int slave_id); + + Tick recvAtomic(MemMapPacketPtr req_pkt, int slave_id); + + void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + void endResp(MemMapPacketPtr resp_pkt, int slave_id); + /** slave interface ends here */ + + /** Classic master interface */ + void recvRangeChange(int master_id){}; + + bool recvTimingResp(MemMapPacketPtr resp_pkt, int master_id); + + void recvRetryReq(int master_id); + /** master interface ends here */ + + virtual void init(); + + unsigned int drain(Event *de); + + protected: + + enum SlaveId {MEM_SLAVE_ID, N_SLAVE_ID}; + + enum MasterId {MEM_MASTER_ID, N_MASTER_ID}; + + /** + * The slave port, using new begin/end calls. + */ + MemMapSlavePort slavePort; + + /** + * The master port, using classic send/recv calls. + */ + MemMapMasterPort masterPort; + + private: + + /** Keep track of the current request packet */ + MemMapPacketPtr currReqPkt; + + /** Used to remember if recvTimingResp needs to be retried */ + bool needRetry; + + void sendRetry(); + + void endReq(); + + EventWrapper + sendRetryEvent; + + EventWrapper + endReqEvent; +}; + +#endif //__MEM_BRIDGE_4PHASE_TO_CLASSIC_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/bridge_4phase_to_classic.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/bridge_4phase_to_classic.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Geza Lore + */ + +/** + * @file + * Bridge that converts the new begin/end handshake to + * the classic sendTiming/recvTiming handshake + */ + +#include "base/trace.hh" +#include "mem/bridge_4phase_to_classic.hh" + +Bridge4PhaseToClassic::Bridge4PhaseToClassic(Params *p) + : MemObject(p), + slavePort(*this, this, "slave", MEM_SLAVE_ID), + masterPort(*this, this, "master", MEM_MASTER_ID), + currReqPkt(NULL), needRetry(false), sendRetryEvent(this), + endReqEvent(this) +{ + regPort(&masterPort); + regPort(&slavePort); +} + +void +Bridge4PhaseToClassic::init() +{ + // Make sure that both sides are connected to. + if (!slavePort.isConnected() || !masterPort.isConnected()) + fatal("Both ports of bridge must be connected.\n"); +} + +unsigned int +Bridge4PhaseToClassic::getBlockSize(int slave_id) const +{ + return masterPort->getBlockSize(); +} + +AddrRangeList +Bridge4PhaseToClassic::getAddrRanges(int slave_id) const +{ + return masterPort->getAddrRanges(); +} + +Tick +Bridge4PhaseToClassic::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + return masterPort->sendAtomic(pkt); +} + +void +Bridge4PhaseToClassic::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + masterPort->sendDebug(pkt); +} + +// Classic +bool +Bridge4PhaseToClassic::recvTimingResp(MemMapPacketPtr resp_pkt, int master_id) +{ + if (slavePort->respLinkState() == PortInterface::IDLE) { + assert(needRetry == false); + slavePort->beginResp(resp_pkt); + return true; + } else { + needRetry = true; + return false; + // we are now responsible for calling retry + } +} + +void +Bridge4PhaseToClassic::recvRetryReq(int master_id) +{ + assert(currReqPkt != NULL); + bool success = masterPort->sendTimingReq(currReqPkt); + if (success) { + schedule(endReqEvent, curTick()); + } +} + + +// 4PHASE +void +Bridge4PhaseToClassic::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + currReqPkt = req_pkt; + bool success = masterPort->sendTimingReq(req_pkt); + if (success) { + schedule(endReqEvent, curTick()); + } +} + +void +Bridge4PhaseToClassic::endReq() +{ + slavePort->endReq(currReqPkt); + currReqPkt = NULL; +} + +void +Bridge4PhaseToClassic::endResp(MemMapPacketPtr resp_pkt, int slave_id) +{ + // slave port is no longer busy, see if we should retry + if (needRetry) { + needRetry = false; + schedule(sendRetryEvent, curTick()); + } +} + +void +Bridge4PhaseToClassic::sendRetry() +{ + masterPort->sendRetryResp(); +} + +unsigned int +Bridge4PhaseToClassic::drain(Event *de) +{ + changeState(Drained); + return 0; +} + +Bridge4PhaseToClassic * +Bridge4PhaseToClassicParams::create() +{ + return new Bridge4PhaseToClassic(this); +} diff -r 6548721032fa -r 042f38a2e360 src/mem/bridge_classic_to_4phase.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/bridge_classic_to_4phase.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Geza Lore + */ + +/** + * @file + * Bridge that converts the classic sendTiming/recvTiming handshake + * to the new 4phase handshake. + */ + +#ifndef __MEM_BRIDGE_CLASSIC_TO_4PHASE_HH__ +#define __MEM_BRIDGE_CLASSIC_TO_4PHASE_HH__ + +#include "mem/mem_object.hh" +#include "params/BridgeClassicTo4Phase.hh" + +class BridgeClassicTo4Phase : public MemObject, + public MemMapSlaveInterfaceClassic, + public MemMapMasterInterface +{ + public: + + typedef BridgeClassicTo4PhaseParams Params; + + /** + * Create a classic to 4phase bridge. + */ + BridgeClassicTo4Phase(Params *p); + + virtual void init(); + + /** Slave interface */ + unsigned int getBlockSize(int slave_id) const; + + AddrRangeList getAddrRanges(int slave_id) const; + + void recvDebug(MemMapPacketPtr req_pkt, int slave_id); + + Tick recvAtomic(MemMapPacketPtr req_pkt, int slave_id); + + bool recvTimingReq(MemMapPacketPtr req_pkt, int slave_id); + + void recvRetryResp(int slave_id); + /** slave interface ends here */ + + /** Master interface 4phase*/ + void recvRangeChange(int master_id) { + } + + void beginResp(MemMapPacketPtr resp_pkt, int master_id); + + void endReq(MemMapPacketPtr req_pkt, int master_id); + /** master interface ends here */ + + unsigned int drain(Event *de); + + protected: + + enum SlaveId {MEM_SLAVE_ID, N_SLAVE_ID}; + + enum MasterId {MEM_MASTER_ID, N_MASTER_ID}; + + /** + * The slave port, using classic send/recv calls. + */ + MemMapSlavePort slavePort; + + /** + * The master port, using new 4-phase begin/end calls. + */ + MemMapMasterPort masterPort; + + private: + + /** Current response packet */ + MemMapPacketPtr currRespPkt; + + /** Used to remember if recvTimingReq needs to be retried */ + bool needRetry; + + void sendRetry(); + + void endResp(); + + EventWrapper + sendRetryEvent; + + EventWrapper + endRespEvent; +}; + +#endif //__MEM_BRIDGE_CLASSIC_TO_4PHASE_HH___ diff -r 6548721032fa -r 042f38a2e360 src/mem/bridge_classic_to_4phase.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/bridge_classic_to_4phase.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Geza Lore + */ + +/** + * @file + * Bridge that converts the classic sendTiming/recvTiming handshake + * to the new begin/end handshake. + */ + +#include "base/trace.hh" +#include "mem/bridge_classic_to_4phase.hh" + +BridgeClassicTo4Phase::BridgeClassicTo4Phase(Params *p) + : MemObject(p), + slavePort(*this, this, "slave", MEM_SLAVE_ID), + masterPort(*this, this, "master", MEM_MASTER_ID), + currRespPkt(NULL), needRetry(false), sendRetryEvent(this), + endRespEvent(this) +{ + regPort(&masterPort); + regPort(&slavePort); +} + +void +BridgeClassicTo4Phase::init() +{ + // Make sure that both sides are connected to. + if (!slavePort.isConnected() || !masterPort.isConnected()) + fatal("Both ports of bridge must be connected.\n"); +} + +unsigned int +BridgeClassicTo4Phase::getBlockSize(int slave_id) const +{ + return masterPort->getBlockSize(); +} + +AddrRangeList +BridgeClassicTo4Phase::getAddrRanges(int slave_id) const +{ + return masterPort->getAddrRanges(); +} + +Tick +BridgeClassicTo4Phase::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + return masterPort->sendAtomic(pkt); +} + +void +BridgeClassicTo4Phase::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + masterPort->sendDebug(pkt); +} + +// Classic +bool +BridgeClassicTo4Phase::recvTimingReq(MemMapPacketPtr req_pkt, int slave_id) +{ + if (masterPort->reqLinkState() == PortInterface::IDLE) { + assert(needRetry == false); + masterPort->beginReq(req_pkt); + return true; + } else { + needRetry = true; + return false; + // we are now responsible for calling retry + } +} + +void +BridgeClassicTo4Phase::recvRetryResp(int slave_id) +{ + assert(currRespPkt != NULL); + bool success = slavePort->sendTimingResp(currRespPkt); + if (success) { + schedule(endRespEvent, curTick()); + } +} + + +// 4PHASE +void +BridgeClassicTo4Phase::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + currRespPkt = resp_pkt; + bool success = slavePort->sendTimingResp(resp_pkt); + if (success) { + schedule(endRespEvent, curTick()); + } +} + +void +BridgeClassicTo4Phase::endResp() +{ + masterPort->endResp(currRespPkt); + currRespPkt = NULL; +} + +void +BridgeClassicTo4Phase::endReq(MemMapPacketPtr req_pkt, int master_id) +{ + if (needRetry) { + needRetry = false; + schedule(sendRetryEvent, curTick()); + } +} + +void +BridgeClassicTo4Phase::sendRetry() +{ + slavePort->sendRetryReq(); +} + +unsigned int +BridgeClassicTo4Phase::drain(Event *de) +{ + // This just signals drained, as it is a temporary component + changeState(Drained); + return 0; +} + +BridgeClassicTo4Phase * +BridgeClassicTo4PhaseParams::create() +{ + return new BridgeClassicTo4Phase(this); +} diff -r 6548721032fa -r 042f38a2e360 src/mem/bus.hh --- a/src/mem/bus.hh Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,380 +0,0 @@ -/* - * 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: Ron Dreslinski - * Ali Saidi - */ - -/** - * @file - * Declaration of a bus object. - */ - -#ifndef __MEM_BUS_HH__ -#define __MEM_BUS_HH__ - -#include -#include -#include - -#include "base/hashmap.hh" -#include "base/range.hh" -#include "base/range_map.hh" -#include "base/types.hh" -#include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/port.hh" -#include "mem/request.hh" -#include "params/Bus.hh" -#include "sim/eventq.hh" - -class Bus : public MemObject -{ - /** Declaration of the buses port type, one will be instantiated for each - of the interfaces connecting to the bus. */ - class BusPort : public Port - { - bool _onRetryList; - - /** A pointer to the bus to which this port belongs. */ - Bus *bus; - - /** A id to keep track of the intercafe ID this port is connected to. */ - int id; - - public: - - /** Constructor for the BusPort.*/ - BusPort(const std::string &_name, Bus *_bus, int _id) - : Port(_name, _bus), _onRetryList(false), bus(_bus), id(_id) - { } - - bool onRetryList() - { return _onRetryList; } - - void onRetryList(bool newVal) - { _onRetryList = newVal; } - - int getId() { return id; } - - protected: - - /** When reciving a timing request from the peer port (at id), - pass it to the bus. */ - virtual bool recvTiming(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvTiming(pkt); } - - /** When reciving a Atomic requestfrom the peer port (at id), - pass it to the bus. */ - virtual Tick recvAtomic(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvAtomic(pkt); } - - /** When reciving a Functional requestfrom the peer port (at id), - pass it to the bus. */ - virtual void recvFunctional(PacketPtr pkt) - { pkt->setSrc(id); bus->recvFunctional(pkt); } - - /** When reciving a status changefrom the peer port (at id), - pass it to the bus. */ - 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 - // this bus... - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { bus->addressRanges(resp, snoop, id); } - - // Ask the bus to ask everyone on the bus what their block size is and - // take the max of it. This might need to be changed a bit if we ever - // support multiple block sizes. - virtual unsigned deviceBlockSize() const - { return bus->findBlockSize(id); } - - }; - - class BusFreeEvent : public Event - { - Bus * bus; - - public: - BusFreeEvent(Bus * _bus); - void process(); - const char *description() const; - }; - - /** a globally unique id for this bus. */ - int busId; - /** the clock speed for the bus */ - int clock; - /** cycles of overhead per transaction */ - int headerCycles; - /** the width of the bus in bytes */ - int width; - /** the next tick at which the bus will be idle */ - Tick tickNextIdle; - - Event * drainEvent; - - - static const int defaultId = -3; //Make it unique from Broadcast - - typedef range_map::iterator PortIter; - range_map portMap; - - AddrRangeList defaultRange; - - typedef std::vector::iterator SnoopIter; - std::vector snoopPorts; - - /** Function called by the port when the bus is recieving a Timing - transaction.*/ - bool recvTiming(PacketPtr pkt); - - /** Function called by the port when the bus is recieving a Atomic - transaction.*/ - Tick recvAtomic(PacketPtr pkt); - - /** Function called by the port when the bus is recieving a Functional - transaction.*/ - void recvFunctional(PacketPtr 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); - - /** Find which port connected to this bus (if any) should be given a packet - * with this address. - * @param addr Address to find port for. - * @return id of port that the packet should be sent out of. - */ - int findPort(Addr addr); - - // Cache for the findPort function storing recently used ports from portMap - struct PortCache { - bool valid; - int id; - Addr start; - Addr end; - }; - - PortCache portCache[3]; - - // Checks the cache and returns the id of the port that has the requested - // address within its range - inline int checkPortCache(Addr addr) { - if (portCache[0].valid && addr >= portCache[0].start && - addr < portCache[0].end) { - return portCache[0].id; - } - if (portCache[1].valid && addr >= portCache[1].start && - addr < portCache[1].end) { - return portCache[1].id; - } - if (portCache[2].valid && addr >= portCache[2].start && - addr < portCache[2].end) { - return portCache[2].id; - } - - return -1; - } - - // Clears the earliest entry of the cache and inserts a new port entry - inline void updatePortCache(short id, Addr start, Addr end) { - portCache[2].valid = portCache[1].valid; - portCache[2].id = portCache[1].id; - portCache[2].start = portCache[1].start; - portCache[2].end = portCache[1].end; - - portCache[1].valid = portCache[0].valid; - portCache[1].id = portCache[0].id; - portCache[1].start = portCache[0].start; - portCache[1].end = portCache[0].end; - - portCache[0].valid = true; - portCache[0].id = id; - portCache[0].start = start; - portCache[0].end = end; - } - - // Clears the cache. Needs to be called in constructor. - inline void clearPortCache() { - portCache[2].valid = false; - portCache[1].valid = false; - portCache[0].valid = false; - } - - /** Process address range request. - * @param resp addresses that we can respond to - * @param snoop addresses that we would like to snoop - * @param id ide of the busport that made the request. - */ - void addressRanges(AddrRangeList &resp, bool &snoop, int id); - - /** Calculate the timing parameters for the packet. Updates the - * firstWordTime and finishTime fields of the packet object. - * Returns the tick at which the packet header is completed (which - * will be all that is sent if the target rejects the packet). - */ - Tick calcPacketTiming(PacketPtr pkt); - - /** Occupy the bus until until */ - void occupyBus(Tick until); - - /** Ask everyone on the bus what their size is - * @param id id of the busport that made the request - * @return the max of all the sizes - */ - unsigned findBlockSize(int id); - - BusFreeEvent busIdle; - - bool inRetry; - std::set inRecvStatusChange; - - /** max number of bus ids we've handed out so far */ - short maxId; - - /** An array of pointers to the peer port interfaces - connected to this bus.*/ - m5::hash_map interfaces; - - /** An array of pointers to ports that retry should be called on because the - * original send failed for whatever reason.*/ - std::list retryList; - - void addToRetryList(BusPort * port) - { - if (!inRetry) { - // The device wasn't retrying a packet, or wasn't at an appropriate - // time. - assert(!port->onRetryList()); - port->onRetryList(true); - retryList.push_back(port); - } else { - if (port->onRetryList()) { - // The device was retrying a packet. It didn't work, so we'll leave - // it at the head of the retry list. - assert(port == retryList.front()); - inRetry = false; - } - else { - port->onRetryList(true); - retryList.push_back(port); - } - } - } - - /** Port that handles requests that don't match any of the interfaces.*/ - BusPort *defaultPort; - - BusPort *funcPort; - int funcPortId; - - /** If true, use address range provided by default device. Any - address not handled by another port and not in default device's - range will cause a fatal error. If false, just send all - addresses not handled by another port to default device. */ - bool useDefaultRange; - - unsigned defaultBlockSize; - unsigned cachedBlockSize; - bool cachedBlockSizeValid; - - // Cache for the peer port interfaces - struct BusCache { - bool valid; - short id; - BusPort *port; - }; - - BusCache busCache[3]; - - // Checks the peer port interfaces cache for the port id and returns - // a pointer to the matching port - inline BusPort* checkBusCache(short id) { - if (busCache[0].valid && id == busCache[0].id) { - return busCache[0].port; - } - if (busCache[1].valid && id == busCache[1].id) { - return busCache[1].port; - } - if (busCache[2].valid && id == busCache[2].id) { - return busCache[2].port; - } - - return NULL; - } - - // Replaces the earliest entry in the cache with a new entry - inline void updateBusCache(short id, BusPort *port) { - busCache[2].valid = busCache[1].valid; - busCache[2].id = busCache[1].id; - busCache[2].port = busCache[1].port; - - busCache[1].valid = busCache[0].valid; - busCache[1].id = busCache[0].id; - busCache[1].port = busCache[0].port; - - busCache[0].valid = true; - busCache[0].id = id; - busCache[0].port = port; - } - - // Invalidates the cache. Needs to be called in constructor. - inline void clearBusCache() { - busCache[2].valid = false; - busCache[1].valid = false; - busCache[0].valid = false; - } - - - public: - - /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1); - virtual void deletePortRefs(Port *p); - - virtual void init(); - virtual void startup(); - - unsigned int drain(Event *de); - - Bus(const BusParams *p); -}; - -#endif //__MEM_BUS_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/bus.cc --- a/src/mem/bus.cc Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,670 +0,0 @@ -/* - * 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 - */ - -/** - * @file - * Definition of a bus object. - */ - -#include -#include - -#include "base/misc.hh" -#include "base/trace.hh" -#include "mem/bus.hh" - -Bus::Bus(const BusParams *p) - : MemObject(p), busId(p->bus_id), clock(p->clock), - headerCycles(p->header_cycles), width(p->width), tickNextIdle(0), - drainEvent(NULL), busIdle(this), inRetry(false), maxId(0), - defaultPort(NULL), funcPort(NULL), funcPortId(-4), - useDefaultRange(p->use_default_range), defaultBlockSize(p->block_size), - cachedBlockSize(0), cachedBlockSizeValid(false) -{ - //width, clock period, and header cycles must be positive - if (width <= 0) - fatal("Bus width must be positive\n"); - if (clock <= 0) - fatal("Bus clock period must be positive\n"); - if (headerCycles <= 0) - fatal("Number of header cycles must be positive\n"); - clearBusCache(); - clearPortCache(); -} - -Port * -Bus::getPort(const std::string &if_name, int idx) -{ - if (if_name == "default") { - if (defaultPort == NULL) { - defaultPort = new BusPort(csprintf("%s-default",name()), this, - defaultId); - cachedBlockSizeValid = false; - return defaultPort; - } else - fatal("Default port already set\n"); - } - int id; - if (if_name == "functional") { - if (!funcPort) { - id = maxId++; - funcPort = new BusPort(csprintf("%s-p%d-func", name(), id), this, id); - funcPortId = id; - interfaces[id] = funcPort; - } - return funcPort; - } - - // if_name ignored? forced to be empty? - id = maxId++; - assert(maxId < std::numeric_limits::max()); - BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id); - interfaces[id] = bp; - cachedBlockSizeValid = false; - return bp; -} - -void -Bus::deletePortRefs(Port *p) -{ - - BusPort *bp = dynamic_cast(p); - if (bp == NULL) - panic("Couldn't convert Port* to BusPort*\n"); - // If this is our one functional port - if (funcPort == bp) - return; - interfaces.erase(bp->getId()); - clearBusCache(); - delete bp; -} - -/** Get the ranges of anyone other buses that we are connected to. */ -void -Bus::init() -{ - m5::hash_map::iterator intIter; - - for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) - intIter->second->sendStatusChange(Port::RangeChange); -} - -Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) - : bus(_bus) -{} - -void -Bus::BusFreeEvent::process() -{ - bus->recvRetry(-1); -} - -const char * -Bus::BusFreeEvent::description() const -{ - return "bus became available"; -} - -Tick -Bus::calcPacketTiming(PacketPtr pkt) -{ - // Bring tickNextIdle up to the present tick. - // There is some potential ambiguity where a cycle starts, which - // might make a difference when devices are acting right around a - // cycle boundary. Using a < allows things which happen exactly on - // a cycle boundary to take up only the following cycle. Anything - // that happens later will have to "wait" for the end of that - // cycle, and then start using the bus after that. - if (tickNextIdle < curTick()) { - tickNextIdle = curTick(); - if (tickNextIdle % clock != 0) - tickNextIdle = curTick() - (curTick() % clock) + clock; - } - - Tick headerTime = tickNextIdle + headerCycles * clock; - - // The packet will be sent. Figure out how long it occupies the bus, and - // how much of that time is for the first "word", aka bus width. - int numCycles = 0; - if (pkt->hasData()) { - // If a packet has data, it needs ceil(size/width) cycles to send it - int dataSize = pkt->getSize(); - numCycles += dataSize/width; - if (dataSize % width) - numCycles++; - } - - // The first word will be delivered after the current tick, the delivery - // of the address if any, and one bus cycle to deliver the data - pkt->firstWordTime = headerTime + clock; - - pkt->finishTime = headerTime + numCycles * clock; - - return headerTime; -} - -void Bus::occupyBus(Tick until) -{ - if (until == 0) { - // shortcut for express snoop packets - return; - } - - tickNextIdle = until; - reschedule(busIdle, tickNextIdle, true); - - DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n", - curTick(), tickNextIdle); -} - -/** Function called by the port when the bus is receiving a Timing - * transaction.*/ -bool -Bus::recvTiming(PacketPtr pkt) -{ - short src = pkt->getSrc(); - - BusPort *src_port; - if (src == defaultId) - src_port = defaultPort; - else { - src_port = checkBusCache(src); - if (src_port == NULL) { - src_port = interfaces[src]; - updateBusCache(src, src_port); - } - } - - // If the bus is busy, or other devices are in line ahead of the current - // one, put this device on the retry list. - if (!pkt->isExpressSnoop() && - (tickNextIdle > curTick() || - (retryList.size() && (!inRetry || src_port != retryList.front())))) - { - addToRetryList(src_port); - DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n", - src, pkt->getDest(), pkt->cmdString(), pkt->getAddr()); - return false; - } - - DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x\n", - src, pkt->getDest(), pkt->cmdString(), pkt->getAddr()); - - Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt); - Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime; - - short dest = pkt->getDest(); - int dest_port_id; - Port *dest_port; - - if (dest == Packet::Broadcast) { - dest_port_id = findPort(pkt->getAddr()); - dest_port = (dest_port_id == defaultId) ? - defaultPort : interfaces[dest_port_id]; - SnoopIter s_end = snoopPorts.end(); - for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - BusPort *p = *s_iter; - if (p != dest_port && p != src_port) { - // cache is not allowed to refuse snoop - bool success M5_VAR_USED = p->sendTiming(pkt); - assert(success); - } - } - } else { - assert(dest < maxId); - assert(dest != src); // catch infinite loops - dest_port_id = dest; - if (dest_port_id == defaultId) - dest_port = defaultPort; - else { - dest_port = checkBusCache(dest); - if (dest_port == NULL) { - dest_port = interfaces[dest_port_id]; - // updateBusCache(dest_port_id, dest_port); - } - } - dest_port = (dest_port_id == defaultId) ? - defaultPort : interfaces[dest_port_id]; - } - - if (dest_port_id == src) { - // Must be forwarded snoop up from below... - assert(dest == Packet::Broadcast); - } else { - // send to actual target - if (!dest_port->sendTiming(pkt)) { - // Packet not successfully sent. Leave or put it on the retry list. - // illegal to block responses... can lead to deadlock - assert(!pkt->isResponse()); - // It's also illegal to force a transaction to retry after - // someone else has committed to respond. - assert(!pkt->memInhibitAsserted()); - DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x TGT RETRY\n", - src, pkt->getDest(), pkt->cmdString(), pkt->getAddr()); - addToRetryList(src_port); - occupyBus(headerFinishTime); - return false; - } - // send OK, fall through... pkt may have been deleted by - // target at this point, so it should *not* be referenced - // again. We'll set it to NULL here just to be safe. - pkt = NULL; - } - - occupyBus(packetFinishTime); - - // Packet was successfully sent. - // Also take care of retries - if (inRetry) { - DPRINTF(Bus, "Remove retry from list %d\n", src); - retryList.front()->onRetryList(false); - retryList.pop_front(); - inRetry = false; - } - return true; -} - -void -Bus::recvRetry(int id) -{ - // If there's anything waiting, and the bus isn't busy... - if (retryList.size() && curTick() >= tickNextIdle) { - //retryingPort = retryList.front(); - inRetry = true; - DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name()); - retryList.front()->sendRetry(); - // If inRetry is still true, sendTiming wasn't called - if (inRetry) - { - retryList.front()->onRetryList(false); - retryList.pop_front(); - inRetry = false; - - //Bring tickNextIdle up to the present - while (tickNextIdle < curTick()) - tickNextIdle += clock; - - //Burn a cycle for the missed grant. - tickNextIdle += clock; - - reschedule(busIdle, tickNextIdle, true); - } - } - //If we weren't able to drain before, we might be able to now. - if (drainEvent && retryList.size() == 0 && curTick() >= tickNextIdle) { - drainEvent->process(); - // Clear the drain event once we're done with it. - drainEvent = NULL; - } -} - -int -Bus::findPort(Addr addr) -{ - /* An interval tree would be a better way to do this. --ali. */ - int dest_id; - - dest_id = checkPortCache(addr); - if (dest_id != -1) - return dest_id; - - // Check normal port ranges - PortIter i = portMap.find(RangeSize(addr,1)); - if (i != portMap.end()) { - dest_id = i->second; - updatePortCache(dest_id, i->first.start, i->first.end); - return dest_id; - } - - // Check if this matches the default range - if (useDefaultRange) { - AddrRangeIter a_end = defaultRange.end(); - for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) { - if (*i == addr) { - DPRINTF(Bus, " found addr %#llx on default\n", addr); - return defaultId; - } - } - - panic("Unable to find destination for addr %#llx\n", addr); - } - - DPRINTF(Bus, "Unable to find destination for addr %#llx, " - "will use default port\n", addr); - return defaultId; -} - - -/** Function called by the port when the bus is receiving a Atomic - * transaction.*/ -Tick -Bus::recvAtomic(PacketPtr pkt) -{ - DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", - pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); - assert(pkt->getDest() == Packet::Broadcast); - assert(pkt->isRequest()); - - // Variables for recording original command and snoop response (if - // any)... if a snooper respondes, we will need to restore - // original command so that additional snoops can take place - // properly - MemCmd orig_cmd = pkt->cmd; - MemCmd snoop_response_cmd = MemCmd::InvalidCmd; - Tick snoop_response_latency = 0; - int orig_src = pkt->getSrc(); - - int target_port_id = findPort(pkt->getAddr()); - BusPort *target_port; - if (target_port_id == defaultId) - target_port = defaultPort; - else { - target_port = checkBusCache(target_port_id); - if (target_port == NULL) { - target_port = interfaces[target_port_id]; - updateBusCache(target_port_id, target_port); - } - } - - SnoopIter s_end = snoopPorts.end(); - for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - BusPort *p = *s_iter; - // same port should not have both target addresses and snooping - assert(p != target_port); - if (p->getId() != pkt->getSrc()) { - Tick latency = p->sendAtomic(pkt); - if (pkt->isResponse()) { - // response from snoop agent - assert(pkt->cmd != orig_cmd); - assert(pkt->memInhibitAsserted()); - // should only happen once - assert(snoop_response_cmd == MemCmd::InvalidCmd); - // save response state - snoop_response_cmd = pkt->cmd; - snoop_response_latency = latency; - // restore original packet state for remaining snoopers - pkt->cmd = orig_cmd; - pkt->setSrc(orig_src); - pkt->setDest(Packet::Broadcast); - } - } - } - - Tick response_latency = 0; - - // we can get requests sent up from the memory side of the bus for - // snooping... don't send them back down! - if (target_port_id != pkt->getSrc()) { - response_latency = target_port->sendAtomic(pkt); - } - - // if we got a response from a snooper, restore it here - if (snoop_response_cmd != MemCmd::InvalidCmd) { - // no one else should have responded - assert(!pkt->isResponse()); - assert(pkt->cmd == orig_cmd); - pkt->cmd = snoop_response_cmd; - response_latency = snoop_response_latency; - } - - // why do we have this packet field and the return value both??? - pkt->finishTime = curTick() + response_latency; - return response_latency; -} - -/** Function called by the port when the bus is receiving a Functional - * transaction.*/ -void -Bus::recvFunctional(PacketPtr pkt) -{ - if (!pkt->isPrint()) { - // don't do DPRINTFs on PrintReq as it clutters up the output - DPRINTF(Bus, - "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", - pkt->getSrc(), pkt->getDest(), pkt->getAddr(), - pkt->cmdString()); - } - assert(pkt->getDest() == Packet::Broadcast); - - int port_id = findPort(pkt->getAddr()); - Port *port = (port_id == defaultId) ? defaultPort : interfaces[port_id]; - // The packet may be changed by another bus on snoops, restore the - // id after each - int src_id = pkt->getSrc(); - - assert(pkt->isRequest()); // hasn't already been satisfied - - SnoopIter s_end = snoopPorts.end(); - for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - BusPort *p = *s_iter; - if (p != port && p->getId() != src_id) { - p->sendFunctional(pkt); - } - if (pkt->isResponse()) { - break; - } - pkt->setSrc(src_id); - } - - // If the snooping hasn't found what we were looking for, keep going. - if (!pkt->isResponse() && port_id != pkt->getSrc()) { - port->sendFunctional(pkt); - } -} - -/** Function called by the port when the bus is receiving a status change.*/ -void -Bus::recvStatusChange(Port::Status status, int id) -{ - AddrRangeList ranges; - bool snoops; - AddrRangeIter iter; - - if (inRecvStatusChange.count(id)) - return; - inRecvStatusChange.insert(id); - - assert(status == Port::RangeChange && - "The other statuses need to be implemented."); - - DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); - - clearPortCache(); - if (id == defaultId) { - defaultRange.clear(); - // Only try to update these ranges if the user set a default responder. - if (useDefaultRange) { - defaultPort->getPeerAddressRanges(ranges, snoops); - assert(snoops == false); - for(iter = ranges.begin(); iter != ranges.end(); iter++) { - defaultRange.push_back(*iter); - DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", - iter->start, iter->end); - } - } - } else { - - assert((id < maxId && id >= 0) || id == defaultId); - BusPort *port = interfaces[id]; - - // Clean out any previously existent ids - for (PortIter portIter = portMap.begin(); - portIter != portMap.end(); ) { - if (portIter->second == id) - portMap.erase(portIter++); - else - portIter++; - } - - for (SnoopIter s_iter = snoopPorts.begin(); - s_iter != snoopPorts.end(); ) { - if ((*s_iter)->getId() == id) - s_iter = snoopPorts.erase(s_iter); - else - s_iter++; - } - - port->getPeerAddressRanges(ranges, snoops); - - if (snoops) { - DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id); - snoopPorts.push_back(port); - } - - for (iter = ranges.begin(); iter != ranges.end(); iter++) { - DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", - iter->start, iter->end, id); - if (portMap.insert(*iter, id) == portMap.end()) { - int conflict_id = portMap.find(*iter)->second; - fatal("%s has two ports with same range:\n\t%s\n\t%s\n", - name(), interfaces[id]->getPeer()->name(), - interfaces[conflict_id]->getPeer()->name()); - } - } - } - DPRINTF(MMU, "port list has %d entries\n", portMap.size()); - - // tell all our peers that our address range has changed. - // Don't tell the device that caused this change, it already knows - m5::hash_map::iterator intIter; - - for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) - if (intIter->first != id && intIter->first != funcPortId) - intIter->second->sendStatusChange(Port::RangeChange); - - if (id != defaultId && defaultPort) - defaultPort->sendStatusChange(Port::RangeChange); - inRecvStatusChange.erase(id); -} - -void -Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id) -{ - resp.clear(); - snoop = false; - - DPRINTF(BusAddrRanges, "received address range request, returning:\n"); - - for (AddrRangeIter dflt_iter = defaultRange.begin(); - dflt_iter != defaultRange.end(); dflt_iter++) { - resp.push_back(*dflt_iter); - DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start, - dflt_iter->end); - } - for (PortIter portIter = portMap.begin(); - portIter != portMap.end(); portIter++) { - bool subset = false; - for (AddrRangeIter dflt_iter = defaultRange.begin(); - dflt_iter != defaultRange.end(); dflt_iter++) { - if ((portIter->first.start < dflt_iter->start && - portIter->first.end >= dflt_iter->start) || - (portIter->first.start < dflt_iter->end && - portIter->first.end >= dflt_iter->end)) - fatal("Devices can not set ranges that itersect the default set\ - but are not a subset of the default set.\n"); - if (portIter->first.start >= dflt_iter->start && - portIter->first.end <= dflt_iter->end) { - subset = true; - DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", - portIter->first.start, portIter->first.end); - } - } - if (portIter->second != id && !subset) { - resp.push_back(portIter->first); - DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", - portIter->first.start, portIter->first.end); - } - } - - for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end(); - s_iter++) { - if ((*s_iter)->getId() != id) { - snoop = true; - break; - } - } -} - -unsigned -Bus::findBlockSize(int id) -{ - if (cachedBlockSizeValid) - return cachedBlockSize; - - unsigned max_bs = 0; - - PortIter p_end = portMap.end(); - for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) { - unsigned tmp_bs = interfaces[p_iter->second]->peerBlockSize(); - if (tmp_bs > max_bs) - max_bs = tmp_bs; - } - SnoopIter s_end = snoopPorts.end(); - for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - unsigned tmp_bs = (*s_iter)->peerBlockSize(); - if (tmp_bs > max_bs) - max_bs = tmp_bs; - } - if (max_bs == 0) - max_bs = defaultBlockSize; - - if (max_bs != 64) - warn_once("Blocksize found to not be 64... hmm... probably not.\n"); - cachedBlockSize = max_bs; - cachedBlockSizeValid = true; - return max_bs; -} - - -unsigned int -Bus::drain(Event * de) -{ - //We should check that we're not "doing" anything, and that noone is - //waiting. We might be idle but have someone waiting if the device we - //contacted for a retry didn't actually retry. - if (retryList.size() || (curTick() < tickNextIdle && busIdle.scheduled())) { - drainEvent = de; - return 1; - } - return 0; -} - -void -Bus::startup() -{ - if (tickNextIdle < curTick()) - tickNextIdle = (curTick() / clock) * clock + clock; -} - -Bus * -BusParams::create() -{ - return new Bus(this); -} diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/BaseCache.py --- a/src/mem/cache/BaseCache.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/BaseCache.py Fri Aug 05 18:14:06 2011 +0100 @@ -73,6 +73,17 @@ "Use the CPU ID to separate calculations of prefetches") prefetch_data_accesses_only = Param.Bool(False, "Only prefetch on data not on instruction accesses") - cpu_side = Port("Port on side closer to CPU") - mem_side = Port("Port on side closer to MEM") - addr_range = Param.AddrRange(AllMemory, "The address range for the CPU-side port") + mem_slave = Port("MEM_MAP", "SLAVE", + "Memory-mapped slave (to connect e.g. to CPU)") + cache_up_master = Port("CACHE_MNT", "MASTER", + "Cache maintenance master for higher levels") + cache_down_master = Port("CACHE_MNT", "MASTER", + "Cache maintenance master for lower levels") + mem_master = Port("MEM_MAP", "MASTER", + "memory mapped master (to connect e.g. to memory)") + cache_slave = Port("CACHE_MNT", "SLAVE", "Cache maintenance slave") + + addr_range = Param.AddrRange(AllMemory, + "The address range for the memory-mapped " \ + "slave port") + diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/base.hh --- a/src/mem/cache/base.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/base.hh Fri Aug 05 18:14:06 2011 +0100 @@ -28,6 +28,7 @@ * Authors: Erik Hallnor * Steve Reinhardt * Ron Dreslinski + * Andreas Hansson */ /** @@ -38,11 +39,6 @@ #ifndef __BASE_CACHE_HH__ #define __BASE_CACHE_HH__ -#include -#include -#include -#include - #include "base/misc.hh" #include "base/statistics.hh" #include "base/trace.hh" @@ -50,9 +46,7 @@ #include "config/full_system.hh" #include "mem/cache/mshr_queue.hh" #include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/tport.hh" -#include "mem/request.hh" +#include "mem/packet_queue.hh" #include "params/BaseCache.hh" #include "sim/eventq.hh" #include "sim/sim_exit.hh" @@ -61,8 +55,25 @@ /** * A basic cache interface. Implements some common functions for speed. */ -class BaseCache : public MemObject +class BaseCache : public MemObject, + public MemMapSlaveInterface, + public MemMapMasterInterface, + public CacheMntSlaveInterface, + public CacheMntMasterInterface { + + /** + * Make the base prefetcher a friend of ours. + */ + friend class BasePrefetcher; + + /** + * Make the base tags a friend of ours. + */ + friend class BaseTags; + + private: + /** * Indexes to enumerate the MSHR queues. */ @@ -71,6 +82,27 @@ MSHRQueue_WriteBuffer }; + public: + + /** + * Reasons for cache to request a bus. These are also used by the + * prefetched, so it has to be public. + */ + enum RequestCause { + Request_MSHR = MSHRQueue_MSHRs, + Request_WB = MSHRQueue_WriteBuffer, + Request_PF, + NUM_REQUEST_CAUSES + }; + + protected: + + typedef BaseCacheParams Params; + BaseCache(const Params *p); + virtual ~BaseCache() {} + + virtual void init(); + /** * Reasons for caches to be blocked. */ @@ -81,72 +113,48 @@ NUM_BLOCKED_CAUSES }; - public: /** - * Reasons for cache to request a bus. + * Enumeration of the different slave ports. */ - enum RequestCause { - Request_MSHR = MSHRQueue_MSHRs, - Request_WB = MSHRQueue_WriteBuffer, - Request_PF, - NUM_REQUEST_CAUSES - }; + enum SlaveId {MEM_SLAVE_ID, CACHE_SLAVE_ID, N_SLAVE_ID}; - private: + /** + * Enumeration of the different master ports. + */ + enum MasterId {MEM_MASTER_ID, CACHE_UP_MASTER_ID, CACHE_DOWN_MASTER_ID, + N_MASTER_ID}; - class CachePort : public SimpleTimingPort - { - public: - BaseCache *cache; + /** + * The memory-mapped slave port and its associated packet queue. + */ + MemMapSlavePort memSlavePort; + RespPacketQueue4Phase memSlavePktQueue; - protected: - CachePort(const std::string &_name, BaseCache *_cache, - const std::string &_label); + /** + * The cache-maintenance master that goes along the memory-mapped + * slave port (towards the CPU) and upper levels of the cache + * hierarchy. + */ + CacheMntMasterPort cacheUpMasterPort; - virtual void recvStatusChange(Status status); + /** + * The cache-maintenance master that goes along the memory-mapped + * master port (towards the memory) and lower levels of the cache + * hierarchy. + */ + CacheMntMasterPort cacheDownMasterPort; + + /** + * The memory-mapped master port for requests going towards the + * memory, and the corresponding master object. + */ + MemMapMasterPort memMasterPort; - virtual unsigned deviceBlockSize() const; - - bool recvRetryCommon(); - - typedef EventWrapper - SendRetryEvent; - - const std::string label; - - public: - void setOtherPort(CachePort *_otherPort) { otherPort = _otherPort; } - - void setBlocked(); - - void clearBlocked(); - - bool checkFunctional(PacketPtr pkt); - - CachePort *otherPort; - - bool blocked; - - bool mustSendRetry; - - void requestBus(RequestCause cause, Tick time) - { - DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause); - if (!waitingOnRetry) { - schedSendEvent(time); - } - } - - void respond(PacketPtr pkt, Tick time) { - schedSendTiming(pkt, time); - } - }; - - public: //Made public so coherence can get at it. - CachePort *cpuSidePort; - CachePort *memSidePort; - - protected: + /** + * The cache-interference slave port and associated packet queue. + */ + CacheMntSlavePort cacheSlavePort; + RespPacketQueue cacheSlavePktQueue; /** Miss status registers */ MSHRQueue mshrQueue; @@ -155,7 +163,7 @@ MSHRQueue writeBuffer; MSHR *allocateBufferInternal(MSHRQueue *mq, Addr addr, int size, - PacketPtr pkt, Tick time, bool requestBus) + Packet* pkt, Tick time, bool requestBus) { MSHR *mshr = mq->allocate(addr, size, pkt, time, order++); @@ -170,7 +178,7 @@ return mshr; } - void markInServiceInternal(MSHR *mshr, PacketPtr pkt) + void markInServiceInternal(MSHR *mshr, Packet* pkt) { MSHRQueue *mq = mshr->queue; bool wasFull = mq->isFull(); @@ -194,6 +202,16 @@ /** Do we forward snoops from mem side port through to cpu side port? */ bool forwardSnoops; + /** a pending memory request that is waiting to be accepted. */ + MemMapPacketPtr currMemReqPkt; + + /** accept a pending memory-mapped request (currMemReqPkt) */ + virtual void acceptMemReq() = 0; + + /** Event used to accept a pending memory-mapped request */ + EventWrapper acceptMemReqEvent; + /** * Bit vector of the blocking reasons for the access path. * @sa #BlockedCause @@ -223,8 +241,175 @@ /** number of cpus sharing this cache - from config file */ int _numCpus; - public: - int numCpus() { return _numCpus; } + Addr blockAlign(Addr addr) const { return (addr & ~(Addr(blkSize - 1))); } + + AddrRangeList getAddrRanges(int slave_id) const + { + assert(slave_id == MEM_SLAVE_ID); + AddrRangeList ranges; + ranges.push_back(addrRange); + return ranges; + } + + MSHR *allocateMissBuffer(Packet* pkt, Tick time, bool requestBus) + { + assert(!pkt->req->isUncacheable()); + return allocateBufferInternal(&mshrQueue, + blockAlign(pkt->getAddr()), blkSize, + pkt, time, requestBus); + } + + MSHR *allocateWriteBuffer(Packet* pkt, Tick time, bool requestBus) + { + assert(pkt->isWrite() && !pkt->isRead()); + return allocateBufferInternal(&writeBuffer, + pkt->getAddr(), pkt->getSize(), + pkt, time, requestBus); + } + + MSHR *allocateUncachedReadBuffer(Packet* pkt, Tick time, bool requestBus) + { + assert(pkt->req->isUncacheable()); + assert(pkt->isRead()); + return allocateBufferInternal(&mshrQueue, + pkt->getAddr(), pkt->getSize(), + pkt, time, requestBus); + } + + /** + * Returns true if the cache is blocked for accesses. + */ + bool isBlocked() + { + return blocked != 0; + } + + /** + * Marks the access path of the cache as blocked for the given cause. This + * also sets the blocked flag in the slave interface. + * @param cause The reason for the cache blocking. + */ + void setBlocked(BlockedCause cause) + { + uint8_t flag = 1 << cause; + if (blocked == 0) { + blocked_causes[cause]++; + blockedCycle = curTick(); + } + blocked |= flag; + DPRINTF(Cache,"Blocking for cause %d, mask=%d\n", cause, blocked); + } + + /** + * Marks the cache as unblocked for the given cause. This also clears the + * blocked flags in the appropriate interfaces. + * @param cause The newly unblocked cause. + * @warning Calling this function can cause a blocked request on the bus to + * access the cache. The cache must be in a state to handle that request. + */ + void clearBlocked(BlockedCause cause) + { + uint8_t flag = 1 << cause; + blocked &= ~flag; + DPRINTF(Cache,"Unblocking for cause %d, mask=%d\n", cause, blocked); + if (blocked == 0) { + blocked_cycles[cause] += curTick() - blockedCycle; + // we cleared the block, if we have a pending request then + // accept it + if (currMemReqPkt != NULL) { + // go ahead and accept the request that we have waiting + schedule(acceptMemReqEvent, curTick()); + } + } + } + + MemMapPacketPtr memMasterCurrReqPkt; + + virtual void sendMemReq() = 0; + + EventWrapper sendMemReqEvent; + + /** + * Request the master bus for the given cause and time. + * @param cause The reason for the request. + * @param time The time to make the request. + */ + void requestMemSideBus(RequestCause cause, Tick time) + { + DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause); + if (memMasterPort->reqLinkState() == PortInterface::IDLE && + !sendMemReqEvent.scheduled()) { + schedule(sendMemReqEvent, time); + } + } + + /** + * Clear the master bus request for the given cause. + * @param cause The request reason to clear. + */ + void deassertMemSideBusRequest(RequestCause cause) + { + // Obsolete... we no longer signal bus requests explicitly so + // we can't deassert them. Leaving this in as a no-op since + // the prefetcher calls it to indicate that it no longer wants + // to request a prefetch, and someday that might be + // interesting again. + } + + virtual bool inCache(Addr addr) = 0; + + virtual bool inMissQueue(Addr addr) = 0; + + void incMissCount(Packet* pkt, int id) + { + + if (pkt->cmd == MemCmd::Writeback) { + assert(id == -1); + misses[pkt->cmdToIndex()][0]++; + /* same thing for writeback hits as misses - no context id + * available, meanwhile writeback hit/miss stats are not used + * in any aggregate hit/miss calculations, so just lump them all + * in bucket 0 */ +#if FULL_SYSTEM + } else if (id == -1) { + // Device accesses have id -1 + // lump device accesses into their own bucket + misses[pkt->cmdToIndex()][_numCpus]++; +#endif + } else { + misses[pkt->cmdToIndex()][id % _numCpus]++; + } + + if (missCount) { + --missCount; + if (missCount == 0) + exitSimLoop("A cache reached the maximum miss count"); + } + } + void incHitCount(Packet* pkt, int id) + { + + /* Writeback requests don't have a context id associated with + * them, so attributing a hit to a -1 context id is obviously a + * problem. I've noticed in the stats that hits are split into + * demand and non-demand hits - neither of which include writeback + * hits, so here, I'll just put the writeback hits into bucket 0 + * since it won't mess with any other stats -hsul */ + if (pkt->cmd == MemCmd::Writeback) { + assert(id == -1); + hits[pkt->cmdToIndex()][0]++; +#if FULL_SYSTEM + } else if (id == -1) { + // Device accesses have id -1 + // lump device accesses into their own bucket + hits[pkt->cmdToIndex()][_numCpus]++; +#endif + } else { + /* the % is necessary in case there are switch cpus */ + hits[pkt->cmdToIndex()][id % _numCpus]++; + } + } + // Statistics /** * @addtogroup CacheStatistics @@ -363,181 +548,37 @@ * @} */ + public: + + /** + * Query block size of a cache. + * @return The block size + */ + virtual unsigned + getBlockSize(int slave_id) const + { + return blkSize; + } + + int numCpus() { return _numCpus; } + + virtual unsigned int drain(Event *de); + + /** + * Get the cache-specific colour. + * + * @param r red in 0-255 + * @param g green in 0-255 + * @param b blue in 0-255 + */ + virtual void getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const; + /** * Register stats for this object. */ virtual void regStats(); - public: - typedef BaseCacheParams Params; - BaseCache(const Params *p); - ~BaseCache() {} - - virtual void init(); - - /** - * Query block size of a cache. - * @return The block size - */ - unsigned - getBlockSize() const - { - return blkSize; - } - - - Addr blockAlign(Addr addr) const { return (addr & ~(Addr(blkSize - 1))); } - - - const Range &getAddrRange() const { return addrRange; } - - MSHR *allocateMissBuffer(PacketPtr pkt, Tick time, bool requestBus) - { - assert(!pkt->req->isUncacheable()); - return allocateBufferInternal(&mshrQueue, - blockAlign(pkt->getAddr()), blkSize, - pkt, time, requestBus); - } - - MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time, bool requestBus) - { - assert(pkt->isWrite() && !pkt->isRead()); - return allocateBufferInternal(&writeBuffer, - pkt->getAddr(), pkt->getSize(), - pkt, time, requestBus); - } - - MSHR *allocateUncachedReadBuffer(PacketPtr pkt, Tick time, bool requestBus) - { - assert(pkt->req->isUncacheable()); - assert(pkt->isRead()); - return allocateBufferInternal(&mshrQueue, - pkt->getAddr(), pkt->getSize(), - pkt, time, requestBus); - } - - /** - * Returns true if the cache is blocked for accesses. - */ - bool isBlocked() - { - return blocked != 0; - } - - /** - * Marks the access path of the cache as blocked for the given cause. This - * also sets the blocked flag in the slave interface. - * @param cause The reason for the cache blocking. - */ - void setBlocked(BlockedCause cause) - { - uint8_t flag = 1 << cause; - if (blocked == 0) { - blocked_causes[cause]++; - blockedCycle = curTick(); - cpuSidePort->setBlocked(); - } - blocked |= flag; - DPRINTF(Cache,"Blocking for cause %d, mask=%d\n", cause, blocked); - } - - /** - * Marks the cache as unblocked for the given cause. This also clears the - * blocked flags in the appropriate interfaces. - * @param cause The newly unblocked cause. - * @warning Calling this function can cause a blocked request on the bus to - * access the cache. The cache must be in a state to handle that request. - */ - void clearBlocked(BlockedCause cause) - { - uint8_t flag = 1 << cause; - blocked &= ~flag; - DPRINTF(Cache,"Unblocking for cause %d, mask=%d\n", cause, blocked); - if (blocked == 0) { - blocked_cycles[cause] += curTick() - blockedCycle; - cpuSidePort->clearBlocked(); - } - } - - /** - * Request the master bus for the given cause and time. - * @param cause The reason for the request. - * @param time The time to make the request. - */ - void requestMemSideBus(RequestCause cause, Tick time) - { - memSidePort->requestBus(cause, time); - } - - /** - * Clear the master bus request for the given cause. - * @param cause The request reason to clear. - */ - void deassertMemSideBusRequest(RequestCause cause) - { - // Obsolete... we no longer signal bus requests explicitly so - // we can't deassert them. Leaving this in as a no-op since - // the prefetcher calls it to indicate that it no longer wants - // to request a prefetch, and someday that might be - // interesting again. - } - - virtual unsigned int drain(Event *de); - - virtual bool inCache(Addr addr) = 0; - - virtual bool inMissQueue(Addr addr) = 0; - - void incMissCount(PacketPtr pkt, int id) - { - - if (pkt->cmd == MemCmd::Writeback) { - assert(id == -1); - misses[pkt->cmdToIndex()][0]++; - /* same thing for writeback hits as misses - no context id - * available, meanwhile writeback hit/miss stats are not used - * in any aggregate hit/miss calculations, so just lump them all - * in bucket 0 */ -#if FULL_SYSTEM - } else if (id == -1) { - // Device accesses have id -1 - // lump device accesses into their own bucket - misses[pkt->cmdToIndex()][_numCpus]++; -#endif - } else { - misses[pkt->cmdToIndex()][id % _numCpus]++; - } - - if (missCount) { - --missCount; - if (missCount == 0) - exitSimLoop("A cache reached the maximum miss count"); - } - } - void incHitCount(PacketPtr pkt, int id) - { - - /* Writeback requests don't have a context id associated with - * them, so attributing a hit to a -1 context id is obviously a - * problem. I've noticed in the stats that hits are split into - * demand and non-demand hits - neither of which include writeback - * hits, so here, I'll just put the writeback hits into bucket 0 - * since it won't mess with any other stats -hsul */ - if (pkt->cmd == MemCmd::Writeback) { - assert(id == -1); - hits[pkt->cmdToIndex()][0]++; -#if FULL_SYSTEM - } else if (id == -1) { - // Device accesses have id -1 - // lump device accesses into their own bucket - hits[pkt->cmdToIndex()][_numCpus]++; -#endif - } else { - /* the % is necessary in case there are switch cpus */ - hits[pkt->cmdToIndex()][id % _numCpus]++; - } - } - }; #endif //__BASE_CACHE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/base.cc --- a/src/mem/cache/base.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/base.cc Fri Aug 05 18:14:06 2011 +0100 @@ -40,17 +40,20 @@ using namespace std; -BaseCache::CachePort::CachePort(const std::string &_name, BaseCache *_cache, - const std::string &_label) - : SimpleTimingPort(_name, _cache), cache(_cache), - label(_label), otherPort(NULL), - blocked(false), mustSendRetry(false) -{ -} - - BaseCache::BaseCache(const Params *p) : MemObject(p), + + memSlavePort(*this, this, "mem_slave", MEM_SLAVE_ID), + memSlavePktQueue(this, memSlavePort), + + cacheUpMasterPort(*this, this, "cache_up_master", + CACHE_UP_MASTER_ID), + cacheDownMasterPort(*this, this, "cache_down_master", + CACHE_DOWN_MASTER_ID), + memMasterPort(*this, this, "mem_master", MEM_MASTER_ID), + cacheSlavePort(*this, this, "cache_slave", CACHE_SLAVE_ID), + cacheSlavePktQueue(this, cacheSlavePort), + mshrQueue("MSHRs", p->mshrs, 4, MSHRQueue_MSHRs), writeBuffer("write buffer", p->write_buffers, p->mshrs+1000, MSHRQueue_WriteBuffer), @@ -58,83 +61,30 @@ hitLatency(p->latency), numTarget(p->tgts_per_mshr), forwardSnoops(p->forward_snoops), + currMemReqPkt(NULL), + acceptMemReqEvent(this), blocked(0), noTargetMSHR(NULL), missCount(p->max_miss_count), drainEvent(NULL), addrRange(p->addr_range), - _numCpus(p->num_cpus) + _numCpus(p->num_cpus), + memMasterCurrReqPkt(NULL), + sendMemReqEvent(this) { + regPort(&memSlavePort); + regPort(&cacheUpMasterPort); + regPort(&cacheDownMasterPort); + regPort(&memMasterPort); + regPort(&cacheSlavePort); } void -BaseCache::CachePort::recvStatusChange(Port::Status status) -{ - if (status == Port::RangeChange) { - otherPort->sendStatusChange(Port::RangeChange); - } -} - - -bool -BaseCache::CachePort::checkFunctional(PacketPtr pkt) -{ - pkt->pushLabel(label); - bool done = SimpleTimingPort::checkFunctional(pkt); - pkt->popLabel(); - return done; -} - - -unsigned -BaseCache::CachePort::deviceBlockSize() const -{ - return cache->getBlockSize(); -} - - -bool -BaseCache::CachePort::recvRetryCommon() -{ - assert(waitingOnRetry); - waitingOnRetry = false; - return false; -} - - -void -BaseCache::CachePort::setBlocked() -{ - assert(!blocked); - DPRINTF(Cache, "Cache Blocking\n"); - blocked = true; - //Clear the retry flag - mustSendRetry = false; -} - -void -BaseCache::CachePort::clearBlocked() -{ - assert(blocked); - DPRINTF(Cache, "Cache Unblocking\n"); - blocked = false; - if (mustSendRetry) - { - DPRINTF(Cache, "Cache Sending Retry\n"); - mustSendRetry = false; - SendRetryEvent *ev = new SendRetryEvent(this, true); - // @TODO: need to find a better time (next bus cycle?) - schedule(ev, curTick() + 1); - } -} - - -void BaseCache::init() { - if (!cpuSidePort || !memSidePort) - panic("Cache not hooked up on both sides\n"); - cpuSidePort->sendStatusChange(Port::RangeChange); + // check that the ports are connected + if (!memSlavePort.isConnected() || !memMasterPort.isConnected()) + panic("Cache not connected properly\n"); } @@ -633,7 +583,7 @@ unsigned int BaseCache::drain(Event *de) { - int count = memSidePort->drain(de) + cpuSidePort->drain(de); + int count = memSlavePktQueue.drain(de) + cacheSlavePktQueue.drain(de); // Set status if (count != 0) { @@ -646,3 +596,12 @@ changeState(SimObject::Drained); return 0; } + +void +BaseCache::getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const +{ + r = 0xBD; + g = 0xA4; + b = 0xCB; +} diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/blk.hh --- a/src/mem/cache/blk.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/blk.hh Fri Aug 05 18:14:06 2011 +0100 @@ -39,7 +39,6 @@ #include "base/printable.hh" #include "sim/core.hh" // for Tick -#include "arch/isa_traits.hh" // for Addr #include "mem/packet.hh" #include "mem/request.hh" @@ -92,6 +91,8 @@ /** Which curTick() will this block be accessable */ Tick whenReady; + Tick lastWritten; + /** * The set this block belongs to. * @todo Move this into subclasses when we fix CacheTags to use them. @@ -222,7 +223,7 @@ * redundant records on the list, but that's OK, as they'll all * get blown away at the next store. */ - void trackLoadLocked(PacketPtr pkt) + void trackLoadLocked(Packet* pkt) { assert(pkt->isLLSC()); lockList.push_front(Lock(pkt->req)); @@ -239,7 +240,7 @@ * @return True if write should proceed, false otherwise. Returns * false only in the case of a failed store conditional. */ - bool checkWrite(PacketPtr pkt) + bool checkWrite(Packet* pkt) { Request *req = pkt->req; if (pkt->isLLSC()) { diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/builder.cc --- a/src/mem/cache/builder.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/builder.cc Fri Aug 05 18:14:06 2011 +0100 @@ -41,7 +41,6 @@ #include "mem/config/cache.hh" #include "mem/cache/base.hh" #include "mem/cache/cache.hh" -#include "mem/bus.hh" #include "params/BaseCache.hh" // Tag Templates @@ -64,7 +63,6 @@ using namespace std; -using namespace TheISA; #define BUILD_CACHE(TAGS, tags) \ do { \ diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/cache.hh --- a/src/mem/cache/cache.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/cache.hh Fri Aug 05 18:14:06 2011 +0100 @@ -59,6 +59,7 @@ class Cache : public BaseCache { public: + /** Define the type of cache block to use. */ typedef typename TagStore::BlkType BlkType; /** A typedef for a list of BlkType pointers. */ @@ -66,63 +67,6 @@ protected: - class CpuSidePort : public CachePort - { - public: - CpuSidePort(const std::string &_name, - Cache *_cache, - const std::string &_label); - - // BaseCache::CachePort just has a BaseCache *; this function - // lets us get back the type info we lost when we stored the - // cache pointer there. - Cache *myCache() { - return static_cast *>(cache); - } - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); - - virtual bool recvTiming(PacketPtr pkt); - - virtual Tick recvAtomic(PacketPtr pkt); - - virtual void recvFunctional(PacketPtr pkt); - }; - - class MemSidePort : public CachePort - { - public: - MemSidePort(const std::string &_name, - Cache *_cache, - const std::string &_label); - - // BaseCache::CachePort just has a BaseCache *; this function - // lets us get back the type info we lost when we stored the - // cache pointer there. - Cache *myCache() { - return static_cast *>(cache); - } - - void sendPacket(); - - void processSendEvent(); - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); - - virtual bool recvTiming(PacketPtr pkt); - - virtual void recvRetry(); - - virtual Tick recvAtomic(PacketPtr pkt); - - virtual void recvFunctional(PacketPtr pkt); - - typedef EventWrapper - SendEvent; - }; - /** Tag and data Storage */ TagStore *tags; @@ -142,6 +86,8 @@ */ const bool prefetchOnAccess; + MemMapPacketPtr memMasterCurrRespPkt; + /** * Does all the processing necessary to perform the provided request. * @param pkt The memory request to perform. @@ -150,13 +96,13 @@ * @param update True if the replacement data should be updated. * @return Boolean indicating whether the request was satisfied. */ - bool access(PacketPtr pkt, BlkType *&blk, - int &lat, PacketList &writebacks); + bool access(MemMapPacketPtr pkt, BlkType *&blk, + int &lat, MemMapPacketList &writebacks); /** *Handle doing the Compare and Swap function for SPARC. */ - void cmpAndSwap(BlkType *blk, PacketPtr pkt); + void cmpAndSwap(BlkType *blk, MemMapPacketPtr pkt); /** * Find a block frame for new block at address addr, assuming that @@ -164,7 +110,7 @@ * any to provided packet list. Return free block frame. May * return NULL if there are no replaceable blocks at the moment. */ - BlkType *allocateBlock(Addr addr, PacketList &writebacks); + BlkType *allocateBlock(Addr addr, MemMapPacketList &writebacks); /** * Populates a cache block and handles all outstanding requests for the @@ -176,14 +122,14 @@ * @return Pointer to the new cache block. */ BlkType *handleFill(PacketPtr pkt, BlkType *blk, - PacketList &writebacks); + MemMapPacketList &writebacks); - void satisfyCpuSideRequest(PacketPtr pkt, BlkType *blk, + void satisfyCpuSideRequest(MemMapPacketPtr pkt, BlkType *blk, bool deferred_response = false, bool pending_downgrade = false); - bool satisfyMSHR(MSHR *mshr, PacketPtr pkt, BlkType *blk); + bool satisfyMSHR(MSHR *mshr, Packet* pkt, BlkType *blk); - void doTimingSupplyResponse(PacketPtr req_pkt, uint8_t *blk_data, + void doTimingSupplyResponse(CacheMntPacketPtr req_pkt, uint8_t *blk_data, bool already_copied, bool pending_inval); /** @@ -191,7 +137,7 @@ * @param blk The cache block being snooped. * @param new_state The new coherence state for the block. */ - void handleSnoop(PacketPtr ptk, BlkType *blk, + void handleSnoop(CacheMntPacketPtr ptk, BlkType *blk, bool is_timing, bool is_deferred, bool pending_inval); /** @@ -199,50 +145,63 @@ * @param blk The block to writeback. * @return The writeback request for the block. */ - PacketPtr writebackBlk(BlkType *blk); + MemMapPacketPtr writebackBlk(BlkType *blk); + + void sendMemReq(); public: /** Instantiates a basic cache object. */ Cache(const Params *p, TagStore *tags, BasePrefetcher *prefetcher); - virtual Port *getPort(const std::string &if_name, int idx = -1); - virtual void deletePortRefs(Port *p); - void regStats(); + void init(); /** * Performs the access specified by the request. * @param pkt The request to perform. * @return The result of the access. */ - bool timingAccess(PacketPtr pkt); + void recvDebug(MemMapPacketPtr pkt, int slave_id); /** * Performs the access specified by the request. * @param pkt The request to perform. * @return The result of the access. */ - Tick atomicAccess(PacketPtr pkt); + Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + + void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + void acceptMemReq(); + + void endResp(MemMapPacketPtr resp_pkt, int slave_id); + + /** * Performs the access specified by the request. * @param pkt The request to perform. * @return The result of the access. */ - void functionalAccess(PacketPtr pkt, CachePort *incomingPort, - CachePort *otherSidePort); + void recvDebug(CacheMntPacketPtr pkt, int slave_id); /** * Handles a response (cache line fill/write ack) from the bus. * @param pkt The request being responded to. */ - void handleResponse(PacketPtr pkt); + void handleResponse(MemMapPacketPtr pkt); /** * Snoops bus transactions to maintain coherence. * @param pkt The current bus transaction. */ - void snoopTiming(PacketPtr pkt); + bool recvTimingReq(CacheMntPacketPtr pkt, int slave_id); + + /** + * Receive a retry on a given slave port. + * @param slave_id The unique id of the slave port + */ + void recvRetryResp(int slave_id); /** * Snoop for the provided request in the cache and return the estimated @@ -250,7 +209,31 @@ * @param pkt The memory request to snoop * @return The estimated completion time. */ - Tick snoopAtomic(PacketPtr pkt); + Tick recvAtomic(CacheMntPacketPtr pkt, int slave_id); + + + + /** + * Master interface for the cache maintenance-port + */ + bool recvTimingResp(CacheMntPacketPtr resp_pkt, int master_id); + + void recvRetryReq(int master_id); + + /** + * Memory-mapped master itnerface + */ + void beginResp(MemMapPacketPtr resp_pkt, int master_id); + + void acceptMemResp(); + + EventWrapper acceptMemRespEvent; + + void endReq(MemMapPacketPtr req_pkt, int master_id); + + + /** Shared between mem and cache */ + void recvRangeChange(int master_id); /** * Squash all requests associated with specified thread. @@ -270,8 +253,8 @@ * @return A new Packet containing the request, or NULL if the * current request in cpu_pkt should just be forwarded on. */ - PacketPtr getBusPacket(PacketPtr cpu_pkt, BlkType *blk, - bool needsExclusive); + MemMapPacketPtr getBusPacket(Packet* cpu_pkt, BlkType *blk, + bool needsExclusive); /** * Return the next MSHR to service, either a pending miss from the @@ -286,7 +269,7 @@ * cache gets granted the downstream bus in timing mode. * @return The request to service, NULL if none found. */ - PacketPtr getTimingPacket(); + MemMapPacketPtr getTimingPacket(); /** * Marks a request as in service (sent on the bus). This can have side @@ -294,13 +277,13 @@ * are successfully sent. * @param pkt The request that was sent on the bus. */ - void markInService(MSHR *mshr, PacketPtr pkt = 0); + void markInService(MSHR *mshr, Packet* pkt = 0); /** * Perform the given writeback request. * @param pkt The writeback request. */ - void doWriteback(PacketPtr pkt); + void doWriteback(Packet* pkt); /** * Return whether there are any outstanding misses. diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/cache_impl.hh --- a/src/mem/cache/cache_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/cache_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -66,18 +66,13 @@ tags(tags), prefetcher(pf), doFastWrites(true), - prefetchOnAccess(p->prefetch_on_access) + prefetchOnAccess(p->prefetch_on_access), + memMasterCurrRespPkt(NULL), + acceptMemRespEvent(this) { tempBlock = new BlkType(); tempBlock->data = new uint8_t[blkSize]; - cpuSidePort = new CpuSidePort(p->name + "-cpu_side_port", this, - "CpuSidePort"); - memSidePort = new MemSidePort(p->name + "-mem_side_port", this, - "MemSidePort"); - cpuSidePort->setOtherPort(memSidePort); - memSidePort->setOtherPort(cpuSidePort); - tags->setCache(this); if (prefetcher) prefetcher->setCache(this); @@ -94,38 +89,15 @@ } template -Port * -Cache::getPort(const std::string &if_name, int idx) +void +Cache::init() { - if (if_name == "" || if_name == "cpu_side") { - return cpuSidePort; - } else if (if_name == "mem_side") { - return memSidePort; - } else if (if_name == "functional") { - CpuSidePort *funcPort = - new CpuSidePort(name() + "-cpu_side_funcport", this, - "CpuSideFuncPort"); - funcPort->setOtherPort(memSidePort); - return funcPort; - } else { - panic("Port name %s unrecognized\n", if_name); - } + BaseCache::init(); } template void -Cache::deletePortRefs(Port *p) -{ - if (cpuSidePort == p || memSidePort == p) - panic("Can only delete functional ports\n"); - - delete p; -} - - -template -void -Cache::cmpAndSwap(BlkType *blk, PacketPtr pkt) +Cache::cmpAndSwap(BlkType *blk, MemMapPacketPtr pkt) { uint64_t overwrite_val; bool overwrite_mem; @@ -165,7 +137,7 @@ template void -Cache::satisfyCpuSideRequest(PacketPtr pkt, BlkType *blk, +Cache::satisfyCpuSideRequest(MemMapPacketPtr pkt, BlkType *blk, bool deferred_response, bool pending_downgrade) { @@ -187,6 +159,7 @@ if (blk->checkWrite(pkt)) { pkt->writeDataToBlock(blk->data, blkSize); blk->status |= BlkDirty; + blk->lastWritten = curTick(); } } else if (pkt->isRead()) { if (pkt->isLLSC()) { @@ -255,7 +228,7 @@ template void -Cache::markInService(MSHR *mshr, PacketPtr pkt) +Cache::markInService(MSHR *mshr, Packet* pkt) { markInServiceInternal(mshr, pkt); #if 0 @@ -302,8 +275,8 @@ template bool -Cache::access(PacketPtr pkt, BlkType *&blk, - int &lat, PacketList &writebacks) +Cache::access(MemMapPacketPtr pkt, BlkType *&blk, + int &lat, MemMapPacketList &writebacks) { if (pkt->req->isUncacheable()) { if (pkt->req->isClearLL()) { @@ -360,6 +333,7 @@ } std::memcpy(blk->data, pkt->getPtr(), blkSize); blk->status |= BlkDirty; + blk->lastWritten = curTick(); if (pkt->isSupplyExclusive()) { blk->status |= BlkWritable; } @@ -380,54 +354,149 @@ return false; } +template +bool +Cache::recvTimingResp(CacheMntPacketPtr pkt, int master_id) +{ + assert(master_id == CACHE_UP_MASTER_ID); -class ForwardResponseRecord : public Packet::SenderState, public FastAlloc -{ - Packet::SenderState *prevSenderState; - int prevSrc; -#ifndef NDEBUG - BaseCache *cache; -#endif - public: - ForwardResponseRecord(Packet *pkt, BaseCache *_cache) - : prevSenderState(pkt->senderState), prevSrc(pkt->getSrc()) -#ifndef NDEBUG - , cache(_cache) -#endif - {} - void restore(Packet *pkt, BaseCache *_cache) - { - assert(_cache == cache); - pkt->senderState = prevSenderState; - pkt->setDest(prevSrc); - } -}; + // this came from our upwards facing cache maintenance master port + // as a response from an upper cache level, i.e. towards the CPU, + // now going back towards the memory + // we charge hitLatency even for the cache responses + cacheSlavePktQueue.insert(pkt, curTick() + hitLatency); + + return true; +} template -bool -Cache::timingAccess(PacketPtr pkt) +void +Cache::beginResp(MemMapPacketPtr resp_pkt, int master_id) { -//@todo Add back in MemDebug Calls -// MemDebug::cacheAccess(pkt); + assert(master_id == MEM_MASTER_ID); + assert(memMasterCurrRespPkt == NULL); + + memMasterCurrRespPkt = resp_pkt; + schedule(acceptMemRespEvent, curTick()); +} + +template +void +Cache::acceptMemResp() +{ + handleResponse(memMasterCurrRespPkt); + memMasterPort->endResp(memMasterCurrRespPkt); + delete memMasterCurrRespPkt; + memMasterCurrRespPkt = NULL; +} + +template +void +Cache::endReq(MemMapPacketPtr req_pkt, int master_id) +{ + assert(memMasterCurrReqPkt == req_pkt); + + DPRINTF(CachePort, "ending master request\n"); + + Tick nextReady = nextMSHRReadyTime(); + // @TODO: need to factor in prefetch requests here somehow + if (nextReady != MaxTick) { + if (!sendMemReqEvent.scheduled()) { + DPRINTF(CachePort, "more packets to send @ %d\n", nextReady); + schedule(sendMemReqEvent, std::max(nextReady, curTick() + 1)); + } + } + memMasterCurrReqPkt = NULL; +} + +template +void +Cache::recvRetryReq(int master_id) +{ + panic("Cache does not expect retry request on cache maintenance ports"); +} + +template +void +Cache::sendMemReq() +{ + // we are here either as the result of a retry on a request that + // was not immediately accepted, or because we have scheduled a + // bus request in the future + + // check for packets (requests & writebacks) + memMasterCurrReqPkt = getTimingPacket(); + + // note that this might be a new one from last time round + if (memMasterCurrReqPkt == NULL) { + // can happen if e.g. we attempt a writeback and fail, but + // before the retry, the writeback is eliminated because + // we snoop another cache's ReadEx. + } else { + MSHR *mshr = dynamic_cast(memMasterCurrReqPkt->senderState); + + if (memMasterPort->reqLinkState() == PortInterface::IDLE) { + markInService(mshr, memMasterCurrReqPkt); + memMasterPort->beginReq(memMasterCurrReqPkt); + } else { + // port is not idle, so don't start the request + DPRINTF(CachePort, "now waiting for port to be idle\n"); + if (!mshr->isForwardNoResponse()) { + delete memMasterCurrReqPkt; + memMasterCurrReqPkt = NULL; + } + } + } +} + +template +void +Cache::recvRangeChange(int master_id) +{ +} + +template +void +Cache::beginReq(MemMapPacketPtr pkt, int slave_id) +{ + assert(currMemReqPkt == NULL); + currMemReqPkt = pkt; + + // the inhibited request get special treatment and shouldn't care + // if the cache is blocked + if (isBlocked() && !pkt->memInhibitAsserted()) { + DPRINTF(Cache, "Awaiting unblock before accepting request\n"); + // remember that we have a pending request so that we can tell the + // master to retry once we become unblocked + } else { + schedule(acceptMemReqEvent, curTick()); + } +} + +template +void +Cache::acceptMemReq() +{ + // if we got blocked between the beginning of the request and the + // accepting then don't do anything + if (isBlocked()) + return; + + // we are not blocked, so deal with the request + + // keep track of the current one + assert(currMemReqPkt != NULL); + MemMapPacketPtr pkt = currMemReqPkt; // we charge hitLatency for doing just about anything here Tick time = curTick() + hitLatency; - if (pkt->isResponse()) { - // must be cache-to-cache response from upper to lower level - ForwardResponseRecord *rec = - dynamic_cast(pkt->senderState); - assert(rec != NULL); - rec->restore(pkt, this); - delete rec; - memSidePort->respond(pkt, time); - return true; - } - - assert(pkt->isRequest()); - + // this came from our mem slave port (connected e.g. to the CPU's + // master port) as a request, and is sent out through one of our + // master ports (e.g. to go to the final slave such as a memory) if (pkt->memInhibitAsserted()) { + // another cache already committed to responding DPRINTF(Cache, "mem inhibited on 0x%x: not responding\n", pkt->getAddr()); assert(!pkt->req->isUncacheable()); @@ -436,16 +505,34 @@ // that have shared copies. Not necessary if we know that // supplier had exclusive copy to begin with. if (pkt->needsExclusive() && !pkt->isSupplyExclusive()) { - Packet *snoopPkt = new Packet(pkt, true); // clear flags + // clear flags + CacheMntPacketPtr snoopPkt = new CacheMntPacket(pkt, true); + + // this should not be an express snoop since it is going + // downwards, but if not we have to deal with flow control snoopPkt->setExpressSnoop(); + snoopPkt->assertMemInhibit(); - memSidePort->sendTiming(snoopPkt); - // main memory will delete snoopPkt + + // a snoop packet, send it towards the lower cache levels + // (if any) + cacheDownMasterPort->sendTimingReq(snoopPkt); + + // cannot assume that this is immediately accepted (unless + // we make it an express snoop)! + + // the final slave (I/O device, memory etc) will delete the + // request as it has inhibit asserted } // since we're the official target but we aren't responding, - // delete the packet now. + // delete the packet now. why does this not cause problems? + // the snoop packet is sent out, and now we deallocate the + // original request and the data pointer! the snoop must be + // instantaneous or this will cause segfaults. delete pkt; - return true; + memSlavePort->endReq(currMemReqPkt); + currMemReqPkt = NULL; + return; // true; } if (pkt->req->isUncacheable()) { @@ -465,12 +552,15 @@ allocateUncachedReadBuffer(pkt, time, true); } assert(pkt->needsResponse()); // else we should delete it here?? - return true; + memSlavePort->endReq(currMemReqPkt); + currMemReqPkt = NULL; + + return; // true; } int lat = hitLatency; BlkType *blk = NULL; - PacketList writebacks; + MemMapPacketList writebacks; bool satisfied = access(pkt, blk, lat, writebacks); @@ -490,7 +580,7 @@ "with an outstanding miss to the same address\n"); } blk = handleFill(NULL, pkt, BlkValid | BlkWritable, - writebacks); + writebacks); ++fastWrites; } } @@ -503,8 +593,11 @@ if (satisfied) { if (needsResponse) { + // respond to a request from the master connected to our + // slave port, e.g. a CPU, without any intervention of the + // master side of the cache, i.e. no forwarding pkt->makeTimingResponse(); - cpuSidePort->respond(pkt, curTick()+lat); + memSlavePktQueue.insert(pkt, curTick()+lat); } else { delete pkt; } @@ -579,19 +672,23 @@ // copy writebacks to write buffer while (!writebacks.empty()) { - PacketPtr wbPkt = writebacks.front(); + Packet* wbPkt = writebacks.front(); allocateWriteBuffer(wbPkt, time, true); writebacks.pop_front(); } - return true; + // no more pending request (if there was one) + memSlavePort->endReq(currMemReqPkt); + currMemReqPkt = NULL; + // packet is accepted + return; // true; } // See comment in cache.hh. template -PacketPtr -Cache::getBusPacket(PacketPtr cpu_pkt, BlkType *blk, +MemMapPacketPtr +Cache::getBusPacket(Packet* cpu_pkt, BlkType *blk, bool needsExclusive) { bool blkValid = blk && blk->isValid(); @@ -626,17 +723,18 @@ // block is invalid cmd = needsExclusive ? MemCmd::ReadExReq : MemCmd::ReadReq; } - PacketPtr pkt = new Packet(cpu_pkt->req, cmd, Packet::Broadcast, blkSize); + MemMapPacketPtr pkt = new MemMapPacket(cpu_pkt->req, cmd, blkSize); pkt->allocate(); return pkt; } - template Tick -Cache::atomicAccess(PacketPtr pkt) +Cache::recvAtomic(MemMapPacketPtr pkt, int slave_id) { + assert(!pkt->isError()); + int lat = hitLatency; // @TODO: make this a parameter @@ -656,7 +754,7 @@ if (!last_level_cache) { DPRINTF(Cache, "forwarding mem-inhibited %s on 0x%x\n", pkt->cmdString(), pkt->getAddr()); - lat += memSidePort->sendAtomic(pkt); + lat += memMasterPort->sendAtomic(pkt); } } else { DPRINTF(Cache, "rcvd mem-inhibited %s on 0x%x: not responding\n", @@ -671,11 +769,11 @@ // access in timing mode BlkType *blk = NULL; - PacketList writebacks; + MemMapPacketList writebacks; if (!access(pkt, blk, lat, writebacks)) { // MISS - PacketPtr bus_pkt = getBusPacket(pkt, blk, pkt->needsExclusive()); + MemMapPacketPtr bus_pkt = getBusPacket(pkt, blk, pkt->needsExclusive()); bool is_forward = (bus_pkt == NULL); @@ -692,13 +790,14 @@ CacheBlk::State old_state = blk ? blk->status : 0; #endif - lat += memSidePort->sendAtomic(bus_pkt); + // send it to the memside + lat += memMasterPort->sendAtomic(bus_pkt); + + assert(!bus_pkt->isError()); DPRINTF(Cache, "Receive response: %s for addr %x in state %i\n", bus_pkt->cmdString(), bus_pkt->getAddr(), old_state); - assert(!bus_pkt->wasNacked()); - // If packet was a forward, the response (if any) is already // in place in the bus_pkt == pkt structure, so we don't need // to do anything. Otherwise, use the separate bus_pkt to @@ -738,8 +837,8 @@ // Handle writebacks if needed while (!writebacks.empty()){ - PacketPtr wbPkt = writebacks.front(); - memSidePort->sendAtomic(wbPkt); + MemMapPacketPtr wbPkt = writebacks.front(); + memMasterPort->sendAtomic(wbPkt); writebacks.pop_front(); delete wbPkt; } @@ -756,9 +855,7 @@ template void -Cache::functionalAccess(PacketPtr pkt, - CachePort *incomingPort, - CachePort *otherSidePort) +Cache::recvDebug(MemMapPacketPtr pkt, int slave_id) { Addr blk_addr = blockAlign(pkt->getAddr()); BlkType *blk = tags->findBlock(pkt->getAddr()); @@ -784,10 +881,9 @@ (mshr && mshr->inService && mshr->isPendingDirty())); bool done = have_dirty - || incomingPort->checkFunctional(pkt) || mshrQueue.checkFunctional(pkt, blk_addr) || writeBuffer.checkFunctional(pkt, blk_addr) - || otherSidePort->checkFunctional(pkt); + || memSlavePktQueue.checkFunctional(pkt); DPRINTF(Cache, "functional %s %x %s%s%s\n", pkt->cmdString(), pkt->getAddr(), @@ -800,10 +896,65 @@ if (done) { pkt->makeResponse(); } else { - otherSidePort->sendFunctional(pkt); + memMasterPort->sendDebug(pkt); } } +template +void +Cache::recvDebug(CacheMntPacketPtr pkt, int slave_id) +{ + Addr blk_addr = blockAlign(pkt->getAddr()); + BlkType *blk = tags->findBlock(pkt->getAddr()); + MSHR *mshr = mshrQueue.findMatch(blk_addr); + + pkt->pushLabel(name()); + + CacheBlkPrintWrapper cbpw(blk); + + // Note that just because an L2/L3 has valid data doesn't mean an + // L1 doesn't have a more up-to-date modified copy that still + // needs to be found. As a result we always update the request if + // we have it, but only declare it satisfied if we are the owner. + + // see if we have data at all (owned or otherwise) + bool have_data = blk && blk->isValid() + && pkt->checkFunctional(&cbpw, blk_addr, blkSize, blk->data); + + // data we have is dirty if marked as such or if valid & ownership + // pending due to outstanding UpgradeReq + bool have_dirty = + have_data && (blk->isDirty() || + (mshr && mshr->inService && mshr->isPendingDirty())); + + bool done = have_dirty + || mshrQueue.checkFunctional(pkt, blk_addr) + || writeBuffer.checkFunctional(pkt, blk_addr) + || cacheSlavePktQueue.checkFunctional(pkt); + + DPRINTF(Cache, "functional %s %x %s%s%s\n", + pkt->cmdString(), pkt->getAddr(), + (blk && blk->isValid()) ? "valid " : "", + have_data ? "data " : "", done ? "done " : ""); + + // We're leaving the cache, so pop cache->name() label + pkt->popLabel(); + + if (done) { + pkt->makeResponse(); + } else { + // away from the memory, snoop in upper-levels of the + // cache hierarchy + if (forwardSnoops && cacheUpMasterPort.isConnected()) { + cacheUpMasterPort->sendDebug(pkt); + } + // why would this ever make sense when we do not have to + // update the upper levels of the caches? non-inclusive + // caches? + } +} + + ///////////////////////////////////////////////////// // @@ -814,7 +965,7 @@ template void -Cache::handleResponse(PacketPtr pkt) +Cache::handleResponse(MemMapPacketPtr pkt) { Tick time = curTick() + hitLatency; MSHR *mshr = dynamic_cast(pkt->senderState); @@ -822,12 +973,6 @@ assert(mshr); - if (pkt->wasNacked()) { - //pkt->reinitFromRequest(); - warn("NACKs from devices not connected to the same bus " - "not implemented\n"); - return; - } if (is_error) { DPRINTF(Cache, "Cache received packet with error for address %x, " "cmd: %s\n", pkt->getAddr(), pkt->cmdString()); @@ -849,7 +994,7 @@ BlkType *blk = tags->findBlock(pkt->getAddr()); int stats_cmd_idx = initial_tgt->pkt->cmdToIndex(); Tick miss_latency = curTick() - initial_tgt->recvTime; - PacketList writebacks; + MemMapPacketList writebacks; if (pkt->req->isUncacheable()) { mshr_uncacheable_lat[stats_cmd_idx][0/*pkt->req->threadId()*/] += @@ -883,11 +1028,18 @@ while (mshr->hasTargets()) { MSHR::Target *target = mshr->getTarget(); + // a potential cache packet, here to keep gcc happy + CacheMntPacketPtr cache_pkt = + dynamic_cast(target->pkt); + + MemMapPacketPtr mem_pkt = + dynamic_cast(target->pkt); + switch (target->source) { case MSHR::Target::FromCPU: Tick completion_time; if (is_fill) { - satisfyCpuSideRequest(target->pkt, blk, + satisfyCpuSideRequest(mem_pkt, blk, true, mshr->hasPostDowngrade()); // How many bytes past the first request is this one int transfer_offset = @@ -928,7 +1080,13 @@ // isInvalidate() set otherwise. target->pkt->cmd = MemCmd::ReadRespWithInvalidate; } - cpuSidePort->respond(target->pkt, completion_time); + + // forwarding a response from the master port to the slave + // port after completion time, note that this is + // shared/merged with the responses created directly as a + // results of non-forwarded requests that are satisfied + assert(mem_pkt != NULL); + memSlavePktQueue.insert(mem_pkt, completion_time); break; case MSHR::Target::FromPrefetcher: @@ -945,7 +1103,9 @@ // response to snoop request DPRINTF(Cache, "processing deferred snoop...\n"); assert(!(pkt->isInvalidate() && !mshr->hasPostInvalidate())); - handleSnoop(target->pkt, blk, true, true, + + assert(cache_pkt != NULL); + handleSnoop(cache_pkt, blk, true, true, mshr->hasPostInvalidate()); break; @@ -982,7 +1142,7 @@ // copy writebacks to write buffer while (!writebacks.empty()) { - PacketPtr wbPkt = writebacks.front(); + Packet* wbPkt = writebacks.front(); allocateWriteBuffer(wbPkt, time, true); writebacks.pop_front(); } @@ -993,15 +1153,13 @@ } tags->invalidateBlk(blk); } - - delete pkt; } template -PacketPtr +MemMapPacketPtr Cache::writebackBlk(BlkType *blk) { assert(blk && blk->isValid() && blk->isDirty()); @@ -1010,7 +1168,8 @@ Request *writebackReq = new Request(tags->regenerateBlkAddr(blk->tag, blk->set), blkSize, 0); - PacketPtr writeback = new Packet(writebackReq, MemCmd::Writeback, -1); + MemMapPacketPtr writeback = new MemMapPacket(writebackReq, + MemCmd::Writeback); if (blk->isWritable()) { writeback->setSupplyExclusive(); } @@ -1024,7 +1183,7 @@ template typename Cache::BlkType* -Cache::allocateBlock(Addr addr, PacketList &writebacks) +Cache::allocateBlock(Addr addr, MemMapPacketList &writebacks) { BlkType *blk = tags->findVictim(addr, writebacks); @@ -1062,8 +1221,8 @@ // writebacks atomically once the original request is complete). template typename Cache::BlkType* -Cache::handleFill(PacketPtr pkt, BlkType *blk, - PacketList &writebacks) +Cache::handleFill(Packet* pkt, BlkType *blk, + MemMapPacketList &writebacks) { Addr addr = pkt->getAddr(); #if TRACING_ON @@ -1093,6 +1252,7 @@ } else { // existing block... probably an upgrade assert(blk->tag == tags->extractTag(addr)); + // either we're getting new data or the block should already be valid assert(pkt->hasData() || blk->isValid()); // don't clear block status... if block is already dirty we @@ -1120,6 +1280,7 @@ // if we got new data, copy it in if (pkt->isRead()) { std::memcpy(blk->data, pkt->getPtr(), blkSize); + blk->lastWritten = curTick(); } blk->whenReady = pkt->finishTime; @@ -1137,12 +1298,13 @@ template void Cache:: -doTimingSupplyResponse(PacketPtr req_pkt, uint8_t *blk_data, +doTimingSupplyResponse(CacheMntPacketPtr req_pkt, uint8_t *blk_data, bool already_copied, bool pending_inval) { // timing-mode snoop responses require a new packet, unless we // already made a copy... - PacketPtr pkt = already_copied ? req_pkt : new Packet(req_pkt); + CacheMntPacketPtr pkt = already_copied ? req_pkt : + new CacheMntPacket(req_pkt); assert(req_pkt->isInvalidate() || pkt->sharedAsserted()); pkt->allocate(); pkt->makeTimingResponse(); @@ -1159,12 +1321,12 @@ // invalidate it. pkt->cmd = MemCmd::ReadRespWithInvalidate; } - memSidePort->respond(pkt, curTick() + hitLatency); + cacheSlavePktQueue.insert(pkt, curTick() + hitLatency); } template void -Cache::handleSnoop(PacketPtr pkt, BlkType *blk, +Cache::handleSnoop(CacheMntPacketPtr pkt, BlkType *blk, bool is_timing, bool is_deferred, bool pending_inval) { @@ -1180,36 +1342,37 @@ bool invalidate = pkt->isInvalidate(); bool M5_VAR_USED needs_exclusive = pkt->needsExclusive(); + if (forwardSnoops && !cacheUpMasterPort.isConnected()) + panic("%s cannot forward snoops if cache master is not connected", + name()); + if (forwardSnoops) { // first propagate snoop upward to see if anyone above us wants to // handle it. save & restore packet src since it will get // rewritten to be relative to cpu-side bus (if any) bool alreadyResponded = pkt->memInhibitAsserted(); if (is_timing) { - Packet *snoopPkt = new Packet(pkt, true); // clear flags - snoopPkt->setExpressSnoop(); - snoopPkt->senderState = new ForwardResponseRecord(pkt, this); - cpuSidePort->sendTiming(snoopPkt); - if (snoopPkt->memInhibitAsserted()) { + CacheMntPacket snoopPkt(pkt, true); // clear flags + snoopPkt.setExpressSnoop(); + cacheUpMasterPort->sendTimingReq(&snoopPkt); + if (snoopPkt.memInhibitAsserted()) { // cache-to-cache response from some upper cache assert(!alreadyResponded); pkt->assertMemInhibit(); - } else { - delete snoopPkt->senderState; } - if (snoopPkt->sharedAsserted()) { + + if (snoopPkt.sharedAsserted()) { pkt->assertShared(); } - delete snoopPkt; + // why does the snoop packet not de-allocate the request and cause + // problems here? } else { - int origSrc = pkt->getSrc(); - cpuSidePort->sendAtomic(pkt); + cacheUpMasterPort->sendAtomic(pkt); if (!alreadyResponded && pkt->memInhibitAsserted()) { // cache-to-cache response from some upper cache: // forward response to original requester assert(pkt->isResponse()); } - pkt->setSrc(origSrc); } } @@ -1269,16 +1432,20 @@ template -void -Cache::snoopTiming(PacketPtr pkt) +bool +Cache::recvTimingReq(CacheMntPacketPtr pkt, int slave_id) { + // cache maintenance packets from lower levels of cache + // Note that some deferred snoops don't have requests, since the // original access may have already completed if ((pkt->req && pkt->req->isUncacheable()) || pkt->cmd == MemCmd::Writeback) { //Can't get a hit on an uncacheable address //Revisit this for multi level coherence - return; + + // message is accepted + return true; } BlkType *blk = tags->findBlock(pkt->getAddr()); @@ -1293,7 +1460,9 @@ blk_addr); if (mshr->getNumTargets() > numTarget) warn("allocating bonus target for snoop"); //handle later - return; + + // message is accepted + return true; } //We also need to check the writeback buffers and handle those @@ -1307,7 +1476,7 @@ mshr = writebacks[i]; assert(!mshr->isUncacheable()); assert(mshr->getNumTargets() == 1); - PacketPtr wb_pkt = mshr->getTarget()->pkt; + Packet* wb_pkt = mshr->getTarget()->pkt; assert(wb_pkt->cmd == MemCmd::Writeback); assert(!pkt->memInhibitAsserted()); @@ -1341,16 +1510,37 @@ } handleSnoop(pkt, blk, true, false, false); + + // packet is always accepted + return true; } +template +void +Cache::endResp(MemMapPacketPtr, int slave_id) +{ + memSlavePktQueue.endDeferred(); +} + +template +void +Cache::recvRetryResp(int slave_id) +{ + panic("Cache does not expect retry response on cache maintenance ports"); + //if (slave_id == CACHE_SLAVE_ID) { + // cacheSlavePktQueue.sendDeferredPacket(); + //} else { + // panic("Unknown slave port"); + //} +} template Tick -Cache::snoopAtomic(PacketPtr pkt) +Cache::recvAtomic(CacheMntPacketPtr pkt, int slave_id) { if (pkt->req->isUncacheable() || pkt->cmd == MemCmd::Writeback) { // Can't get a hit on an uncacheable address - // Revisit this for multi level coherence + // Delay the equivalent of a cache tag look-up and invalidate return hitLatency; } @@ -1422,7 +1612,7 @@ assert(!miss_mshr && !write_mshr); if (prefetcher && !mshrQueue.isFull()) { // If we have a miss queue slot, we can try a prefetch - PacketPtr pkt = prefetcher->getPacket(); + MemMapPacketPtr pkt = prefetcher->getPacket(); if (pkt) { Addr pf_addr = blockAlign(pkt->getAddr()); if (!tags->findBlock(pf_addr) && !mshrQueue.findMatch(pf_addr)) { @@ -1440,7 +1630,7 @@ template -PacketPtr +MemMapPacketPtr Cache::getTimingPacket() { MSHR *mshr = getNextMSHR(); @@ -1450,19 +1640,22 @@ } // use request from 1st target - PacketPtr tgt_pkt = mshr->getTarget()->pkt; - PacketPtr pkt = NULL; + Packet* tgt_pkt_int = mshr->getTarget()->pkt; + MemMapPacketPtr tgt_pkt = dynamic_cast(tgt_pkt_int); + assert(tgt_pkt != NULL); + MemMapPacketPtr pkt = NULL; if (tgt_pkt->cmd == MemCmd::SCUpgradeFailReq || tgt_pkt->cmd == MemCmd::StoreCondFailReq) { // SCUpgradeReq or StoreCondReq saw invalidation while queued // in MSHR, so now that we are getting around to processing // it, just treat it as if we got a failure response - pkt = new Packet(tgt_pkt); + pkt = new MemMapPacket(tgt_pkt); pkt->cmd = MemCmd::UpgradeFailResp; pkt->senderState = mshr; pkt->firstWordTime = pkt->finishTime = curTick(); handleResponse(pkt); + delete pkt; return NULL; } else if (mshr->isForwardNoResponse()) { // no response expected, just forward packet as it is @@ -1478,7 +1671,7 @@ // not a cache block request, but a response is expected // make copy of current packet to forward, keep current // copy for response handling - pkt = new Packet(tgt_pkt); + pkt = new MemMapPacket(tgt_pkt); pkt->allocate(); if (pkt->isWrite()) { pkt->setData(tgt_pkt->getPtr()); @@ -1506,215 +1699,3 @@ return nextReady; } - - -/////////////// -// -// CpuSidePort -// -/////////////// - -template -void -Cache::CpuSidePort:: -getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) -{ - // CPU side port doesn't snoop; it's a target only. It can - // potentially respond to any address. - snoop = false; - resp.push_back(myCache()->getAddrRange()); -} - - -template -bool -Cache::CpuSidePort::recvTiming(PacketPtr pkt) -{ - // illegal to block responses... can lead to deadlock - if (pkt->isRequest() && !pkt->memInhibitAsserted() && blocked) { - DPRINTF(Cache,"Scheduling a retry while blocked\n"); - mustSendRetry = true; - return false; - } - - myCache()->timingAccess(pkt); - return true; -} - - -template -Tick -Cache::CpuSidePort::recvAtomic(PacketPtr pkt) -{ - return myCache()->atomicAccess(pkt); -} - - -template -void -Cache::CpuSidePort::recvFunctional(PacketPtr pkt) -{ - myCache()->functionalAccess(pkt, this, otherPort); -} - - -template -Cache:: -CpuSidePort::CpuSidePort(const std::string &_name, Cache *_cache, - const std::string &_label) - : BaseCache::CachePort(_name, _cache, _label) -{ -} - -/////////////// -// -// MemSidePort -// -/////////////// - -template -void -Cache::MemSidePort:: -getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) -{ - // Memory-side port always snoops, but never passes requests - // through to targets on the cpu side (so we don't add anything to - // the address range list). - snoop = true; -} - - -template -bool -Cache::MemSidePort::recvTiming(PacketPtr pkt) -{ - // this needs to be fixed so that the cache updates the mshr and sends the - // packet back out on the link, but it probably won't happen so until this - // gets fixed, just panic when it does - if (pkt->wasNacked()) - panic("Need to implement cache resending nacked packets!\n"); - - if (pkt->isRequest() && blocked) { - DPRINTF(Cache,"Scheduling a retry while blocked\n"); - mustSendRetry = true; - return false; - } - - if (pkt->isResponse()) { - myCache()->handleResponse(pkt); - } else { - myCache()->snoopTiming(pkt); - } - return true; -} - - -template -Tick -Cache::MemSidePort::recvAtomic(PacketPtr pkt) -{ - // in atomic mode, responses go back to the sender via the - // function return from sendAtomic(), not via a separate - // sendAtomic() from the responder. Thus we should never see a - // response packet in recvAtomic() (anywhere, not just here). - assert(!pkt->isResponse()); - return myCache()->snoopAtomic(pkt); -} - - -template -void -Cache::MemSidePort::recvFunctional(PacketPtr pkt) -{ - myCache()->functionalAccess(pkt, this, otherPort); -} - - - -template -void -Cache::MemSidePort::sendPacket() -{ - // if we have responses that are ready, they take precedence - if (deferredPacketReady()) { - bool success = sendTiming(transmitList.front().pkt); - - if (success) { - //send successful, remove packet - transmitList.pop_front(); - } - - waitingOnRetry = !success; - } else { - // check for non-response packets (requests & writebacks) - PacketPtr pkt = myCache()->getTimingPacket(); - if (pkt == NULL) { - // can happen if e.g. we attempt a writeback and fail, but - // before the retry, the writeback is eliminated because - // we snoop another cache's ReadEx. - waitingOnRetry = false; - } else { - MSHR *mshr = dynamic_cast(pkt->senderState); - - bool success = sendTiming(pkt); - - waitingOnRetry = !success; - if (waitingOnRetry) { - DPRINTF(CachePort, "now waiting on a retry\n"); - if (!mshr->isForwardNoResponse()) { - delete pkt; - } - } else { - myCache()->markInService(mshr, pkt); - } - } - } - - - // tried to send packet... if it was successful (no retry), see if - // we need to rerequest bus or not - if (!waitingOnRetry) { - Tick nextReady = std::min(deferredPacketReadyTime(), - myCache()->nextMSHRReadyTime()); - // @TODO: need to facotr in prefetch requests here somehow - if (nextReady != MaxTick) { - DPRINTF(CachePort, "more packets to send @ %d\n", nextReady); - schedule(sendEvent, std::max(nextReady, curTick() + 1)); - } else { - // no more to send right now: if we're draining, we may be done - if (drainEvent && !sendEvent->scheduled()) { - drainEvent->process(); - drainEvent = NULL; - } - } - } -} - -template -void -Cache::MemSidePort::recvRetry() -{ - assert(waitingOnRetry); - sendPacket(); -} - - -template -void -Cache::MemSidePort::processSendEvent() -{ - assert(!waitingOnRetry); - sendPacket(); -} - - -template -Cache:: -MemSidePort::MemSidePort(const std::string &_name, Cache *_cache, - const std::string &_label) - : BaseCache::CachePort(_name, _cache, _label) -{ - // override default send event from SimpleTimingPort - delete sendEvent; - sendEvent = new SendEvent(this); -} diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/mshr.hh --- a/src/mem/cache/mshr.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/mshr.hh Fri Aug 05 18:14:06 2011 +0100 @@ -65,12 +65,12 @@ Tick recvTime; //!< Time when request was received (for stats) Tick readyTime; //!< Time when request is ready to be serviced Counter order; //!< Global order (for memory consistency mgmt) - PacketPtr pkt; //!< Pending request packet. + Packet* pkt; //!< Pending request packet. Source source; //!< Did request come from cpu, memory, or prefetcher? bool markedPending; //!< Did we mark upstream MSHR //!< as downstreamPending? - Target(PacketPtr _pkt, Tick _readyTime, Counter _order, + Target(Packet* _pkt, Tick _readyTime, Counter _order, Source _source, bool _markedPending) : recvTime(curTick()), readyTime(_readyTime), order(_order), pkt(_pkt), source(_source), markedPending(_markedPending) @@ -89,11 +89,11 @@ TargetList(); void resetFlags() { needsExclusive = hasUpgrade = false; } bool isReset() { return !needsExclusive && !hasUpgrade; } - void add(PacketPtr pkt, Tick readyTime, Counter order, + void add(Packet* pkt, Tick readyTime, Counter order, Target::Source source, bool markPending); void replaceUpgrades(); void clearDownstreamPending(); - bool checkFunctional(PacketPtr pkt); + bool checkFunctional(Packet* pkt); void print(std::ostream &os, int verbosity, const std::string &prefix) const; }; @@ -197,10 +197,10 @@ * @param size The number of bytes to request. * @param pkt The original miss. */ - void allocate(Addr addr, int size, PacketPtr pkt, + void allocate(Addr addr, int size, Packet* pkt, Tick when, Counter _order); - bool markInService(PacketPtr pkt); + bool markInService(Packet* pkt); void clearDownstreamPending(); @@ -213,8 +213,8 @@ * Add a request to the list of targets. * @param target The target. */ - void allocateTarget(PacketPtr target, Tick when, Counter order); - bool handleSnoop(PacketPtr target, Counter order); + void allocateTarget(MemMapPacketPtr target, Tick when, Counter order); + bool handleSnoop(CacheMntPacketPtr target, Counter order); /** A simple constructor. */ MSHR(); @@ -270,7 +270,7 @@ void handleFill(Packet *pkt, CacheBlk *blk); - bool checkFunctional(PacketPtr pkt); + bool checkFunctional(Packet* pkt); /** * Prints the contents of this MSHR for debugging. diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/mshr.cc --- a/src/mem/cache/mshr.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/mshr.cc Fri Aug 05 18:14:06 2011 +0100 @@ -64,7 +64,7 @@ inline void -MSHR::TargetList::add(PacketPtr pkt, Tick readyTime, +MSHR::TargetList::add(Packet* pkt, Tick readyTime, Counter order, Target::Source source, bool markPending) { if (source != Target::FromSnoop) { @@ -93,7 +93,7 @@ static void -replaceUpgrade(PacketPtr pkt) +replaceUpgrade(Packet* pkt) { if (pkt->cmd == MemCmd::UpgradeReq) { pkt->cmd = MemCmd::ReadExReq; @@ -139,7 +139,7 @@ bool -MSHR::TargetList::checkFunctional(PacketPtr pkt) +MSHR::TargetList::checkFunctional(Packet* pkt) { Iterator end_i = end(); for (Iterator i = begin(); i != end_i; ++i) { @@ -172,7 +172,7 @@ void -MSHR::allocate(Addr _addr, int _size, PacketPtr target, +MSHR::allocate(Addr _addr, int _size, Packet* target, Tick whenReady, Counter _order) { addr = _addr; @@ -208,7 +208,7 @@ } bool -MSHR::markInService(PacketPtr pkt) +MSHR::markInService(Packet* pkt) { assert(!inService); if (isForwardNoResponse()) { @@ -246,7 +246,7 @@ * Adds a target to an MSHR */ void -MSHR::allocateTarget(PacketPtr pkt, Tick whenReady, Counter _order) +MSHR::allocateTarget(MemMapPacketPtr pkt, Tick whenReady, Counter _order) { // if there's a request already in service for this MSHR, we will // have to defer the new target until after the response if any of @@ -283,7 +283,7 @@ } bool -MSHR::handleSnoop(PacketPtr pkt, Counter _order) +MSHR::handleSnoop(CacheMntPacketPtr pkt, Counter _order) { if (!inService || (pkt->isExpressSnoop() && downstreamPending)) { // Request has not been issued yet, or it's been issued @@ -332,7 +332,7 @@ // Actual target device (typ. PhysicalMemory) will delete the // packet on reception, so we need to save a copy here. - PacketPtr cp_pkt = new Packet(pkt, true); + CacheMntPacketPtr cp_pkt = new CacheMntPacket(pkt, true); targets->add(cp_pkt, curTick(), _order, Target::FromSnoop, downstreamPending && targets->needsExclusive); ++ntargets; @@ -412,7 +412,7 @@ bool -MSHR::checkFunctional(PacketPtr pkt) +MSHR::checkFunctional(Packet* pkt) { // For printing, we treat the MSHR as a whole as single entity. // For other requests, we iterate over the individual targets diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/mshr_queue.hh --- a/src/mem/cache/mshr_queue.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/mshr_queue.hh Fri Aug 05 18:14:06 2011 +0100 @@ -119,7 +119,7 @@ */ MSHR *findPending(Addr addr, int size) const; - bool checkFunctional(PacketPtr pkt, Addr blk_addr); + bool checkFunctional(Packet* pkt, Addr blk_addr); /** * Allocates a new MSHR for the request and size. This places the request @@ -130,7 +130,7 @@ * * @pre There are free entries. */ - MSHR *allocate(Addr addr, int size, PacketPtr &pkt, + MSHR *allocate(Addr addr, int size, Packet* &pkt, Tick when, Counter order); /** @@ -160,7 +160,7 @@ * readyList. Deallocates the MSHR if it does not expect a response. * @param mshr The MSHR to mark in service. */ - void markInService(MSHR *mshr, PacketPtr pkt); + void markInService(MSHR *mshr, Packet* pkt); /** * Mark an in service entry as pending, used to resend a request. diff -r 6548721032fa -r 042f38a2e360 src/mem/cache/mshr_queue.cc --- a/src/mem/cache/mshr_queue.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/cache/mshr_queue.cc Fri Aug 05 18:14:06 2011 +0100 @@ -90,7 +90,7 @@ bool -MSHRQueue::checkFunctional(PacketPtr pkt, Addr blk_addr) +MSHRQueue::checkFunctional(Packet* pkt, Addr blk_addr) { pkt->pushLabel(label); MSHR::ConstIterator i = allocatedList.begin(); @@ -148,7 +148,7 @@ MSHR * -MSHRQueue::allocate(Addr addr, int size, PacketPtr &pkt, +MSHRQueue::allocate(Addr addr, int size, Packet* &pkt, Tick when, Counter order) { assert(!freeList.empty()); @@ -197,7 +197,7 @@ } void -MSHRQueue::markInService(MSHR *mshr, PacketPtr pkt) +MSHRQueue::markInService(MSHR *mshr, Packet* pkt) { if (mshr->markInService(pkt)) { deallocate(mshr); diff -r 6548721032fa -r 042f38a2e360 src/mem/coherent_bus.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/coherent_bus.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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: Ron Dreslinski + * Ali Saidi + * Andreas Hansson + */ + +/** + * @file + * Declaration of a coherent bus object. + */ + +#ifndef __MEM_COHERENT_BUS_HH__ +#define __MEM_COHERENT_BUS_HH__ + +#include "mem/noncoherent_bus.hh" +#include "params/CoherentBus.hh" + +class CoherentBus : public NonCoherentBus, + public CacheMntSlaveInterface, + public CacheMntMasterInterface +{ + + public: + + /** + * Get a bus port. + */ + virtual CacheMntMasterPort* getCacheMntMasterPort(const std::string& name, + int idx = -1); + + virtual CacheMntSlavePort* getCacheMntSlavePort(const std::string& name, + int idx = -1); + + + /** + * Construct a coherent bus based on a set of parameters. + */ + CoherentBus(const CoherentBusParams *p); + + /** + * Destruct a coherent bus. + */ + ~CoherentBus() {}; + + private: + + /** Function called by the port when the bus is recieving a Timing + transaction.*/ + bool recvTimingResp(CacheMntPacketPtr pkt, int master_id); + + /** Function called by the port when the bus is recieving a Timing + transaction.*/ + bool recvTimingReq(CacheMntPacketPtr pkt, int slave_id); + + /** Function called by the port when the bus is recieving a Atomic + transaction.*/ + Tick recvAtomic(CacheMntPacketPtr pkt, int slave_id); + + /** Function called by the port when the bus is recieving a Functional + transaction.*/ + void recvDebug(CacheMntPacketPtr pkt, int slave_id); + + virtual void forwardTimingReq(MemMapPacketPtr req_pkt, + MemObject* source_object) const; + + void forwardTimingReq(CacheMntPacketPtr req_pkt, + MemObject* source_object) const; + + /** Function called by the port when the bus is recieving a Atomic + transaction.*/ + Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + + /** Function called by the port when the bus is recieving a Functional + transaction.*/ + void recvDebug(MemMapPacketPtr pkt, int slave_id); + + /** + * Function called by the slave ports when they are told to retry + * a response. + */ + void recvRetryResp(int slave_id) { + panic("Unexpected recvRetryResp"); + } + + /** + * Function called by the master ports when they are told to retry + * a request. + */ + void recvRetryReq(int master_id) { + panic("Unexpected recvRetryReq"); + } + +}; + +#endif //__MEM_COHERENT_BUS_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/coherent_bus.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/coherent_bus.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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: Andreas Hansson + */ + +/** + * @file + * Definition of a coherent bus. + */ + +#include "base/misc.hh" +#include "base/trace.hh" +#include "mem/coherent_bus.hh" + +CoherentBus::CoherentBus(const CoherentBusParams *p) + : NonCoherentBus(p) +{ +} + +CacheMntMasterPort* +CoherentBus::getCacheMntMasterPort(const std::string& name, int idx) +{ + // give the new port the next available index + int id = cacheMasterPorts.size(); + + std::string _name = "cache_master"; + + // create the actual port and add it in the list + CacheMntMasterPort* masterPort = + new CacheMntMasterPort(*this, this, _name, id); + regPort(masterPort); + + return masterPort; +} + +CacheMntSlavePort* +CoherentBus::getCacheMntSlavePort(const std::string& name, int idx) +{ + // give the new port the next available index + int id = cacheSlavePorts.size(); + + // we are looking at a slave port + std::string _name = "cache_slave"; + + // create the port and keep track of it on the right spot + // corresponding to its id + CacheMntSlavePort* slavePort = + new CacheMntSlavePort(*this, this, _name, id); + regPort(slavePort); + + return slavePort; +} + +void +CoherentBus::recvDebug(CacheMntPacketPtr pkt, int slave_id) +{ + // don't do DPRINTFs on PrintReq as it clutters up the output + if (!pkt->isPrint()) { + DPRINTF(Bus, + "recvDebug: cache packet src %d addr 0x%x cmd %s\n", + slave_id, pkt->getAddr(), pkt->cmdString()); + } + + // get the corresponding slave port + CacheMntSlavePort* slave_port = cacheSlavePorts[slave_id]; + + // the debug snoop request should not change the state of any + // cache on a read, but update all copies on a write, thus iterate + // over the snoop masters until we get a hit (a response) for a + // read, or iterate over them all in the case of a write (since we + // do not know what copies need updating) + bool got_snoop_resp = false; + CacheMntMasterIter sm = cacheMasterPorts.begin(); + while (!got_snoop_resp && sm != cacheMasterPorts.end()) { + // get the current one and increment + CacheMntMasterPort* p = *sm++; + + // do not send the snoop request back to where we came from + if (slave_port->getMasterPort()->getOwner() != + p->getSlavePort()->getOwner()) { + // we get away with sending the same packet (pointer) to + // everyone for debug accesses + (*p)->sendDebug(pkt); + + // we could have the response at this point + got_snoop_resp = pkt->isResponse(); + } + } +} + +Tick +CoherentBus::recvAtomic(CacheMntPacketPtr pkt, int slave_id) +{ + DPRINTF(Bus, "recvAtomic: cache packet src %d addr 0x%x cmd %s\n", + slave_id, pkt->getAddr(), pkt->cmdString()); + + // get the corresponding slave port + CacheMntSlavePort* slave_port = cacheSlavePorts[slave_id]; + + // keep track of a potential snoop response and its latency + Tick snoop_latency = 0; + + MemCmd req_cmd = pkt->cmd; + MemCmd snoop_cmd = MemCmd::InvalidCmd; + + // every single cache must be given the opportunity to snoop, so iterate + // over all the cache masters, even if we get a response + for (CacheMntMasterIter sm = cacheMasterPorts.begin(); + sm != cacheMasterPorts.end(); ++sm) { + CacheMntMasterPort* p = *sm; + + // dont send the request back to the module it came from + if (slave_port->getMasterPort()->getOwner() != + p->getSlavePort()->getOwner()) { + + // send the packet to the snoop master + Tick latency = (*p)->sendAtomic(pkt); + + // if we got a response save it and restore the packet so we + // can send it to the next one + if (pkt->isResponse()) { + snoop_latency = latency; + snoop_cmd = pkt->cmd; + pkt->cmd = req_cmd; + } + } + } + + // if anyone responsed on the snoop restore the packet here + if (snoop_cmd != MemCmd::InvalidCmd) + pkt->cmd = snoop_cmd; + + return snoop_latency; +} + +bool +CoherentBus::recvTimingReq(CacheMntPacketPtr req_pkt, int slave_id) +{ + // get the corresponding slave port + CacheMntSlavePort* slave_port = cacheSlavePorts[slave_id]; + + // set the source port + req_pkt->getPath().push(slave_id); + + CacheMntPacket snoop_pkt(req_pkt); + snoop_pkt.copyData(req_pkt); + forwardTimingReq(&snoop_pkt, slave_port->getMasterPort()->getOwner()); + + // why do we get away without placing this packet on the heap? it + // disappears at the end of this function and could be hanging + // around in queues and modules all over the system + + // no response here, but the caches implicitly communicate through + // status changes in the request + if (snoop_pkt.sharedAsserted()) + req_pkt->assertShared(); + + if (snoop_pkt.memInhibitAsserted()) + req_pkt->assertMemInhibit(); + + return true; +} + +bool +CoherentBus::recvTimingResp(CacheMntPacketPtr respPkt, int master_id) +{ + assert(!respPkt->getPath().empty()); + + // if we created the packet as a snoop (i.e. a memory-mapped + // request that we broadcasted as a snoop) then we have to deal + // with it and it should be sent back to a memory-mapped port + if (outstandingReq[respPkt->req]) { + //outstandingReq[respPkt->req] = false; + + // now to be aggregated based on the cache response (what we + // are now looking at) and the response from a slave device + // which is inhibited + MemMapPacket* memPkt = new MemMapPacket(respPkt); + memPkt->copyData(respPkt); + + // note that it becomes our problem to deal with the response + // flow control to the original memory-mapped port, treat it + // as a beginResp from an internal port + NonCoherentBus::beginResp(memPkt, -1); + + return true; + } else { + // find out where to go + int slave_id = respPkt->getPath().top(); + respPkt->getPath().pop(); + + assert(slave_id < cacheSlavePorts.size()); + CacheMntSlavePort* slave_port = cacheSlavePorts[slave_id]; + (*slave_port)->sendTimingResp(respPkt); + + return true; + } +} + +void +CoherentBus::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + // don't do DPRINTFs on PrintReq as it clutters up the output + if (!pkt->isPrint()) { + DPRINTF(Bus, + "recvDebug: packet src %d addr 0x%x cmd %s\n", + slave_id, pkt->getAddr(), pkt->cmdString()); + } + + // get the corresponding slave port + MemMapSlavePort* slave_port = memSlavePorts[slave_id]; + + // the debug access should not change the state of any cache on a + // read, and update all copies on a write, thus iterate over the + // snoop masters until we get a hit (a response) for a read, or + // iterate over them all in the case of a write (since we do not + // know what copies need updating) + bool got_snoop_resp = false; + + // we get away with sending the same packet to everyone + // for debug accesses + CacheMntPacket snoop_pkt(pkt); + snoop_pkt.copyData(pkt); + + CacheMntMasterIter sm = cacheMasterPorts.begin(); + + while (!got_snoop_resp && sm != cacheMasterPorts.end()) { + CacheMntMasterPort* p = *sm; + + // do not send the snoop request back to where we came from + if (slave_port->getMasterPort()->getOwner() != + p->getSlavePort()->getOwner()) { + (*p)->sendDebug(&snoop_pkt); + + // we could have the response in case of a read hit + got_snoop_resp = snoop_pkt.isResponse(); + } + + // next + ++sm; + } + + // in contrast to the atomic/timing transport the debug transport + // can stop as soon as the data is found and all caches are + // updated, thus, if the snooping hasn't found what we were + // looking for, keep going, otherwise we're done + if (got_snoop_resp) { + // make the original packet reflect that we got a response... + pkt->makeResponse(); + } else { + // proceed with a normal non-coherent access + NonCoherentBus::recvDebug(pkt, slave_id); + } +} + +Tick +CoherentBus::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + DPRINTF(Bus, "recvAtomic: packet src %d addr 0x%x cmd %s\n", + slave_id, pkt->getAddr(), pkt->cmdString()); + + // get the corresponding slave port + MemMapSlavePort* slave_port = memSlavePorts[slave_id]; + + // keep track of a potential snoop response and latency + MemCmd snoop_response = MemCmd::InvalidCmd; + Tick snoop_latency = 0; + bool snoop_hit = false; + + // every single cache must be given the opportunity to snoop (and + // potentially update its state), so iterate over all the cache + // masters + for (CacheMntMasterIter sm = cacheMasterPorts.begin(); + sm != cacheMasterPorts.end(); ++sm) { + CacheMntMasterPort* p = *sm; + + // dont send the request back to the module it came from + if (slave_port->getMasterPort()->getOwner() != + p->getSlavePort()->getOwner()) { + // take a copy of the request packet, including all flags + CacheMntPacket snoop_pkt(pkt); + // also copy the pointer to any data + snoop_pkt.copyData(pkt); + + // send the packet to the snoop master + Tick latency = (*p)->sendAtomic(&snoop_pkt); + + // the snoop packet has technically already come back to + // us and could have a changed state + if (snoop_pkt.sharedAsserted()) + pkt->assertShared(); + + // got a hit in a cache and now have the response, must + // still give all the caches the opportunity to snoop + if (snoop_pkt.isResponse()) { + // should only get a response from one cache, this is + // due to the inhibit asserted trick + assert(snoop_hit == false); + snoop_hit = true; + + assert(snoop_pkt.memInhibitAsserted()); + pkt->assertMemInhibit(); + + // save response state + snoop_response = snoop_pkt.cmd; + snoop_latency = latency; + } + } + } + + // even in the case of a snoop hit we have to send the request to + // the actual slave. the read is satisfied, but we have to + // notify lower levels that it is now shared? + Tick latency = NonCoherentBus::recvAtomic(pkt, slave_id); + + // if we got a response from a snoop master, restore it here and + // report the time it took to get the snoop hit + if (snoop_hit) { + // no one else should have responded as it should be inhibited + assert(!pkt->isResponse()); + pkt->cmd = snoop_response; + latency = snoop_latency; + } + + return latency; +} + +void +CoherentBus::forwardTimingReq(MemMapPacketPtr req_pkt, + MemObject* source_object) const +{ + // take a copy of the packet, why do we get away without placing + // this packet on the heap? it disappears at the end of this + // function and could be hanging around in queues and modules all + // over the system + CacheMntPacket snoop_pkt(req_pkt); + snoop_pkt.copyData(req_pkt); + + // note that this packet is not an express snoop as it originates + // from a bus and not a cache + forwardTimingReq(&snoop_pkt, source_object); + + // no response here, but the caches implicitly communicate through + // status changes in the request + if (snoop_pkt.sharedAsserted()) + req_pkt->assertShared(); + + if (snoop_pkt.memInhibitAsserted()) + req_pkt->assertMemInhibit(); +} + +void +CoherentBus::forwardTimingReq(CacheMntPacketPtr snoop_pkt, + MemObject* source_object) const +{ + // this is our "snoop controller" so to speak + for (CacheMntMasterConstIter sm = cacheMasterPorts.begin(); + sm != cacheMasterPorts.end(); ++sm) { + CacheMntMasterPort* p = *sm; + + if (p->getSlavePort()->getOwner() != source_object) { + // how come we get away with forwarding the same packet to + // many destinations? if there are responses the path will + // be a complete mess (as they all modify the same one) + + // cache is not allowed to refuse snoop + bool success M5_VAR_USED = (*p)->sendTimingReq(snoop_pkt); + assert(success); + } + } +} + +CoherentBus* +CoherentBusParams::create() +{ + return new CoherentBus(this); +} diff -r 6548721032fa -r 042f38a2e360 src/mem/fs_translating_proxy.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/fs_translating_proxy.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,161 @@ +/* + * The confidential and proprietary information contained in this file may + * only be used by a person authorised under and to the extent permitted + * by a subsisting licensing agreement from ARM Limited. + * + * (C) COPYRIGHT 2011 ARM Limited. + * ALL RIGHTS RESERVED + * + * This entire notice must be reproduced on all copies of this file + * and copies of this file may only be made by a person if such person is + * permitted to do so under the terms of a subsisting license agreement + * from ARM Limited. + * + * Authors: Geza Lore + */ + +/** + * @file FSTranslatingProxy Object Declaration. + * Port proxies are used when non + * structural entities need access to the memory system. Proxy objects + * replace the previous FunctionalPort, TranslatingPort and VirtualPort + * objects, which provided the same functionality as the proxies, but + * were instances of ports not corresponding to real structural ports of + * the simulated system. + * @sa PortProxy + * @sa SETranslatingProxy + */ + +#ifndef __MEM_FS_TRANSLATING_PROXY_HH__ +#define __MEM_FS_TRANSLATING_PROXY_HH__ + +#include + +#include "mem/port_proxy.hh" +#include "arch/vtophys.hh" + +class ThreadContext; + +/** + * This object is a proxy for a structural port, + * to be used for debug accesses in FS mode. + * + * This proxy object is used when non structural entities + * (e.g. thread contexts, object file loaders) need access to the + * memory system. It calls the corresponding functions on the underlying + * structural port, and provides templatized convenience access functions. + * + * The addresses are interpreted as virtual addresses, address translation + * is performed using the standard translation mechanism of the actual ISA. + * + * @sa PortProxy + * @sa SETranslatingProxy + */ +class FSTranslatingProxy : public PortProxy +{ + private: + ThreadContext *_tc; + + public: + FSTranslatingProxy(ThreadContext *tc); + FSTranslatingProxy(MemMapMasterPort &port); + FSTranslatingProxy(MemMapMasterPort *port); + virtual ~FSTranslatingProxy(); + + public: + /** + * Read size bytes memory at address and store in p. + * @sa MasterPort::readMem + */ + virtual void readMem( Addr address, uint8_t* p, int size); + + /** + * Write size bytes from p to address. + * @sa MasterPort::writeMem + */ + virtual void writeMem(Addr address, uint8_t* p, int size); + + /** + * Fill size bytes starting at addr with byte value val. + * @sa MasterPort::setMem + */ + virtual void setMem( Addr address, uint8_t v, int size); + + /** + * Read sizeof(T) bytes from address and return as object T. + */ + template + T read(Addr address); + + /** + * Write object T to address. Writes sizeof(T) bytes. + */ + template + void write(Addr address, T data); + + /** + * Read sizeof(T) bytes from address and return as object T. + * Performs Guest to Host endianness transform. + */ + template + T readGtoH(Addr address); + + /** + * Write object T to address. Writes sizeof(T) bytes. + * Performs Host to Guest endianness transform. + */ + template + void writeHtoG(Addr address, T data); +}; + +template +T +FSTranslatingProxy::read(Addr address) +{ + if (_tc == NULL) { + return PortProxy::read(TheISA::vtophys(address)); + } else { + return PortProxy::read(TheISA::vtophys(_tc,address)); + } +} + +template +void +FSTranslatingProxy::write(Addr address, T data) +{ + if (_tc == NULL) { + PortProxy::write(TheISA::vtophys(address), data); + } else { + PortProxy::write(TheISA::vtophys(_tc,address), data); + } +} + +template +T +FSTranslatingProxy::readGtoH(Addr address) +{ + if (_tc == NULL) { + return PortProxy::readGtoH(TheISA::vtophys(address)); + } else { + return PortProxy::readGtoH(TheISA::vtophys(_tc,address)); + } +} + +template +void +FSTranslatingProxy::writeHtoG(Addr address, T data) +{ + if (_tc == NULL) { + PortProxy::writeHtoG(TheISA::vtophys(address), data); + } else { + PortProxy::writeHtoG(TheISA::vtophys(_tc,address), data); + } +} + +void CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen); +void CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen); +void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); +void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); + +#endif // __MEM_FS_TRANSLATING_PROXY_HH__ + diff -r 6548721032fa -r 042f38a2e360 src/mem/fs_translating_proxy.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/fs_translating_proxy.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,136 @@ +/* + * The confidential and proprietary information contained in this file may + * only be used by a person authorised under and to the extent permitted + * by a subsisting licensing agreement from ARM Limited. + * + * (C) COPYRIGHT 2011 ARM Limited. + * ALL RIGHTS RESERVED + * + * This entire notice must be reproduced on all copies of this file + * and copies of this file may only be made by a person if such person is + * permitted to do so under the terms of a subsisting license agreement + * from ARM Limited. + * + * Authors: Geza Lore + */ + +#include "mem/fs_translating_proxy.hh" + +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "sim/system.hh" + +#include "base/chunk_generator.hh" + +using namespace TheISA; + +FSTranslatingProxy::FSTranslatingProxy(ThreadContext *tc) +: PortProxy(*(tc->getCpuPtr()->getDataPort())), _tc(tc) +{ +} + +FSTranslatingProxy::FSTranslatingProxy(MemMapMasterPort &port) +: PortProxy(port), _tc(NULL) +{} + +FSTranslatingProxy::FSTranslatingProxy(MemMapMasterPort *port) +: PortProxy(port), _tc(NULL) +{} + +FSTranslatingProxy::~FSTranslatingProxy() +{} + +void +FSTranslatingProxy::readMem( Addr address, uint8_t* p, int size) +{ + Addr paddr; + for (ChunkGenerator gen(address, size, TheISA::PageBytes); + !gen.done(); gen.next()) + { + if (_tc) + paddr = TheISA::vtophys(_tc,gen.addr()); + else + paddr = TheISA::vtophys(gen.addr()); + + PortProxy::readMem(paddr, p, gen.size()); + p += gen.size(); + } +} + +void +FSTranslatingProxy::writeMem(Addr address, uint8_t* p, int size) +{ + Addr paddr; + for (ChunkGenerator gen(address, size, TheISA::PageBytes); + !gen.done(); gen.next()) + { + if (_tc) + paddr = TheISA::vtophys(_tc,gen.addr()); + else + paddr = TheISA::vtophys(gen.addr()); + + PortProxy::writeMem(paddr, p, gen.size()); + p += gen.size(); + } +} + +void +FSTranslatingProxy::setMem( Addr address, uint8_t v, int size) +{ + Addr paddr; + for (ChunkGenerator gen(address, size, TheISA::PageBytes); + !gen.done(); gen.next()) + { + if (_tc) + paddr = TheISA::vtophys(_tc,gen.addr()); + else + paddr = TheISA::vtophys(gen.addr()); + + PortProxy::setMem(paddr, v, gen.size()); + } +} + +void +CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) +{ + uint8_t *dst = (uint8_t *)dest; + FSTranslatingProxy *vp = tc->getVirtProxy(); + + vp->readMem(src, dst, cplen); +} + +void +CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) +{ + uint8_t *src = (uint8_t *)source; + FSTranslatingProxy *vp = tc->getVirtProxy(); + + vp->writeMem(dest, src, cplen); +} + +void +CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) +{ + int len = 0; + char *start = dst; + FSTranslatingProxy *vp = tc->getVirtProxy(); + + do { + vp->readMem(vaddr++, (uint8_t*)dst++, 1); + } while (len < maxlen && start[len++] != 0 ); + + dst[len] = 0; +} + +void +CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) +{ + FSTranslatingProxy *vp = tc->getVirtProxy(); + for (ChunkGenerator gen(vaddr, strlen(src), TheISA::PageBytes); !gen.done(); + gen.next()) + { + vp->writeMem(gen.addr(), (uint8_t*)src, gen.size()); + src += gen.size(); + } +} + diff -r 6548721032fa -r 042f38a2e360 src/mem/mem_object.hh --- a/src/mem/mem_object.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/mem_object.hh Fri Aug 05 18:14:06 2011 +0100 @@ -41,25 +41,168 @@ #include "sim/sim_object.hh" /** - * The base MemoryObject class, allows for an accesor function to a - * simobj that returns the Port. + * The base MemoryObject class with a fall back implementation for the + * master and slave accessor member functions. */ class MemObject : public SimObject { - public: - typedef MemObjectParams Params; - const Params *params() const - { return dynamic_cast(_params); } - MemObject(const Params *params); + private: + + /** + * Convenience function for the printing of this memobject. Takes + * the RGB values and turns them into an HTML-compatible colour + * description. + * + * @param r red in 0-255 + * @param g green in 0-255 + * @param b blue in 0-255 + * + * @return a string on the form #RRGGBB + */ + std::string rgbToHtml(int r, int g, int b) const; + + /** + * Get a port based on name and index by searching through a collection + * of ports identified by their begin and end iterators. + * + * T has to be a subclass of Port (and thus have shortName and + * getId, but there is no convenient way of enforcing this. + * + * @param name port short name + * @param idx an integer port index or -1 if not used + * @param begin the start of the collection + * @param end the end of the collection + */ + template + T* getPort(const std::string& name, int idx, + const typename std::vector::const_iterator& begin, + const typename std::vector::const_iterator& end) const { + T* port = NULL; + typename std::vector::const_iterator p = begin; + while (port == NULL && p != end) { + // if the name matches and the idx is either to be ignored (-1) + // or matching + if ((*p)->shortName() == name && + (idx == -1 || idx == (*p)->getId())) { + port = *p; + } + ++p; + } + return port; + } + + protected: + + typedef std::vector::iterator MemMapMasterIter; + typedef std::vector::iterator CacheMntMasterIter; + + typedef std::vector::const_iterator \ + MemMapMasterConstIter; + typedef std::vector::const_iterator \ + CacheMntMasterConstIter; + + typedef std::vector::iterator MemMapSlaveIter; + + const MemObjectParams *params() const + { return dynamic_cast(_params); } + + MemObject(const MemObjectParams *params); + + void regPort(MemMapSlavePort* slavePort); + + void regPort(MemMapMasterPort* masterPort); + + void regPort(CacheMntSlavePort* slavePort); + + void regPort(CacheMntMasterPort* masterPort); + + /** + * The memory-mapped master and slave ports. + */ + std::vector memMasterPorts; + std::vector memSlavePorts; + + /** + * The cache maintenance ports of this MemObject, sorted into + * master and slave ports. + */ + std::vector cacheMasterPorts; + std::vector cacheSlavePorts; public: - /** Additional function to return the Port of a memory object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1) = 0; - /** Tell object that this port is about to disappear, so it should remove it - * from any structures that it's keeping it in. */ - virtual void deletePortRefs(Port *p) ; + /** + * Get a memory-mapped master port of a mem object. + * + * @param name The name of the master port + * @param idx The index if the port is a vector port + */ + virtual MemMapMasterPort* getMemMapMasterPort(const std::string& name, + int idx = -1); + + virtual CacheMntMasterPort* getCacheMntMasterPort(const std::string& name, + int idx = -1); + + /** + * Get a slave port of a mem object. + * + * @param name The name of the master port + * @param idx The index if the port is a vector port + */ + virtual MemMapSlavePort* getMemMapSlavePort(const std::string& name, + int idx = -1); + + virtual CacheMntSlavePort* getCacheMntSlavePort(const std::string& name, + int idx = -1); + + /** + * Get all the memory-mapped master ports of this MemObject. + */ + std::vector getMemMapMasterPorts() const; + + /** + * Get all the memory-mapped slave ports of this MemObject. + */ + std::vector getMemMapSlavePorts() const; + + /** + * Get all the cache-maintenance master ports of this MemObject. + */ + std::vector getCacheMntMasterPorts() const; + + /** + * Get all the cache-maintenance slave ports of this MemObject. + */ + std::vector getCacheMntSlavePorts() const; + + /** + * Override the default behaviour and print the node this + * MemObject corresponds to. + * + * @param os the output stream to print to + */ + void printDotNodes(std::ostream& os) const; + + /** + * Override the default behaviour and print the edges this + * MemObject has going outwards. + * + * @param os the output stream to print to + */ + void printDotEdges(std::ostream& os) const; + + /** + * Make it possible for the different modules to set a colour when + * printed. + * + * @param r red in 0-255 + * @param g green in 0-255 + * @param b blue in 0-255 + */ + virtual void getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const; + }; #endif //__MEM_MEM_OBJECT_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/mem_object.cc --- a/src/mem/mem_object.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/mem_object.cc Fri Aug 05 18:14:06 2011 +0100 @@ -26,17 +26,323 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Steve Reinhardt + * Andreas Hansson */ +#include + #include "mem/mem_object.hh" -MemObject::MemObject(const Params *params) +MemObject::MemObject(const MemObjectParams *params) : SimObject(params) { } +MemMapMasterPort* +MemObject::getMemMapMasterPort(const std::string& name, int idx) +{ + MemMapMasterPort* port = getPort(name, idx, + memMasterPorts.begin(), + memMasterPorts.end()); + + if (port == NULL) + panic("Memory-mapped master port '%s' does not exist on module %s", + name, this->name()); + return port; +} + +CacheMntMasterPort* +MemObject::getCacheMntMasterPort(const std::string& name, int idx) +{ + CacheMntMasterPort* port = + getPort(name, idx, + cacheMasterPorts.begin(), + cacheMasterPorts.end()); + + if (port == NULL) + panic("Cache-maintenance master port '%s' does not exist on module %s", + name, this->name()); + return port; +} + +MemMapSlavePort* +MemObject::getMemMapSlavePort(const std::string& name, int idx) +{ + MemMapSlavePort* port = getPort(name, idx, + memSlavePorts.begin(), + memSlavePorts.end()); + + if (port == NULL) + panic("Memory-mapped slave port '%s' does not exist on module %s", name, + this->name()); + return port; +} + +CacheMntSlavePort* +MemObject::getCacheMntSlavePort(const std::string& name, int idx) +{ + CacheMntSlavePort* port = + getPort(name, idx, + cacheSlavePorts.begin(), + cacheSlavePorts.end()); + + if (port == NULL) + panic("Cache-maintenance slave port '%s' does not exist on module %s", + name, this->name()); + return port; +} + void -MemObject::deletePortRefs(Port *p) +MemObject::regPort(MemMapSlavePort* slavePort) { - panic("This object does not support port deletion\n"); + memSlavePorts.push_back(slavePort); } + +void +MemObject::regPort(CacheMntSlavePort* slavePort) +{ + cacheSlavePorts.push_back(slavePort); +} + +void +MemObject::regPort(MemMapMasterPort* masterPort) +{ + memMasterPorts.push_back(masterPort); +} + +void +MemObject::regPort(CacheMntMasterPort* masterPort) +{ + cacheMasterPorts.push_back(masterPort); +} + +std::vector +MemObject::getMemMapMasterPorts() const +{ + return memMasterPorts; +} + +std::vector +MemObject::getCacheMntMasterPorts() const +{ + return cacheMasterPorts; +} + +std::vector +MemObject::getMemMapSlavePorts() const +{ + return memSlavePorts; +} + +std::vector +MemObject::getCacheMntSlavePorts() const +{ + return cacheSlavePorts; +} + +void +MemObject::printDotNodes(std::ostream& os) const +{ + // a set of colours for the ports, dark enough to have white text + // in the boxes + std::string memPortColour = "#174F7E"; + std::string cachePortColour = "#D37E3E"; + + // get the colour based on the module + unsigned char r, g, b; + getDotColour(r, g, b); + + // get the HTML equivalent + std::string moduleColour = rgbToHtml(r, g, b); + + // let the font colour be the same but darker (chosen arbitrarily) + std::string moduleFontColour = rgbToHtml(0.4 * r, 0.4 * g, 0.4 * b); + + // create a cluster for this mem object, set the right attributes to + // make it look pretty + os << + "subgraph cluster_" << shortName() << " {\n" << + "style=\"rounded,filled\";\n" << + "color=\"" << moduleFontColour << "\";\n" << + "fillcolor=\"" << moduleColour << "\";\n" << + "fontname=\"Arial\";\n" << + "fontsize=14;\n" << + "fontcolor=\"" << moduleFontColour << "\";\n" << + "node [style=\"rounded,filled\"," << + "fontname=\"Arial\",fontsize=12];\n" << + "label=\"" << shortName() << "\";\n"; + + // create a node for each master and slave port, let the protocol + // determine the colour, the href is used to force e.g. Firefox to + // actually render the tooltip, which is currently only used by + // the memory-mapped slaves + for (std::vector::const_iterator m = + memMasterPorts.begin(); m != memMasterPorts.end(); ++m) { + os << "node [shape=box," << + "label=\"" << (*m)->shortName() << "\"," << + "fontcolor=\"white\",color=\"" << + memPortColour << "\",href=\"#\",tooltip=\"\"] " << + shortName() << "_" << (*m)->shortName() << ";\n"; + } + + for (std::vector::const_iterator m = + cacheMasterPorts.begin(); m != cacheMasterPorts.end(); ++m) { + os << "node [shape=box," << + "label=\"" << (*m)->shortName() << "\"," << + "fontcolor=\"white\",color=\"" << + cachePortColour << "\",href=\"#\",tooltip=\"\"] " << + shortName() << "_" << (*m)->shortName() << ";\n"; + } + + for (std::vector::const_iterator s = + memSlavePorts.begin(); s != memSlavePorts.end(); ++s) { + os << "node [shape=box," << + "label=\"" << (*s)->shortName() << "\",href=\"#\",tooltip=\""; + + + // print the address ranges if not connected to the default + // port (in which case it does not need and does not have an + // address range), this is put in the SVG title attribute, and + // unfortunately it is not implemented by all browsers at the + // moment: http://www.w3.org/Graphics/SVG/IG/wiki/SVG_Bugs + if ((*s)->getMasterPort()->shortName() != "default_mem_master") { + // get the address ranges through the slave interface + // (normally only accessed through a master port) + AddrRangeList addrRanges = + (*s)->getInterface()->getAddrRanges((*s)->getId()); + + // save the flags so we can restore them, the go for hex + std::_Ios_Fmtflags flags = os.flags(); + os << std::hex; + + AddrRangeList::const_iterator r = addrRanges.begin(); + while (r != addrRanges.end()) { + // if this is not the first one, add a line break + // according to the HTML title standard, i.e. a line + // feed (U+000A LINE FEED) + if (r != addrRanges.begin()) + os << " "; + os << "[0x" << r->start << " - 0x" << r->end << "]"; + ++r; + } + os.flags(flags); + } + + os << "\",fontcolor=\"white\",color=\"" << + memPortColour << "\"] " << + shortName() << "_" << (*s)->shortName() << ";\n"; + } + + for (std::vector::const_iterator s = + cacheSlavePorts.begin(); s != cacheSlavePorts.end(); ++s) { + os << "node [shape=box,label=\"" << (*s)->shortName() << + "\",fontcolor=\"white\",color=\"" << + cachePortColour << "\",href=\"#\",tooltip=\"\"] " << + shortName() << "_" << (*s)->shortName() << ";\n"; + } + + os << "}\n"; + + // traverse our children, but try and be clever about the sorting + // and put master modules first, then master slave, and slaves + // only at the end, this serves to help with the layout + std::vector memMasterOnlyChildren; + std::vector memMasterSlaveChildren; + std::vector memSlaveOnlyChildren; + std::vector otherChildren; + std::vector nonMemObjectChildren; + for (std::vector::const_iterator c = _children.begin(); + c != _children.end(); ++c) { + MemObject* memObject = dynamic_cast(*c); + if (memObject != NULL) { + if (memObject->getCacheMntSlavePorts().empty() && + memObject->getCacheMntMasterPorts().empty()) { + // no cache ports + if (memObject->getMemMapMasterPorts().empty()) + // e.g. memories and I/O devices + memSlaveOnlyChildren.push_back(*c); + else if (memObject->getMemMapSlavePorts().empty()) + // e.g. CPUs, GPUs + memMasterOnlyChildren.push_back(*c); + else + // e.g. I/O busses, bridges + memMasterSlaveChildren.push_back(*c); + } else { + // e.g. caches, coherent busses + otherChildren.push_back(*c); + } + } else { + nonMemObjectChildren.push_back(*c); + } + } + + // now put the four bins together again + std::vector sortedChildren; + + copy(memMasterOnlyChildren.begin(), memMasterOnlyChildren.end(), + back_inserter(sortedChildren)); + copy(otherChildren.begin(), otherChildren.end(), + back_inserter(sortedChildren)); + copy(memMasterSlaveChildren.begin(), memMasterSlaveChildren.end(), + back_inserter(sortedChildren)); + copy(memSlaveOnlyChildren.begin(), memSlaveOnlyChildren.end(), + back_inserter(sortedChildren)); + copy(nonMemObjectChildren.begin(), nonMemObjectChildren.end(), + back_inserter(sortedChildren)); + + // finally iterate over the children + for (std::vector::const_iterator c = sortedChildren.begin(); + c != sortedChildren.end(); ++c) { + (*c)->printDotNodes(os); + } +} + +void +MemObject::printDotEdges(std::ostream& os) const +{ + // only print the outgoing edges and leave the incoming ones for + // the mem object at the other end + for (std::vector::const_iterator m = + memMasterPorts.begin(); m != memMasterPorts.end(); ++m) { + if ((*m)->isConnected()) { + os << shortName() << "_" << (*m)->shortName() << " -> " << + (*m)->getSlavePort()->getOwner()->shortName() << "_" << + (*m)->getSlavePort()->shortName() << ";\n"; + } + } + + for (std::vector::const_iterator m = + cacheMasterPorts.begin(); m != cacheMasterPorts.end(); ++m) { + if ((*m)->isConnected()) { + os << shortName() << "_" << (*m)->shortName() << " -> " << + (*m)->getSlavePort()->getOwner()->shortName() << "_" << + (*m)->getSlavePort()->shortName() << ";\n"; + } + } + + // traverse our children + SimObject::printDotEdges(os); +} + +void +MemObject::getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const +{ + // default colour if not overridden in subclasses + r = 0xAD; + g = 0xD6; + b = 0xE8; +} + +std::string +MemObject::rgbToHtml(int r, int g, int b) const +{ + // output the r, g, b as hex with a fixed width of two, padded + // with zeroes, take care to cast to int first otherwhise the + // characters themselves will be output (rather than their ASCII + // representation) + std::ostringstream oss; + oss << "#" << std::hex << std::setw(2) << std::setfill('0') << + static_cast(r) << static_cast(g) << static_cast(b); + return oss.str(); +} diff -r 6548721032fa -r 042f38a2e360 src/mem/module_interface.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/module_interface.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Hansson + */ + +/** + * @file Declaration of abstract interfaces for master and slave + * modules. These are contracts that define what members a master and + * slave module must provide, similar to an sc_export in SystemC and + * TLM2. These interfaces are accessed through the corresponding port + * interfaces from the neighbouring modules, similar to sc_ports in + * SystemC. + */ + +#ifndef __MEM_MODULE_INTERFACE_HH__ +#define __MEM_MODULE_INTERFACE_HH__ + +#include + +#include "base/range.hh" +#include "base/types.hh" +#include "mem/packet.hh" + +/** + * This typedef is used to clean up manipulation of address ranges, + * and in particular getAddrRanges(), as part of the memory-mapped + * slave interface. It is declared outside any object since it's also + * used by some mem objects. Eventually this typedef should move to + * where Addr is defined. + */ +typedef std::list > AddrRangeList; + +/** + * A master interface is a protocol-agnostic abstract class that + * governs what members a master module is required to have. Note that + * the payload type is a template parameter. The base class does not + * have any functionality besides the classic accessors. Even these + * are to be removed but are currently present to enable adapters to + * exist in a transition phase. + */ +template +class MasterInterface +{ + + private: + + /** + * Prevent the copy constructor and assignment operator from being + * called. + */ + MasterInterface(const MasterInterface&); + MasterInterface& operator=(const MasterInterface&); + + protected: + + /** + * Abstract base class that is default constructible and has a + * virtual destructor. + */ + MasterInterface() {} + virtual ~MasterInterface() {} + + public: + + /** + * Typefed for the packet type + */ + typedef PKT pkt_t; + + /** + * Classic timed interface (to be removed). Called by the + * corresponding slave port when a response packet is sent from + * the slave to the master. + * + * @param resp_pkt the response packet + * @param master_id the identifier of the master port + * + * @return true if the response is accepted immediately + */ + virtual bool recvTimingResp(pkt_t* resp_pkt, int master_id) + { + panic("This is a 4-phase interface. Classic call received\n"); + } + + /** + * Classic timed interface (to be removed). Called by the + * corresponding slave port when a request was not immediately + * accepted, and the master should try and send it again. + * + * @param master_id the identifier of the master port + */ + virtual void recvRetryReq(int master_id) + { + panic("This is a 4-phase interface. Classic call received\n"); + } + +}; + +/** + * Abstract timed master interface, with a 4-phase begin/end + * request/response handshake. All interfaces that should be + * approximately timed should inherit from this. The cache maintenance + * currently do not as they are instantaneous. + */ +template +class TimedMasterInterface : public MasterInterface +{ + + public: + + /** + * Called when response transfer starts, transfering the thread of + * execution to the master interface until it calls endResp. A + * second beginResp may only be called after an endResp. + * + * @param resp_pkt the response packet + * @param master_id the identifier of the master port + */ + virtual void beginResp(pkt_t* resp_pkt, int master_id) = 0; + + /** + * Called when a request transfer ends, transfering the thread of + * execution to the master again. After this call another beginReq + * can be issues. + * + * @param req_pkt the request packet + * @param master_id the identifier of the master port + */ + virtual void endReq(pkt_t* req_pkt, int master_id) = 0; + +}; + +/** + * A slave interface is a protocol-agnostic abstract class that govers + * what members a slave module is required to have. The only general + * access functions are debug and atomic transfers, always present + * irrespective of the protocol of the interface. Current the classic + * accessors are also present, but these are to be removed and are + * only present to enable adapters to exist in a transition phase. + */ +template +class SlaveInterface +{ + + public: + + /** + * Typefed for the packet type + */ + typedef PKT pkt_t; + + /** + * Perform a debug transport for debugger access and + * e.g. boot-time loading. Only uses the forward path, + * i.e. packets are requests only when the call is made and + * potentially turned into responses upon returning. + * + * @param pkt A pointer to the packet + * @param slave_id The slave port identifier + */ + virtual void recvDebug(pkt_t* pkt, int slave_id) = 0; + + /** + * Perform an atomic access. Only uses the forward path, + * i.e. packets are requests only when the call is made and + * potentially turned into responses upon returning. + * + * @param pkt A pointer to the packet + * @param slave_id The slave port identifier + * + * @return The time required to complete the request + */ + virtual Tick recvAtomic(pkt_t* pkt, int slave_id) = 0; + + /** + * Classic timed interface (to be removed). Called by a + * corresponding master port to performa a timing access. + * + * @param req_pkt A pointer to the request packet + * @param slave_id The slave port identifier + * + * @return Whether the request is accepted or not + */ + virtual bool recvTimingReq(pkt_t* req_pkt, int slave_id) + { + panic("This is a 4-phase interface. Classic call received\n"); + } + + /** + * Classic timed interface (to be removed). Called by a master + * port if we failed to send a response packet and had to wait and + * later be retransmitted. + * + * @param slave_id The slave port identifier + */ + virtual void recvRetryResp(int slave_id) + { + panic("This is a 4-phase interface. Classic call received\n"); + } + +}; + +/** + * Abstract timed slave interface, with a 4-phase begin/end + * request/response handshake. + */ +template +class TimedSlaveInterface : public SlaveInterface +{ + + public: + + /** + * Called when request transfer starts, transfering the thread of + * execution to the slave interface until it calls endReq. + * + * @param req_pkt the request packet + * @param slave_id the identifier of the slave port + */ + virtual void beginReq(pkt_t* req_pkt, int slave_id) = 0; + + /** + * Called when response transfer ends, transfering the thread of + * execution back to the slave. + * + * @param resp_pkt the response packet + * @param slave_id the identifier of the slave port + */ + virtual void endResp(pkt_t* req_pkt, int slave_id) = 0; + +}; + +/** + * Abstract protocol specific slave interface for memory-mapped + * slaves. Besides the basic functionality of the timed slave + * interface, this also adds the ability to get address ranges and + * block sizes. + */ +class MemMapSlaveInterface : public TimedSlaveInterface +{ + + public: + + /** + * Get the address ranges of the slave port. This is called during + * intialisation to get the address map, and in the case of PCI + * devices also during run-time if the address ranges change and + * the slave sends a range change to the master. + * + * @param slave_id the identifier of the slave port + * + * @return a list of (non-overlapping) address ranges + */ + virtual AddrRangeList getAddrRanges(int slave_id) const = 0; + + /** + * Get the (maximum) block size of the slave port in bytes. + * + * @param slave_id the identifier of the slave port + * + * @return the block size in bytes + */ + virtual unsigned getBlockSize(int slave_id) const = 0; + +}; + +/** + * Specialisation of the master interface for the memory-mapped + * protocol. In addition to the 4-phase timed members, also add a + * member to receive address range changes. + */ +class MemMapMasterInterface : public TimedMasterInterface +{ + + public: + + /** + * Signal that the address range of the connected slave port has + * changed. This is currently used only by the PCI configuration + * ranges. + * + * @param master_id the identifier of the master port + */ + virtual void recvRangeChange(int master_id) = 0; +}; + +/** + * For backwards-compatibility and the option of having adapters in a + * transition phase, define a memory-mapped interface using the + * classic handshakes. + */ +class MemMapMasterInterfaceClassic : public MemMapMasterInterface +{ + + public: + + /** + * Classic interface (to be removed). Called by the corresponding + * slave port when a response packet is sent from the slave to the + * master. + * + * @param resp_pkt the response packet + * @param master_id the identifier of the master port + * + * @return true if the response is accepted immediately + */ + virtual bool recvTimingResp(pkt_t* resp_pkt, int master_id) = 0; + + /** + * Classic interface (to be removed). Called by the corresponding + * slave port when a request was not immediately accepted, and the + * master should try and send it again. + * + * @param master_id the identifier of the master port + */ + virtual void recvRetryReq(int master_id) = 0; + + /** + * Called when response transfer starts, transfering the thread of + * execution to the master interface until it calls endResp. + * + * @param resp_pkt the response packet + * @param master_id the identifier of the master port + */ + virtual void beginResp(pkt_t* resp_pkt, int master_id) + { + panic("This is a classic interface. 4-phase call received\n"); + } + + /** + * Called when a request transfer ends, transfering the thread of + * execution to the master again. + * + * @param req_pkt the request packet + * @param master_id the identifier of the master port + */ + virtual void endReq(pkt_t* req_pkt, int master_id) + { + panic("This is a classic interface. 4-phase call received\n"); + } +}; + +/** + * For backwards-compatibility and the option of having adapters in a + * transition phase, define a memory-mapped interface using the + * classic handshakes. + */ +class MemMapSlaveInterfaceClassic : public MemMapSlaveInterface +{ + + public: + + /** + * Classic interface (to be removed). Receive a request packet. If + * necessary this later gives rise to a response. + * + * @param req_pkt A pointer to the request packet + * @param slave_id The slave port identifier + * + * @return Whether the request is accepted or not + */ + virtual bool recvTimingReq(pkt_t* req_pkt, int slave_id) = 0; + + /** + * Classic interface (to be removed). Called by a master port if + * we failed to send a response packet and had to wait and later + * be retransmitted. + * + * @param slave_id The slave port identifier + */ + virtual void recvRetryResp(int slave_id) = 0; + + /** + * Called when request transfer starts, transfering the thread of + * execution to the slave interface until it calls endReq. + * + * @param req_pkt the request packet + * @param slave_id the identifier of the slave port + */ + virtual void beginReq(pkt_t* req_pkt, int slave_id) + { + panic("This is a classic interface. 4-phase call received\n"); + } + + /** + * Called when response transfer ends, transfering the thread of + * execution back to the slave. + * + * @param resp_pkt the response packet + * @param slave_id the identifier of the slave port + */ + virtual void endResp(pkt_t* req_pkt, int slave_id) + { + panic("This is a classic interface. 4-phase call received\n"); + } + +}; + +/** + * Specialisation for the cache-maintenance protocol, which by + * construction is untimed in its interactions (all transfers are + * instantaneous, but the components themselves may add delays). This + * interface does not add any new members. + */ +class CacheMntMasterInterface : public MasterInterface +{ +}; + +/** + * Protocol specific slave interface for cache maintenance slaves, + * which by construction is untimed in its interactions (all transfers + * are instantaneous, but the components themselves may add delays). + */ +class CacheMntSlaveInterface : public SlaveInterface +{ +}; + +#endif //__MEM_MODULE_INTERFACE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/mport.hh --- a/src/mem/mport.hh Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2008 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: Gabe Black - */ - -#ifndef __MEM_MPORT_HH__ -#define __MEM_MPORT_HH__ - -#include "mem/tport.hh" - -/* - * This file defines a port class which is used for sending and receiving - * messages. These messages are atomic units which don't interact and - * should be smaller than a cache block. This class is based on - * the underpinnings of SimpleTimingPort, but it tweaks some of the external - * functions. - */ - -class MessagePort : public SimpleTimingPort -{ - public: - MessagePort(std::string pname, MemObject *_owner = NULL) : - SimpleTimingPort(pname, _owner) - {} - - virtual ~MessagePort() - {} - - void - recvFunctional(PacketPtr pkt) - { - recvAtomic(pkt); - } - - Tick recvAtomic(PacketPtr pkt); - - virtual Tick recvMessage(PacketPtr pkt) = 0; - - // Accept and ignore responses. - virtual Tick recvResponse(PacketPtr pkt) - { - return 0; - } - - // Since by default we're assuming everything we send is accepted, panic. - virtual Tick recvNack(PacketPtr pkt) - { - panic("Unhandled message nack.\n"); - } - - void sendMessageTiming(PacketPtr pkt, Tick latency); - Tick sendMessageAtomic(PacketPtr pkt); -}; - -#endif diff -r 6548721032fa -r 042f38a2e360 src/mem/mport.cc --- a/src/mem/mport.cc Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2008 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: Gabe Black - */ - -#include "mem/mport.hh" - -Tick -MessagePort::recvAtomic(PacketPtr pkt) -{ - if (pkt->cmd == MemCmd::MessageReq) { - // We received a message. - return recvMessage(pkt); - } else if (pkt->cmd == MemCmd::MessageResp) { - return recvResponse(pkt); - } else if (pkt->wasNacked()) { - return recvNack(pkt); - } else if (pkt->isError()) { - panic("Packet is error.\n"); - } else { - panic("Unexpected memory command %s.\n", pkt->cmd.toString()); - } -} - -void -MessagePort::sendMessageTiming(PacketPtr pkt, Tick latency) -{ - schedSendTiming(pkt, curTick() + latency); -} - -Tick -MessagePort::sendMessageAtomic(PacketPtr pkt) -{ - Tick latency = sendAtomic(pkt); - assert(pkt->isResponse()); - latency += recvResponse(pkt); - return latency; -} diff -r 6548721032fa -r 042f38a2e360 src/mem/noncoherent_bus.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/noncoherent_bus.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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: Andreas Hansson + * Geza Lore + */ + +/** + * @file + * Declaration of a non-coherentbus object, which uses the new begin/end + * handshakes. + */ + +#ifndef __MEM_NON_COHERENT_BUS_HH__ +#define __MEM_NON_COHERENT_BUS_HH__ + +#include +#include +#include + +#include "base/hashmap.hh" +#include "base/range.hh" +#include "base/range_map.hh" +#include "base/types.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "mem/module_interface.hh" +#include "params/NonCoherentBus.hh" +#include "sim/eventq.hh" + +class NonCoherentBus : public MemObject, + public MemMapSlaveInterface, + public MemMapMasterInterface +{ + + public: + + /** + * Create a non-coherent bus based on the parameters passed. + */ + NonCoherentBus(const NonCoherentBusParams *p); + + virtual ~NonCoherentBus() {}; + + /** + * Override init from MemObject and initialise address ranges. + */ + virtual void init(); + + /** + * Return (and in this instance also create) the memory-mapped + * master ports of this non-coherent bus. + */ + virtual MemMapMasterPort* getMemMapMasterPort(const std::string& name, + int idx = -1); + + /** + * Return (and in this instance also create) the memory-mapped + * slave ports of this non-coherent bus. + */ + virtual MemMapSlavePort* getMemMapSlavePort(const std::string& name, + int idx = -1); + + unsigned int drain(Event *de); + + /** + * Get the bus-specific colour. + * + * @param r red in 0-255 + * @param g green in 0-255 + * @param b blue in 0-255 + */ + virtual void getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const; + + protected: + + /** + * A master interface block corresponds to the structural entity + * in a bus layer that switches between a number of bus slave + * ports (connected masters), arbitrates between them, and governs + * the flow control to a single bus master port (connected slave). + */ + class MasterInterfaceBlock + { + + }; + + /** + * The state of the bus, being either idle, or busy doing requests + * or responses. IDLE means there are no transfers in progress. + * BUSY_REQ means there is a request transfer in progress (beginReq + * called on a slave port, but no corresponding endReq was called + * yet). BUSY_RESP is similar, with response transfers. + */ + enum BusState {IDLE, BUSY}; + + /** + * Holding the current global state of the bus + */ + BusState reqState; + + BusState respState; + + /** + * Holds the id of the target master when a slave received a beginReq. + * Used for checking in endReq. + */ + int targetMasterId; + + /** + * Holds the id of the source slave when it received a beginReq. + * Used for sendgind endReq to the right palce. + */ + int sourceSlaveId; + + /** + * Holds the id of the target slave when a master received a beginResp. + * Used for checking in endResp. + */ + int targetSlaveId; + + /** + * Holds the id of the source master when it received a beginReq. + * Used for sendind endResp to the right palce. + */ + int sourceMasterId; + + /** + * Queue of pending requests and the slave ids they originated from. + */ + std::queue > pendingReqs; + + /** + * Queue of pending responses and the master ids they originated from. + */ + std::queue > pendingResps; + + /** + * Handle pending requests. + */ + void handlePendingReq(); + + /** + * Handle pending responses. + */ + void handlePendingResp(); + + /** + * Function called by the master ports when the bus is recieving a + * range change. + */ + void recvRangeChange(int master_id); + + /** + * Function called by the slave ports when the bus is recieving a + * debug request. + */ + virtual void recvDebug(MemMapPacketPtr pkt, int slave_id); + + /** + * Function called by the slave ports when the bus is recieving an + * atomic request. + */ + virtual Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + + virtual void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + virtual void endReq(MemMapPacketPtr req_pkt, int master_id); + + virtual void beginResp(MemMapPacketPtr resp_pkt, int master_id); + + virtual void endResp(MemMapPacketPtr resp_pkt, int slave_id); + + /** + * Find which port connected to this bus (if any) should be given + * a packet with this address. + * + * @param addr Address to find port for. + * @return id of port that the packet should be sent out of. + */ + int decodeAddr(Addr addr); + + /** + * Return the address ranges this port is responsible for. + * + * @param slave_id id of the bus slave port that made the request. + */ + AddrRangeList getAddrRanges(int slave_id) const; + + /** + * Ask everyone on the bus what their size is + * + * @param slave_id id of the slave port that made the request + * @return the max of all the sizes + */ + unsigned getBlockSize(int slave_id) const; + + protected: + + std::map outstandingReq; + + Tick calcPacketTiming(MemMapPacketPtr pkt); + + virtual void forwardTimingReq(MemMapPacketPtr req_pkt, + MemObject* source_object) const; + + void acceptResp(MemMapPacketPtr resp_pkt, int master_id); + + private: + + /** the clock speed for the bus */ + int clock; + /** cycles of overhead per transaction */ + int headerCycles; + /** the width of the bus in bytes */ + int width; + + Event* drainEvent; + + /** which port to consider the default if set */ + int defaultId; + + typedef range_map::iterator PortMapIter; + typedef range_map::const_iterator PortMapConstIter; + range_map portMap; + + AddrRangeList defaultRange; + + // Cache for the decodeAddr function storing recently used ports + // from portMap + struct AddrCache { + bool valid; + int master_id; + Addr start; + Addr end; + }; + + AddrCache portCache[3]; + + // Checks the cache and returns the id of the port that has the + // requested address within its range + inline int checkAddrCache(Addr addr) { + if (portCache[0].valid && addr >= portCache[0].start && + addr < portCache[0].end) { + return portCache[0].master_id; + } + if (portCache[1].valid && addr >= portCache[1].start && + addr < portCache[1].end) { + return portCache[1].master_id; + } + if (portCache[2].valid && addr >= portCache[2].start && + addr < portCache[2].end) { + return portCache[2].master_id; + } + + return -1; + } + + // Clears the earliest entry of the cache and inserts a new port entry + inline void updateAddrCache(short master_id, Addr start, Addr end) { + portCache[2].valid = portCache[1].valid; + portCache[2].master_id = portCache[1].master_id; + portCache[2].start = portCache[1].start; + portCache[2].end = portCache[1].end; + + portCache[1].valid = portCache[0].valid; + portCache[1].master_id = portCache[0].master_id; + portCache[1].start = portCache[0].start; + portCache[1].end = portCache[0].end; + + portCache[0].valid = true; + portCache[0].master_id = master_id; + portCache[0].start = start; + portCache[0].end = end; + } + + // Clears the cache. Needs to be called in constructor. + inline void clearAddrCache() { + portCache[2].valid = false; + portCache[1].valid = false; + portCache[0].valid = false; + } + + /** + * Update the address ranges looking at the slave connected to a + * given memory-mapped master port. + * + * @param masterPort the port to enquire + */ + void updateAddrRanges(const MemMapMasterPort* masterPort); + + void acceptReq(MemMapPacketPtr req_pkt, int slave_id); + + + + /** If true, use address range provided by default device. Any + address not handled by another port and not in default device's + range will cause a fatal error. If false, just send all + addresses not handled by another port to default device. */ + bool useDefaultRange; + + unsigned defaultBlockSize; + + unsigned int blockSize; + + EventWrapper + handlePendingReqEvent; + + EventWrapper + handlePendingRespEvent; + + /** Mimimum tick, when the bus can become IDLE. (This simulates + * transfer time) */ + Tick minUnblockReqTick; + + Tick minUnblockRespTick; + + /** This simulates transfer time by going to IDLE late. */ + void unblockReq(); + + void unblockResp(); + + EventWrapper unblockReqEvent; + + + EventWrapper unblockRespEvent; + +}; + +#endif //__MEM_BUS_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/noncoherent_bus.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/noncoherent_bus.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * 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: Andreas Hansson + * Geza Lore + */ + +/** + * @file + * Definition of a multi-layer split request/response non-coherent bus. + */ + +#include "base/misc.hh" +#include "base/trace.hh" +#include "mem/noncoherent_bus.hh" + +using namespace std; + +typedef AddrRangeList::const_iterator AddrRangeIter; + +NonCoherentBus::NonCoherentBus(const NonCoherentBusParams *p) + : MemObject(p), + reqState(IDLE), respState(IDLE), + targetMasterId(-1), + sourceSlaveId(-1), + targetSlaveId(-1), + sourceMasterId(-1), + clock(p->clock), headerCycles(p->header_cycles), width(p->width), + drainEvent(NULL), + defaultId(-1), useDefaultRange(p->use_default_range), + defaultBlockSize(p->block_size), blockSize(0), + handlePendingReqEvent(this), handlePendingRespEvent(this), + unblockReqEvent(this), unblockRespEvent(this) +{ + // width, clock period, and header cycles must be positive + if (width <= 0) + fatal("Bus width must be positive\n"); + if (clock <= 0) + fatal("Bus clock period must be positive\n"); + if (headerCycles <= 0) + fatal("Number of header cycles must be positive\n"); + clearAddrCache(); + + // the default ranges is used by the PCI bus to have a number of + // configuration sub-ranges covered by individual devices, and an + // overall pciconfig space slave covering the whole space. thus, a + // two tier scheme is necessary where we first check in the normal + // port map, and then in the second level which in this case is + // inclusive. +} + +void +NonCoherentBus::init() +{ + // iterate over our memory-mapped master ports and determine the + // address ranges of the slaves that are connected to those ports. + for (MemMapMasterConstIter m = memMasterPorts.begin(); + m != memMasterPorts.end(); ++m) { + updateAddrRanges(*m); + } + + // establish the block size + for (MemMapMasterConstIter m = memMasterPorts.begin(); + m != memMasterPorts.end(); ++m) { + MemMapMasterPort* master_port = *m; + unsigned tmp_bs = (*master_port)->getBlockSize(); + if (tmp_bs > blockSize) + blockSize = tmp_bs; + } + + if (blockSize == 0) + blockSize = defaultBlockSize; +} + +MemMapMasterPort* +NonCoherentBus::getMemMapMasterPort(const std::string& name, int idx) +{ + // give the new port the next available index + int id = memMasterPorts.size(); + + std::string _name = "mem_master"; + + // take special care for the default port + if (name == "default_mem_master") { + // if the default port is not yet assigned + if (defaultId == -1) { + defaultId = id; + _name = name; + } else { + fatal("Default port already set\n"); + } + } + + // create the actual port and add it in the list + MemMapMasterPort* masterPort = new MemMapMasterPort(*this, this, + _name, id); + regPort(masterPort); + return masterPort; +} + +MemMapSlavePort* +NonCoherentBus::getMemMapSlavePort(const std::string& name, int idx) +{ + // give the new port the next available index + int id = memSlavePorts.size(); + + // we are looking at a slave port + std::string _name = "mem_slave"; + + // create the port and keep track of it on the right spot + // corresponding to its id + MemMapSlavePort* slavePort = new MemMapSlavePort(*this, this, _name, id); + regPort(slavePort); + return slavePort; +} + +void NonCoherentBus::updateAddrRanges(const MemMapMasterPort* masterPort) +{ + // clear the cache just in case + clearAddrCache(); + + int id = masterPort->getId(); + + if (id == defaultId) { + // only update these ranges if routing to the default port is to be + // based on address + if (useDefaultRange) { + defaultRange.clear(); + AddrRangeList ranges = (*masterPort)->getAddrRanges(); + copy(ranges.begin(), ranges.end(), back_inserter(defaultRange)); + } + } else { + // Clean out any previously existent mappings for this port + for (PortMapIter portIter = portMap.begin(); + portIter != portMap.end(); ) { + if (portIter->second == id) + portMap.erase(portIter++); + else + portIter++; + } + + AddrRangeList ranges = (*masterPort)->getAddrRanges(); + + for (AddrRangeIter r = ranges.begin(); r != ranges.end(); ++r) { + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", + r->start, r->end, id); + if (portMap.insert(*r, id) == portMap.end()) { + int conflict_id = portMap.find(*r)->second; + fatal("%s has ports with range %#llx - %#llx:\n" \ + "\t%s\n\t%s\n", + name(), r->start, r->end, + memMasterPorts[id]->getSlavePort()->name(), + memMasterPorts[conflict_id]->getSlavePort()->name()); + } + } + } + + // iterate over the normal ports and make sure they either are + // subsets of the default ranges or do not overlap at all + if (useDefaultRange) { + for (PortMapIter p = portMap.begin(); p != portMap.end(); ++p) { + for (AddrRangeIter dflt_iter = defaultRange.begin(); + dflt_iter != defaultRange.end(); dflt_iter++) { + if ((p->first.start < dflt_iter->start && + p->first.end >= dflt_iter->start) || + (p->first.start < dflt_iter->end && + p->first.end >= dflt_iter->end)) + fatal("Cannot set ranges that intersect the" \ + "default set and are not a subset.\n"); + } + } + } +} + +void +NonCoherentBus::recvRangeChange(int master_id) +{ + DPRINTF(BusAddrRanges, "received RangeChange from master port id %d\n", + master_id); + + updateAddrRanges(memMasterPorts[master_id]); +} + +AddrRangeList +NonCoherentBus::getAddrRanges(int slave_id) const +{ + // from the perspective of the connected memory-mapped master (on + // the slave port specified by slave_id, what memory address + // ranges are visible through the bus slave port) + AddrRangeList ranges; + + // copy the default ranges + copy(defaultRange.begin(), defaultRange.end(), back_inserter(ranges)); + + // iterate over the normal port maps (range -> id) + for (PortMapConstIter p = portMap.begin(); p != portMap.end(); ++p) { + // for each entry see if it overlaps with any of the default ranges + bool subset = false; + for (AddrRangeIter dflt_iter = defaultRange.begin(); + dflt_iter != defaultRange.end(); dflt_iter++) { + if (p->first.start >= dflt_iter->start && + p->first.end <= dflt_iter->end) { + subset = true; + DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", + p->first.start, p->first.end); + } + } + + // if is is not a subset, add it + if (!subset) { + ranges.push_back(p->first); + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", + p->first.start, p->first.end); + } + } + + return ranges; +} + + +int +NonCoherentBus::decodeAddr(Addr addr) +{ + // get the master id from the address cache if possible + int master_id = checkAddrCache(addr); + if (master_id != -1) + return master_id; + + // check the firts tier port ranges for a hit + PortMapIter i = portMap.find(RangeSize(addr,1)); + if (i != portMap.end()) { + master_id = i->second; + updateAddrCache(master_id, i->first.start, i->first.end); + return master_id; + } + + // check if we are to use the default range and if it matches + + // default ranges are really only used to implement a two tier + // address lookup where the default range is shadowing the primary + // port map and is allowed to overlap. + if (useDefaultRange) { + AddrRangeIter a_end = defaultRange.end(); + for (AddrRangeIter i = defaultRange.begin(); i != a_end; ++i) { + if (*i == addr) { + DPRINTF(Bus, " found addr %#llx on default\n", addr); + return defaultId; + } + } + + // set to use range check for default, but no match + panic("%s Unable to find destination for addr %#llx\n", name(), addr); + } + + // ignore range and fall back, but default has to be set, check + if (defaultId == -1) + panic("%s Unable to find destination for addr %#llx\n", name(), addr); + + DPRINTF(Bus, "Unable to find destination for addr %#llx, " + "will use default port\n", addr); + return defaultId; +} + + +unsigned +NonCoherentBus::getBlockSize(int slave_id) const +{ + // supposedly in bytes, and used to determine two things, the + // alignment requirements and the maximum transaction size. should + // be split into two + if (blockSize != 64) + warn_once("Blocksize found to not be 64... hmm... probably not.\n"); + + return blockSize; +} + +void +NonCoherentBus::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + // get the master id according to the address + int master_id = decodeAddr(pkt->getAddr()); + MemMapMasterPort* master_port = memMasterPorts[master_id]; + + if (!pkt->isPrint()) { + // don't do DPRINTFs on PrintReq as it clutters up the output + DPRINTF(Bus, "recvDebug: packet src %d dst %d addr 0x%x cmd %s\n", + slave_id, master_id, pkt->getAddr(), pkt->cmdString()); + } + + (*master_port)->sendDebug(pkt); +} + +Tick +NonCoherentBus::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + // get the master id according to the address + int master_id = decodeAddr(pkt->getAddr()); + MemMapMasterPort* master_port = memMasterPorts[master_id]; + + DPRINTF(Bus, "recvAtomic: packet src %d dst %d addr 0x%x cmd %s\n", + slave_id, master_id, pkt->getAddr(), pkt->cmdString()); + + // send it to the actual slave where it might be sunk (if inhibited) + return (*master_port)->sendAtomic(pkt); +} + +void +NonCoherentBus::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + // let everyone raise a request before making a decision + pendingReqs.push(make_pair(req_pkt, slave_id)); + + if (reqState == IDLE && !handlePendingReqEvent.scheduled()) { + schedule(handlePendingReqEvent,curTick() + 1); + } +} + +void +NonCoherentBus::forwardTimingReq(MemMapPacketPtr req_pkt, + MemObject* source_object) const +{ +} + +void +NonCoherentBus::acceptReq(MemMapPacketPtr req_pkt, int slave_id) +{ + assert(reqState == IDLE); + + reqState = BUSY; + + // keep track that we have an outstanding request to this address + // this is used by the coherency mechanism in determining what to + // do with responses + if (req_pkt->needsResponse()) + outstandingReq[req_pkt->req] = true; + + MemMapSlavePort* slave_port = memSlavePorts[slave_id]; + + // push the source port onto the path + req_pkt->getPath().push(slave_id); + + // give the coherent bus an opportunity to forward the timing + // request since it shares the rest of the functionality with the + // non-coherent bus (the non-coherent bus does nothing on this call) + forwardTimingReq(req_pkt, slave_port->getMasterPort()->getOwner()); + + calcPacketTiming(req_pkt); + minUnblockReqTick = req_pkt->finishTime; + + // save source id to call end on later + sourceSlaveId = slave_id; + // determine where we are going based on the address + targetMasterId = decodeAddr(req_pkt->getAddr()); + + DPRINTF(NCBus,"beginReq from slave %d to master %d\n", + sourceSlaveId, targetMasterId); + + (*memMasterPorts[targetMasterId])->beginReq(req_pkt); +} + +void +NonCoherentBus::endReq(MemMapPacketPtr req_pkt, int master_id) +{ + // Check that same target acknowledged as we sent it to + assert(master_id == targetMasterId); + + DPRINTF(NCBus,"endReq from master %d\n", master_id); + + // signal end to source + (*memSlavePorts[sourceSlaveId])->endReq(req_pkt); + + if (minUnblockReqTick <= curTick()) { + reqState = IDLE; + schedule(handlePendingReqEvent,curTick()); + } else { + schedule(unblockReqEvent, minUnblockReqTick); + } +} + +void +NonCoherentBus::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + // let everyone raise a request before making a decision + pendingResps.push(make_pair(resp_pkt, master_id)); + + if (respState == IDLE && !handlePendingRespEvent.scheduled()) { + schedule(handlePendingRespEvent, curTick() + 1); + } +} + +void +NonCoherentBus::acceptResp(MemMapPacketPtr resp_pkt, int master_id) +{ + assert(respState == IDLE); + + respState = BUSY; + + calcPacketTiming(resp_pkt); + minUnblockRespTick = resp_pkt->finishTime; + + // no matter where the response came from (memory-mapped slave or + // a cache snoop) we should still have a it passing through as a + // memory-mapped response here + assert(outstandingReq[resp_pkt->req]); + + outstandingReq[resp_pkt->req] = false; + + // save source id to call end on later + sourceMasterId = master_id; + + // find out where to go by stepping backwards through the path + targetSlaveId = resp_pkt->getPath().top(); + + DPRINTF(NCBus,"beginResp from master %d to slave %d\n", + sourceMasterId, targetSlaveId); + + resp_pkt->getPath().pop(); + + (*memSlavePorts[targetSlaveId])->beginResp(resp_pkt); +} + +void +NonCoherentBus::endResp(MemMapPacketPtr resp_pkt, int slave_id) +{ + // Check that same target acknowledged as we sent it to + assert(slave_id == targetSlaveId); + + DPRINTF(NCBus,"endResp from slave %d\n", slave_id); + + // signal end to source if it is not internal (used by the + // coherent bus for aggregated cache snoop responses) + if (sourceMasterId != -1) + (*memMasterPorts[sourceMasterId])->endResp(resp_pkt); + + if (minUnblockRespTick <= curTick()) { + respState = IDLE; + schedule(handlePendingRespEvent,curTick()); + } else { + schedule(unblockRespEvent, minUnblockRespTick); + } +} + +void +NonCoherentBus::handlePendingReq() +{ + if (!pendingReqs.empty()) { + // first come first served + std::pair req = pendingReqs.front(); + pendingReqs.pop(); + + acceptReq(req.first, req.second); + } else if (drainEvent != NULL && respState == IDLE) { + drainEvent->process(); + drainEvent = NULL; + } +} + +void +NonCoherentBus::handlePendingResp() +{ + // check if there are pending responses or requests and dispatch them + if (!pendingResps.empty()) { + // first come first served + std::pair resp = pendingResps.front(); + pendingResps.pop(); + + acceptResp(resp.first, resp.second); + } else if (drainEvent != NULL && reqState == IDLE) { + drainEvent->process(); + drainEvent = NULL; + } +} + +Tick +NonCoherentBus::calcPacketTiming(MemMapPacketPtr pkt) +{ + // determine the current time rounded to the closest following + // clock edge + Tick now = curTick(); + if (now % clock != 0) { + now = ((now / clock) + 1) * clock; + } + + Tick headerTime = now + headerCycles * clock; + + // The packet will be sent. Figure out how long it occupies the bus, and + // how much of that time is for the first "word", aka bus width. + int numCycles = 0; + if (pkt->hasData()) { + // If a packet has data, it needs ceil(size/width) cycles to send it + int dataSize = pkt->getSize(); + numCycles += dataSize/width; + if (dataSize % width) + numCycles++; + } + + // The first word will be delivered after the current tick, the delivery + // of the address if any, and one bus cycle to deliver the data + pkt->firstWordTime = headerTime + clock; + + pkt->finishTime = headerTime + numCycles * clock; + + return headerTime; +} + +void +NonCoherentBus::unblockReq() +{ + reqState = IDLE; + handlePendingReq(); +} + +void +NonCoherentBus::unblockResp() +{ + respState = IDLE; + handlePendingResp(); +} + +unsigned int +NonCoherentBus::drain(Event * de) +{ + // if we are not idle keep the event until drained + if (reqState != IDLE || respState != IDLE) { + drainEvent = de; + return 1; + } + return 0; +} + +void +NonCoherentBus::getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const +{ + r = 0xF3; + g = 0xB1; + b = 0x7F; +} + +NonCoherentBus* +NonCoherentBusParams::create() +{ + return new NonCoherentBus(this); +} diff -r 6548721032fa -r 042f38a2e360 src/mem/packet.hh --- a/src/mem/packet.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/packet.hh Fri Aug 05 18:14:06 2011 +0100 @@ -41,6 +41,7 @@ #include #include +#include #include #include "base/cast.hh" @@ -53,14 +54,10 @@ #include "mem/request.hh" #include "sim/core.hh" -struct Packet; -typedef Packet *PacketPtr; typedef uint8_t* PacketDataPtr; -typedef std::list PacketList; class MemCmd { - friend class Packet; public: /** @@ -100,7 +97,6 @@ // @TODO these should be classified as responses rather than // requests; coding them as requests initially for backwards // compatibility - NetworkNackError, // nacked at network layer (not by protocol) InvalidDestError, // packet dest field invalid BadAddressError, // memory address invalid // Fake simulator-only commands @@ -203,14 +199,16 @@ */ class Packet : public FastAlloc, public Printable { + friend class MemMapPacket; + friend class CacheMntPacket; + public: typedef uint32_t FlagsType; typedef ::Flags Flags; typedef short NodeID; - private: - static const FlagsType PUBLIC_FLAGS = 0x00000000; - static const FlagsType PRIVATE_FLAGS = 0x00007F0F; + protected: + static const FlagsType COPY_FLAGS = 0x0000000F; static const FlagsType SHARED = 0x00000001; @@ -225,9 +223,7 @@ /// Are the 'addr' and 'size' fields valid? static const FlagsType VALID_ADDR = 0x00000100; static const FlagsType VALID_SIZE = 0x00000200; - /// Is the 'src' field valid? - static const FlagsType VALID_SRC = 0x00000400; - static const FlagsType VALID_DST = 0x00000800; + /// Is the data pointer set to a value that shouldn't be freed /// when the packet is destroyed? static const FlagsType STATIC_DATA = 0x00001000; @@ -249,7 +245,10 @@ /// A pointer to the original request. RequestPtr req; - private: + protected: + + bool isForwarded; + /** * A pointer to the data being transfered. It can be differnt * sizes at each level of the heirarchy so it belongs in the @@ -266,30 +265,34 @@ /// The size of the request or transfer. unsigned size; - /** - * 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. - */ - NodeID src; + // The path a packet has traversed. Used to determine the response + // path for timing accesses + std::stack path; /** - * 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). + * delete the data pointed to in the data pointer. Ok to call to + * matter how data was allocted. */ - NodeID dest; + void + deleteData() + { + if (flags.isSet(ARRAY_DATA)) + delete [] data; + else if (flags.isSet(DYNAMIC_DATA)) + delete data; - /** - * The original value of the command field. Only valid when the - * current command field is an error condition; in that case, the - * previous contents of the command field are copied here. This - * field is *not* set on non-error responses. - */ - MemCmd origCmd; + flags.clear(STATIC_DATA|DYNAMIC_DATA|ARRAY_DATA); + data = NULL; + } + + protected: + + Packet(Request *_req, MemCmd _cmd) : + cmd(_cmd), req(_req), isForwarded(false), + data(NULL), time(curTick()), senderState(NULL) { + } + + Packet& operator=(const Packet& p); public: /// Used to calculate latencies for each packet. @@ -301,10 +304,6 @@ /// The time at which the first chunk of the packet will be transmitted Tick firstWordTime; - /// The special destination address indicating that the packet - /// should be routed based on its address. - static const NodeID Broadcast = -1; - /** * A virtual base opaque structure used to hold state associated * with the packet but specific to the sending device (e.g., an @@ -413,27 +412,21 @@ bool isPrint() const { return cmd.isPrint(); } // Snoop flags - void assertMemInhibit() { flags.set(MEM_INHIBIT); } - bool memInhibitAsserted() { return flags.isSet(MEM_INHIBIT); } + void assertMemInhibit() + { + assert(!req->isUncacheable()); + flags.set(MEM_INHIBIT); + } + + bool memInhibitAsserted() const { return flags.isSet(MEM_INHIBIT); } void assertShared() { flags.set(SHARED); } - bool sharedAsserted() { return flags.isSet(SHARED); } + bool sharedAsserted() const { return flags.isSet(SHARED); } - // Special control flags - void setExpressSnoop() { flags.set(EXPRESS_SNOOP); } - bool isExpressSnoop() { return flags.isSet(EXPRESS_SNOOP); } + + void setSupplyExclusive() { flags.set(SUPPLY_EXCLUSIVE); } void clearSupplyExclusive() { flags.clear(SUPPLY_EXCLUSIVE); } - bool isSupplyExclusive() { return flags.isSet(SUPPLY_EXCLUSIVE); } - - // Network error conditions... encapsulate them as methods since - // their encoding keeps changing (from result field to command - // field, etc.) - void - setNacked() - { - assert(isResponse()); - cmd = MemCmd::NetworkNackError; - } + bool isSupplyExclusive() const { return flags.isSet(SUPPLY_EXCLUSIVE); } void setBadAddress() @@ -442,140 +435,43 @@ cmd = MemCmd::BadAddressError; } - bool wasNacked() const { return cmd == MemCmd::NetworkNackError; } bool hadBadAddress() const { return cmd == MemCmd::BadAddressError; } void copyError(Packet *pkt) { assert(pkt->isError()); cmd = pkt->cmd; } - bool isSrcValid() { return flags.isSet(VALID_SRC); } - /// Accessor function to get the source index of the packet. - NodeID getSrc() const { assert(flags.isSet(VALID_SRC)); return src; } - /// Accessor function to set the source index of the packet. - void setSrc(NodeID _src) { src = _src; flags.set(VALID_SRC); } - /// Reset source field, e.g. to retransmit packet on different bus. - void clearSrc() { flags.clear(VALID_SRC); } - - bool isDestValid() { return flags.isSet(VALID_DST); } - /// Accessor function for the destination index of the packet. - NodeID getDest() const { assert(flags.isSet(VALID_DST)); return dest; } - /// Accessor function to set the destination index of the packet. - void setDest(NodeID _dest) { dest = _dest; flags.set(VALID_DST); } + std::stack& getPath() { return path; } + const std::stack& getPath() const { return path; } Addr getAddr() const { assert(flags.isSet(VALID_ADDR)); return addr; } unsigned getSize() const { assert(flags.isSet(VALID_SIZE)); return size; } Addr getOffset(int blkSize) const { return getAddr() & (Addr)(blkSize - 1); } - /** - * It has been determined that the SC packet should successfully update - * memory. Therefore, convert this SC packet to a normal write. - */ - void - convertScToWrite() + void copyData(Packet* pkt) { - assert(isLLSC()); - assert(isWrite()); - cmd = MemCmd::WriteReq; - } + // copy the pointer to data even if it is dynamic, and update + // the flag + if (pkt->flags & DYNAMIC_DATA) { + data = pkt->data; + flags.set(DYNAMIC_DATA); + } - /** - * When ruby is in use, Ruby will monitor the cache line and thus M5 - * phys memory should treat LL ops as normal reads. - */ - void - convertLlToRead() - { - assert(isLLSC()); - assert(isRead()); - cmd = MemCmd::ReadReq; - } - - /** - * 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, MemCmd _cmd, NodeID _dest) - : flags(VALID_DST), cmd(_cmd), req(_req), data(NULL), - dest(_dest), time(curTick()), senderState(NULL) - { - if (req->hasPaddr()) { - addr = req->getPaddr(); - flags.set(VALID_ADDR); - } - if (req->hasSize()) { - size = req->getSize(); - flags.set(VALID_SIZE); - } - } - - /** - * Alternate constructor if you are trying to create a packet with - * a request that is for a whole block, not the address from the - * req. this allows for overriding the size/addr of the req. - */ - Packet(Request *_req, MemCmd _cmd, NodeID _dest, int _blkSize) - : flags(VALID_DST), cmd(_cmd), req(_req), data(NULL), - dest(_dest), time(curTick()), senderState(NULL) - { - if (req->hasPaddr()) { - addr = req->getPaddr() & ~(_blkSize - 1); - flags.set(VALID_ADDR); - } - size = _blkSize; - flags.set(VALID_SIZE); - } - - /** - * Alternate constructor for copying a packet. Copy all fields - * *except* if the original packet's data was dynamic, don't copy - * that, as we can't guarantee that the new packet's lifetime is - * less than that of the original packet. In this case the new - * packet should allocate its own data. - */ - Packet(Packet *pkt, bool clearFlags = false) - : cmd(pkt->cmd), req(pkt->req), - data(pkt->flags.isSet(STATIC_DATA) ? pkt->data : NULL), - addr(pkt->addr), size(pkt->size), src(pkt->src), dest(pkt->dest), - time(curTick()), senderState(pkt->senderState) - { - if (!clearFlags) - flags.set(pkt->flags & COPY_FLAGS); - - flags.set(pkt->flags & (VALID_ADDR|VALID_SIZE|VALID_SRC|VALID_DST)); - flags.set(pkt->flags & STATIC_DATA); + // make sure we do not delete the request or any data on + // destruction of this packet + isForwarded = true; } /** * clean up packet variables */ - ~Packet() + virtual ~Packet() { - // If this is a request packet for which there's no response, - // delete the request object here, since the requester will - // never get the chance. - if (req && isRequest() && !needsResponse()) - delete req; - 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->hasPaddr()); - flags = 0; - addr = req->getPaddr(); - size = req->getSize(); - time = req->time(); - - flags.set(VALID_ADDR|VALID_SIZE); - deleteData(); + if (!isForwarded) { + // If this is a request packet for which there's no response, + // delete the request object here, since the requester will + // never get the chance. + if (req && isRequest() && !needsResponse()) + delete req; + deleteData(); + } } /** @@ -589,16 +485,11 @@ { assert(needsResponse()); assert(isRequest()); - origCmd = cmd; cmd = cmd.responseCommand(); // responses are never express, even if the snoop that // triggered them was flags.clear(EXPRESS_SNOOP); - - dest = src; - flags.set(VALID_DST, flags.isSet(VALID_SRC)); - flags.clear(VALID_SRC); } void @@ -613,20 +504,6 @@ makeResponse(); } - /** - * 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(wasNacked()); - cmd = origCmd; - assert(needsResponse()); - setDest(Broadcast); - } - void setSize(unsigned size) { @@ -738,22 +615,6 @@ writeData(blk_data + getOffset(blkSize)); } - /** - * delete the data pointed to in the data pointer. Ok to call to - * matter how data was allocted. - */ - void - deleteData() - { - if (flags.isSet(ARRAY_DATA)) - delete [] data; - else if (flags.isSet(DYNAMIC_DATA)) - delete data; - - flags.clear(STATIC_DATA|DYNAMIC_DATA|ARRAY_DATA); - data = NULL; - } - /** If there isn't data in the packet, allocate some. */ void allocate() @@ -782,7 +643,7 @@ * another packet (i.e. an in-transit request or response). */ bool - checkFunctional(PacketPtr other) + checkFunctional(Packet* other) { uint8_t *data = other->hasData() ? other->getPtr() : NULL; return checkFunctional(other, other->getAddr(), other->getSize(), @@ -813,4 +674,132 @@ const std::string &prefix = "") const; }; +class MemMapPacket : public Packet +{ + + public: + + /** + * 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. + */ + MemMapPacket(Request *_req, MemCmd _cmd) + : Packet(_req, _cmd) + { + if (req->hasPaddr()) { + addr = req->getPaddr(); + flags.set(VALID_ADDR); + } + if (req->hasSize()) { + size = req->getSize(); + flags.set(VALID_SIZE); + } + } + + /** + * Alternate constructor if you are trying to create a packet with + * a request that is for a whole block, not the address from the + * req. this allows for overriding the size/addr of the req. + */ + MemMapPacket(Request *_req, MemCmd _cmd, int _blkSize) + : Packet(_req, _cmd) + { + if (req->hasPaddr()) { + addr = req->getPaddr() & ~(_blkSize - 1); + flags.set(VALID_ADDR); + } + size = _blkSize; + flags.set(VALID_SIZE); + } + + /** + * Alternate constructor for copying a packet, ultimately to + * create a response. Copy all fields *except* if the original + * packet's data was dynamic, don't copy that, as we can't + * guarantee that the new packet's lifetime is less than that of + * the original packet. In this case the new packet should + * allocate its own data. + */ + MemMapPacket(Packet* pkt) + : Packet(pkt->req, pkt->cmd) { + + data = pkt->flags.isSet(STATIC_DATA) ? pkt->data : NULL; + addr = pkt->addr; + size = pkt->size; + path = pkt->path; + senderState = pkt->senderState; + + flags.set(pkt->flags & COPY_FLAGS); + flags.set(pkt->flags & (VALID_ADDR|VALID_SIZE)); + flags.set(pkt->flags & STATIC_DATA); + } + + ~MemMapPacket() {} + +}; + +class CacheMntPacket : public Packet +{ + public: + + /** + * Alternate constructor for copying a packet. Copy all fields + * *except* if the original packet's data was dynamic, don't copy + * that, as we can't guarantee that the new packet's lifetime is + * less than that of the original packet. In this case the new + * packet should allocate its own data. + */ + CacheMntPacket(Packet* pkt, bool clearFlags = false) + : Packet(pkt->req, pkt->cmd) { + + data = pkt->flags.isSet(STATIC_DATA) ? pkt->data : NULL; + addr = pkt->addr; + size = pkt->size; + path = pkt->path; + senderState = pkt->senderState; + + if (!clearFlags) + flags.set(pkt->flags & COPY_FLAGS); + + flags.set(pkt->flags & (VALID_ADDR|VALID_SIZE)); + flags.set(pkt->flags & STATIC_DATA); + } + + /** + * Constructor. This is only there to make the blobHelper happy + * and should be removed. + */ + CacheMntPacket(Request *_req, MemCmd _cmd) + : Packet(_req, _cmd) + { + if (req->hasPaddr()) { + addr = req->getPaddr(); + flags.set(VALID_ADDR); + } + if (req->hasSize()) { + size = req->getSize(); + flags.set(VALID_SIZE); + } + } + + ~CacheMntPacket() {} + + // Special control flags + // express snoop is only applicable to requests + void setExpressSnoop() { flags.set(EXPRESS_SNOOP); } + bool isExpressSnoop() const { return flags.isSet(EXPRESS_SNOOP); } + +}; + +/** Convenience typedefs; */ +typedef MemMapPacket* MemMapPacketPtr; +typedef CacheMntPacket* CacheMntPacketPtr; + +/** Note that the pure packet should not be used and will be removed. */ +typedef Packet* PacketPtr; + +typedef std::list MemMapPacketList; + #endif //__MEM_PACKET_HH diff -r 6548721032fa -r 042f38a2e360 src/mem/packet_queue.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/packet_queue.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,389 @@ +/* + * 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: Geza Lore + * Andreas Hansson + */ + +#ifndef __MEM_PACKET_QUEUE_HH__ +#define __MEM_PACKET_QUEUE_HH__ + +/** + * @file Declaration of a simple packet queue that is associated with + * a port on which it attempts to send packets according to the time + * stamp given to them at insertion. The packet queue is responsible + * for the flow control of the port, but relies on the module + * notifying the queue when a transfer ends. + */ +#include + +#include "mem/port.hh" +#include "sim/eventq.hh" + +/** + * A packet queue is a class that holds deferred packets and later + * sends them using the associated slave port or master port. This + * class is the parent of both the classic and 4-phase packet queue. + */ +template +class PacketQueue +{ + + public: + + /** + * Get the type of the packets. + */ + typedef typename PROT::pkt_t pkt_t; + + /** + * A queue is required to provide a name, based on the port. + */ + virtual std::string name() const = 0; + + /** + * Insert a packet in the send queue + * + * @param pkt packet to insert + * @param absolute time (in ticks) to send packet + */ + virtual void insert(pkt_t* pkt, Tick when) = 0; + + /** + * Hook for draining the packet queue. + * + * @param An event which is used to signal back to the caller + * @returns A number indicating how many times process will be called + */ + virtual unsigned int drain(Event *de) = 0; + + /** + * Check the list of queued packets against the supplied + * request and potentially make it a response. + * + * @param pkt the packet accessing the queue + * @returns True if a mathching packet was found + */ + bool checkFunctional(pkt_t* pkt); + + protected: + + /** + * Create a packet queue linked to an event manager that is used + * for scheduling, and with a notification event that is used to + * signal when something is successfully sent from the queue. + * + * @param em an event manager used for scheduling sends and retrys + * @param sendNotificationEvent an event used to signal when + * packets are sent + */ + PacketQueue(EventManager* em, Event* sendNotificationEvent); + + /** + * Virtual desctructor since the class should be inherited. + */ + virtual ~PacketQueue(); + + /** The manager which is used for the event queue */ + EventManager* em; + + /** A deferred packet to transmit at or after a specific tick. */ + class DeferredPacket { + public: + Tick tick; ///< The tick when the packet is ready to transmit + pkt_t* pkt; ///< Pointer to the packet to transmit + DeferredPacket(Tick t, pkt_t* p) + : tick(t), pkt(p) + {} + }; + + /** Convenience typedef for a list of deferred packets. */ + typedef typename std::list DeferredPacketList; + + /** + * A list of outgoing packets that haven't been serviced yet. + */ + DeferredPacketList transmitList; + + /** + * If we need to drain, keep the drain event around until we're + * done. + */ + Event *drainEvent; + + /** + * The only way an owner could know that a packet has left the + * queue is through a call(back) to this event + */ + Event* sendNotificationEvent; + +}; + +/** + * A subclass of the packet queue that uses the classic send/retry + * port semantics for sending packets. + */ +template +class PacketQueueClassic : public PacketQueue +{ + + protected: + + /** + * Get the type of the packets from our parent. + */ + typedef typename PacketQueue::pkt_t pkt_t; + + /** + * Get the type of DeferredPackets and lists from our parent. + */ + typedef typename PacketQueue::DeferredPacket DeferredPacket; + typedef typename PacketQueue::DeferredPacketList DeferredPacketList; + + /** + * This function attempts to send deferred packets. Scheduled to + * be called in the future via sendEvent. + */ + void processSendEvent(); + + /** + * The actual function used to send the packet. This is + * implemented in the RespPacketQueue and ReqPacketQueue. + */ + virtual bool sendFunction(pkt_t* pkt) = 0; + + /** + * This class is used to implemented sendTiming() with a delay. When + * a delay is requested a the event is scheduled if it isn't already. + * When the event time expires it attempts to send the packet. + * If it cannot, the packet sent when recvRetry() is called. + **/ + EventWrapper sendEvent; + + /** + * Remember whether we've got a pending packet and are awaiting a + * retry. + */ + bool pendingPkt; + + public: + + PacketQueueClassic(EventManager* em, Event* sendNotificationEvent); + virtual ~PacketQueueClassic(); + + virtual void insert(pkt_t* pkt, Tick when); + + virtual unsigned int drain(Event *de); + + /** Attempt to send the packet at the head of the deferred packet + * list. Caller must guarantee that the deferred packet list is + * non-empty and that the head packet is scheduled for curTick() (or + * earlier). + */ + void sendDeferredPacket(); + +}; + +template +class PacketQueue4Phase : public PacketQueue +{ + + protected: + + /** + * Get the type of the packets. + */ + typedef typename PacketQueue::pkt_t pkt_t; + + typedef typename PacketQueue::DeferredPacket DeferredPacket; + typedef typename PacketQueue::DeferredPacketList DeferredPacketList; + + /** + * Function used by the 4-phase port to determine if the port used + * by the queue is idle or not. + */ + virtual bool portIsIdle() + { + return false; + } + + /** + * Begin the transfer of the packet at the head of the deferred + * packet list. Caller must guarantee that the deferred packet + * list is non-empty and that the head packet is scheduled for + * curTick() (or earlier). + */ + void beginDeferred(); + + EventWrapper beginDeferredEvent; + + /** + * The actual function used to begin the packet transfer. This is + * implemented in the RespPacketQueue4Phase and + * ReqPacketQueue4Phase. + */ + virtual void beginFunction(pkt_t* pkt) = 0; + + public: + + PacketQueue4Phase(EventManager* em, Event* sendNotificationEvent); + virtual ~PacketQueue4Phase(); + + virtual void insert(pkt_t* pkt, Tick when); + + virtual unsigned int drain(Event *de); + + /** signals the end of the current request or response */ + void endDeferred(); +}; + +template +class RespPacketQueue : public PacketQueueClassic +{ + private: + + /** + * Get the packet type from our base class. + */ + typedef typename PacketQueueClassic::pkt_t pkt_t; + + /** + * Get the type of the slave port based on the protocol. + */ + typedef SlavePort slave_port_t; + + /** The slave port used to send the packets. */ + slave_port_t& slavePort; + + public: + + /** The actual function used to send the packet. */ + bool sendFunction(pkt_t* pkt) + { + return slavePort->sendTimingResp(pkt); + } + + std::string name() const; + + public: + + RespPacketQueue(EventManager* em, slave_port_t& slavePort, + Event* sendRespNotificationEvent = NULL); + virtual ~RespPacketQueue(); + + +}; + +template +class RespPacketQueue4Phase : public PacketQueue4Phase +{ + private: + + /** + * Get the packet type from our base class. + */ + typedef typename PacketQueue4Phase::pkt_t pkt_t; + + /** + * Get the type of the slave port based on the protocol. + */ + typedef SlavePort slave_port_t; + + /** The slave port used to send the packets. */ + slave_port_t& slavePort; + + public: + + /** + * Determine if the port is idle by looking at the port response + * state. + */ + bool portIsIdle() + { + return slavePort->respLinkState() == PortInterface::IDLE; + } + + inline void beginFunction(pkt_t* pkt) + { + slavePort->beginResp(pkt); + } + + public: + + std::string name() const; + + RespPacketQueue4Phase(EventManager* em, slave_port_t& slavePort, + Event* sendRespNotificationEvent = NULL); + virtual ~RespPacketQueue4Phase(); +}; + +template +class ReqPacketQueue4Phase : public PacketQueue4Phase +{ + private: + + /** + * Get the packet type from our base class. + */ + typedef typename PacketQueue4Phase::pkt_t pkt_t; + + /** + * Get the type of the master port based on the protocol. + */ + typedef MasterPort master_port_t; + + /** The master port used to send the packets. */ + master_port_t& masterPort; + + public: + + /** + * Determine if the port is idle by looking at the port response + * state. + */ + bool portIsIdle() + { + return masterPort->reqLinkState() == PortInterface::IDLE; + } + + inline void beginFunction(pkt_t* pkt) + { + masterPort->beginReq(pkt); + } + + public: + + std::string name() const; + + ReqPacketQueue4Phase(EventManager* em, master_port_t& masterPort, + Event* sendRespNotificationEvent = NULL); + virtual ~ReqPacketQueue4Phase(); +}; + +#endif // __MEM_PACKET_QUEUE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/packet_queue.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/packet_queue.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,350 @@ +/* + * 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: Geza Lore + * Andreas Hansson + */ + +#include "mem/packet_queue.hh" + +using namespace std; + + +template +PacketQueue::PacketQueue(EventManager* em, Event* sendNotificationEvent) + : em(em), drainEvent(NULL), sendNotificationEvent(sendNotificationEvent) +{ +} + +template +PacketQueue::~PacketQueue() +{ +} + +template +bool +PacketQueue::checkFunctional(pkt_t* pkt) +{ + typename DeferredPacketList::iterator i = transmitList.begin(); + bool found = false; + + while (!found && i != transmitList.end()) { + pkt_t* target = i->pkt; + // if the target contains data, and it overlaps the request, + // we need to update data, that also means we are done + if (pkt->checkFunctional(target)) { + found = true; + pkt->makeResponse(); + } + ++i; + } + + return found; +} + +template +PacketQueueClassic::PacketQueueClassic(EventManager* em, + Event* sendNotificationEvent) + : PacketQueue(em, sendNotificationEvent), + sendEvent(this), pendingPkt(false) +{ +} + +template +PacketQueueClassic::~PacketQueueClassic() +{ +} + +template +void +PacketQueueClassic::insert(pkt_t* pkt, Tick when) +{ + assert(when >= curTick()); + + // In a template definition, unqualified names will no longer find + // members of a dependent base, use this-> to make it qualified. + + // Nothing is on the list: add it and schedule an event + if (this->transmitList.empty() || when < this->transmitList.front().tick) { + + this->transmitList.push_front(DeferredPacket(when, pkt)); + + if (!pendingPkt) { + if (!sendEvent.scheduled()) { + this->em->schedule(sendEvent, when); + } else if (sendEvent.when() > when) { + this->em->reschedule(sendEvent, when); + } + } + } else if (when >= this->transmitList.back().tick) { + // list is non-empty & this belongs at the end + this->transmitList.push_back(DeferredPacket(when, pkt)); + } else { + // this belongs in the middle somewhere, find the right spot + typename DeferredPacketList::iterator i = + this->transmitList.begin(); + + // already checked for insertion at front, and list not being + // empty + i++; + + while (i != this->transmitList.end() && i->tick <= when) + ++i; + this->transmitList.insert(i, DeferredPacket(when, pkt)); + } +} + + +template +void +PacketQueueClassic::processSendEvent() +{ + assert(!pendingPkt); + sendDeferredPacket(); +} + +template +unsigned int +PacketQueueClassic::drain(Event *de) +{ + if (this->transmitList.empty() && !sendEvent.scheduled()) + return 0; + this->drainEvent = de; + return 1; +} + +template +void +PacketQueueClassic::sendDeferredPacket() +{ + // take packet off list here; if recvTiming() on the other side + // calls sendTiming() back on us (like SimpleTimingCpu does), then + // we get confused by having a non-active packet on transmitList + DeferredPacket dp = this->transmitList.front(); + this->transmitList.pop_front(); + + bool success = sendFunction(dp.pkt); + + if (success) { + if (this->sendNotificationEvent != NULL) + this->em->schedule(this->sendNotificationEvent, curTick() + 1); + + if (!this->transmitList.empty() && !sendEvent.scheduled()) { + Tick time = this->transmitList.front().tick; + this->em->schedule(sendEvent, time <= curTick() ? + curTick() + 1 : time); + } + + if (this->transmitList.empty() && this->drainEvent != NULL && + !sendEvent.scheduled()) { + this->drainEvent->process(); + this->drainEvent = NULL; + } + } else { + // Unsuccessful, need to put back on transmitList. Callee + // should not have messed with it (since it didn't accept that + // packet), so we can just push it back on the front. + assert(!sendEvent.scheduled()); + this->transmitList.push_front(dp); + } + + pendingPkt = !success; +} + +template +PacketQueue4Phase::PacketQueue4Phase(EventManager* em, + Event* sendNotificationEvent) + : PacketQueue(em, sendNotificationEvent), + beginDeferredEvent(this) +{ +} + +template +PacketQueue4Phase::~PacketQueue4Phase() +{ +} + +template +void +PacketQueue4Phase::insert(pkt_t* pkt, Tick when) +{ + assert(when >= curTick()); + + // list is empty, or we have a packet that is due before the first + // one + if (this->transmitList.empty() || when < this->transmitList.front().tick) { + + this->transmitList.push_front(DeferredPacket(when, pkt)); + + if (portIsIdle()) { + if (!beginDeferredEvent.scheduled()) { + this->em->schedule(beginDeferredEvent, when); + } else if (beginDeferredEvent.when() > when) { + this->em->reschedule(beginDeferredEvent, when); + } + } + // else it will be scheduled once the port goes from busy + // to idle + } else if (when >= this->transmitList.back().tick) { + // list is non-empty & this belongs at the end + this->transmitList.push_back(DeferredPacket(when, pkt)); + } else { + // this belongs in the middle somewhere, find the right spot + typename DeferredPacketList::iterator i = this->transmitList.begin(); + // already checked for insertion at front, and list not being + // empty + i++; + + while (i != this->transmitList.end() && i->tick <= when) + ++i; + this->transmitList.insert(i, DeferredPacket(when, pkt)); + } +} + +template +unsigned int +PacketQueue4Phase::drain(Event *de) +{ + if (this->transmitList.empty() && !beginDeferredEvent.scheduled()) + return 0; + this->drainEvent = de; + return 1; +} + + +template +void +PacketQueue4Phase::beginDeferred() +{ + // take packet off list and call the appropriate begin req/resp + // function + DeferredPacket dp = this->transmitList.front(); + this->transmitList.pop_front(); + + beginFunction(dp.pkt); +} + +template +void +PacketQueue4Phase::endDeferred() +{ + // end the current req/resp + + // notify the owner if we are supposed to do so + if (this->sendNotificationEvent != NULL) + this->em->schedule(this->sendNotificationEvent, curTick() + 1); + + // if there is more to do and it is time to do it, schedule the + // next begin + if (!this->transmitList.empty() && !beginDeferredEvent.scheduled()) { + Tick time = this->transmitList.front().tick; + this->em->schedule(beginDeferredEvent, time <= curTick() ? + curTick() + 1 : time); + } + + // if nothing is waiting, or ongoing, and we were asked to drain, + // signal that it is now done + if (this->transmitList.empty() && this->drainEvent != NULL && + !beginDeferredEvent.scheduled()) { + this->drainEvent->process(); + this->drainEvent = NULL; + } +} + +template +RespPacketQueue::RespPacketQueue(EventManager* em, + slave_port_t& slavePort, + Event* sendNotificationEvent) : + PacketQueueClassic(em, sendNotificationEvent), + slavePort(slavePort) +{ +} + +template +RespPacketQueue::~RespPacketQueue() +{ +} + +template +string +RespPacketQueue::name() const +{ + return slavePort.name() + "-queue"; +} + +template +RespPacketQueue4Phase::RespPacketQueue4Phase(EventManager* em, + slave_port_t& slavePort, + Event* sendNotificationEvent) + : PacketQueue4Phase(em, sendNotificationEvent), + slavePort(slavePort) +{ +} + +template +RespPacketQueue4Phase::~RespPacketQueue4Phase() +{ +} + +template +std::string +RespPacketQueue4Phase::name() const +{ + return slavePort.name() + "-queue"; +} + +template +ReqPacketQueue4Phase::ReqPacketQueue4Phase(EventManager* em, + master_port_t& masterPort, + Event* sendNotificationEvent) + : PacketQueue4Phase(em, sendNotificationEvent), + masterPort(masterPort) +{ +} + +template +ReqPacketQueue4Phase::~ReqPacketQueue4Phase() +{ +} + +template +std::string +ReqPacketQueue4Phase::name() const +{ + return masterPort.name() + "-queue"; +} + +/** Packet queue template instantiations. */ +template class PacketQueue; +template class PacketQueue; + +template class PacketQueueClassic; +template class RespPacketQueue; + +template class PacketQueue4Phase; +template class RespPacketQueue4Phase; +template class ReqPacketQueue4Phase; diff -r 6548721032fa -r 042f38a2e360 src/mem/physical.hh --- a/src/mem/physical.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/physical.hh Fri Aug 05 18:14:06 2011 +0100 @@ -34,53 +34,32 @@ #ifndef __PHYSICAL_MEMORY_HH__ #define __PHYSICAL_MEMORY_HH__ -#include -#include - -#include "base/range.hh" #include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/tport.hh" +#include "mem/module_interface.hh" +#include "mem/packet_queue.hh" #include "params/PhysicalMemory.hh" #include "sim/eventq.hh" -// -// Functional model for a contiguous block of physical memory. (i.e. RAM) -// -class PhysicalMemory : public MemObject +/** + * A contiguous block of memory. (i.e. RAM) with a simple first-come, + * first-served policy and an unlimited number of outstanding + * transactions. + */ +class PhysicalMemory : public MemObject, public MemMapSlaveInterface { - protected: - - class MemoryPort : public SimpleTimingPort - { - PhysicalMemory *memory; - - public: - - MemoryPort(const std::string &_name, PhysicalMemory *_memory); - - protected: - - virtual Tick recvAtomic(PacketPtr pkt); - - virtual void recvFunctional(PacketPtr pkt); - - virtual void recvStatusChange(Status status); - - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); - - virtual unsigned deviceBlockSize() const; - }; - - int numPorts; - private: + // prevent copying of a MainMemory object PhysicalMemory(const PhysicalMemory &specmem); const PhysicalMemory &operator=(const PhysicalMemory &specmem); + MemMapPacketPtr currReqPkt; + + void acceptReq(); + + EventWrapper acceptReqEvent; + protected: class LockedAddr { @@ -118,12 +97,12 @@ // inline a quick check for an empty locked addr list (hopefully // the common case), and do the full list search (if necessary) in // this out-of-line function - bool checkLockedAddrList(PacketPtr pkt); + bool checkLockedAddrList(MemMapPacketPtr pkt); // Record the address of a load-locked operation so that we can // clear the execution context's lock flag if a matching store is // performed - void trackLoadLocked(PacketPtr pkt); + void trackLoadLocked(MemMapPacketPtr pkt); // Compare a store address with any locked addresses so we can // clear the lock flag appropriately. Return value set to 'false' @@ -132,7 +111,7 @@ // requesting execution context), 'true' otherwise. Note that // this method must be called on *all* stores since even // non-conditional stores must clear any matching lock addresses. - bool writeOK(PacketPtr pkt) { + bool writeOK(MemMapPacketPtr pkt) { Request *req = pkt->req; if (lockedAddrList.empty()) { // no locked addrs: nothing to check, store_conditional fails @@ -150,17 +129,27 @@ uint8_t *pmemAddr; Tick lat; Tick lat_var; - std::vector ports; - typedef std::vector::iterator PortIterator; + + MemMapSlavePort port; + RespPacketQueue4Phase respPktQueue; uint64_t _size; uint64_t _start; - public: - uint64_t size() { return _size; } - uint64_t start() { return _start; } public: + uint64_t size() const { return _size; } + uint64_t start() const { return _start; } + + public: + typedef PhysicalMemoryParams Params; + + /** + * Create an enum for the slave id so that our subclasses can + * extend the list in a reasonable manner. + */ + enum SlaveId {MEM_SLAVE_ID, N_SLAVE_ID}; + PhysicalMemory(const Params *p); virtual ~PhysicalMemory(); @@ -171,17 +160,28 @@ } public: - unsigned deviceBlockSize() const; - void getAddressRanges(AddrRangeList &resp, bool &snoop); - virtual Port *getPort(const std::string &if_name, int idx = -1); + + void recvDebug(MemMapPacketPtr pkt, int slave_id); + + Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + + void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + void endResp(MemMapPacketPtr resp_pkt, int slave_id); + + AddrRangeList getAddrRanges(int slave_id) const; + + unsigned getBlockSize(int slave_id) const; + void virtual init(); unsigned int drain(Event *de); + virtual void getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const; + protected: - Tick doAtomicAccess(PacketPtr pkt); - void doFunctionalAccess(PacketPtr pkt); - virtual Tick calculateLatency(PacketPtr pkt); - void recvStatusChange(Port::Status status); + + virtual Tick calculateLatency(MemMapPacketPtr pkt); public: virtual void serialize(std::ostream &os); diff -r 6548721032fa -r 042f38a2e360 src/mem/physical.cc --- a/src/mem/physical.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/physical.cc Fri Aug 05 18:14:06 2011 +0100 @@ -53,6 +53,7 @@ #include #include +#include "arch/isa_traits.hh" #include "arch/registers.hh" #include "base/intmath.hh" #include "base/misc.hh" @@ -64,11 +65,17 @@ #include "mem/physical.hh" #include "sim/eventq.hh" + using namespace std; using namespace TheISA; PhysicalMemory::PhysicalMemory(const Params *p) - : MemObject(p), pmemAddr(NULL), lat(p->latency), lat_var(p->latency_var), + : MemObject(p), + currReqPkt(NULL), + acceptReqEvent(this), + pmemAddr(NULL), lat(p->latency), lat_var(p->latency_var), + port(*this, this, "port"), + respPktQueue(this, port), _size(params()->range.size()), _start(params()->range.start) { if (size() % TheISA::PageBytes != 0) @@ -77,7 +84,6 @@ if (params()->null) return; - if (params()->file == "") { int map_flags = MAP_ANON | MAP_PRIVATE; pmemAddr = (uint8_t *)mmap(NULL, size(), @@ -102,19 +108,17 @@ //If requested, initialize all the memory to 0 if (p->zero) memset(pmemAddr, 0, size()); + + // register the one port we have + regPort(&port); } void PhysicalMemory::init() { - if (ports.size() == 0) { + if (!port.isConnected()) { fatal("PhysicalMemory object %s is unconnected!", name()); } - - for (PortIterator pi = ports.begin(); pi != ports.end(); ++pi) { - if (*pi) - (*pi)->sendStatusChange(Port::RangeChange); - } } PhysicalMemory::~PhysicalMemory() @@ -123,15 +127,8 @@ munmap((char*)pmemAddr, size()); } -unsigned -PhysicalMemory::deviceBlockSize() const -{ - //Can accept anysize request - return 0; -} - Tick -PhysicalMemory::calculateLatency(PacketPtr pkt) +PhysicalMemory::calculateLatency(MemMapPacketPtr pkt) { Tick latency = lat; if (lat_var != 0) @@ -144,7 +141,7 @@ // Add load-locked to tracking list. Should only be called if the // operation is a load and the LLSC flag is set. void -PhysicalMemory::trackLoadLocked(PacketPtr pkt) +PhysicalMemory::trackLoadLocked(MemMapPacketPtr pkt) { Request *req = pkt->req; Addr paddr = LockedAddr::mask(req->getPaddr()); @@ -175,7 +172,7 @@ // conflict with locked addresses, and for success/failure of store // conditionals. bool -PhysicalMemory::checkLockedAddrList(PacketPtr pkt) +PhysicalMemory::checkLockedAddrList(MemMapPacketPtr pkt) { Request *req = pkt->req; Addr paddr = LockedAddr::mask(req->getPaddr()); @@ -243,6 +240,7 @@ default: \ DPRINTF(MemoryAccess, "%s of size %i on address 0x%x\n", \ A, pkt->getSize(), pkt->getAddr()); \ + DDUMP(MemoryAccess, pkt->getPtr(), pkt->getSize());\ } \ } while (0) @@ -253,7 +251,7 @@ #endif Tick -PhysicalMemory::doAtomicAccess(PacketPtr pkt) +PhysicalMemory::recvAtomic(MemMapPacketPtr pkt, int slave_id) { assert(pkt->getAddr() >= start() && pkt->getAddr() + pkt->getSize() <= start() + size()); @@ -330,136 +328,110 @@ return calculateLatency(pkt); } +void +PhysicalMemory::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + pkt->pushLabel(name()); -void -PhysicalMemory::doFunctionalAccess(PacketPtr pkt) -{ - assert(pkt->getAddr() >= start() && - pkt->getAddr() + pkt->getSize() <= start() + size()); + if (!respPktQueue.checkFunctional(pkt)) { + assert(pkt->getAddr() >= start() && + pkt->getAddr() + pkt->getSize() <= start() + size()); - uint8_t *hostAddr = pmemAddr + pkt->getAddr() - start(); + uint8_t *hostAddr = pmemAddr + pkt->getAddr() - start(); - if (pkt->isRead()) { - if (pmemAddr) - memcpy(pkt->getPtr(), hostAddr, pkt->getSize()); - TRACE_PACKET("Read"); - pkt->makeAtomicResponse(); - } else if (pkt->isWrite()) { - if (pmemAddr) - memcpy(hostAddr, pkt->getPtr(), pkt->getSize()); - TRACE_PACKET("Write"); - pkt->makeAtomicResponse(); - } else if (pkt->isPrint()) { - Packet::PrintReqState *prs = - dynamic_cast(pkt->senderState); - // Need to call printLabels() explicitly since we're not going - // through printObj(). - prs->printLabels(); - // Right now we just print the single byte at the specified address. - ccprintf(prs->os, "%s%#x\n", prs->curPrefix(), *hostAddr); - } else { - panic("PhysicalMemory: unimplemented functional command %s", - pkt->cmdString()); - } -} - - -Port * -PhysicalMemory::getPort(const std::string &if_name, int idx) -{ - // Accept request for "functional" port for backwards compatibility - // with places where this function is called from C++. I'd prefer - // to move all these into Python someday. - if (if_name == "functional") { - return new MemoryPort(csprintf("%s-functional", name()), this); - } - - if (if_name != "port") { - panic("PhysicalMemory::getPort: unknown port %s requested", if_name); - } - - if (idx >= (int)ports.size()) { - ports.resize(idx + 1); - } - - if (ports[idx] != NULL) { - panic("PhysicalMemory::getPort: port %d already assigned", idx); - } - - MemoryPort *port = - new MemoryPort(csprintf("%s-port%d", name(), idx), this); - - ports[idx] = port; - return port; -} - - -void -PhysicalMemory::recvStatusChange(Port::Status status) -{ -} - -PhysicalMemory::MemoryPort::MemoryPort(const std::string &_name, - PhysicalMemory *_memory) - : SimpleTimingPort(_name, _memory), memory(_memory) -{ } - -void -PhysicalMemory::MemoryPort::recvStatusChange(Port::Status status) -{ - memory->recvStatusChange(status); -} - -void -PhysicalMemory::MemoryPort::getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) -{ - memory->getAddressRanges(resp, snoop); -} - -void -PhysicalMemory::getAddressRanges(AddrRangeList &resp, bool &snoop) -{ - snoop = false; - resp.clear(); - resp.push_back(RangeSize(start(), size())); -} - -unsigned -PhysicalMemory::MemoryPort::deviceBlockSize() const -{ - return memory->deviceBlockSize(); -} - -Tick -PhysicalMemory::MemoryPort::recvAtomic(PacketPtr pkt) -{ - return memory->doAtomicAccess(pkt); -} - -void -PhysicalMemory::MemoryPort::recvFunctional(PacketPtr pkt) -{ - pkt->pushLabel(memory->name()); - - if (!checkFunctional(pkt)) { - // Default implementation of SimpleTimingPort::recvFunctional() - // calls recvAtomic() and throws away the latency; we can save a - // little here by just not calculating the latency. - memory->doFunctionalAccess(pkt); + if (pkt->isRead()) { + if (pmemAddr) + memcpy(pkt->getPtr(), hostAddr, pkt->getSize()); + TRACE_PACKET("Read"); + pkt->makeAtomicResponse(); + } else if (pkt->isWrite()) { + if (pmemAddr) + memcpy(hostAddr, pkt->getPtr(), pkt->getSize()); + TRACE_PACKET("Write"); + pkt->makeAtomicResponse(); + } else if (pkt->isPrint()) { + Packet::PrintReqState *prs = + dynamic_cast(pkt->senderState); + // Need to call printLabels() explicitly since we're not going + // through printObj(). + prs->printLabels(); + // Right now we just print the single byte at the specified address. + ccprintf(prs->os, "%s%#x\n", prs->curPrefix(), *hostAddr); + } else { + panic("PhysicalMemory: unimplemented functional command %s", + pkt->cmdString()); + } } pkt->popLabel(); } +AddrRangeList +PhysicalMemory::getAddrRanges(int slave_id) const +{ + AddrRangeList ranges; + ranges.push_back(RangeSize(start(), size())); + return ranges; +} + +unsigned +PhysicalMemory::getBlockSize(int slave_id) const +{ + // at the moment the bus only uses this to determine the maximum + // block size, so 0 is a safe default + return 0; +} + +void +PhysicalMemory::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + // deal with the request if it is not inhibited + if (!req_pkt->memInhibitAsserted()) { + // perform an atomic access which also turns the packet around + // to go back to requester if response expected + Tick latency = recvAtomic(req_pkt, slave_id); + if (req_pkt->isResponse()) { + respPktQueue.insert(req_pkt, curTick() + latency); + } + } + + // even in the case of an inhibited request we have to + // accept and end the request + + // keep track of the packet until we accept it + assert(currReqPkt == NULL); + currReqPkt = req_pkt; + + // schedule to accept after the request accept delay + schedule(acceptReqEvent, curTick()); +} + +void +PhysicalMemory::acceptReq() +{ + // consider the request to end here + port->endReq(currReqPkt); + + // our responsibility to delete packet if inhibited or no response + if (currReqPkt->memInhibitAsserted() || !currReqPkt->isResponse()) + delete currReqPkt; + + currReqPkt = NULL; +} + +void +PhysicalMemory::endResp(MemMapPacketPtr resp_pkt, int slave_id) +{ + // we are told that the response is over, so let the packet queue + // know it can get on with the next one + respPktQueue.endDeferred(); +} + unsigned int PhysicalMemory::drain(Event *de) { - int count = 0; - for (PortIterator pi = ports.begin(); pi != ports.end(); ++pi) { - count += (*pi)->drain(de); - } + int count = respPktQueue.drain(de); if (count) changeState(Draining); @@ -469,6 +441,15 @@ } void +PhysicalMemory::getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const +{ + r = 0x78; + g = 0xA4; + b = 0x93; +} + +void PhysicalMemory::serialize(ostream &os) { if (!pmemAddr) diff -r 6548721032fa -r 042f38a2e360 src/mem/port.hh --- a/src/mem/port.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/port.hh Fri Aug 05 18:14:06 2011 +0100 @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -25,278 +37,432 @@ * (INCLUDING 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 + * Authors: Andreas Hansson */ /** - * @file - * Port Object Declaration. Ports are used to interface memory objects to - * each other. They will always come in pairs, and we refer to the other - * port object as the peer. These are used to make the design more - * modular so that a specific interface between every type of objcet doesn't - * have to be created. + * @file Declaration of the abstract class Port and its two subclasses + * MasterPort and SlavePort. */ #ifndef __MEM_PORT_HH__ #define __MEM_PORT_HH__ -#include +#include "mem/protocol.hh" -#include "base/misc.hh" -#include "base/range.hh" -#include "base/types.hh" -#include "mem/packet.hh" -#include "mem/request.hh" -#include "sim/eventq.hh" - -/** This typedef is used to clean up the parameter list of - * getDeviceAddressRanges() and getPeerAddressRanges(). It's declared - * outside the Port object since it's also used by some mem objects. - * Eventually we should move this typedef to wherever Addr is - * defined. +/** + * Forward declaration of memobject to avoid circular dependencies. */ - -typedef std::list > AddrRangeList; -typedef std::list >::iterator AddrRangeIter; - -class EventQueue; class MemObject; /** - * Ports are used to interface memory objects to - * each other. They will always come in pairs, and we refer to the other - * port object as the peer. These are used to make the design more - * modular so that a specific interface between every type of objcet doesn't - * have to be created. - * - * Recv accesor functions are being called from the peer interface. - * Send accessor functions are being called from the device the port is - * associated with, and it will call the peer recv. accessor function. + * Ports are used to connect memory objects to each other, and also + * provide an interface for interactions between the objects. Ports + * alway come in pairs, and we refer to the other port object as the + * peer. A port is either a master or a slave and the peer is always + * of the opposite role. This applies at all levels in the protocol + * stack, e.g. FIFO master/slave on the link layer, packet + * master/slave on the network level, and memory-mapped master/slave + * on the transport level. A coherent master/coherent slave (e.g. a + * cache) has both a master and a slave port on each side, one memory + * mapped, and one cache extension port, amounting to four ports in + * total. The ports themselves only determine the structural + * organisation of the memory objects, and the syntax and semantics of + * the interactions are determined by protocol-specific interfaces. */ -class Port : public EventManager +class Port { - protected: - /** Descriptive name (for DPRINTF output) */ - mutable 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; - - /** A pointer to the MemObject that owns this port. This may not be set. */ - MemObject *owner; - - public: - /** - * Constructor. - * - * @param _name Port name for DPRINTF output. Should include name - * of memory system object to which the port belongs. - * @param _owner Pointer to the MemObject that owns this port. - * Will not necessarily be set. - */ - Port(const std::string &_name, MemObject *_owner); - - /** Return port name (for DPRINTF). */ - const std::string &name() const { return portName; } - - virtual ~Port(); - - // mey be better to use subclasses & RTTI? - /** Holds the ports status. Currently just that a range recomputation needs - * to be done. */ - enum Status { - RangeChange - }; - - void setName(const std::string &name) - { portName = name; } - - /** Function to set the pointer for the peer port. */ - virtual void setPeer(Port *port); - - /** Function to get the pointer to the peer port. */ - Port *getPeer() { return peer; } - - /** Function to set the owner of this port. */ - void setOwner(MemObject *_owner); - - /** Function to return the owner of this port. */ - MemObject *getOwner() { return owner; } - - /** Inform the peer port to delete itself and notify it's owner about it's - * demise. */ - void removeConn(); - - virtual bool isDefaultPort() const { return false; } - - bool isConnected() { return peer && !peer->isDefaultPort(); } protected: - /** These functions are protected because they should only be - * called by a peer port, never directly by any outside object. */ + /** + * Abstract base class for ports. + * + * @param owner Pointer to the structural owner of this port + * @param name Local name of the port that is also used for lookups + * @param id Port identifier and index to be used in master/slave arrays + */ + Port(MemObject* owner, const std::string& name, int id); - /** Called to recive a timing call from the peer port. */ - virtual bool recvTiming(PacketPtr pkt) = 0; + /** + * Virtual destructor due to inheritance. + */ + virtual ~Port(); - /** Called to recive a atomic call from the peer port. */ - virtual Tick recvAtomic(PacketPtr pkt) = 0; + /** A pointer to the MemObject that owns this port */ + MemObject* _owner; - /** Called to recive a functional call from the peer port. */ - virtual void recvFunctional(PacketPtr pkt) = 0; + /** Descriptive name */ + const std::string _name; - /** Called to recieve a status change from the peer port. */ - virtual void recvStatusChange(Status status) = 0; + /** Name including parent (and parents parent etc). */ + const std::string _path; - /** Called by a peer port if the send was unsuccesful, and had to - wait. This shouldn't be valid for response paths (IO Devices). - so it is set to panic if it isn't already defined. - */ - 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 - this function to be called, so it just returns 0. Anytthing that is - concerned with the size should just ignore that. - */ - virtual unsigned deviceBlockSize() const { return 0; } - - /** The peer port is requesting us to reply with a list of the ranges we - are responsible for. - @param resp is a list of ranges responded to - @param snoop is a list of ranges snooped - */ - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { panic("??"); } + /** + * An id of the this port to enable the interface behind it to + * distinguish between multiple incoming paths. This class does + * not limit the id to be unique within the object, protocol, or + * role, but typically this would be recommended (e.g. two master + * ports of the same protocol would have different ids) + */ + const int _id; public: - /** Function called by associated memory device (cache, memory, iodevice) - in order to send a timing request to the port. Simply calls the peer - 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 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(PacketPtr pkt) { return peer->recvTiming(pkt); } + /** + * Function to return the owner of this port. + * @return A non-null MemObject pointer + */ + MemObject* getOwner() const; - /** 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. + /** + * Return port name including the path. + * @return A string with the name also including the parent */ - Tick sendAtomic(PacketPtr pkt) - { return peer->recvAtomic(pkt); } + const std::string name() const; - /** Function called by the associated device to send a functional access, - an access in which the data is instantly updated everywhere in the - memory system, without affecting the current state of any block or - moving the block. - */ - void sendFunctional(PacketPtr pkt) - { return peer->recvFunctional(pkt); } + /** + * Return the port name without information about the parent. + * @return A string name of the port on its own + */ + std::string shortName() const; - /** Called by the associated device to send a status change to the device - connected to the peer interface. - */ - void sendStatusChange(Status status) {peer->recvStatusChange(status); } + /** + * Get the port id. + * @return An integer indicating the port id or -1 if not set. + */ + int getId() const; - /** When a timing access doesn't return a success, some time later the - Retry will be sent. - */ - void sendRetry() { return peer->recvRetry(); } + /** + * Tell if the port is connected or not. + * @return True if the port is connected + */ + virtual bool isConnected() const = 0; +}; - /** Called by the associated device if it wishes to find out the blocksize - of the device on attached to the peer port. - */ - unsigned peerBlockSize() const { return peer->deviceBlockSize(); } +/** forward declaration */ +class BaseMasterPort; - /** Called by the associated device if it wishes to find out the address - ranges connected to the peer ports devices. - */ - void getPeerAddressRanges(AddrRangeList &resp, bool &snoop) - { peer->getDeviceAddressRanges(resp, snoop); } +/** + * Specialisation of a protocol-agnostic port that acts as a slave, + * i.e. connects to a master. + */ +class BaseSlavePort : public Port { - /** This function is a wrapper around sendFunctional() - that breaks a larger, arbitrarily aligned access into - appropriate chunks. The default implementation can use - getBlockSize() to determine the block size and go from there. - */ - virtual void readBlob(Addr addr, uint8_t *p, int size); + protected: - /** This function is a wrapper around sendFunctional() - that breaks a larger, arbitrarily aligned access into - appropriate chunks. The default implementation can use - getBlockSize() to determine the block size and go from there. - */ - virtual void writeBlob(Addr addr, uint8_t *p, int size); + /** + * Construct a slave port. We need a pointer to the memory object + * owning this port. + * + * @param owner Pointer to the MemObject that owns this port. + * @param name Local name of the port that is also used for lookups + * @param id Port identifier and index to be used + */ + BaseSlavePort(MemObject* owner, const std::string& name, int id = -1); - /** Fill size bytes starting at addr with byte value val. This - should not need to be virtual, since it can be implemented in - terms of writeBlob(). However, it shouldn't be - performance-critical either, so it could be if we wanted to. - */ - virtual void memsetBlob(Addr addr, uint8_t val, int size); + /** + * Destruct a slave port. + */ + virtual ~BaseSlavePort(); - /** Inject a PrintReq for the given address to print the state of - * that address throughout the memory system. For debugging. + /** A potential master port that this slave port is connected to */ + BaseMasterPort* _masterPort; + + public: + + /** + * Get the master port the slave port is connected to. + * @return A pointer to the master port. */ - void printAddr(Addr a); + BaseMasterPort* getMasterPort() const; + + /** + * Find out if the port is connected. + * @return true if the port is connected + */ + bool isConnected() const; +}; + +/** forward declaration */ +template +class MasterPort; + +/** + * A slave port with a specific protocol. A slave port has a slave + * port interface corresponding to its protocol. This port interface + * is used to access the connected master. A slave port also has a + * slave module interface that implements the actual slave + * functionality. Note that the interface and owner typically are the + * same, but it is possible to separate the structural owner from the + * implementation of the interface. + */ +template +class SlavePort : public BaseSlavePort { + + public: + + /** + * Type of the slave interface this port passes to a master port + * on binding. + */ + typedef typename PROT::slave_if_t slave_if_t; + + /** + * Type of the slave port interface. + */ + typedef typename PROT::slave_port_if_t slave_port_if_t; + + /** + * Construct a slave port associated with a specific slave + * interface. The owner is for structural information, i.e. what + * object contains and owns this port. The slaveInterface is for + * the interaction with a master. The owner and slave interface + * are typically the same, but could be separate with the owner + * just encapsulating and enumerating the ports and the interface + * being in a separate module or class. + * + * @param slave_interface the slave interface + * @param owner Pointer to the MemObject that owns this port + * @param name Local name of the port that is also used for lookups + * @param id Port identifier and index to be used + */ + SlavePort(slave_if_t& slave_interface, MemObject* owner, + const std::string& name, int id = -1); + + /** + * Destruct a slave port. + */ + ~SlavePort(); + + /** + * Bind to a master port. This will only succeed if the master + * port has a compatible interface. Note that the base slave port + * does not implement any binding functionality, as this is left + * for the protocol specific slave port. + * + * @param master_port A master port through which we access a + * compatible module interface + */ + void bind(MasterPort* master_port); + + /** + * Overload the pointer operator to access the interface of the + * port. This is done exactly the same way in TLM2 sockets. + * + * @return a const pointer to the port interface + */ + inline slave_port_if_t* operator->() + { + return &slavePortInterface; + } + + /** + * Overload the pointer operator to access the interface of the + * port. This is done exactly the same way in TLM2 sockets. + * + * @return a const pointer to the port interface + */ + inline const slave_port_if_t* operator->() const + { + return &slavePortInterface; + } + + /** + * Get a pointer to the port interface, for use during binding. + * + * @return a pointer to the slave port interface + */ + slave_port_if_t* getPortInterface() { + return &slavePortInterface; + } + + /** + * Get a pointer to the interface, for use during binding. + * + * @return a pointer to the slave interface + */ + slave_if_t* getInterface() const { + return &slaveInterface; + } private: - /** Internal helper function for read/writeBlob(). + /** + * The slave that this port belongs to and that implements the + * actual exported interface. We never use this ourselves, merely + * pass it to the master port on binding. */ - void blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd); + slave_if_t& slaveInterface; + + /** + * This is the interface that is used by our owner to communicate to the + * exported interface of the master. + */ + slave_port_if_t slavePortInterface; }; -/** A simple functional port that is only meant for one way communication to - * physical memory. It is only meant to be used to load data into memory before - * the simulation begins. +/** + * Specialisation of a protocol-agnostic port that acts as a master, + * i.e. connects to a slave. */ - -class FunctionalPort : public Port -{ - public: - FunctionalPort(const std::string &_name, MemObject *_owner = NULL) - : Port(_name, _owner) - {} +class BaseMasterPort : public Port { protected: - virtual bool recvTiming(PacketPtr pkt) { panic("FuncPort is UniDir"); - M5_DUMMY_RETURN } - virtual Tick recvAtomic(PacketPtr pkt) { panic("FuncPort is UniDir"); - M5_DUMMY_RETURN } - virtual void recvFunctional(PacketPtr pkt) { panic("FuncPort is UniDir"); } - virtual void recvStatusChange(Status status) {} + + /** + * Construct a base master port. + * + * @param owner Pointer to the MemObject that owns this port. + * @param name Local name of the port that is also used for lookups + * @param id Port identifier and index to be used in master/slave arrays + */ + BaseMasterPort(MemObject* owner, const std::string& name, int id = -1); + + /** + * Destruct a base master port. + */ + virtual ~BaseMasterPort(); + + /** A potential slave port that this master port is connected to */ + BaseSlavePort* _slavePort; public: - /** a write function that also does an endian conversion. */ - template - inline void writeHtoG(Addr addr, T d); - /** a read function that also does an endian conversion. */ - template - inline T readGtoH(Addr addr); + /** + * Bind this master port to a base slave port. By doing this the + * master is also updating the slave port. This is called from + * Python during the system instantiation and elaboration using + * the base classes. The actual master port is responsible for + * implementing this function and take the protocol interface + * binding into account. + * + * @param slave_port The slave port to bind to + */ + virtual void bind(BaseSlavePort* slave_port) = 0; - template - inline void write(Addr addr, T d) + /** + * Get the slave port the master port is connected to. + * @return A pointer to the slave port. + */ + BaseSlavePort* getSlavePort() const; + + /** + * Find out if the port is connected. + * @return true if the port is connected + */ + bool isConnected() const; + +}; + +template +class MasterPort : public BaseMasterPort { + + public: + + /** + * Type of the master interface this port passes to a slave port + * on binding. + */ + typedef typename PROT::master_if_t master_if_t; + + /** + * Type of the master port interface. + */ + typedef typename PROT::master_port_if_t master_port_if_t; + + /** + * Construct a master port associated with a specific master + * interface. The owner is for structural information, i.e. what + * object contains and owns this port. The master interface is for + * the interaction with a slave. The owner and master interface + * are typically the same, but could be separate with the owner + * just encapsulating and enumerating the ports and the interface + * being in a separate module or class. + * + * @param master_interface the master interface of this port + * @param owner Pointer to the MemObject that owns this port + * @param name Local name of the port that is also used for lookups + * @param id Port identifier and index to be used + */ + MasterPort(master_if_t& master_interface, MemObject* owner, + const std::string& name, int id = -1); + + /** + * Destruct the master port. + */ + ~MasterPort(); + + /** + * Bind to a base slave port. This will only succeed if the slave + * port has a compatible interface. + * + * @param slave_port The slave port to bind to + */ + void bind(BaseSlavePort* slave_port); + + /** + * Overload the pointer operator to access the interface of the + * port. This is done exactly the same way in TLM2 sockets. + * + * @return a const pointer to the port interface + */ + inline master_port_if_t* operator->() { - writeBlob(addr, (uint8_t*)&d, sizeof(T)); + return &masterPortInterface; } - template - inline T read(Addr addr) + /** + * Overload the pointer operator to access the interface of the + * port. This is done exactly the same way in TLM2 sockets. + * + * @return a const pointer to the port interface + */ + inline const master_port_if_t* operator->() const { - T d; - readBlob(addr, (uint8_t*)&d, sizeof(T)); - return d; + return &masterPortInterface; } + + /** + * Get a pointer to the port interface, for use during binding. + * + * @return a pointer to the slave port interface + */ + master_port_if_t* getPortInterface() { + return &masterPortInterface; + } + + /** + * Get a pointer to the interface, for use during binding. + * + * @return a pointer to the master interface + */ + master_if_t* getInterface() const { + return &masterInterface; + } + + private: + + /** + * The master that this port belongs to and that implements the + * actual interface. + */ + master_if_t& masterInterface; + + /** + * This is the interface that is used by our owner to communicate to the + * exported interface of the master. + */ + master_port_if_t masterPortInterface; + }; + +/** Convenience typedef for now. */ +typedef SlavePort MemMapSlavePort; +typedef MasterPort MemMapMasterPort; + +typedef SlavePort CacheMntSlavePort; +typedef MasterPort CacheMntMasterPort; + #endif //__MEM_PORT_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/port.cc --- a/src/mem/port.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/mem/port.cc Fri Aug 05 18:14:06 2011 +0100 @@ -1,6 +1,15 @@ /* - * Copyright (c) 2002-2005 The Regents of The University of Michigan - * All rights reserved. + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -25,75 +34,19 @@ * (INCLUDING 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 + * Authors: Andreas Hansson */ /** * @file * Port object definitions. */ -#include +#include "base/trace.hh" +#include "mem/port.hh" +#include "mem/mem_object.hh" -#include "base/chunk_generator.hh" -#include "base/trace.hh" -#include "mem/mem_object.hh" -#include "mem/port.hh" - -class DefaultPeerPort : public Port -{ - protected: - void blowUp() const - { - fatal("%s: Unconnected port!", peer->name()); - } - - public: - DefaultPeerPort() - : Port("default_port", NULL) - { } - - bool recvTiming(PacketPtr) - { - blowUp(); - return false; - } - - Tick recvAtomic(PacketPtr) - { - blowUp(); - return 0; - } - - void recvFunctional(PacketPtr) - { - blowUp(); - } - - void recvStatusChange(Status) - { - blowUp(); - } - - unsigned - deviceBlockSize() const - { - blowUp(); - return 0; - } - - void getDeviceAddressRanges(AddrRangeList &, bool &) - { - blowUp(); - } - - bool isDefaultPort() const { return true; } -}; - -DefaultPeerPort defaultPeerPort; - -Port::Port(const std::string &_name, MemObject *_owner) - : EventManager(_owner), portName(_name), peer(&defaultPeerPort), - owner(_owner) +Port::Port(MemObject* owner, const std::string& name, int id) + : _owner(owner), _name(name), _id(id) { } @@ -101,76 +54,155 @@ { } -void -Port::setPeer(Port *port) +MemObject* +Port::getOwner() const { - DPRINTF(Config, "setting peer to %s\n", port->name()); - - peer = port; + return _owner; } -void -Port::setOwner(MemObject *_owner) +const std::string +Port::name() const { - eventq = _owner->queue(); - owner = _owner; + // the long name is composed of the owner name and our "default" + // separator this should be defined somewhere central so that it + // is uniform + return _owner->name() + "_" + shortName(); } -void -Port::removeConn() +std::string +Port::shortName() const { - if (peer->getOwner()) - peer->getOwner()->deletePortRefs(peer); - peer = NULL; + return _name; } -void -Port::blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) +int +Port::getId() const { - Request req; - - for (ChunkGenerator gen(addr, size, peerBlockSize()); - !gen.done(); gen.next()) { - req.setPhys(gen.addr(), gen.size(), 0); - Packet pkt(&req, cmd, Packet::Broadcast); - pkt.dataStatic(p); - sendFunctional(&pkt); - p += gen.size(); - } + return _id; } -void -Port::writeBlob(Addr addr, uint8_t *p, int size) +BaseSlavePort::BaseSlavePort(MemObject* owner, const std::string& name, + int id) : + Port(owner, name, id), _masterPort(NULL) { - blobHelper(addr, p, size, MemCmd::WriteReq); } -void -Port::readBlob(Addr addr, uint8_t *p, int size) +BaseSlavePort::~BaseSlavePort() { - blobHelper(addr, p, size, MemCmd::ReadReq); } -void -Port::memsetBlob(Addr addr, uint8_t val, int size) +BaseMasterPort* +BaseSlavePort::getMasterPort() const { - // quick and dirty... - uint8_t *buf = new uint8_t[size]; - - std::memset(buf, val, size); - blobHelper(addr, buf, size, MemCmd::WriteReq); - - delete [] buf; + if (_masterPort == NULL) + panic("Trying to getMasterPort on slave port %s that is not connected", + name()); + return _masterPort; } +bool +BaseSlavePort::isConnected() const +{ + return _masterPort != NULL; +} +// The protocol/interface specific slave port +template +SlavePort::SlavePort(slave_if_t& slave_interface, MemObject* owner, + const std::string& name, int id) : + BaseSlavePort(owner, name, id), + slaveInterface(slave_interface), + slavePortInterface(BaseSlavePort::name()) +{ +} + +template +SlavePort::~SlavePort() +{ +} + +template void -Port::printAddr(Addr a) +SlavePort::bind(MasterPort* master_port) { - Request req(a, 1, 0); - Packet pkt(&req, MemCmd::PrintReq, Packet::Broadcast); - Packet::PrintReqState prs(std::cerr); - pkt.senderState = &prs; + // called by the master and the master checks the validity of the + // binding + _masterPort = master_port; - sendFunctional(&pkt); + // bind the slave port interface to a master interface, with a + // given port id, and pass the corresponding master port interface + // in case there is any interface-specific binding necessary + slavePortInterface.bind(master_port->getInterface(), master_port->getId(), + master_port->getPortInterface()); } + +BaseMasterPort::BaseMasterPort(MemObject* owner, const std::string& name, + int id) + : Port(owner, name, id), _slavePort(NULL) +{ +} + +BaseMasterPort::~BaseMasterPort() +{ +} + +BaseSlavePort* +BaseMasterPort::getSlavePort() const +{ + if (_slavePort == NULL) + panic("Trying to getSlavePort on master port %s that is not connected", + name()); + + return _slavePort; +} + +bool +BaseMasterPort::isConnected() const +{ + return _slavePort != NULL; +} + +// The protocol/interface specific master port +template +MasterPort::MasterPort(master_if_t& master_interface, MemObject* owner, + const std::string& name, int id) : + BaseMasterPort(owner, name, id), + masterInterface(master_interface), + masterPortInterface(BaseMasterPort::name()) +{ +} + +template +MasterPort::~MasterPort() +{ +} + +template +void +MasterPort::bind(BaseSlavePort* slave_port) +{ + // we keep track of the slave port + _slavePort = slave_port; + + // attempt to cast the slave port from the base to a compatible type + SlavePort* cast_port = dynamic_cast* >(slave_port); + + // if the port is indeed of the compatible type, bind the + // port and module interface + if (cast_port != NULL) { + masterPortInterface.bind(cast_port->getInterface(), + cast_port->getId(), + cast_port->getPortInterface()); + } else { + panic("Master port %s cannot bind to %s", name(), slave_port->name()); + } + + // and the slave port keeps track of us + cast_port->bind(this); +} + +/** Port template instantiations. To be removed with _impl.hh file */ +template class SlavePort; +template class SlavePort; +template class MasterPort; +template class MasterPort; diff -r 6548721032fa -r 042f38a2e360 src/mem/port_impl.hh --- a/src/mem/port_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * 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_traits.hh" -#include "config/the_isa.hh" -#include "mem/port.hh" -#include "sim/byteswap.hh" - -template -void -FunctionalPort::writeHtoG(Addr addr, T d) -{ - d = TheISA::htog(d); - writeBlob(addr, (uint8_t*)&d, sizeof(T)); -} - - -template -T -FunctionalPort::readGtoH(Addr addr) -{ - T d; - readBlob(addr, (uint8_t*)&d, sizeof(T)); - return TheISA::gtoh(d); -} - diff -r 6548721032fa -r 042f38a2e360 src/mem/port_interface.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/port_interface.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,654 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Hansson + */ + +/** + * @file Declaration of abstract interfaces for master and slave + * ports. These are used to access the module interface that acts as + * contracts. As such, the port interfaces are similar to sc_ports in + * that they require a certain interface from the module interfaces + * they are bound to. A master port interface thus has a corresponding + * slave module interface, and vice versa. + */ + +#ifndef __MEM_PORT_INTERFACE_HH__ +#define __MEM_PORT_INTERFACE_HH__ + +#include "mem/module_interface.hh" + +/** + * The generic port interface is merely keeping track of the name, and + * defines the type used for identifying the basic link state. + */ +class PortInterface +{ + + protected: + + /** + * Abstract class that should never be instantiated, and a virtual + * destructor to match. + * + * @param name the interface name + */ + PortInterface(const std::string& name) : _name(name) {} + + /** + * Virtual destructor. + */ + virtual ~PortInterface() {} + + /** + * Name used for debugging. + */ + std::string _name; + + public: + + /** + * State of link between master and slave ports using begin end + * handshake. This is needed for the interfacing of the timed + * 4-phase and classic ports, and also useful to poke at the + * current state of ports, e.g. in the caches and the CPUs. + */ + enum LinkState {IDLE, BUSY}; + +}; + +/** + * Create a protocol specific slave port interface, talking to a + * specific type of master interface, with a specific payload + * type. Note that the payload could be embedded into the master + * interface. + */ +template +class SlavePortInterface : public PortInterface +{ + public: + + /** + * Give the master module interface type a name. + */ + typedef MASTER_IF master_if_t; + + /** + * Get the packet type defined from the master interface we + * interact with. + */ + typedef typename MASTER_IF::pkt_t pkt_t; + + /** + * Make the event queue happy + */ + std::string name() const { return _name; } + + /** + * Send a response packet to the connected master port. + * + * @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 possibly issue a successful + * sendTiming. + */ + inline bool sendTimingResp(pkt_t* resp_pkt) const { + // only using backward path so should only see responses + assert(resp_pkt->isResponse()); + return masterInterface->recvTimingResp(resp_pkt, masterId); + } + + /** + * When a master tries to send a request packet and we do not + * accept it we must at some later point tell the master to retry + * sending the request. + */ + inline void sendRetryReq() const { + masterInterface->recvRetryReq(masterId); + } + + protected: + + /** + * Abstract base class. + */ + SlavePortInterface(const std::string& name); + + virtual ~SlavePortInterface() {} + + /** + * Bind this slave port interface to a compatible master interface + * and a specific master id. The leaf nodes in the interface + * hierarchy, i.e. the actual port interfaces that are used in the + * end can implement specific functionality as they get called + * with the corresponding port interface as well. + * + * @param master_interface the master interface + * @param master_id the port id on the master interface in question + */ + void bind(master_if_t* master_interface, unsigned int master_id); + + /** + * The master exported interface that implements the actual + * functionality. + */ + master_if_t* masterInterface; + + /** + * The master port id so we do not have to get it from the + * connected master port all the time. This id is passed to the + * master module interface to be able to determine which of its + * master ports is responsible. + */ + int masterId; + +}; + +template +class TimedSlavePortInterface : + public SlavePortInterface +{ + + public: + + /** + * Give the master module interface type a name. + */ + typedef MASTER_IF master_if_t; + + /** + * Get the packet type defined from the slave interface we + * interact with and give it a name. + */ + typedef typename MASTER_IF::pkt_t pkt_t; + + /** + * Crude hack for now to bind the state on the two sides. + */ + PortInterface::LinkState* bindState() { return &_respLinkState; } + + /** + * Return state of req link. + */ + PortInterface::LinkState reqLinkState() const; + + /** + * Return state of resp link. + */ + PortInterface::LinkState respLinkState() const; + + /** + * Called by the slave to signal the end of a request packet + * transfer. This calls endReq on the peer masterInterface. Link + * state is checked (in an assertion), and updated after the + * masterInterface endReq call returns. This means that it is not + * allowed to call beginReq() directly through endReq on the + * master side. + */ + inline void endReq(pkt_t* req_pkt) + { + assert(*this->_reqLinkState == PortInterface::BUSY); + this->masterInterface->endReq(req_pkt, this->masterId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + *this->_reqLinkState = PortInterface::IDLE; + } + + /** + * Called by the slave to begin transfering a response + * packet. This calls beginResp on the peer masterInterface. Link + * state is checked (in an assertion), and updated after the + * masterInterface beginResp call returns. This means that it is + * not allowed to call endResp() directly through beginResp on the + * master side. + */ + inline void beginResp(pkt_t* resp_pkt) + { + assert(this->_respLinkState == PortInterface::IDLE); + this->masterInterface->beginResp(resp_pkt, this->masterId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + this->_respLinkState = PortInterface::BUSY; + } + + protected: + + /** + * Abstract base class. + */ + TimedSlavePortInterface(const std::string& name) : + SlavePortInterface(name), + _reqLinkState(NULL), + _respLinkState(PortInterface::IDLE) {} + + virtual ~TimedSlavePortInterface() {} + + /** + * Pointer to state of the request link, kept by the corresponding + * master port interface. + */ + PortInterface::LinkState* _reqLinkState; + + /** + * State of response link + */ + PortInterface::LinkState _respLinkState; + +}; + +/** Forward declaration */ +class MemMapMasterPortInterface; + +/** + * Specialisation for the memory-mapped slave port interface. This + * interface adds the ability to send an address range change. + */ +class MemMapSlavePortInterface : + public TimedSlavePortInterface +{ + + public: + + MemMapSlavePortInterface(const std::string& name) : + TimedSlavePortInterface(name) { + } + + /** + * Tell the connected master that our address range has changed + */ + void sendRangeChange() const; + + /** + * Bind this slave port interface to a compatible timed master + * interface. + * + * @param master_interface the master interface + * @param master_id the port id on the master interface in question + * @param master_port_interface the corresponding port interface + * on the master side + */ + void bind(master_if_t* master_interface, + unsigned int master_id, + MemMapMasterPortInterface* master_port_interface); + +}; + +/** Forward declaration */ +class CacheMntMasterPortInterface; + +/** + * Specialisation for the cache-maintenance slave port interface. This + * interface does not add any functions. + */ +class CacheMntSlavePortInterface : + public SlavePortInterface +{ + + public: + + CacheMntSlavePortInterface(const std::string& name) : + SlavePortInterface(name) { + } + + /** + * Bind this slave port interface to a compatible timed master + * interface. + * + * @param master_interface the master interface + * @param master_id the port id on the master interface in question + * @param master_port_interface the corresponding port interface + * on the master side + */ + void bind(master_if_t* master_interface, + unsigned int master_id, + CacheMntMasterPortInterface* master_port_interface) { + SlavePortInterface::bind(master_interface, + master_id); + } + +}; + +template +class MasterPortInterface : public PortInterface +{ + + public: + + /** + * Give the slave interface type a name. + */ + typedef SLAVE_IF slave_if_t; + + /** + * Get the packet type defined from the slave interface we + * interact with and give it a name. + */ + typedef typename SLAVE_IF::pkt_t pkt_t; + + /** + * Make the event queue happy + */ + std::string name() const { return _name; } + + /** + * Debug interface can be used to gain access to a targets memory + * or registers in a non-intrusive manner. No side effects, waits + * or event notifications must happen in the course of the method. + */ + inline void sendDebug(pkt_t* pkt) const { + // only using forward path so should only see requests + assert(pkt->isRequest()); + return slaveInterface->recvDebug(pkt, slaveId); + } + + /** + * 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 zero time, without interleaving with other memory + * accesses. Returns estimated latency of access. + */ + inline Tick sendAtomic(pkt_t* pkt) const { + // only using forward path so should only see requests + assert(pkt->isRequest()); + return slaveInterface->recvAtomic(pkt, slaveId); + } + + /** + * Send a request through the port to the connected slave port. + * + * @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 possibly issue a successful + * sendTiming. + */ + inline bool sendTimingReq(pkt_t* req_pkt) const { + // only using forward path so should only see requests + assert(req_pkt->isRequest()); + return slaveInterface->recvTimingReq(req_pkt, slaveId); + } + + /** + * When a slave tries to send a response packet and we do not + * accept it we must at some later point tell the slave to retry + * sending the response to us. + */ + inline void sendRetryResp() const { + return slaveInterface->recvRetryResp(slaveId); + } + + protected: + + /** + * Abstract base class. + */ + MasterPortInterface(const std::string& name); + + virtual ~MasterPortInterface() {} + + /** + * Bind this master port interface to a compatible slave + * interface. The leaf nodes in the interface hierarchy, i.e. the + * actual port interfaces that are used in the end can implement + * specific functionality as they get called with the + * corresponding port interface as well. + * + * @param slave_interface the slave interface + * @param slave_id the port id on the slave interface in question + */ + void bind(slave_if_t* slave_interface, unsigned int slave_id); + + /** + * The slave exported interface that implements the actual + * functionality. + */ + slave_if_t* slaveInterface; + + /** + * The slave port id so we do not have to get it all the + * time. This is passed to the slave interface to be able to + * determine which of its slave ports is responsible. + */ + int slaveId; + +}; + +template +class TimedMasterPortInterface : + public MasterPortInterface +{ + + public: + + /** + * Give the slave interface type a name. + */ + typedef SLAVE_IF slave_if_t; + + /** + * Get the packet type defined from the slave interface we + * interact with and give it a name. + */ + typedef typename SLAVE_IF::pkt_t pkt_t; + + /** + * Crude hack for now to bind the state on the two sides. + */ + PortInterface::LinkState* bindState() { return &_reqLinkState; } + + /** + * Return state of request link. + */ + PortInterface::LinkState reqLinkState() const; + + /** + * Return state of response link. + */ + PortInterface::LinkState respLinkState() const; + + /** + * Called by the master to begin transfering a request + * packet. This calls beginReq on the peer slaveInterface. Link + * state is checked (in an assertion), and updated after the + * slaveInterface beginReq call returns. This means that it is + * not allowed to call endReq() directly through beginReq on the + * slave side. + */ + inline void beginReq(pkt_t* req_pkt) + { + assert(this->_reqLinkState == PortInterface::IDLE); + this->slaveInterface->beginReq(req_pkt, this->slaveId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + this->_reqLinkState = PortInterface::BUSY; + } + + /** + * Called by the master to signal the end of a response packet + * transfer. This calls endResp on the peer slaveInterface. Link + * state is checked (in an assertion), and updated after the + * slaveInterface endResp call returns. This means that it is not + * allowed to call beginResp() directly through endResp on the + * slave side. + */ + inline void endResp(pkt_t* resp_pkt) + { + assert(*this->_respLinkState == PortInterface::BUSY); + this->slaveInterface->endResp(resp_pkt, this->slaveId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + *this->_respLinkState = PortInterface::IDLE; + } + + protected: + + /** + * Abstract base class. + */ + TimedMasterPortInterface(const std::string& name) : + MasterPortInterface(name), + _reqLinkState(PortInterface::IDLE), + _respLinkState(NULL) {} + + virtual ~TimedMasterPortInterface() {} + + /** + * State of the request link. + */ + PortInterface::LinkState _reqLinkState; + + /** + * Pointer to the state of the response link, kept by the + * corresponding slave port interface. + */ + PortInterface::LinkState* _respLinkState; + +}; + +/** + * Specialisation for the memory-mapped master port interface. This + * interface adds a number of convenience functions for debug access + * to memory. It also provides functions to get the address ranges and + * the block size of the slave module it is connected to. + */ +class MemMapMasterPortInterface : + public TimedMasterPortInterface +{ + + private: + + /** + * The maximum burst size as perceived by the connected slave + * port. This is set on the first call. + */ + mutable unsigned int slaveBlockSize; + + /** + * Internal helper function for read/writeMem(). + */ + void blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) const; + + public: + + /** + * Only a name + */ + MemMapMasterPortInterface(const std::string& name); + + /** + * Bind this master port interface to a compatible slave + * interface. + * + * @param slave_interface the slave (module) interface + * @param slave_id the port id on the slave interface in question + * @param slave_port_interface the corresponding port interface + * on the slave side + */ + void bind(slave_if_t* slave_interface, + unsigned int slave_id, + MemMapSlavePortInterface* slave_port_interface); + + /** + * Get the address ranges of the port that we are responding to as a + * memory-mapped slave. + * @return a list of non-overlapping ranges + */ + AddrRangeList getAddrRanges() const; + + /** + * The block size (translation unit?) of the slave with the port, + * or potentially the agreed size for all the slaves attached to a + * bus. Supposedly in bytes. It used to determine two things, the + * alignment requirements and the maximum transaction size. In a + * lot of slaves it is just returning zero which is fine if it is + * about alignment requirements. Should be split into + * two. getMaxBurstSize, getMinAlignment + */ + unsigned int getBlockSize() const; + + /** + * Read size bytes memory at address and store in p. This function + * is a wrapper around recvDebug() that breaks a larger, + * arbitrarily aligned access into appropriate chunks. + */ + void readMem(Addr addr, uint8_t *p, int size) const; + + /** + * Write size bytes from p to address. This function is a wrapper + * around recvDebug() that breaks a larger, arbitrarily + * aligned access into appropriate chunks. + */ + void writeMem(Addr addr, uint8_t *p, int size) const; + + /** + * Fill size bytes starting at addr with byte value val. This + * function is a wrapper around recvDebug() that breaks a + * larger, arbitrarily aligned access into appropriate chunks. + */ + void setMem(Addr addr, uint8_t val, int size) const; + +}; + +/** + * Specialisation of the cache-maintenance master port interface. This + * adds nothing in addition to the basic master port interface. + */ +class CacheMntMasterPortInterface : + public MasterPortInterface +{ + + public: + + CacheMntMasterPortInterface(const std::string& name) : + MasterPortInterface(name) {} + + /** + * Bind this master port interface to a compatible slave + * interface. + * + * @param slave_interface the slave (module) interface + * @param slave_id the port id on the slave interface in question + * @param slave_port_interface the corresponding port interface + * on the slave side + */ + void bind(slave_if_t* slave_interface, + unsigned int slave_id, + CacheMntSlavePortInterface* slave_port_interface) { + MasterPortInterface::bind(slave_interface, + slave_id); + } +}; + +#endif // __MEM_PORT_INTERFACE_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/port_interface.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/port_interface.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Hansson + */ + +/** + * @file Port interface definitions and currently also template + * instantiation. This should be split to a _impl.hh file. + */ +#include + +#include "base/trace.hh" +#include "base/chunk_generator.hh" +#include "mem/port_interface.hh" + +template +SlavePortInterface::SlavePortInterface(const std::string& name) : + PortInterface(name), masterInterface(NULL), masterId(-1) +{ +} + +template +void +SlavePortInterface::bind(master_if_t* master_interface, + unsigned int master_id) +{ + masterInterface = master_interface; + masterId = master_id; +} + +template +PortInterface::LinkState +TimedSlavePortInterface::respLinkState() const +{ + return _respLinkState; +} + +template +PortInterface::LinkState +TimedSlavePortInterface::reqLinkState() const +{ + return *_reqLinkState; +} + +void +MemMapSlavePortInterface::sendRangeChange() const +{ + masterInterface->recvRangeChange(masterId); +} + +void +MemMapSlavePortInterface::bind(master_if_t* master_interface, + unsigned int master_id, + MemMapMasterPortInterface* + master_port_interface) +{ + SlavePortInterface::bind(master_interface, + master_id); + _reqLinkState = master_port_interface->bindState(); +} + + +template +MasterPortInterface::MasterPortInterface(const std::string& name) : + PortInterface(name), slaveInterface(NULL), slaveId(-1) +{ +} + +template +void +MasterPortInterface::bind(slave_if_t* slave_interface, + unsigned int slave_id) +{ + slaveInterface = slave_interface; + slaveId = slave_id; +} + +template +PortInterface::LinkState +TimedMasterPortInterface::reqLinkState() const +{ + return _reqLinkState; +} + +template +PortInterface::LinkState +TimedMasterPortInterface::respLinkState() const +{ + return *_respLinkState; +} + +MemMapMasterPortInterface::MemMapMasterPortInterface(const std::string& name) : + TimedMasterPortInterface(name), slaveBlockSize(0) +{ +} + +void +MemMapMasterPortInterface::bind(slave_if_t* slave_interface, + unsigned int slave_id, + MemMapSlavePortInterface* + slave_port_interface) +{ + MasterPortInterface::bind(slave_interface, + slave_id); + _respLinkState = slave_port_interface->bindState(); +} + +AddrRangeList +MemMapMasterPortInterface::getAddrRanges() const +{ + return slaveInterface->getAddrRanges(slaveId); +} + +unsigned +MemMapMasterPortInterface::getBlockSize() const +{ + // if not yet set, get the block size and store it so that we only + // have to get it once (this cannot be done safely at binding time + // since the binding order then can affect the output) + if (slaveBlockSize == 0) + slaveBlockSize = slaveInterface->getBlockSize(slaveId); + return slaveBlockSize; +} + + +void +MemMapMasterPortInterface::blobHelper(Addr addr,uint8_t* p, int size, + MemCmd cmd) const +{ + Request req; + + // we chop the access up according to the maximum burst of the + // slave to make sure that the outstanding transactions stored in + // caches, buses etc can be checked against our debug access + for (ChunkGenerator gen(addr, size, getBlockSize()); + !gen.done(); gen.next()) { + req.setPhys(gen.addr(), gen.size(), 0); + pkt_t pkt(&req, cmd); + pkt.dataStatic(p); + sendDebug(&pkt); + p += gen.size(); + } +} + +void +MemMapMasterPortInterface::writeMem(Addr addr, uint8_t *p, int size) const +{ + blobHelper(addr, p, size, MemCmd::WriteReq); +} + +void +MemMapMasterPortInterface::readMem(Addr addr, uint8_t *p, int size) const +{ + blobHelper(addr, p, size, MemCmd::ReadReq); +} + +void +MemMapMasterPortInterface::setMem(Addr addr, uint8_t val, int size) const +{ + // quick and dirty... + uint8_t *buf = new uint8_t[size]; + + std::memset(buf, val, size); + blobHelper(addr, buf, size, MemCmd::WriteReq); + + delete [] buf; +} + +/** Port interface template instantiations. To be removed with _impl.hh file */ +template class MasterPortInterface; +template class TimedMasterPortInterface; + +template class SlavePortInterface; +template class TimedSlavePortInterface; + +template class MasterPortInterface; +template class SlavePortInterface; diff -r 6548721032fa -r 042f38a2e360 src/mem/port_proxy.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/port_proxy.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Geza Lore + */ + +/** + * @file PortProxy Object Declaration. Port proxies are used when non + * structural entities need access to the memory system. Proxy objects + * replace the previous FunctionalPort, TranslatingPort and VirtualPort + * objects, which provided the same functionality as the proxies, but + * were instances of ports not corresponding to real structural ports of + * the simulated system. + * @sa SETranslatingProxy + * @sa FSTranslatingProxy + */ + +#ifndef __MEM_PORT_PROXY_HH__ +#define __MEM_PORT_PROXY_HH__ + +#include "base/types.hh" + +#include "mem/port.hh" + +#include "arch/isa_traits.hh" +#include "config/the_isa.hh" +#include "sim/byteswap.hh" + +/** + * This object is a proxy for a structural port, + * to be used for debug accesses. + * + * This proxy object is used when non structural entities + * (e.g. thread contexts, object file loaders) need access to the + * memory system. It calls the corresponding functions on the underlying + * structural port, and provides templatized convenience access functions. + * + * The addresses are interpreted as physical addresses. + * + * @sa SETranslatingProxy + * @sa FSTranslatingProxy + */ +class PortProxy +{ + protected: + MemMapMasterPort &_port; + + public: + PortProxy(MemMapMasterPort &port); + PortProxy(MemMapMasterPort *port); + virtual ~PortProxy(); + + public: + /** + * Read size bytes memory at address and store in p. + * @sa MasterPort::readMem + */ + virtual void readMem( Addr address, uint8_t* p, int size); + + /** + * Write size bytes from p to address. + * @sa MasterPort::writeMem + */ + virtual void writeMem(Addr address, uint8_t* p, int size); + + /** + * Fill size bytes starting at addr with byte value val. + * @sa MasterPort::setMem + */ + virtual void setMem( Addr address, uint8_t v, int size); + + /** + * Read sizeof(T) bytes from address and return as object T. + */ + template + T read(Addr address); + + /** + * Write object T to address. Writes sizeof(T) bytes. + */ + template + void write(Addr address, T data); + + /** + * Read sizeof(T) bytes from address and return as object T. + * Performs Guest to Host endianness transform. + */ + template + T readGtoH(Addr address); + + /** + * Write object T to address. Writes sizeof(T) bytes. + * Performs Host to Guest endianness transform. + */ + template + void writeHtoG(Addr address, T data); +}; + + +template +T +PortProxy::read(Addr address) +{ + T data; + _port->readMem(address, (uint8_t*)&data, sizeof(T)); + return data; +} + +template +void +PortProxy::write(Addr address, T data) +{ + _port->writeMem(address, (uint8_t*)&data, sizeof(T)); +} + +template +T +PortProxy::readGtoH(Addr address) +{ + T data; + _port->readMem(address, (uint8_t*)&data, sizeof(T)); + return TheISA::gtoh(data); +} + +template +void +PortProxy::writeHtoG(Addr address, T data) +{ + data = TheISA::htog(data); + _port->writeMem(address, (uint8_t*)&data, sizeof(T)); +} + +#endif // __MEM_PORT_PROXY_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/port_proxy.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/port_proxy.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Geza Lore + */ + +#include "mem/port_proxy.hh" + +PortProxy::PortProxy(MemMapMasterPort &port) +: _port(port) +{} + +PortProxy::PortProxy(MemMapMasterPort *port) +: _port(*port) +{ + assert(port != NULL); +} + +PortProxy::~PortProxy() +{} + +void +PortProxy::readMem( Addr address, uint8_t* p, int size) +{ + _port->readMem(address, p, size); +} + +void +PortProxy::writeMem(Addr address, uint8_t* p, int size) +{ + _port->writeMem(address, p, size); +} + +void +PortProxy::setMem( Addr address, uint8_t v, int size) +{ + _port->setMem(address, v, size); +} diff -r 6548721032fa -r 042f38a2e360 src/mem/protocol.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/protocol.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Hansson + */ + +/** + * @file Declaration of protocols by bundling up packet types and + * interfaces in a convenient and consistent way. + */ + +#ifndef __MEM_PROTOCOL_HH__ +#define __MEM_PROTOCOL_HH__ + +#include "mem/port_interface.hh" + +/** + * A protocol is merely a bundle of a master port interface and a + * matching slave port interface. This wraps a number of typedefs for + * the master and slave port interface, the master and slave module + * interfaces, and the packet type (that is assumed to be the same in + * both directions). + */ +template +class Protocol +{ + + public: + + /** + * The type of the master and slave port interface + */ + typedef MASTER_PORT_IF master_port_if_t; + typedef SLAVE_PORT_IF slave_port_if_t; + + /** + * The type of the master and slave module interface + */ + typedef typename MASTER_PORT_IF::slave_if_t slave_if_t; + typedef typename SLAVE_PORT_IF::master_if_t master_if_t; + + /** + * The type of the packet based on the master port interface. + */ + typedef typename MASTER_PORT_IF::pkt_t pkt_t; + +}; + +/** + * Convenience typedef for the memory-mapped protocol. + */ +typedef Protocol MemMapProtocol; + +/** + * Convenience typedef for the cache-maintenance protocol. + */ +typedef Protocol CacheMntProtocol; + +#endif // __MEM_PROTOCOL_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/se_translating_proxy.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/se_translating_proxy.hh Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,121 @@ +/* + * The confidential and proprietary information contained in this file may + * only be used by a person authorised under and to the extent permitted + * by a subsisting licensing agreement from ARM Limited. + * + * (C) COPYRIGHT 2011 ARM Limited. + * ALL RIGHTS RESERVED + * + * This entire notice must be reproduced on all copies of this file + * and copies of this file may only be made by a person if such person is + * permitted to do so under the terms of a subsisting license agreement + * from ARM Limited. + * + * Authors: Geza Lore + */ + +/** + * @file SETranslatingProxy Object Declaration. + * Port proxies are used when non + * structural entities need access to the memory system. Proxy objects + * replace the previous FunctionalPort, TranslatingPort and VirtualPort + * objects, which provided the same functionality as the proxies, but + * were instances of ports not corresponding to real structural ports of + * the simulated system. + * @sa PortProxy + * @sa FSTranslatingProxy + */ + +#ifndef __MEM_SE_TRANSLATING_PROXY_HH__ +#define __MEM_SE_TRANSLATING_PROXY_HH__ + +#include + +#include "mem/port_proxy.hh" + +#include "mem/page_table.hh" +#include "sim/process.hh" + +/** + * This object is a proxy for a structural port, + * to be used for debug accesses in SE mode. + * + * This proxy object is used when non structural entities + * (e.g. thread contexts, object file loaders) need access to the + * memory system. It calls the corresponding functions on the underlying + * structural port, and provides templatized convenience access functions. + * + * The addresses are interpreted as virtual addresses, address translation + * is performed using the page tables of the process passed at construction. + * + * @sa PortProxy + * @sa FSTranslatingProxy + */ +class SETranslatingProxy : public PortProxy +{ + /** + * @todo: not sure of the exact usage of this, it was migrated from the + * transalating ports. Final version should document + */ + public: + enum AllocType { + Always, + Never, + NextPage + }; + + public: + SETranslatingProxy(Process *p, AllocType a, MemMapMasterPort &port); + SETranslatingProxy(Process *p, AllocType a, MemMapMasterPort *port); + virtual ~SETranslatingProxy(); + + private: + // helper functions for read/write/set functions + bool tryReadMem( Addr address, uint8_t* p, int size); + bool tryWriteMem(Addr address, uint8_t* p, int size); + bool trySetMem( Addr address, uint8_t v, int size); + + bool tryWriteString(Addr addr, const char *str); + + public: + // helper function required for syscall emulation + bool tryReadString(std::string &str, Addr addr); + + public: + /** + * Read size bytes memory at address and store in p. + * @sa MasterPort::readMem + */ + virtual void readMem( Addr address, uint8_t* p, int size); + + /** + * Write size bytes from p to address. + * @sa MasterPort::writeMem + */ + virtual void writeMem(Addr address, uint8_t* p, int size); + + /** + * Fill size bytes starting at addr with byte value val. + * @sa MasterPort::setMem + */ + virtual void setMem( Addr address, uint8_t v, int size); + + /** + * Read string from address, place into str. + */ + void readString(std::string &str, Addr addr); + + /** + * Write string str to address. + */ + void writeString(Addr addr, const char *str); + + private: + PageTable *pTable; + Process *process; + AllocType allocating; + +}; + +#endif // __MEM_SE_TRANSLATING_PROXY_HH__ + diff -r 6548721032fa -r 042f38a2e360 src/mem/se_translating_proxy.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/se_translating_proxy.cc Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,198 @@ +/* + * The confidential and proprietary information contained in this file may + * only be used by a person authorised under and to the extent permitted + * by a subsisting licensing agreement from ARM Limited. + * + * (C) COPYRIGHT 2011 ARM Limited. + * ALL RIGHTS RESERVED + * + * This entire notice must be reproduced on all copies of this file + * and copies of this file may only be made by a person if such person is + * permitted to do so under the terms of a subsisting license agreement + * from ARM Limited. + * + * Authors: Geza Lore + */ + +#include "mem/se_translating_proxy.hh" + +#include "base/chunk_generator.hh" + +#include + +using namespace TheISA; + +SETranslatingProxy::SETranslatingProxy(Process *p, AllocType a, + MemMapMasterPort &port) +: PortProxy(port) +{ + assert(p != NULL); + pTable = p->pTable; + process = p; + allocating = a; +} + +SETranslatingProxy::SETranslatingProxy(Process *p, AllocType a, + MemMapMasterPort *port) +: PortProxy(port) +{ + assert(p != NULL); + pTable = p->pTable; + process = p; + allocating = a; +} + +SETranslatingProxy::~SETranslatingProxy() +{ +} + + +bool +SETranslatingProxy::tryReadMem(Addr addr, uint8_t *p, int size) +{ + Addr paddr; + int prevSize = 0; + + for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { + + if (!pTable->translate(gen.addr(),paddr)) + return false; + + PortProxy::readMem(paddr, p + prevSize, gen.size()); + prevSize += gen.size(); + } + + return true; +} + +bool +SETranslatingProxy::tryWriteMem(Addr addr, uint8_t *p, int size) +{ + + Addr paddr; + int prevSize = 0; + + for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { + + if (!pTable->translate(gen.addr(), paddr)) { + if (allocating == Always) { + pTable->allocate(roundDown(gen.addr(), VMPageSize), + VMPageSize); + } else if (allocating == NextPage) { + // check if we've accessed the next page on the stack + if (!process->checkAndAllocNextPage(gen.addr())) + panic("Page table fault when accessing virtual address %#x " + "during functional write\n", gen.addr()); + } else { + return false; + } + pTable->translate(gen.addr(), paddr); + } + + PortProxy::writeMem(paddr, p + prevSize, gen.size()); + prevSize += gen.size(); + } + + return true; +} + +bool +SETranslatingProxy::trySetMem(Addr addr, uint8_t val, int size) +{ + Addr paddr; + + for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { + + if (!pTable->translate(gen.addr(), paddr)) { + if (allocating == Always) { + pTable->allocate(roundDown(gen.addr(), VMPageSize), + VMPageSize); + pTable->translate(gen.addr(), paddr); + } else { + return false; + } + } + + PortProxy::setMem(paddr, val, gen.size()); + } + + return true; +} + + +bool +SETranslatingProxy::tryWriteString(Addr addr, const char *str) +{ + Addr paddr,vaddr; + uint8_t c; + + vaddr = addr; + + do { + c = *str++; + if (!pTable->translate(vaddr++,paddr)) + return false; + + PortProxy::writeMem(paddr, &c, 1); + } while (c); + + return true; +} + +bool +SETranslatingProxy::tryReadString(std::string &str, Addr addr) +{ + Addr paddr,vaddr; + uint8_t c; + + vaddr = addr; + + do { + if (!pTable->translate(vaddr++,paddr)) + return false; + + PortProxy::readMem(paddr, &c, 1); + str += c; + } while (c); + + return true; +} + + +//////////// + +void +SETranslatingProxy::readMem( Addr address, uint8_t* p, int size) +{ + if(!tryReadMem(address, p, size)) + fatal("readMem failed at vaddress 0x%x\n",address); +} + +void +SETranslatingProxy::writeMem(Addr address, uint8_t* p, int size) +{ + if(!tryWriteMem(address, p, size)) + fatal("writeMem failed at vaddress 0x%x\n",address); +} + +void +SETranslatingProxy::setMem( Addr address, uint8_t v, int size) +{ + if(!trySetMem(address, v, size)) + fatal("setMem failed at vaddress 0x%x\n",address); +} + +void +SETranslatingProxy::writeString(Addr addr, const char *str) +{ + if(!tryWriteString(addr, str)) + fatal("writeString failed at vaddress 0x%x\n", addr); +} + +void +SETranslatingProxy::readString(std::string &str, Addr addr) +{ + if(!tryReadString(str, addr)) + fatal("readString failed at vaddress 0x%x\n", addr); +} + diff -r 6548721032fa -r 042f38a2e360 src/mem/tport.hh --- a/src/mem/tport.hh Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,177 +0,0 @@ -/* - * 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 __MEM_TPORT_HH__ -#define __MEM_TPORT_HH__ - -/** - * @file - * - * Declaration of SimpleTimingPort. - */ - -#include "mem/port.hh" -#include "sim/eventq.hh" -#include -#include - -/** - * A simple port for interfacing objects that basically have only - * functional memory behavior (e.g. I/O devices) to the memory system. - * Both timing and functional accesses are implemented in terms of - * atomic accesses. A derived port class thus only needs to provide - * recvAtomic() to support all memory access modes. - * - * The tricky part is handling recvTiming(), where the response must - * be scheduled separately via a later call to sendTiming(). This - * feature is handled by scheduling an internal event that calls - * sendTiming() after a delay, and optionally rescheduling the - * response if it is nacked. - */ -class SimpleTimingPort : public Port -{ - protected: - /** A deferred packet, buffered to transmit later. */ - class DeferredPacket { - public: - Tick tick; ///< The tick when the packet is ready to transmit - PacketPtr pkt; ///< Pointer to the packet to transmit - DeferredPacket(Tick t, PacketPtr p) - : tick(t), pkt(p) - {} - }; - - typedef std::list DeferredPacketList; - typedef std::list::iterator DeferredPacketIterator; - - /** A list of outgoing timing response packets that haven't been - * serviced yet. */ - DeferredPacketList transmitList; - - /** This function attempts to send deferred packets. Scheduled to - * be called in the future via SendEvent. */ - void processSendEvent(); - - /** - * This class is used to implemented sendTiming() with a delay. When - * a delay is requested a the event is scheduled if it isn't already. - * When the event time expires it attempts to send the packet. - * If it cannot, the packet sent when recvRetry() is called. - **/ - Event *sendEvent; - - /** If we need to drain, keep the drain event around until we're done - * here.*/ - Event *drainEvent; - - /** Remember whether we're awaiting a retry from the bus. */ - bool waitingOnRetry; - - /** Check the list of buffered packets against the supplied - * functional request. */ - bool checkFunctional(PacketPtr funcPkt); - - /** Check whether we have a packet ready to go on the transmit list. */ - bool deferredPacketReady() - { return !transmitList.empty() && transmitList.front().tick <= curTick(); } - - Tick deferredPacketReadyTime() - { return transmitList.empty() ? MaxTick : transmitList.front().tick; } - - void - schedSendEvent(Tick when) - { - if (waitingOnRetry) { - assert(!sendEvent->scheduled()); - return; - } - - if (!sendEvent->scheduled()) { - schedule(sendEvent, when); - } else if (sendEvent->when() > when) { - reschedule(sendEvent, when); - } - } - - - /** Schedule a sendTiming() event to be called in the future. - * @param pkt packet to send - * @param absolute time (in ticks) to send packet - */ - void schedSendTiming(PacketPtr pkt, Tick when); - - /** Attempt to send the packet at the head of the deferred packet - * list. Caller must guarantee that the deferred packet list is - * non-empty and that the head packet is scheduled for curTick() (or - * earlier). - */ - void sendDeferredPacket(); - - /** This function is notification that the device should attempt to send a - * packet again. */ - virtual void recvRetry(); - - /** Implemented using recvAtomic(). */ - void recvFunctional(PacketPtr pkt); - - /** Implemented using recvAtomic(). */ - bool recvTiming(PacketPtr pkt); - - /** - * Simple ports generally don't care about any status - * changes... can always override this in cases where that's not - * true. */ - virtual void recvStatusChange(Status status) { } - - - public: - SimpleTimingPort(std::string pname, MemObject *_owner); - ~SimpleTimingPort(); - - /** Hook for draining timing accesses from the system. The - * associated SimObject's drain() functions should be implemented - * something like this when this class is used: - \code - PioDevice::drain(Event *de) - { - unsigned int count; - count = SimpleTimingPort->drain(de); - if (count) - changeState(Draining); - else - changeState(Drained); - return count; - } - \endcode - */ - unsigned int drain(Event *de); -}; - -#endif // __MEM_TPORT_HH__ diff -r 6548721032fa -r 042f38a2e360 src/mem/tport.cc --- a/src/mem/tport.cc Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/* - * 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 "mem/tport.hh" - -using namespace std; - -SimpleTimingPort::SimpleTimingPort(string pname, MemObject *_owner) - : Port(pname, _owner), sendEvent(0), drainEvent(NULL), - waitingOnRetry(false) -{ - sendEvent = new EventWrapper(this); -} - -SimpleTimingPort::~SimpleTimingPort() -{ - delete sendEvent; -} - -bool -SimpleTimingPort::checkFunctional(PacketPtr pkt) -{ - DeferredPacketIterator i = transmitList.begin(); - DeferredPacketIterator end = transmitList.end(); - - for (; i != end; ++i) { - PacketPtr target = i->pkt; - // If the target contains data, and it overlaps the - // probed request, need to update data - if (pkt->checkFunctional(target)) { - return true; - } - } - - return false; -} - -void -SimpleTimingPort::recvFunctional(PacketPtr pkt) -{ - if (!checkFunctional(pkt)) { - // Just do an atomic access and throw away the returned latency - recvAtomic(pkt); - } -} - -bool -SimpleTimingPort::recvTiming(PacketPtr pkt) -{ - // If the device is only a slave, it should only be sending - // responses, which should never get nacked. There used to be - // code to hanldle nacks here, but I'm pretty sure it didn't work - // correctly with the drain code, so that would need to be fixed - // if we ever added it back. - - if (pkt->memInhibitAsserted()) { - // snooper will supply based on copy of packet - // still target's responsibility to delete packet - delete pkt; - return true; - } - - bool needsResponse = pkt->needsResponse(); - Tick latency = recvAtomic(pkt); - // turn packet around to go back to requester if response expected - if (needsResponse) { - // recvAtomic() should already have turned packet into - // atomic response - assert(pkt->isResponse()); - schedSendTiming(pkt, curTick() + latency); - } else { - delete pkt; - } - - return true; -} - - -void -SimpleTimingPort::schedSendTiming(PacketPtr pkt, Tick when) -{ - assert(when > curTick()); - assert(when < curTick() + SimClock::Int::ms); - - // Nothing is on the list: add it and schedule an event - if (transmitList.empty() || when < transmitList.front().tick) { - transmitList.push_front(DeferredPacket(when, pkt)); - schedSendEvent(when); - return; - } - - // list is non-empty & this belongs at the end - if (when >= transmitList.back().tick) { - transmitList.push_back(DeferredPacket(when, pkt)); - return; - } - - // this belongs in the middle somewhere - DeferredPacketIterator i = transmitList.begin(); - i++; // already checked for insertion at front - DeferredPacketIterator end = transmitList.end(); - - for (; i != end; ++i) { - if (when < i->tick) { - transmitList.insert(i, DeferredPacket(when, pkt)); - return; - } - } - assert(false); // should never get here -} - - -void -SimpleTimingPort::sendDeferredPacket() -{ - assert(deferredPacketReady()); - // take packet off list here; if recvTiming() on the other side - // calls sendTiming() back on us (like SimpleTimingCpu does), then - // we get confused by having a non-active packet on transmitList - DeferredPacket dp = transmitList.front(); - transmitList.pop_front(); - bool success = sendTiming(dp.pkt); - - if (success) { - if (!transmitList.empty() && !sendEvent->scheduled()) { - Tick time = transmitList.front().tick; - schedule(sendEvent, time <= curTick() ? curTick()+1 : time); - } - - if (transmitList.empty() && drainEvent && !sendEvent->scheduled()) { - drainEvent->process(); - drainEvent = NULL; - } - } else { - // Unsuccessful, need to put back on transmitList. Callee - // should not have messed with it (since it didn't accept that - // packet), so we can just push it back on the front. - assert(!sendEvent->scheduled()); - transmitList.push_front(dp); - } - - waitingOnRetry = !success; - - if (waitingOnRetry) { - DPRINTF(Bus, "Send failed, waiting on retry\n"); - } -} - - -void -SimpleTimingPort::recvRetry() -{ - DPRINTF(Bus, "Received retry\n"); - assert(waitingOnRetry); - sendDeferredPacket(); -} - - -void -SimpleTimingPort::processSendEvent() -{ - assert(!waitingOnRetry); - sendDeferredPacket(); -} - - -unsigned int -SimpleTimingPort::drain(Event *de) -{ - if (transmitList.size() == 0 && !sendEvent->scheduled()) - return 0; - drainEvent = de; - return 1; -} diff -r 6548721032fa -r 042f38a2e360 src/mem/translating_port.hh --- a/src/mem/translating_port.hh Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2001-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: Ron Dreslinski - * Ali Saidi - */ - -#ifndef __MEM_TRANSLATING_PROT_HH__ -#define __MEM_TRANSLATING_PROT_HH__ - -#include "mem/port.hh" - -class PageTable; -class Process; - -class TranslatingPort : public FunctionalPort -{ - public: - enum AllocType { - Always, - Never, - NextPage - }; - - private: - PageTable *pTable; - Process *process; - AllocType allocating; - - public: - TranslatingPort(const std::string &_name, - Process *p, AllocType alloc); - virtual ~TranslatingPort(); - - bool tryReadBlob(Addr addr, uint8_t *p, int size); - bool tryWriteBlob(Addr addr, uint8_t *p, int size); - bool tryMemsetBlob(Addr addr, uint8_t val, int size); - bool tryWriteString(Addr addr, const char *str); - bool tryReadString(std::string &str, Addr addr); - - virtual void readBlob(Addr addr, uint8_t *p, int size); - virtual void writeBlob(Addr addr, uint8_t *p, int size); - virtual void memsetBlob(Addr addr, uint8_t val, int size); - - void writeString(Addr addr, const char *str); - void readString(std::string &str, Addr addr); -}; - -#endif diff -r 6548721032fa -r 042f38a2e360 src/mem/translating_port.cc --- a/src/mem/translating_port.cc Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2001-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: Ron Dreslinski - * Steve Reinhardt - */ - -#include - -#include "base/chunk_generator.hh" -#include "config/the_isa.hh" -#include "mem/port.hh" -#include "mem/translating_port.hh" -#include "mem/page_table.hh" -#include "sim/process.hh" - -using namespace TheISA; - -TranslatingPort::TranslatingPort(const std::string &_name, - Process *p, AllocType alloc) - : FunctionalPort(_name), pTable(p->pTable), process(p), - allocating(alloc) -{ } - -TranslatingPort::~TranslatingPort() -{ } - -bool -TranslatingPort::tryReadBlob(Addr addr, uint8_t *p, int size) -{ - Addr paddr; - int prevSize = 0; - - for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { - - if (!pTable->translate(gen.addr(),paddr)) - return false; - - Port::readBlob(paddr, p + prevSize, gen.size()); - prevSize += gen.size(); - } - - return true; -} - -void -TranslatingPort::readBlob(Addr addr, uint8_t *p, int size) -{ - if (!tryReadBlob(addr, p, size)) - fatal("readBlob(0x%x, ...) failed", addr); -} - - -bool -TranslatingPort::tryWriteBlob(Addr addr, uint8_t *p, int size) -{ - - Addr paddr; - int prevSize = 0; - - for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { - - if (!pTable->translate(gen.addr(), paddr)) { - if (allocating == Always) { - pTable->allocate(roundDown(gen.addr(), VMPageSize), - VMPageSize); - } else if (allocating == NextPage) { - // check if we've accessed the next page on the stack - if (!process->checkAndAllocNextPage(gen.addr())) - panic("Page table fault when accessing virtual address %#x " - "during functional write\n", gen.addr()); - } else { - return false; - } - pTable->translate(gen.addr(), paddr); - } - - Port::writeBlob(paddr, p + prevSize, gen.size()); - prevSize += gen.size(); - } - - return true; -} - - -void -TranslatingPort::writeBlob(Addr addr, uint8_t *p, int size) -{ - if (!tryWriteBlob(addr, p, size)) - fatal("writeBlob(0x%x, ...) failed", addr); -} - -bool -TranslatingPort::tryMemsetBlob(Addr addr, uint8_t val, int size) -{ - Addr paddr; - - for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { - - if (!pTable->translate(gen.addr(), paddr)) { - if (allocating == Always) { - pTable->allocate(roundDown(gen.addr(), VMPageSize), - VMPageSize); - pTable->translate(gen.addr(), paddr); - } else { - return false; - } - } - - Port::memsetBlob(paddr, val, gen.size()); - } - - return true; -} - -void -TranslatingPort::memsetBlob(Addr addr, uint8_t val, int size) -{ - if (!tryMemsetBlob(addr, val, size)) - fatal("memsetBlob(0x%x, ...) failed", addr); -} - - -bool -TranslatingPort::tryWriteString(Addr addr, const char *str) -{ - Addr paddr,vaddr; - uint8_t c; - - vaddr = addr; - - do { - c = *str++; - if (!pTable->translate(vaddr++,paddr)) - return false; - - Port::writeBlob(paddr, &c, 1); - } while (c); - - return true; -} - -void -TranslatingPort::writeString(Addr addr, const char *str) -{ - if (!tryWriteString(addr, str)) - fatal("writeString(0x%x, ...) failed", addr); -} - -bool -TranslatingPort::tryReadString(std::string &str, Addr addr) -{ - Addr paddr,vaddr; - uint8_t c; - - vaddr = addr; - - do { - if (!pTable->translate(vaddr++,paddr)) - return false; - - Port::readBlob(paddr, &c, 1); - str += c; - } while (c); - - return true; -} - -void -TranslatingPort::readString(std::string &str, Addr addr) -{ - if (!tryReadString(str, addr)) - fatal("readString(0x%x, ...) failed", addr); -} - diff -r 6548721032fa -r 042f38a2e360 src/mem/vport.hh --- a/src/mem/vport.hh Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* - * 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 - */ - -/** - * @file - * Virtual Port Object Declaration. These ports incorporate some translation - * into their access methods. Thus you can use one to read and write data - * to/from virtual addresses. - */ - -#ifndef __MEM_VPORT_HH__ -#define __MEM_VPORT_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 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). - */ - - -class VirtualPort : public FunctionalPort -{ - private: - ThreadContext *tc; - - public: - VirtualPort(const std::string &_name, ThreadContext *_tc = NULL) - : FunctionalPort(_name), tc(_tc) - {} - - /** 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 nullThreadContext() { return tc != NULL; } - - /** Version of readblob that translates virt->phys and deals - * with page boundries. */ - virtual void readBlob(Addr addr, uint8_t *p, int size); - - /** Version of writeBlob that translates virt->phys and deals - * with page boundries. */ - virtual void writeBlob(Addr addr, uint8_t *p, int size); -}; - - -void CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen); -void CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen); -void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); -void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); - -#endif //__MEM_VPORT_HH__ - diff -r 6548721032fa -r 042f38a2e360 src/mem/vport.cc --- a/src/mem/vport.cc Fri Feb 11 18:29:36 2011 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * 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 - */ - -/** - * @file - * Port object definitions. - */ - -#include "base/chunk_generator.hh" -#include "config/the_isa.hh" -#include "cpu/thread_context.hh" -#include "mem/vport.hh" - -void -VirtualPort::readBlob(Addr addr, uint8_t *p, int size) -{ - Addr paddr; - for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); - gen.next()) - { - if (tc) - paddr = TheISA::vtophys(tc,gen.addr()); - else - paddr = TheISA::vtophys(gen.addr()); - - FunctionalPort::readBlob(paddr, p, gen.size()); - p += gen.size(); - } -} - -void -VirtualPort::writeBlob(Addr addr, uint8_t *p, int size) -{ - Addr paddr; - for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); - gen.next()) - { - if (tc) - paddr = TheISA::vtophys(tc,gen.addr()); - else - paddr = TheISA::vtophys(gen.addr()); - - FunctionalPort::writeBlob(paddr, p, gen.size()); - p += gen.size(); - } -} - -void -CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) -{ - uint8_t *dst = (uint8_t *)dest; - VirtualPort *vp = tc->getVirtPort(); - - vp->readBlob(src, dst, cplen); -} - -void -CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) -{ - uint8_t *src = (uint8_t *)source; - VirtualPort *vp = tc->getVirtPort(); - - vp->writeBlob(dest, src, cplen); -} - -void -CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) -{ - int len = 0; - char *start = dst; - VirtualPort *vp = tc->getVirtPort(); - - do { - vp->readBlob(vaddr++, (uint8_t*)dst++, 1); - } while (len < maxlen && start[len++] != 0 ); - - dst[len] = 0; -} - -void -CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) -{ - VirtualPort *vp = tc->getVirtPort(); - for (ChunkGenerator gen(vaddr, strlen(src), TheISA::PageBytes); !gen.done(); - gen.next()) - { - vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); - src += gen.size(); - } -} diff -r 6548721032fa -r 042f38a2e360 src/python/m5/SimObject.py --- a/src/python/m5/SimObject.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/m5/SimObject.py Fri Aug 05 18:14:06 2011 +0100 @@ -788,7 +788,10 @@ cc_params_struct = getattr(m5.internal.params, '%sParams' % self.type) cc_params = cc_params_struct() cc_params.pyobj = self - cc_params.name = str(self) + cc_params.path = str(self) + if self._name: + cc_params.name = self._name + param_names = self._params.keys() param_names.sort() diff -r 6548721032fa -r 042f38a2e360 src/python/m5/main.py --- a/src/python/m5/main.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/m5/main.py Fri Aug 05 18:14:06 2011 +0100 @@ -95,6 +95,8 @@ set_group("Configuration Options") add_option("--dump-config", metavar="FILE", default="config.ini", help="Dump configuration output file [Default: %default]") +add_option("--dump-structure", metavar="FILE", default="system.dot", + help="Dump system structure output file [Default: %default]") # Debugging options set_group("Debugging Options") diff -r 6548721032fa -r 042f38a2e360 src/python/m5/params.py --- a/src/python/m5/params.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/m5/params.py Fri Aug 05 18:14:06 2011 +0100 @@ -582,6 +582,10 @@ value.end = long(self.second) return value + def getLength(self): + value = self.getValue() + return "%dB" % (value.end - value.start + 1) + class TickRange(Range): type = Tick @@ -1252,13 +1256,70 @@ # ##################################################################### +# Port description object. Like a ParamDesc object, this represents a +# logical port in the SimObject class, not a particular port on a +# SimObject instance. The latter are represented by PortRef objects. +class Port(object): + # Port("protocol", "role", "description") + # + # Is there a good way to turn this into something that looks like Enums, + # i.e. symbolic constants within the scope of the port? + def __init__(self, *args): + if len(args) == 3: + self.protocol = args[0] + if (not self.protocol in ("MEM_MAP", "CACHE_MNT", "ETH")): + raise TypeError, \ + 'protocol must be either MEM_MAP, CACHE_MNT or ETH' + self.role = args[1] + if (not self.role in ("MASTER", "SLAVE")): + raise TypeError, 'role must be either MASTER or SLAVE' + self.desc = args[2] + else: + raise TypeError, 'wrong number of arguments, should have ' \ + 'protocol, role and description' + # self.name is set by SimObject class on assignment + # e.g., pio_port = Port(...) sets self.name to 'pio_port' + + # Generate a PortRef for this port on the given SimObject with the + # given name + def makeRef(self, simobj): + return PortRef(simobj, self.name, self.protocol, self.role) + + # Connect an instance of this port (on the given SimObject with + # the given name) with the port described by the supplied PortRef + def connect(self, simobj, ref): + self.makeRef(simobj).connect(ref) + +# VectorPort description object. Like Port, but represents a vector +# of connections (e.g., as on a Bus). +class VectorPort(Port): + def __init__(self, *args): + Port.__init__(self, *args) + self.isVec = True + + def makeRef(self, simobj): + return VectorPortRef(simobj, self.name, self.protocol, self.role) + +# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of +# proxy objects (via set_param_desc()) so that proxy error messages +# make sense. +class PortParamDesc(object): + __metaclass__ = Singleton + + ptype_str = 'Port' + ptype = Port + # Port reference: encapsulates a reference to a particular port on a # particular SimObject. class PortRef(object): - def __init__(self, simobj, name): + def __init__(self, simobj, name, protocol, role): assert(isSimObject(simobj) or isSimObjectClass(simobj)) self.simobj = simobj self.name = name + self.protocol = protocol + self.role = role + if (self.name == "pio" and self.protocol != "MEM_MAP"): + raise AttributeError, "pio with %s" % (self.protocol) self.peer = None # not associated with another port yet self.ccConnected = False # C++ port connection done? self.index = -1 # always -1 for non-vector ports @@ -1277,10 +1338,11 @@ raise AttributeError, "'%s' object has no attribute '%s'" % \ (self.__class__.__name__, attr) - # Full connection is symmetric (both ways). Called via - # SimObject.__setattr__ as a result of a port assignment, e.g., - # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, - # e.g., "obj1.portA[3] = obj2.portB". + # Connect two ports. Called via SimObject.__setattr__ as a result + # of a port assignment, e.g., "obj1.portA = obj2.portB", or via + # VectorPortElementRef.__setitem__, e.g., "obj1.portA[3] = + # obj2.portB". Note that the call is made on the left hand side + # argument. def connect(self, other): if isinstance(other, VectorPortRef): # reference to plain VectorPort is implicit append @@ -1289,6 +1351,7 @@ print "warning: overwriting port", self, \ "value", self.peer, "with", other self.peer.peer = None + self.peer = other if proxy.isproxy(other): other.set_param_desc(PortParamDesc()) @@ -1328,14 +1391,31 @@ def ccConnect(self): from m5.internal.params import connectPorts + if self.role == "SLAVE": # let the master do the connection + return + if self.ccConnected: # already done this return peer = self.peer if not self.peer: # nothing to connect to return + + if str(self.protocol) != str(peer.protocol): + raise TypeError, \ + "cannot connect '%s' and '%s' due to different " \ + "protocols '%s' and '%s'" \ + % (peer, self, peer.protocol, self.protocol) + + if self.role == peer.role: + raise TypeError, \ + "cannot connect '%s' and '%s' due to identical role '%s'" \ + % (peer, self, self.role) + try: + # self is always the master connectPorts(self.simobj.getCCObject(), self.name, self.index, - peer.simobj.getCCObject(), peer.name, peer.index) + peer.simobj.getCCObject(), peer.name, peer.index, + self.protocol) except: print "Error connecting port %s.%s to %s.%s" % \ (self.simobj.path(), self.name, @@ -1347,8 +1427,8 @@ # A reference to an individual element of a VectorPort... much like a # PortRef, but has an index. class VectorPortElementRef(PortRef): - def __init__(self, simobj, name, index): - PortRef.__init__(self, simobj, name) + def __init__(self, simobj, name, protocol, role, index): + PortRef.__init__(self, simobj, name, protocol, role) self.index = index def __str__(self): @@ -1357,10 +1437,12 @@ # A reference to a complete vector-valued port (not just a single element). # Can be indexed to retrieve individual VectorPortElementRef instances. class VectorPortRef(object): - def __init__(self, simobj, name): + def __init__(self, simobj, name, protocol, role): assert(isSimObject(simobj) or isSimObjectClass(simobj)) self.simobj = simobj self.name = name + self.protocol = protocol + self.role = role self.elements = [] def __str__(self): @@ -1375,7 +1457,8 @@ raise TypeError, "VectorPort index must be integer" if key >= len(self.elements): # need to extend list - ext = [VectorPortElementRef(self.simobj, self.name, i) + ext = [VectorPortElementRef(self.simobj, self.name, + self.protocol, self.role, i) for i in range(len(self.elements), key+1)] self.elements.extend(ext) return self.elements[key] @@ -1415,50 +1498,7 @@ def ccConnect(self): [el.ccConnect() for el in self.elements] -# Port description object. Like a ParamDesc object, this represents a -# logical port in the SimObject class, not a particular port on a -# SimObject instance. The latter are represented by PortRef objects. -class Port(object): - # Port("description") or Port(default, "description") - def __init__(self, *args): - if len(args) == 1: - self.desc = args[0] - elif len(args) == 2: - self.default = args[0] - self.desc = args[1] - else: - raise TypeError, 'wrong number of arguments' - # self.name is set by SimObject class on assignment - # e.g., pio_port = Port("blah") sets self.name to 'pio_port' - # Generate a PortRef for this port on the given SimObject with the - # given name - def makeRef(self, simobj): - return PortRef(simobj, self.name) - - # Connect an instance of this port (on the given SimObject with - # the given name) with the port described by the supplied PortRef - def connect(self, simobj, ref): - self.makeRef(simobj).connect(ref) - -# VectorPort description object. Like Port, but represents a vector -# of connections (e.g., as on a Bus). -class VectorPort(Port): - def __init__(self, *args): - Port.__init__(self, *args) - self.isVec = True - - def makeRef(self, simobj): - return VectorPortRef(simobj, self.name) - -# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of -# proxy objects (via set_param_desc()) so that proxy error messages -# make sense. -class PortParamDesc(object): - __metaclass__ = Singleton - - ptype_str = 'Port' - ptype = Port baseEnums = allEnums.copy() baseParams = allParams.copy() diff -r 6548721032fa -r 042f38a2e360 src/python/m5/simulate.py --- a/src/python/m5/simulate.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/m5/simulate.py Fri Aug 05 18:14:06 2011 +0100 @@ -73,13 +73,26 @@ # Initialize the global statistics stats.initSimStats() - # Create the C++ sim objects and connect ports + # Create the C++ sim objects for obj in root.descendants(): obj.createCCObject() + + # Set the parent/child relation to the root + for obj in root.descendants(): + if obj.get_parent() != None: + obj.setParent(obj.get_parent()._ccObject) + + # Connect all ports for obj in root.descendants(): obj.connectPorts() # Do a second pass to finish initializing the sim objects for obj in root.descendants(): obj.init() + # Print a structure diagram showing what the system architecture + # looks like + if options.dump_structure: + dot_file = options.dump_structure + root.printDot(dot_file) + # Do a third pass to initialize statistics for obj in root.descendants(): obj.regStats() for obj in root.descendants(): obj.regFormulas() @@ -87,6 +100,8 @@ # We're done registering statistics. Enable the stats package now. stats.enable() + for obj in root.descendants(): obj.start_of_simulation() + # Restore checkpoint (if any) if ckpt_dir: ckpt = internal.core.getCheckpoint(ckpt_dir) diff -r 6548721032fa -r 042f38a2e360 src/python/swig/pyobject.hh --- a/src/python/swig/pyobject.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/swig/pyobject.hh Fri Aug 05 18:14:06 2011 +0100 @@ -41,7 +41,8 @@ * Connect the described MemObject ports. Called from Python via SWIG. */ int connectPorts(SimObject *o1, const std::string &name1, int i1, - SimObject *o2, const std::string &name2, int i2); + SimObject *o2, const std::string &name2, int i2, + const std::string& protocol); inline void diff -r 6548721032fa -r 042f38a2e360 src/python/swig/pyobject.cc --- a/src/python/swig/pyobject.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/swig/pyobject.cc Fri Aug 05 18:14:06 2011 +0100 @@ -47,24 +47,6 @@ using namespace std; -/** - * Look up a MemObject port. Helper function for connectPorts(). - */ -Port * -lookupPort(SimObject *so, const std::string &name, int i) -{ - MemObject *mo = dynamic_cast(so); - if (mo == NULL) { - warn("error casting SimObject %s to MemObject", so->name()); - return NULL; - } - - Port *p = mo->getPort(name, i); - if (p == NULL) - warn("error looking up port %s on object %s", name, so->name()); - return p; -} - #if FULL_SYSTEM EtherInt * @@ -92,44 +74,54 @@ */ int connectPorts(SimObject *o1, const std::string &name1, int i1, - SimObject *o2, const std::string &name2, int i2) + SimObject *o2, const std::string &name2, int i2, + const std::string& protocol) { MemObject *mo1, *mo2; mo1 = dynamic_cast(o1); mo2 = dynamic_cast(o2); #if FULL_SYSTEM - EtherObject *eo1, *eo2; - EtherDevice *ed1, *ed2; - eo1 = dynamic_cast(o1); - ed1 = dynamic_cast(o1); + if (protocol == "ETH") { + EtherObject *eo1, *eo2; + EtherDevice *ed1, *ed2; + eo1 = dynamic_cast(o1); + ed1 = dynamic_cast(o1); - eo2 = dynamic_cast(o2); - ed2 = dynamic_cast(o2); + eo2 = dynamic_cast(o2); + ed2 = dynamic_cast(o2); - if ((eo1 || ed1) && (eo2 || ed2)) { - EtherInt *p1 = lookupEthPort(o1, name1, i1); - EtherInt *p2 = lookupEthPort(o2, name2, i2); + if ((eo1 || ed1) && (eo2 || ed2)) { + EtherInt *p1 = lookupEthPort(o1, name1, i1); + EtherInt *p2 = lookupEthPort(o2, name2, i2); - if (p1 != NULL && p2 != NULL) { + if (p1 != NULL && p2 != NULL) { - p1->setPeer(p2); - p2->setPeer(p1); + p1->setPeer(p2); + p2->setPeer(p1); - return 1; + return 1; + } } } #endif - Port *p1 = lookupPort(o1, name1, i1); - Port *p2 = lookupPort(o2, name2, i2); - if (p1 == NULL || p2 == NULL) { - warn("connectPorts: port lookup error"); - return 0; + if (mo1 == NULL || mo2 == NULL) { + panic("Error casting SimObjects %s and %s to MemObject", o1->name(), + o2->name()); } - p1->setPeer(p2); - p2->setPeer(p1); + if (protocol == "CACHE_MNT") { + CacheMntMasterPort* master_port = mo1->getCacheMntMasterPort(name1, i1); + CacheMntSlavePort* slave_port = mo2->getCacheMntSlavePort(name2, i2); + master_port->bind(slave_port); + } else if (protocol == "MEM_MAP") { + MemMapMasterPort* master_port = mo1->getMemMapMasterPort(name1, i1); + MemMapSlavePort* slave_port = mo2->getMemMapSlavePort(name2, i2); + master_port->bind(slave_port); + } else { + panic("Unknown protocol %s", protocol); + } return 1; } diff -r 6548721032fa -r 042f38a2e360 src/python/swig/sim_object.i --- a/src/python/swig/sim_object.i Fri Feb 11 18:29:36 2011 -0600 +++ b/src/python/swig/sim_object.i Fri Aug 05 18:14:06 2011 +0100 @@ -50,6 +50,8 @@ }; void init(); + void start_of_simulation(); + void loadState(Checkpoint *cp); void initState(); void regStats(); @@ -62,10 +64,15 @@ void switchOut(); void takeOverFrom(BaseCPU *cpu); SimObject(const SimObjectParams *p); + + void setParent(SimObject *p); + + void printDot(const std::string& file_name); }; int connectPorts(SimObject *o1, const std::string &name1, int i1, - SimObject *o2, const std::string &name2, int i2); + SimObject *o2, const std::string &name2, int i2, + const std::string& protocol); %wrapper %{ // Convert a pointer to the Python object that SWIG wraps around a diff -r 6548721032fa -r 042f38a2e360 src/sim/System.py --- a/src/sim/System.py Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/System.py Fri Aug 05 18:14:06 2011 +0100 @@ -28,23 +28,26 @@ # Authors: Nathan Binkert # Rick Strong -from m5.SimObject import SimObject from m5.defines import buildEnv from m5.params import * from m5.proxy import * +from MemObject import * from PhysicalMemory import * class MemoryMode(Enum): vals = ['invalid', 'atomic', 'timing'] -class System(SimObject): +class System(MemObject): type = 'System' @classmethod def swig_objdecls(cls, code): code('%include "python/swig/system.i"') - physmem = Param.PhysicalMemory(Parent.any, "physical memory") + system_port = Port("MEM_MAP", "MASTER", "Loading port") + + memsize = Param.MemorySize('0MB', "size of main memory") + mem_mode = Param.MemoryMode('atomic', "The mode the memory system is in") work_item_id = Param.Int(-1, "specific work item id") diff -r 6548721032fa -r 042f38a2e360 src/sim/arguments.hh --- a/src/sim/arguments.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/arguments.hh Fri Aug 05 18:14:06 2011 +0100 @@ -36,7 +36,7 @@ #include "arch/vtophys.hh" #include "base/refcnt.hh" #include "base/types.hh" -#include "mem/vport.hh" +#include "mem/fs_translating_proxy.hh" class ThreadContext; diff -r 6548721032fa -r 042f38a2e360 src/sim/process.hh --- a/src/sim/process.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/process.hh Fri Aug 05 18:14:06 2011 +0100 @@ -57,7 +57,7 @@ class SyscallDesc; class System; class ThreadContext; -class TranslatingPort; +class SETranslatingProxy; template struct AuxVector @@ -131,8 +131,8 @@ virtual void initState(); protected: - /// Memory object for initialization (image loading) - TranslatingPort *initVirtMem; + // MemoryProxy for image loading + SETranslatingProxy *initProxy; public: PageTable *pTable; diff -r 6548721032fa -r 042f38a2e360 src/sim/process.cc --- a/src/sim/process.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/process.cc Fri Aug 05 18:14:06 2011 +0100 @@ -45,7 +45,7 @@ #include "cpu/thread_context.hh" #include "mem/page_table.hh" #include "mem/physical.hh" -#include "mem/translating_port.hh" +#include "mem/se_translating_proxy.hh" #include "params/Process.hh" #include "params/LiveProcess.hh" #include "sim/debug.hh" @@ -58,17 +58,8 @@ #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" #elif THE_ISA == ARM_ISA #include "arch/arm/linux/process.hh" -#elif THE_ISA == X86_ISA -#include "arch/x86/linux/process.hh" -#elif THE_ISA == POWER_ISA -#include "arch/power/linux/process.hh" #else #error "THE_ISA not set" #endif @@ -244,12 +235,10 @@ // mark this context as active so it will start ticking. tc->activate(0); - Port *mem_port; - mem_port = system->physmem->getPort("functional"); - initVirtMem = new TranslatingPort("process init port", this, - TranslatingPort::Always); - mem_port->setPeer(initVirtMem); - initVirtMem->setPeer(mem_port); + + initProxy = new SETranslatingProxy(this, SETranslatingProxy::Always, + systemPort()); + } // map simulator fd sim_fd to target fd tgt_fd @@ -627,63 +616,6 @@ default: fatal("Unknown/unsupported operating system."); } -#elif THE_ISA == SPARC_ISA - if (objFile->getArch() != ObjectFile::SPARC64 && - objFile->getArch() != ObjectFile::SPARC32) - fatal("Object file architecture does not match compiled ISA (SPARC)."); - switch (objFile->getOpSys()) { - case ObjectFile::UnknownOpSys: - warn("Unknown operating system; assuming Linux."); - // fall through - case ObjectFile::Linux: - if (objFile->getArch() == ObjectFile::SPARC64) { - process = new Sparc64LinuxProcess(params, objFile); - } else { - process = new Sparc32LinuxProcess(params, objFile); - } - break; - - - case ObjectFile::Solaris: - process = new SparcSolarisProcess(params, objFile); - break; - - default: - fatal("Unknown/unsupported operating system."); - } -#elif THE_ISA == X86_ISA - if (objFile->getArch() != ObjectFile::X86_64 && - objFile->getArch() != ObjectFile::I386) - fatal("Object file architecture does not match compiled ISA (x86)."); - switch (objFile->getOpSys()) { - case ObjectFile::UnknownOpSys: - warn("Unknown operating system; assuming Linux."); - // fall through - case ObjectFile::Linux: - if (objFile->getArch() == ObjectFile::X86_64) { - process = new X86_64LinuxProcess(params, objFile); - } else { - process = new I386LinuxProcess(params, objFile); - } - 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::UnknownOpSys: - warn("Unknown operating system; assuming Linux."); - // fall through - case ObjectFile::Linux: - process = new MipsLinuxProcess(params, objFile); - break; - - default: - fatal("Unknown/unsupported operating system."); - } #elif THE_ISA == ARM_ISA if (objFile->getArch() != ObjectFile::Arm && objFile->getArch() != ObjectFile::Thumb) @@ -701,20 +633,6 @@ default: fatal("Unknown/unsupported operating system."); } -#elif THE_ISA == POWER_ISA - if (objFile->getArch() != ObjectFile::Power) - fatal("Object file architecture does not match compiled ISA (Power)."); - switch (objFile->getOpSys()) { - case ObjectFile::UnknownOpSys: - warn("Unknown operating system; assuming Linux."); - // fall through - case ObjectFile::Linux: - process = new PowerLinuxProcess(params, objFile); - break; - - default: - fatal("Unknown/unsupported operating system."); - } #else #error "THE_ISA not set" #endif diff -r 6548721032fa -r 042f38a2e360 src/sim/process_impl.hh --- a/src/sim/process_impl.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/process_impl.hh Fri Aug 05 18:14:06 2011 +0100 @@ -44,7 +44,7 @@ #include #include -#include "mem/translating_port.hh" +#include "mem/se_translating_proxy.hh" #include "sim/byteswap.hh" @@ -53,21 +53,21 @@ void copyStringArray(std::vector &strings, AddrType array_ptr, AddrType data_ptr, - TranslatingPort* memPort) + SETranslatingProxy* memProxy) { AddrType data_ptr_swap; for (std::vector::size_type i = 0; i < strings.size(); ++i) { data_ptr_swap = htog(data_ptr); - memPort->writeBlob(array_ptr, (uint8_t*)&data_ptr_swap, + memProxy->writeMem(array_ptr, (uint8_t*)&data_ptr_swap, sizeof(AddrType)); - memPort->writeString(data_ptr, strings[i].c_str()); + memProxy->writeString(data_ptr, strings[i].c_str()); array_ptr += sizeof(AddrType); data_ptr += strings[i].size() + 1; } // add NULL terminator data_ptr = 0; - memPort->writeBlob(array_ptr, (uint8_t*)&data_ptr, sizeof(AddrType)); + memProxy->writeMem(array_ptr, (uint8_t*)&data_ptr, sizeof(AddrType)); } diff -r 6548721032fa -r 042f38a2e360 src/sim/pseudo_inst.hh --- a/src/sim/pseudo_inst.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/pseudo_inst.hh Fri Aug 05 18:14:06 2011 +0100 @@ -44,8 +44,10 @@ #if FULL_SYSTEM void arm(ThreadContext *tc); +void quiesceSkip(ThreadContext *tc); void quiesce(ThreadContext *tc); void quiesceNs(ThreadContext *tc, uint64_t ns); +void quiesceUs(ThreadContext *tc, uint64_t us); void quiesceCycles(ThreadContext *tc, uint64_t cycles); uint64_t quiesceTime(ThreadContext *tc); uint64_t readfile(ThreadContext *tc, Addr vaddr, uint64_t len, diff -r 6548721032fa -r 042f38a2e360 src/sim/pseudo_inst.cc --- a/src/sim/pseudo_inst.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/pseudo_inst.cc Fri Aug 05 18:14:06 2011 +0100 @@ -53,7 +53,7 @@ #if FULL_SYSTEM #include "arch/kernel_stats.hh" -#include "sim/vptr.hh" +#include "mem/fs_translating_proxy.hh" #endif using namespace std; @@ -73,6 +73,12 @@ } void +quiesceSkip(ThreadContext *tc) +{ + tc->wakeFromQuiesce(); +} + +void quiesce(ThreadContext *tc) { if (!tc->getCpuPtr()->params()->do_quiesce) @@ -108,6 +114,29 @@ } void +quiesceUs(ThreadContext *tc, uint64_t us) +{ + BaseCPU *cpu = tc->getCpuPtr(); + + if (!cpu->params()->do_quiesce || us == 0) + return; + + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); + + Tick resume = curTick() + SimClock::Int::us * us; + + cpu->reschedule(quiesceEvent, resume, true); + + DPRINTF(Quiesce, "%s: quiesceUs(%d) until %d\n", + cpu->name(), us, resume); + + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); +} + + +void quiesceCycles(ThreadContext *tc, uint64_t cycles) { BaseCPU *cpu = tc->getCpuPtr(); diff -r 6548721032fa -r 042f38a2e360 src/sim/root.hh --- a/src/sim/root.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/root.hh Fri Aug 05 18:14:06 2011 +0100 @@ -111,6 +111,8 @@ /** Schedule the timesync event at initState() when not unserializing */ void initState(); + + void printDot(const std::string& file_name); }; #endif // __SIM_ROOT_HH__ diff -r 6548721032fa -r 042f38a2e360 src/sim/root.cc --- a/src/sim/root.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/root.cc Fri Aug 05 18:14:06 2011 +0100 @@ -32,6 +32,7 @@ */ #include "base/misc.hh" +#include "base/output.hh" #include "sim/root.hh" Root *Root::_root = NULL; @@ -122,6 +123,33 @@ timeSyncEnable(params()->time_sync_enable); } +void +Root::printDot(const std::string& file_name) +{ + // Create a file for the dot output + std::ostream* dotFile = simout.create(file_name); + + // The root always has one child per system. + for (std::vector::const_iterator c = _children.begin(); + c != _children.end(); ++c) { + // Open the graph for the system. + *dotFile << "digraph " << (*c)->shortName() << " {\n" << + "graph [overlap = false];\n"; + + // Go through each system and print the nodes and edges of the + // dot graph + (*c)->printDotNodes(*dotFile); + (*c)->printDotEdges(*dotFile); + + // Close the graph; + *dotFile << "\n}\n"; + } + + // Delete the pointer to the ostream which also should call close on the + // ofstream it corresponds to. + delete dotFile; +} + Root * RootParams::create() { diff -r 6548721032fa -r 042f38a2e360 src/sim/sim_object.hh --- a/src/sim/sim_object.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/sim_object.hh Fri Aug 05 18:14:06 2011 +0100 @@ -38,14 +38,13 @@ #define __SIM_OBJECT_HH__ #include -#include -#include #include #include #include "params/SimObject.hh" #include "sim/eventq.hh" #include "sim/serialize.hh" +#include "mem/port.hh" class BaseCPU; class Event; @@ -65,11 +64,31 @@ }; private: + SimObject *_parent; + + public: + void setParent(SimObject *p) + { + _parent = p; + _parent->_children.push_back(this); + } + + virtual void printDot(const std::string& file_name) {} + + virtual void printDotNodes(std::ostream& os) const; + + virtual void printDotEdges(std::ostream& os) const; + + virtual MemMapMasterPort* systemPort() { return _parent->systemPort(); } + + private: State state; protected: void changeState(State new_state) { state = new_state; } + std::vector _children; + public: State getState() { return state; } @@ -90,7 +109,9 @@ public: - virtual const std::string name() const { return params()->name; } + virtual const std::string name() const { return params()->path; } + + virtual std::string shortName() const { return params()->name; } // The following SimObject initialization methods are called from // the instantiate() method in src/python/m5/simulate.py. See @@ -98,14 +119,25 @@ // invoked. /** - * init() is called after all C++ SimObjects have been created and - * all ports are connected. Initializations that are independent - * of unserialization but rely on a fully instantiated and - * connected SimObject graph should be done here. + * Init corresponds to end_of_elaboration() in SystemC and is + * called after all C++ SimObjects have been created and all ports + * are connected. This is an opportunity for the hardware modules + * to do any initializations that are independent of + * unserialization but rely on a fully instantiated and connected + * SimObject graph. */ virtual void init(); /** + * Called before simulation starts. Simulation-specific + * intialisation code, e.g. loading of binaries etc can be done + * here. All the hardware should be initialised and ready. At this + * point debug accesses to memory can be made as all the + * address-maps are resolved. + */ + virtual void start_of_simulation(); + + /** * loadState() is called on each SimObject when restoring from a * checkpoint. The default implementation simply calls * unserialize() if there is a corresponding section in the diff -r 6548721032fa -r 042f38a2e360 src/sim/sim_object.cc --- a/src/sim/sim_object.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/sim_object.cc Fri Aug 05 18:14:06 2011 +0100 @@ -75,6 +75,11 @@ } void +SimObject::start_of_simulation() +{ +} + +void SimObject::loadState(Checkpoint *cp) { if (cp->sectionExists(name())) @@ -215,3 +220,39 @@ return NULL; } + +void +SimObject::printDotNodes(std::ostream& os) const +{ + // this object only serves to encapsulate a bunch of mem objects, + // so merely add a level of subgraphs + if (!_children.empty()) { + os << + "subgraph cluster_" << shortName() << " {\n" << + "style=\"rounded,filled\";\n" << + "color=\"#5c5b77\";\n" << + "fillcolor=\"#e7e6f8\";\n" << + "fontname=\"Arial\";\n" << + "fontsize=14;\n" << + "fontcolor=\"#5c5b77\";\n" << + "label=\"" << shortName() << "\";\n"; + + // iterate over the children + for (std::vector::const_iterator c = _children.begin(); + c != _children.end(); ++c) { + (*c)->printDotNodes(os); + } + + os << "\n}\n"; + } +} + +void +SimObject::printDotEdges(std::ostream& os) const +{ + // iterate over the children + for (std::vector::const_iterator c = _children.begin(); + c != _children.end(); ++c) { + (*c)->printDotEdges(os); + } +} diff -r 6548721032fa -r 042f38a2e360 src/sim/sim_object_params.hh --- a/src/sim/sim_object_params.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/sim_object_params.hh Fri Aug 05 18:14:06 2011 +0100 @@ -49,7 +49,17 @@ } virtual ~SimObjectParams() {} + /** + * The path contains the concatenated name of the parent and this + * SimObject. + */ + std::string path; + + /** + * The name contains the name of this SimObject alone. + */ std::string name; + PyObject *pyobj; EventQueue *eventq; }; diff -r 6548721032fa -r 042f38a2e360 src/sim/simulate.cc --- a/src/sim/simulate.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/simulate.cc Fri Aug 05 18:14:06 2011 +0100 @@ -84,7 +84,8 @@ if (se_event != limit_event) { assert(limit_event->scheduled()); limit_event->squash(); - hack_once("be nice to actually delete the event here"); + mainEventQueue.deschedule(limit_event); + delete limit_event; } return se_event; diff -r 6548721032fa -r 042f38a2e360 src/sim/syscall_emul.hh --- a/src/sim/syscall_emul.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/syscall_emul.hh Fri Aug 05 18:14:06 2011 +0100 @@ -60,7 +60,7 @@ #include "config/the_isa.hh" #include "cpu/base.hh" #include "cpu/thread_context.hh" -#include "mem/translating_port.hh" +#include "mem/se_translating_proxy.hh" #include "mem/page_table.hh" #include "sim/byteswap.hh" #include "sim/system.hh" @@ -119,18 +119,18 @@ // // copy data into simulator space (read from target memory) // - virtual bool copyIn(TranslatingPort *memport) + virtual bool copyIn(SETranslatingProxy *memProxy) { - memport->readBlob(addr, bufPtr, size); + memProxy->readMem(addr, bufPtr, size); return true; // no EFAULT detection for now } // // copy data out of simulator space (write to target memory) // - virtual bool copyOut(TranslatingPort *memport) + virtual bool copyOut(SETranslatingProxy *memProxy) { - memport->writeBlob(addr, bufPtr, size); + memProxy->writeMem(addr, bufPtr, size); return true; // no EFAULT detection for now } @@ -460,24 +460,24 @@ //Here are a couple convenience functions template static void -copyOutStatBuf(TranslatingPort * mem, Addr addr, +copyOutStatBuf(SETranslatingProxy * memProxy, Addr addr, hst_stat *host, bool fakeTTY = false) { typedef TypedBufferArg tgt_stat_buf; tgt_stat_buf tgt(addr); convertStatBuf(tgt, host, fakeTTY); - tgt.copyOut(mem); + tgt.copyOut(memProxy); } template static void -copyOutStat64Buf(TranslatingPort * mem, Addr addr, +copyOutStat64Buf(SETranslatingProxy * memProxy, Addr addr, hst_stat64 *host, bool fakeTTY = false) { typedef TypedBufferArg tgt_stat_buf; tgt_stat_buf tgt(addr); convertStat64Buf(tgt, host, fakeTTY); - tgt.copyOut(mem); + tgt.copyOut(memProxy); } /// Target ioctl() handler. For the most part, programs call ioctl() @@ -526,7 +526,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) return -EFAULT; @@ -590,7 +590,7 @@ sysinfo->uptime=seconds_since_epoch; sysinfo->totalram=process->system->memSize(); - sysinfo.copyOut(tc->getMemPort()); + sysinfo.copyOut(tc->getMemProxy()); return 0; } @@ -604,7 +604,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -710,7 +710,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -725,7 +725,7 @@ if (result < 0) return -errno; - copyOutStatBuf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStatBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -740,7 +740,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) return -EFAULT; Addr bufPtr = process->getSyscallArg(tc, index); @@ -759,7 +759,7 @@ if (result < 0) return -errno; - copyOutStat64Buf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -790,7 +790,7 @@ if (result < 0) return -errno; - copyOutStat64Buf(tc->getMemPort(), bufPtr, &hostBuf, (fd == 1)); + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf, (fd == 1)); return 0; } @@ -805,7 +805,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -820,7 +820,7 @@ if (result < 0) return -errno; - copyOutStatBuf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStatBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -834,7 +834,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -854,7 +854,7 @@ if (result < 0) return -errno; - copyOutStat64Buf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -880,7 +880,7 @@ if (result < 0) return -errno; - copyOutStatBuf(tc->getMemPort(), bufPtr, &hostBuf, (fd == 1)); + copyOutStatBuf(tc->getMemProxy(), bufPtr, &hostBuf, (fd == 1)); return 0; } @@ -895,7 +895,7 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -910,7 +910,7 @@ if (result < 0) return -errno; - OS::copyOutStatfsBuf(tc->getMemPort(), bufPtr, &hostBuf); + OS::copyOutStatfsBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -935,7 +935,7 @@ if (result < 0) return -errno; - OS::copyOutStatfsBuf(tc->getMemPort(), bufPtr, &hostBuf); + OS::copyOutStatfsBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -954,18 +954,18 @@ return -EBADF; } - TranslatingPort *p = tc->getMemPort(); + SETranslatingProxy *p = tc->getMemProxy(); uint64_t tiov_base = process->getSyscallArg(tc, index); size_t count = process->getSyscallArg(tc, index); struct iovec hiov[count]; for (size_t i = 0; i < count; ++i) { typename OS::tgt_iovec tiov; - p->readBlob(tiov_base + i*sizeof(typename OS::tgt_iovec), + p->readMem(tiov_base + i*sizeof(typename OS::tgt_iovec), (uint8_t*)&tiov, sizeof(typename OS::tgt_iovec)); hiov[i].iov_len = gtoh(tiov.iov_len); hiov[i].iov_base = new char [hiov[i].iov_len]; - p->readBlob(gtoh(tiov.iov_base), (uint8_t *)hiov[i].iov_base, + p->readMem(gtoh(tiov.iov_base), (uint8_t *)hiov[i].iov_base, hiov[i].iov_len); } @@ -1069,7 +1069,7 @@ break; } - rlp.copyOut(tc->getMemPort()); + rlp.copyOut(tc->getMemProxy()); return 0; } @@ -1087,7 +1087,7 @@ tp->tv_sec = TheISA::htog(tp->tv_sec); tp->tv_usec = TheISA::htog(tp->tv_usec); - tp.copyOut(tc->getMemPort()); + tp.copyOut(tc->getMemProxy()); return 0; } @@ -1102,14 +1102,14 @@ std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } TypedBufferArg tp(process->getSyscallArg(tc, index)); - tp.copyIn(tc->getMemPort()); + tp.copyIn(tc->getMemProxy()); struct timeval hostTimeval[2]; for (int i = 0; i < 2; ++i) @@ -1175,7 +1175,7 @@ who); } - rup.copyOut(tc->getMemPort()); + rup.copyOut(tc->getMemProxy()); return 0; } @@ -1200,7 +1200,7 @@ bufp->tms_utime = htog(bufp->tms_utime); // Write back - bufp.copyOut(tc->getMemPort()); + bufp.copyOut(tc->getMemProxy()); // Return clock ticks since system boot return clocks; @@ -1221,8 +1221,8 @@ if(taddr != 0) { typename OS::time_t t = sec; t = htog(t); - TranslatingPort *p = tc->getMemPort(); - p->writeBlob(taddr, (uint8_t*)&t, (int)sizeof(typename OS::time_t)); + SETranslatingProxy *p = tc->getMemProxy(); + p->writeMem(taddr, (uint8_t*)&t, (int)sizeof(typename OS::time_t)); } return sec; } diff -r 6548721032fa -r 042f38a2e360 src/sim/syscall_emul.cc --- a/src/sim/syscall_emul.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/syscall_emul.cc Fri Aug 05 18:14:06 2011 +0100 @@ -159,18 +159,18 @@ // if the address is already there, zero it out else { uint8_t zero = 0; - TranslatingPort *tp = tc->getMemPort(); + SETranslatingProxy *tp = tc->getMemProxy(); // split non-page aligned accesses Addr next_page = roundUp(gen.addr(), VMPageSize); uint32_t size_needed = next_page - gen.addr(); - tp->memsetBlob(gen.addr(), zero, size_needed); + tp->setMem(gen.addr(), zero, size_needed); if (gen.addr() + VMPageSize > next_page && next_page < new_brk && p->pTable->translate(next_page)) { size_needed = VMPageSize - size_needed; - tp->memsetBlob(next_page, zero, size_needed); + tp->setMem(next_page, zero, size_needed); } } } @@ -209,7 +209,7 @@ int bytes_read = read(fd, bufArg.bufferPtr(), nbytes); if (bytes_read != -1) - bufArg.copyOut(tc->getMemPort()); + bufArg.copyOut(tc->getMemProxy()); return bytes_read; } @@ -223,7 +223,7 @@ int nbytes = p->getSyscallArg(tc, index); BufferArg bufArg(bufPtr, nbytes); - bufArg.copyIn(tc->getMemPort()); + bufArg.copyIn(tc->getMemProxy()); int bytes_written = write(fd, bufArg.bufferPtr(), nbytes); @@ -272,7 +272,7 @@ // target platform BufferArg result_buf(result_ptr, sizeof(result)); memcpy(result_buf.bufferPtr(), &result, sizeof(result)); - result_buf.copyOut(tc->getMemPort()); + result_buf.copyOut(tc->getMemProxy()); return 0; } @@ -301,7 +301,7 @@ strncpy((char *)name.bufferPtr(), hostname, name_len); - name.copyOut(tc->getMemPort()); + name.copyOut(tc->getMemProxy()); return 0; } @@ -334,7 +334,7 @@ } } - buf.copyOut(tc->getMemPort()); + buf.copyOut(tc->getMemProxy()); return (result == -1) ? -errno : result; } @@ -346,7 +346,7 @@ string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(path, p->getSyscallArg(tc, index))) return (TheISA::IntReg)-EFAULT; // Adjust path for current working directory @@ -359,7 +359,7 @@ int result = readlink(path.c_str(), (char *)buf.bufferPtr(), bufsiz); - buf.copyOut(tc->getMemPort()); + buf.copyOut(tc->getMemProxy()); return (result == -1) ? -errno : result; } @@ -370,7 +370,7 @@ string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(path, p->getSyscallArg(tc, index))) return (TheISA::IntReg)-EFAULT; // Adjust path for current working directory @@ -387,7 +387,7 @@ string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(path, p->getSyscallArg(tc, index))) return (TheISA::IntReg)-EFAULT; // Adjust path for current working directory @@ -405,12 +405,12 @@ string old_name; int index = 0; - if (!tc->getMemPort()->tryReadString(old_name, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(old_name, p->getSyscallArg(tc, index))) return -EFAULT; string new_name; - if (!tc->getMemPort()->tryReadString(new_name, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(new_name, p->getSyscallArg(tc, index))) return -EFAULT; // Adjust path for current working directory @@ -427,7 +427,7 @@ string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(path, p->getSyscallArg(tc, index))) return -EFAULT; off_t length = p->getSyscallArg(tc, index); @@ -462,7 +462,7 @@ int index = 0; string path; - if (!tc->getMemPort()->tryReadString(path, process->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(path, process->getSyscallArg(tc, index))) return -EFAULT; int64_t length = process->getSyscallArg(tc, index, 64); @@ -515,7 +515,7 @@ string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, p->getSyscallArg(tc, index))) + if (!tc->getMemProxy()->tryReadString(path, p->getSyscallArg(tc, index))) return -EFAULT; /* XXX endianess */ @@ -784,24 +784,24 @@ ctc->clearArchRegs(); // Arch-specific cloning code - #if THE_ISA == ALPHA_ISA or THE_ISA == X86_ISA + #if THE_ISA == ALPHA_ISA //or THE_ISA == X86_ISA // Cloning the misc. regs for these archs is enough TheISA::copyMiscRegs(tc, ctc); - #elif THE_ISA == SPARC_ISA - TheISA::copyRegs(tc, ctc); - - // TODO: Explain what this code actually does :-) - ctc->setIntReg(NumIntArchRegs + 6, 0); - ctc->setIntReg(NumIntArchRegs + 4, 0); - ctc->setIntReg(NumIntArchRegs + 3, NWindows - 2); - ctc->setIntReg(NumIntArchRegs + 5, NWindows); - ctc->setMiscReg(MISCREG_CWP, 0); - ctc->setIntReg(NumIntArchRegs + 7, 0); - ctc->setMiscRegNoEffect(MISCREG_TL, 0); - ctc->setMiscRegNoEffect(MISCREG_ASI, ASI_PRIMARY); - - for (int y = 8; y < 32; y++) - ctc->setIntReg(y, tc->readIntReg(y)); +// #elif THE_ISA == SPARC_ISA +// TheISA::copyRegs(tc, ctc); +// +// // TODO: Explain what this code actually does :-) +// ctc->setIntReg(NumIntArchRegs + 6, 0); +// ctc->setIntReg(NumIntArchRegs + 4, 0); +// ctc->setIntReg(NumIntArchRegs + 3, NWindows - 2); +// ctc->setIntReg(NumIntArchRegs + 5, NWindows); +// ctc->setMiscReg(MISCREG_CWP, 0); +// ctc->setIntReg(NumIntArchRegs + 7, 0); +// ctc->setMiscRegNoEffect(MISCREG_TL, 0); +// ctc->setMiscRegNoEffect(MISCREG_ASI, ASI_PRIMARY); +// +// for (int y = 8; y < 32; y++) +// ctc->setIntReg(y, tc->readIntReg(y)); #else fatal("sys_clone is not implemented for this ISA\n"); #endif @@ -819,10 +819,10 @@ // In SPARC/Linux, clone returns 0 on pseudo-return register if // parent, non-zero if child - #if THE_ISA == SPARC_ISA - tc->setIntReg(TheISA::SyscallPseudoReturnReg, 0); - ctc->setIntReg(TheISA::SyscallPseudoReturnReg, 1); - #endif +// #if THE_ISA == SPARC_ISA +// tc->setIntReg(TheISA::SyscallPseudoReturnReg, 0); +// ctc->setIntReg(TheISA::SyscallPseudoReturnReg, 1); +// #endif ctc->pcState(tc->nextInstAddr()); diff -r 6548721032fa -r 042f38a2e360 src/sim/system.hh --- a/src/sim/system.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/system.hh Fri Aug 05 18:14:06 2011 +0100 @@ -46,7 +46,8 @@ #include "enums/MemoryMode.hh" #include "mem/port.hh" #include "params/System.hh" -#include "sim/sim_object.hh" +#include "mem/mem_object.hh" + #if FULL_SYSTEM #include "kern/system_events.hh" @@ -55,20 +56,38 @@ class BaseCPU; class ThreadContext; class ObjectFile; -class PhysicalMemory; #if FULL_SYSTEM class Platform; -class FunctionalPort; -class VirtualPort; +class PortProxy; #endif class GDBListener; class BaseRemoteGDB; -class System : public SimObject +class System : public MemObject, public MemMapMasterInterface { public: + uint64_t memsize; + + private: + + MemMapMasterPort _systemPort; + + public: + /** Master interface methods */ + void endReq(MemMapPacketPtr req_pkt, int master_id) {} + + void beginResp(MemMapPacketPtr resp_pkt, int master_id) {} + + void recvRangeChange(int master_id) {} + /** End of master interface */ + + public: + + void virtual start_of_simulation(); + MemMapMasterPort* systemPort() { return &_systemPort; } + static const char *MemoryModeStrings[3]; Enums::MemoryMode @@ -78,13 +97,16 @@ return memoryMode; } - /** Change the memory mode of the system. This should only be called by the - * python!! + + /** + * Change the memory mode of the system. This should only be + * called at initialisation time and not at run time. + * * @param mode Mode to change to (atomic/timing) */ void setMemoryMode(Enums::MemoryMode mode); - PhysicalMemory *physmem; + public: PCEventQueue pcEventQueue; std::vector threadContexts; @@ -109,10 +131,9 @@ Platform *platform; uint64_t init_param; - /** Port to physical memory used for writing object files into ram at - * boot.*/ - FunctionalPort *functionalPort; - VirtualPort *virtPort; + /// memory proxy for initalisation + PortProxy *initProxy; + /** kernel symbol table */ SymbolTable *kernelSymtab; diff -r 6548721032fa -r 042f38a2e360 src/sim/system.cc --- a/src/sim/system.cc Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/system.cc Fri Aug 05 18:14:06 2011 +0100 @@ -43,7 +43,6 @@ #include "config/full_system.hh" #include "config/the_isa.hh" #include "mem/mem_object.hh" -#include "mem/physical.hh" #include "sim/byteswap.hh" #include "sim/system.hh" #include "sim/debug.hh" @@ -51,7 +50,7 @@ #if FULL_SYSTEM #include "arch/vtophys.hh" #include "kern/kernel_stats.hh" -#include "mem/vport.hh" +#include "mem/port_proxy.hh" #else #include "params/System.hh" #endif @@ -64,7 +63,9 @@ int System::numSystemsRunning = 0; System::System(Params *p) - : SimObject(p), physmem(p->physmem), _numContexts(0), + : MemObject(p), memsize(p->memsize), + _systemPort(*this, this, "system_port"), + _numContexts(0), #if FULL_SYSTEM init_param(p->init_param), loadAddrMask(p->load_addr_mask), @@ -79,6 +80,8 @@ totalNumInsts(0), instEventQueue("system instruction-based event queue") { + + // add self to global system list systemList.push_back(this); @@ -87,25 +90,28 @@ if (!debugSymbolTable) debugSymbolTable = new SymbolTable; + initProxy = new PortProxy(*systemPort()); +#endif - /** - * Get a functional port to memory - */ - Port *mem_port; - functionalPort = new FunctionalPort(name() + "-fport"); - mem_port = physmem->getPort("functional"); - functionalPort->setPeer(mem_port); - mem_port->setPeer(functionalPort); + regPort(&_systemPort); +} - virtPort = new VirtualPort(name() + "-fport"); - mem_port = physmem->getPort("functional"); - virtPort->setPeer(mem_port); - mem_port->setPeer(virtPort); +System::~System() +{ +#if FULL_SYSTEM + delete kernelSymtab; + delete kernel; + delete initProxy; +#else + panic("System::fixFuncEventAddr needs to be rewritten " + "to work with syscall emulation"); +#endif // FULL_SYSTEM} +} - - /** - * Load the kernel code into memory - */ +void +System::start_of_simulation() +{ +#if FULL_SYSTEM if (params()->kernel == "") { inform("No kernel set for full system simulation. Assuming you know what" " you're doing...\n"); @@ -118,7 +124,7 @@ fatal("Could not load kernel file %s", params()->kernel); // Load program sections into memory - kernel->loadSections(functionalPort, loadAddrMask); + kernel->loadSections(initProxy, loadAddrMask); // setup entry points kernelStart = kernel->textBase(); @@ -151,17 +157,6 @@ activeCpus.clear(); } -System::~System() -{ -#if FULL_SYSTEM - delete kernelSymtab; - delete kernel; -#else - panic("System::fixFuncEventAddr needs to be rewritten " - "to work with syscall emulation"); -#endif // FULL_SYSTEM} -} - void System::setMemoryMode(Enums::MemoryMode mode) { @@ -268,21 +263,25 @@ { Addr return_addr = pagePtr << LogVMPageSize; ++pagePtr; - if (return_addr >= physmem->size()) + if (return_addr >= memsize) { + DPRINTFN("memsize: 0x%x\n",memsize); + DPRINTFN("return_addr: 0x%x\n",return_addr); fatal("Out of memory, please increase size of physical memory."); + + } return return_addr; } Addr System::memSize() { - return physmem->size(); + return memsize; } Addr System::freeMemSize() { - return physmem->size() - (pagePtr << LogVMPageSize); + return memsize - (pagePtr << LogVMPageSize); } #endif diff -r 6548721032fa -r 042f38a2e360 src/sim/tlb.hh --- a/src/sim/tlb.hh Fri Feb 11 18:29:36 2011 -0600 +++ b/src/sim/tlb.hh Fri Aug 05 18:14:06 2011 +0100 @@ -68,7 +68,7 @@ /** Get any port that the TLB or hardware table walker needs. * This is used for migrating port connections during a takeOverFrom() * call. */ - virtual Port* getPort() { return NULL; } + virtual MemMapMasterPort* getPort() { return NULL; } class Translation { diff -r 6548721032fa -r 042f38a2e360 tests/configs/o3-nocaches-timing.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/configs/o3-nocaches-timing.py Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,56 @@ +# Copyright (c) 2006-2007 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 + +import m5 +from m5.objects import * +m5.util.addToPath('../configs/common') + +class MyCache(BaseCache): + assoc = 2 + block_size = 64 + latency = '1ns' + mshrs = 10 + tgts_per_mshr = 5 + +cpu = DerivO3CPU(cpu_id=0) + +cpu.clock = '2GHz' + +system = System(cpu = cpu, + physmem = PhysicalMemory(), + membus = NonCoherentBus()) + +system.system_port = system.membus.mem_slave +system.memsize = system.physmem.range.getLength() +system.membus.mem_master = system.physmem.port + + +cpu.inst_port = system.membus.mem_slave +cpu.data_port = system.membus.mem_slave + +root = Root(system = system) diff -r 6548721032fa -r 042f38a2e360 tests/configs/o3-timing.py --- a/tests/configs/o3-timing.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/o3-timing.py Fri Aug 05 18:14:06 2011 +0100 @@ -38,14 +38,19 @@ tgts_per_mshr = 5 cpu = DerivO3CPU(cpu_id=0) -cpu.addTwoLevelCacheHierarchy(MyCache(size = '128kB'), MyCache(size = '256kB'), +cpu.addTwoLevelCacheHierarchy(MyCache(size = '128kB', forward_snoops = False), + MyCache(size = '256kB', forward_snoops = False), MyCache(size = '2MB')) cpu.clock = '2GHz' system = System(cpu = cpu, physmem = PhysicalMemory(), - membus = Bus()) -system.physmem.port = system.membus.port + membus = CoherentBus()) + + +system.system_port = system.membus.mem_slave +system.memsize = system.physmem.range.getLength() +system.membus.mem_master = system.physmem.port cpu.connectAllPorts(system.membus) root = Root(system = system) diff -r 6548721032fa -r 042f38a2e360 tests/configs/realview-simple-atomic.py --- a/tests/configs/realview-simple-atomic.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/realview-simple-atomic.py Fri Aug 05 18:14:06 2011 +0100 @@ -68,7 +68,7 @@ #cpu cpu = AtomicSimpleCPU(cpu_id=0) #the system -system = FSConfig.makeLinuxArmSystem('atomic', None, False, "RealView_PBX") +system = FSConfig.makeArmSystem('atomic', "RealView_PBX", None, False) system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] system.bridge.filter_ranges_b=[AddrRange(0, size='128MB')] system.iocache = IOCache() diff -r 6548721032fa -r 042f38a2e360 tests/configs/realview-simple-timing.py --- a/tests/configs/realview-simple-timing.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/realview-simple-timing.py Fri Aug 05 18:14:06 2011 +0100 @@ -69,7 +69,7 @@ #cpu cpu = TimingSimpleCPU(cpu_id=0) #the system -system = FSConfig.makeLinuxArmSystem('timing', None, False, "RealView_PBX") +system = FSConfig.makeArmSystem('timing', "RealView_PBX", None, False) system.cpu = cpu #create the l1/l2 bus diff -r 6548721032fa -r 042f38a2e360 tests/configs/simple-atomic.py --- a/tests/configs/simple-atomic.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/simple-atomic.py Fri Aug 05 18:14:06 2011 +0100 @@ -31,8 +31,10 @@ system = System(cpu = AtomicSimpleCPU(cpu_id=0), physmem = PhysicalMemory(), - membus = Bus()) -system.physmem.port = system.membus.port + membus = CoherentBus()) +system.membus.mem_master = system.physmem.port +system.system_port = system.membus.mem_slave +system.memsize = system.physmem.range.getLength() system.cpu.connectAllPorts(system.membus) system.cpu.clock = '2GHz' diff -r 6548721032fa -r 042f38a2e360 tests/configs/simple-timing.py --- a/tests/configs/simple-timing.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/simple-timing.py Fri Aug 05 18:14:06 2011 +0100 @@ -37,12 +37,16 @@ tgts_per_mshr = 5 cpu = TimingSimpleCPU(cpu_id=0) -cpu.addTwoLevelCacheHierarchy(MyCache(size = '128kB'), MyCache(size = '256kB'), +cpu.addTwoLevelCacheHierarchy(MyCache(size = '128kB', forward_snoops = False), + MyCache(size = '256kB', forward_snoops = False), MyCache(size = '2MB', latency='10ns')) system = System(cpu = cpu, physmem = PhysicalMemory(), - membus = Bus()) -system.physmem.port = system.membus.port + membus = CoherentBus()) + +system.system_port = system.membus.mem_slave +system.memsize = system.physmem.range.getLength() +system.membus.mem_master = system.physmem.port cpu.connectAllPorts(system.membus) cpu.clock = '2GHz' diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-nocaches-timing.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/configs/tsunami-nocaches-timing.py Fri Aug 05 18:14:06 2011 +0100 @@ -0,0 +1,47 @@ +# Copyright (c) 2006-2007 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: Andreas Hansson + +import m5 +from m5.objects import * +m5.util.addToPath('../configs/common') +import FSConfig + +#cpu +cpu = TimingSimpleCPU(cpu_id=0) +#the system +system = FSConfig.makeSimpleLinuxAlphaSystem('timing') + +system.cpu = cpu + +system.bus.mem_slave = system.cpu.data_port +system.bus.mem_slave = system.cpu.inst_port + +cpu.clock = '2GHz' + +root = Root(system=system) +m5.ticks.setGlobalFrequency('1THz') diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-o3-dual.py --- a/tests/configs/tsunami-o3-dual.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/tsunami-o3-dual.py Fri Aug 05 18:14:06 2011 +0100 @@ -41,6 +41,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 + forward_snoops = False # ---------------------- # Base L2 Cache @@ -72,19 +73,25 @@ system = FSConfig.makeLinuxAlphaSystem('timing') system.cpu = cpus + #create the l1/l2 bus -system.toL2Bus = Bus() -system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] -system.bridge.filter_ranges_b=[AddrRange(0, size='8GB')] +system.toL2Bus = CoherentBus() + +# everything above the 8 GB limit goes to the I/O bus +system.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + system.iocache = IOCache() -system.iocache.cpu_side = system.iobus.port -system.iocache.mem_side = system.membus.port - +system.iobus.mem_master = system.iocache.mem_slave +system.iocache.mem_master = system.membus.mem_slave +system.membus.cache_master = system.iocache.cache_slave #connect up the l2 cache system.l2c = L2(size='4MB', assoc=8) -system.l2c.cpu_side = system.toL2Bus.port -system.l2c.mem_side = system.membus.port +system.l2c.cache_up_master = system.toL2Bus.cache_slave +system.l2c.cache_down_master = system.membus.cache_slave +system.toL2Bus.mem_master = system.l2c.mem_slave +system.l2c.mem_master = system.membus.mem_slave +system.membus.cache_master = system.l2c.cache_slave system.l2c.num_cpus = 2 #connect up the cpu and l1s diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-o3.py --- a/tests/configs/tsunami-o3.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/tsunami-o3.py Fri Aug 05 18:14:06 2011 +0100 @@ -41,6 +41,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 + forward_snoops = False # ---------------------- # Base L2 Cache @@ -72,19 +73,25 @@ system = FSConfig.makeLinuxAlphaSystem('timing') system.cpu = cpu + #create the l1/l2 bus -system.toL2Bus = Bus() -system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] -system.bridge.filter_ranges_b=[AddrRange(0, size='8GB')] +system.toL2Bus = CoherentBus() + +# everything above the 8 GB limit goes to the I/O bus +system.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + system.iocache = IOCache() -system.iocache.cpu_side = system.iobus.port -system.iocache.mem_side = system.membus.port - +system.iobus.mem_master = system.iocache.mem_slave +system.iocache.mem_master = system.membus.mem_slave +system.membus.cache_master = system.iocache.cache_slave #connect up the l2 cache system.l2c = L2(size='4MB', assoc=8) -system.l2c.cpu_side = system.toL2Bus.port -system.l2c.mem_side = system.membus.port +system.l2c.cache_up_master = system.toL2Bus.cache_slave +system.l2c.cache_down_master = system.membus.cache_slave +system.toL2Bus.mem_master = system.l2c.mem_slave +system.l2c.mem_master = system.membus.mem_slave +system.membus.cache_master = system.l2c.cache_slave #connect up the cpu and l1s cpu.addPrivateSplitL1Caches(L1(size = '32kB', assoc = 1), @@ -95,4 +102,3 @@ root = Root(system=system) m5.ticks.setGlobalFrequency('1THz') - diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-simple-atomic-dual.py --- a/tests/configs/tsunami-simple-atomic-dual.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/tsunami-simple-atomic-dual.py Fri Aug 05 18:14:06 2011 +0100 @@ -40,6 +40,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 + forward_snoops = False # ---------------------- # Base L2 Cache @@ -69,20 +70,26 @@ cpus = [ AtomicSimpleCPU(cpu_id=i) for i in xrange(2) ] #the system system = FSConfig.makeLinuxAlphaSystem('atomic') -system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] -system.bridge.filter_ranges_b=[AddrRange(0, size='8GB')] + +# everything above the 8 GB limit goes to the I/O bus +system.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + system.iocache = IOCache() -system.iocache.cpu_side = system.iobus.port -system.iocache.mem_side = system.membus.port +system.iobus.mem_master = system.iocache.mem_slave +system.iocache.mem_master = system.membus.mem_slave +system.membus.cache_master = system.iocache.cache_slave system.cpu = cpus #create the l1/l2 bus -system.toL2Bus = Bus() +system.toL2Bus = CoherentBus() #connect up the l2 cache system.l2c = L2(size='4MB', assoc=8) -system.l2c.cpu_side = system.toL2Bus.port -system.l2c.mem_side = system.membus.port +system.l2c.cache_up_master = system.toL2Bus.cache_slave +system.l2c.cache_down_master = system.membus.cache_slave +system.toL2Bus.mem_master = system.l2c.mem_slave +system.l2c.mem_master = system.membus.mem_slave +system.membus.cache_master = system.l2c.cache_slave system.l2c.num_cpus = 2 #connect up the cpu and l1s diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-simple-atomic.py --- a/tests/configs/tsunami-simple-atomic.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/tsunami-simple-atomic.py Fri Aug 05 18:14:06 2011 +0100 @@ -40,6 +40,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 + forward_snoops = False # ---------------------- # Base L2 Cache @@ -69,20 +70,27 @@ cpu = AtomicSimpleCPU(cpu_id=0) #the system system = FSConfig.makeLinuxAlphaSystem('atomic') -system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] -system.bridge.filter_ranges_b=[AddrRange(0, size='8GB')] -system.iocache = IOCache() -system.iocache.cpu_side = system.iobus.port -system.iocache.mem_side = system.membus.port system.cpu = cpu #create the l1/l2 bus -system.toL2Bus = Bus() +system.toL2Bus = CoherentBus() + + +# everything above the 8 GB limit goes to the I/O bus +system.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + +system.iocache = IOCache() +system.iobus.mem_master = system.iocache.mem_slave +system.iocache.mem_master = system.membus.mem_slave +system.membus.cache_master = system.iocache.cache_slave #connect up the l2 cache system.l2c = L2(size='4MB', assoc=8) -system.l2c.cpu_side = system.toL2Bus.port -system.l2c.mem_side = system.membus.port +system.l2c.cache_up_master = system.toL2Bus.cache_slave +system.l2c.cache_down_master = system.membus.cache_slave +system.toL2Bus.mem_master = system.l2c.mem_slave +system.l2c.mem_master = system.membus.mem_slave +system.membus.cache_master = system.l2c.cache_slave #connect up the cpu and l1s cpu.addPrivateSplitL1Caches(L1(size = '32kB', assoc = 1), @@ -93,4 +101,3 @@ root = Root(system=system) m5.ticks.setGlobalFrequency('1THz') - diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-simple-timing-dual.py --- a/tests/configs/tsunami-simple-timing-dual.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/tsunami-simple-timing-dual.py Fri Aug 05 18:14:06 2011 +0100 @@ -40,6 +40,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 + forward_snoops = False # ---------------------- # Base L2 Cache @@ -69,20 +70,27 @@ cpus = [ TimingSimpleCPU(cpu_id=i) for i in xrange(2) ] #the system system = FSConfig.makeLinuxAlphaSystem('timing') -system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] -system.bridge.filter_ranges_b=[AddrRange(0, size='8GB')] -system.iocache = IOCache() -system.iocache.cpu_side = system.iobus.port -system.iocache.mem_side = system.membus.port system.cpu = cpus #create the l1/l2 bus -system.toL2Bus = Bus() +system.toL2Bus = CoherentBus() + +# everything above the 8 GB limit goes to the I/O bus +system.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + +system.iocache = IOCache() +system.iobus.mem_master = system.iocache.mem_slave +system.iocache.mem_master = system.membus.mem_slave +system.membus.cache_master = system.iocache.cache_slave #connect up the l2 cache system.l2c = L2(size='4MB', assoc=8) -system.l2c.cpu_side = system.toL2Bus.port -system.l2c.mem_side = system.membus.port +system.l2c.cache_up_master = system.toL2Bus.cache_slave +system.l2c.cache_down_master = system.membus.cache_slave +system.toL2Bus.mem_master = system.l2c.mem_slave +system.l2c.mem_master = system.membus.mem_slave +system.membus.cache_master = system.l2c.cache_slave + system.l2c.num_cpus = 2 #connect up the cpu and l1s @@ -95,5 +103,3 @@ root = Root(system=system) m5.ticks.setGlobalFrequency('1THz') - - diff -r 6548721032fa -r 042f38a2e360 tests/configs/tsunami-simple-timing.py --- a/tests/configs/tsunami-simple-timing.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/tsunami-simple-timing.py Fri Aug 05 18:14:06 2011 +0100 @@ -41,6 +41,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 + forward_snoops = False # ---------------------- # Base L2 Cache @@ -73,18 +74,23 @@ system.cpu = cpu #create the l1/l2 bus -system.toL2Bus = Bus() -system.bridge.filter_ranges_a=[AddrRange(0, Addr.max)] -system.bridge.filter_ranges_b=[AddrRange(0, size='8GB')] +system.toL2Bus = CoherentBus() + +# everything above the 8 GB limit goes to the I/O bus +system.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + system.iocache = IOCache() -system.iocache.cpu_side = system.iobus.port -system.iocache.mem_side = system.membus.port - +system.iobus.mem_master = system.iocache.mem_slave +system.iocache.mem_master = system.membus.mem_slave +system.membus.cache_master = system.iocache.cache_slave #connect up the l2 cache system.l2c = L2(size='4MB', assoc=8) -system.l2c.cpu_side = system.toL2Bus.port -system.l2c.mem_side = system.membus.port +system.l2c.cache_up_master = system.toL2Bus.cache_slave +system.l2c.cache_down_master = system.membus.cache_slave +system.toL2Bus.mem_master = system.l2c.mem_slave +system.l2c.mem_master = system.membus.mem_slave +system.membus.cache_master = system.l2c.cache_slave #connect up the cpu and l1s cpu.addPrivateSplitL1Caches(L1(size = '32kB', assoc = 1), @@ -95,4 +101,3 @@ root = Root(system=system) m5.ticks.setGlobalFrequency('1THz') - diff -r 6548721032fa -r 042f38a2e360 tests/configs/twosys-tsunami-simple-atomic.py --- a/tests/configs/twosys-tsunami-simple-atomic.py Fri Feb 11 18:29:36 2011 -0600 +++ b/tests/configs/twosys-tsunami-simple-atomic.py Fri Aug 05 18:14:06 2011 +0100 @@ -37,11 +37,33 @@ test_sys.cpu = AtomicSimpleCPU(cpu_id=0) test_sys.cpu.connectAllPorts(test_sys.membus) +# everything below 1 GB goes from IO bus to mem bus, note that this +# was empirically determined +test_sys.io2memBridge = Bridge(delay='50ns') +test_sys.iobus.mem_master = test_sys.io2memBridge.slave +test_sys.io2memBridge.master = test_sys.membus.mem_slave +test_sys.io2memBridge.ranges=[AddrRange(0, size='1GB')] + +# everything above the 8 GB limit goes to the I/O bus +test_sys.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + + + drive_sys = makeLinuxAlphaSystem('atomic', SysConfig('netperf-server.rcS')) drive_sys.cpu = AtomicSimpleCPU(cpu_id=0) drive_sys.cpu.connectAllPorts(drive_sys.membus) +# everything below 1 GB goes from IO bus to mem bus, note that this +# was empirically determined +drive_sys.io2memBridge = Bridge(delay='50ns') +drive_sys.iobus.mem_master = drive_sys.io2memBridge.slave +drive_sys.io2memBridge.master = drive_sys.membus.mem_slave +drive_sys.io2memBridge.ranges=[AddrRange(0, size='1GB')] + +# everything above the 8 GB limit goes to the I/O bus +drive_sys.mem2ioBridge.ranges=[AddrRange(0x200000000, Addr.max)] + root = makeDualRoot(test_sys, drive_sys, "ethertrace") maxtick = 199999999