diff -r 2a04edb07407 -r 95f365b864dc configs/common/FSConfig.py --- a/configs/common/FSConfig.py Sun Jul 10 12:56:09 2011 -0500 +++ b/configs/common/FSConfig.py Wed Jul 27 16:46:31 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,52 @@ 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 = NonCoherentBus4Phase() + + # 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.mem2ioClassicTo4PhaseBridge = BridgeClassicTo4Phase() + self.membus.mem_master = self.mem2ioBridge.slave + self.mem2ioBridge.master = self.mem2ioClassicTo4PhaseBridge.slave + self.mem2ioClassicTo4PhaseBridge.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.classic_to_4phase_bridge = BridgeClassicTo4Phase() + self.classic_to_4phase_bridge.slave = self.membus.mem_master + self.classic_to_4phase_bridge.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.ideClassicto4PhaseBridge = BridgeClassicTo4Phase() + self.ideClassicto4PhaseBridge.master = self.iobus.mem_slave + self.ideClassicto4PhaseBridge.slave = self.tsunami.ide.dma + self.iobus.mem_master = self.tsunami.ethernet.pio + self.iobus.mem_master = self.tsunami.ethernet.config + self.ethernetClassicto4PhaseBridge = BridgeClassicTo4Phase() + self.ethernetClassicto4PhaseBridge.master = self.iobus.mem_slave + self.ethernetClassicto4PhaseBridge.slave = self.tsunami.ethernet.dma self.simple_disk = SimpleDisk(disk=RawDiskImage(image_file = mdesc.disk(), read_only = True)) self.intrctrl = IntrControl() @@ -90,392 +117,9 @@ 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): - 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) - 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) - - # - # 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 - - 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.simple_disk = SimpleDisk(disk=RawDiskImage(image_file = mdesc.disk(), - read_only = True)) - self.intrctrl = IntrControl() - self.mem_mode = mem_mode - self.terminal = Terminal() - self.kernel = binary('vmlinux') - self.pal = binary('ts_osfpal') - self.console = binary('console') - self.boot_osflags = 'root=/dev/hda1 console=ttyS0' - - return self - -def makeSparcSystem(mem_mode, mdesc = None): - class CowMmDisk(MmDisk): - image = CowDiskImage(child=RawDiskImage(read_only=True), - read_only=False) - - def childImage(self, ci): - self.image.child.image_file = ci - - self = SparcSystem() - 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') - self.t1000 = T1000() - self.t1000.attachOnChipIO(self.membus) - self.t1000.attachIO(self.iobus) - self.physmem = PhysicalMemory(range = AddrRange(Addr('1MB'), size = '64MB'), zero = True) - self.physmem2 = PhysicalMemory(range = AddrRange(Addr('2GB'), size ='256MB'), zero = True) - self.bridge.side_a = self.iobus.port - self.bridge.side_b = self.membus.port - self.physmem.port = self.membus.port - self.physmem2.port = self.membus.port - self.rom.port = self.membus.port - self.nvram.port = self.membus.port - self.hypervisor_desc.port = self.membus.port - self.partition_desc.port = self.membus.port - self.intrctrl = IntrControl() - self.disk0 = CowMmDisk() - self.disk0.childImage(disk('disk.s10hw2')) - self.disk0.pio = self.iobus.port - self.reset_bin = binary('reset_new.bin') - self.hypervisor_bin = binary('q_new.bin') - self.openboot_bin = binary('openboot_new.bin') - self.nvram_bin = binary('nvram1') - self.hypervisor_desc_bin = binary('1up-hv.bin') - self.partition_desc_bin = binary('1up-md.bin') - - return self - -def makeArmSystem(mem_mode, machine_type, mdesc = None, bare_metal=False): - assert machine_type - - if bare_metal: - self = ArmSystem() - else: - self = LinuxArmSystem() - - if not mdesc: - # generic system - mdesc = SysConfig() - - self.readfile = mdesc.script() - self.iobus = Bus(bus_id=0) - self.membus = MemBus(bus_id=1) - self.membus.badaddr_responder.warn_access = "warn" - self.bridge = Bridge(delay='50ns', nack_delay='4ns') - self.bridge.side_a = self.iobus.port - self.bridge.side_b = self.membus.port - - self.mem_mode = mem_mode - - if machine_type == "RealView_PBX": - self.realview = RealViewPBX() - elif machine_type == "RealView_EB": - self.realview = RealViewEB() - else: - print "Unknown Machine Type" - sys.exit(1) - - use_cf = False - if mdesc.disk()[-4:] == ".img": - use_cf = True - self.cf0 = CowIdeDisk(driveID='master') - self.cf0.childImage(mdesc.disk()) - self.realview.cf_ctrl.disks = [self.cf0] - - if bare_metal: - # EOT character on UART will end the simulation - self.realview.uart.end_on_eot = True - self.physmem = PhysicalMemory(range = AddrRange(Addr('256MB')), - zero = True) - else: - self.kernel = binary('vmlinux.arm') - self.machine_type = machine_type - boot_flags = 'earlyprintk console=ttyAMA0 lpj=19988480 norandmaps ' + \ - 'rw loglevel=8 ' - if use_cf: - self.physmem = PhysicalMemory(range = AddrRange(Addr('256MB')), - zero = True) - boot_flags += "mem=256MB root=/dev/sda1 " - self.nvmem = PhysicalMemory(range = AddrRange(Addr('2GB'), - size = '64MB'), zero = True) - self.nvmem.port = self.membus.port - self.boot_loader = binary('boot.arm') - self.boot_loader_mem = self.nvmem - self.gic_cpu_addr = self.realview.gic.cpu_addr - self.flags_addr = self.realview.realview_io.pio_addr + 0x30 - else: - self.physmem = PhysicalMemory(range = AddrRange(Addr('128MB')), - zero = True) - self.diskmem = PhysicalMemory(range = AddrRange(Addr('128MB'), - size = '128MB'), - file = disk(mdesc.disk())) - self.diskmem.port = self.membus.port - boot_flags += "mem=128MB slram=slram0,0x8000000,+0x8000000 " + \ - "mtdparts=slram0:- root=/dev/mtdblock0 " - - if mdesc.disk().count('android'): - boot_flags += "init=/init " - self.boot_osflags = boot_flags - - self.physmem.port = self.membus.port - self.realview.attachOnChipIO(self.membus) - self.realview.attachIO(self.iobus) - - self.intrctrl = IntrControl() - self.terminal = Terminal() - self.vncserver = VncServer() - - return self - - -def makeLinuxMipsSystem(mem_mode, mdesc = None): - class BaseMalta(Malta): - 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) - - self = LinuxMipsSystem() - 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') - self.physmem = PhysicalMemory(range = AddrRange('1GB')) - self.bridge.side_a = self.iobus.port - self.bridge.side_b = self.membus.port - self.physmem.port = self.membus.port - self.disk0 = CowIdeDisk(driveID='master') - self.disk2 = CowIdeDisk(driveID='master') - self.disk0.childImage(mdesc.disk()) - self.disk2.childImage(disk('linux-bigswap2.img')) - self.malta = BaseMalta() - self.malta.attachIO(self.iobus) - self.malta.ide.pio = self.iobus.port - self.malta.ethernet.pio = self.iobus.port - self.simple_disk = SimpleDisk(disk=RawDiskImage(image_file = mdesc.disk(), - read_only = True)) - self.intrctrl = IntrControl() - self.mem_mode = mem_mode - self.terminal = Terminal() - self.kernel = binary('mips/vmlinux') - self.console = binary('mips/console') - self.boot_osflags = 'root=/dev/hda1 console=ttyS0' - - return self - -def x86IOAddress(port): - IO_address_space_base = 0x8000000000000000 - return IO_address_space_base + port - -def connectX86ClassicSystem(x86_sys): - x86_sys.membus = MemBus(bus_id=1) - x86_sys.physmem.port = x86_sys.membus.port - - # North Bridge - x86_sys.iobus = Bus(bus_id=0) - x86_sys.bridge = Bridge(delay='50ns', nack_delay='4ns') - x86_sys.bridge.side_a = x86_sys.iobus.port - x86_sys.bridge.side_b = x86_sys.membus.port - - # connect the io bus - x86_sys.pc.attachIO(x86_sys.iobus) - -def connectX86RubySystem(x86_sys): - # North Bridge - x86_sys.piobus = Bus(bus_id=0) - - # - # 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 - # - x86_sys.piobus.port = x86_sys.physmem.port - - x86_sys.pc.attachIO(x86_sys.piobus) - - -def makeX86System(mem_mode, numCPUs = 1, mdesc = None, self = None, Ruby = False): - if self == None: - self = X86System() - - if not mdesc: - # generic system - mdesc = SysConfig() - self.readfile = mdesc.script() - - self.mem_mode = mem_mode - - # Physical memory - self.physmem = PhysicalMemory(range = AddrRange(mdesc.mem())) - - # Platform - self.pc = Pc() - - # Create and connect the busses required by each memory system - if Ruby: - connectX86RubySystem(self) - # add the ide to the list of dma devices that later need to attach to - # dma controllers - self._dma_devices = [self.pc.south_bridge.ide] - else: - connectX86ClassicSystem(self) - - self.intrctrl = IntrControl() - - # Disks - disk0 = CowIdeDisk(driveID='master') - disk2 = CowIdeDisk(driveID='master') - disk0.childImage(mdesc.disk()) - disk2.childImage(disk('linux-bigswap2.img')) - self.pc.south_bridge.ide.disks = [disk0, disk2] - - # Add in a Bios information structure. - structures = [X86SMBiosBiosInformation()] - self.smbios_table.structures = structures - - # Set up the Intel MP table - base_entries = [] - ext_entries = [] - for i in xrange(numCPUs): - bp = X86IntelMPProcessor( - local_apic_id = i, - local_apic_version = 0x14, - enable = True, - bootstrap = (i == 0)) - base_entries.append(bp) - io_apic = X86IntelMPIOAPIC( - id = numCPUs, - version = 0x11, - enable = True, - address = 0xfec00000) - self.pc.south_bridge.io_apic.apic_id = io_apic.id - base_entries.append(io_apic) - isa_bus = X86IntelMPBus(bus_id = 0, bus_type='ISA') - base_entries.append(isa_bus) - pci_bus = X86IntelMPBus(bus_id = 1, bus_type='PCI') - base_entries.append(pci_bus) - connect_busses = X86IntelMPBusHierarchy(bus_id=0, - subtractive_decode=True, parent_bus=1) - ext_entries.append(connect_busses) - pci_dev4_inta = X86IntelMPIOIntAssignment( - interrupt_type = 'INT', - polarity = 'ConformPolarity', - trigger = 'ConformTrigger', - source_bus_id = 1, - source_bus_irq = 0 + (4 << 2), - dest_io_apic_id = io_apic.id, - dest_io_apic_intin = 16) - base_entries.append(pci_dev4_inta) - def assignISAInt(irq, apicPin): - assign_8259_to_apic = X86IntelMPIOIntAssignment( - interrupt_type = 'ExtInt', - polarity = 'ConformPolarity', - trigger = 'ConformTrigger', - source_bus_id = 0, - source_bus_irq = irq, - dest_io_apic_id = io_apic.id, - dest_io_apic_intin = 0) - base_entries.append(assign_8259_to_apic) - assign_to_apic = X86IntelMPIOIntAssignment( - interrupt_type = 'INT', - polarity = 'ConformPolarity', - trigger = 'ConformTrigger', - source_bus_id = 0, - source_bus_irq = irq, - dest_io_apic_id = io_apic.id, - dest_io_apic_intin = apicPin) - base_entries.append(assign_to_apic) - assignISAInt(0, 2) - assignISAInt(1, 1) - for i in range(3, 15): - assignISAInt(i, i) - self.intel_mp_table.base_entries = base_entries - self.intel_mp_table.ext_entries = ext_entries - -def setWorkCountOptions(system, options): - if options.work_item_id != None: - system.work_item_id = options.work_item_id - if options.work_begin_cpu_id_exit != None: - system.work_begin_cpu_id_exit = options.work_begin_cpu_id_exit - if options.work_end_exit_count != None: - system.work_end_exit_count = options.work_end_exit_count - if options.work_end_checkpoint_count != None: - system.work_end_ckpt_count = options.work_end_checkpoint_count - if options.work_begin_exit_count != None: - system.work_begin_exit_count = options.work_begin_exit_count - if options.work_begin_checkpoint_count != None: - system.work_begin_ckpt_count = options.work_begin_checkpoint_count - if options.work_cpus_checkpoint_count != None: - system.work_cpus_ckpt_count = options.work_cpus_checkpoint_count - - -def makeLinuxX86System(mem_mode, numCPUs = 1, mdesc = None, Ruby = False): - self = LinuxX86System() - - # Build up the x86 system and then specialize it for Linux - makeX86System(mem_mode, numCPUs, mdesc, self, Ruby) - - # We assume below that there's at least 1MB of memory. We'll require 2 - # just to avoid corner cases. - assert(self.physmem.range.second.getValue() >= 0x200000) - - self.e820_table.entries = \ - [ - # Mark the first megabyte of memory as reserved - X86E820Entry(addr = 0, size = '1MB', range_type = 2), - # Mark the rest as available - X86E820Entry(addr = 0x100000, - size = '%dB' % (self.physmem.range.second - 0x100000 + 1), - range_type = 1) - ] - - # Command line - self.boot_osflags = 'earlyprintk=ttyS0 console=ttyS0 lpj=7999923 ' + \ - 'root=/dev/hda1' - return self - - def makeDualRoot(testSystem, driveSystem, dumpfile): self = Root() self.testsys = testSystem @@ -489,73 +133,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 2a04edb07407 -r 95f365b864dc src/cpu/BaseCPU.py --- a/src/cpu/BaseCPU.py Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/BaseCPU.py Wed Jul 27 16:46:31 2011 +0100 @@ -34,7 +34,7 @@ from m5.params import * from m5.proxy import * -from Bus import Bus +from CoherentBus import CoherentBus from InstTracer import InstTracer from ExeTracer import ExeTracer from MemObject import MemObject @@ -45,31 +45,18 @@ from AlphaTLB import AlphaDTB, AlphaITB if buildEnv['FULL_SYSTEM']: from AlphaInterrupts import AlphaInterrupts -elif buildEnv['TARGET_ISA'] == 'sparc': - from SparcTLB import SparcTLB - if buildEnv['FULL_SYSTEM']: - from SparcInterrupts import SparcInterrupts -elif buildEnv['TARGET_ISA'] == 'x86': - from X86TLB import X86TLB - if buildEnv['FULL_SYSTEM']: - from X86LocalApic import X86LocalApic -elif buildEnv['TARGET_ISA'] == 'mips': - from MipsTLB import MipsTLB - if buildEnv['FULL_SYSTEM']: - from MipsInterrupts import MipsInterrupts elif buildEnv['TARGET_ISA'] == 'arm': from ArmTLB import ArmTLB if buildEnv['FULL_SYSTEM']: from ArmInterrupts import ArmInterrupts -elif buildEnv['TARGET_ISA'] == 'power': - from PowerTLB import PowerTLB - if buildEnv['FULL_SYSTEM']: - from PowerInterrupts import PowerInterrupts class BaseCPU(MemObject): type = 'BaseCPU' abstract = True + inst_port = Port("MEM_MAP", "MASTER", "Instruction port") + data_port = Port("MEM_MAP", "MASTER", "Data port") + system = Param.System(Parent.any, "system object") cpu_id = Param.Int(-1, "CPU identifier") numThreads = Param.Unsigned(1, "number of HW thread contexts") @@ -90,44 +77,18 @@ else: workload = VectorParam.Process("processes to run") - if buildEnv['TARGET_ISA'] == 'sparc': - dtb = Param.SparcTLB(SparcTLB(), "Data TLB") - itb = Param.SparcTLB(SparcTLB(), "Instruction TLB") - if buildEnv['FULL_SYSTEM']: - interrupts = Param.SparcInterrupts( - SparcInterrupts(), "Interrupt Controller") - elif buildEnv['TARGET_ISA'] == 'alpha': + if buildEnv['TARGET_ISA'] == 'alpha': dtb = Param.AlphaTLB(AlphaDTB(), "Data TLB") itb = Param.AlphaTLB(AlphaITB(), "Instruction TLB") if buildEnv['FULL_SYSTEM']: interrupts = Param.AlphaInterrupts( AlphaInterrupts(), "Interrupt Controller") - elif buildEnv['TARGET_ISA'] == 'x86': - dtb = Param.X86TLB(X86TLB(), "Data TLB") - itb = Param.X86TLB(X86TLB(), "Instruction TLB") - if buildEnv['FULL_SYSTEM']: - _localApic = X86LocalApic(pio_addr=0x2000000000000000) - interrupts = \ - Param.X86LocalApic(_localApic, "Interrupt Controller") - elif buildEnv['TARGET_ISA'] == 'mips': - dtb = Param.MipsTLB(MipsTLB(), "Data TLB") - itb = Param.MipsTLB(MipsTLB(), "Instruction TLB") - if buildEnv['FULL_SYSTEM']: - interrupts = Param.MipsInterrupts( - MipsInterrupts(), "Interrupt Controller") elif buildEnv['TARGET_ISA'] == 'arm': dtb = Param.ArmTLB(ArmTLB(), "Data TLB") itb = Param.ArmTLB(ArmTLB(), "Instruction TLB") if buildEnv['FULL_SYSTEM']: interrupts = Param.ArmInterrupts( ArmInterrupts(), "Interrupt Controller") - elif buildEnv['TARGET_ISA'] == 'power': - UnifiedTLB = Param.Bool(True, "Is this a Unified TLB?") - dtb = Param.PowerTLB(PowerTLB(), "Data TLB") - itb = Param.PowerTLB(PowerTLB(), "Instruction TLB") - if buildEnv['FULL_SYSTEM']: - interrupts = Param.PowerInterrupts( - PowerInterrupts(), "Interrupt Controller") else: print "Don't know what TLB to use for ISA %s" % \ buildEnv['TARGET_ISA'] @@ -152,21 +113,28 @@ tracer = Param.InstTracer(default_tracer, "Instruction tracer") - _cached_ports = [] + # memory mapped master ports with cache extensions + _mem_master_ports = ['inst_port', 'data_port'] + # cache manintenance slave ports + _cache_slave_ports = [] + _cache_master_ports = [] + if buildEnv['TARGET_ISA'] in ['x86', 'arm'] and buildEnv['FULL_SYSTEM']: - _cached_ports = ["itb.walker.port", "dtb.walker.port"] + _mem_master_ports = ["itb.walker.port", "dtb.walker.port"] _uncached_ports = [] - if buildEnv['TARGET_ISA'] == 'x86' and buildEnv['FULL_SYSTEM']: - _uncached_ports = ["interrupts.pio", "interrupts.int_port"] def connectCachedPorts(self, bus): - for p in self._cached_ports: - exec('self.%s = bus.port' % p) + for p in self._mem_master_ports: + exec('self.%s = bus.mem_slave' % p) + for p in self._cache_slave_ports: + exec('bus.cache_master = self.%s' % p) + for p in self._cache_master_ports: + exec('self.%s = bus.cache_slave' % p) def connectUncachedPorts(self, bus): for p in self._uncached_ports: - exec('self.%s = bus.port' % p) + exec('self.%s = bus.mem_slave' % p) def connectAllPorts(self, cached_bus, uncached_bus = None): self.connectCachedPorts(cached_bus) @@ -175,27 +143,30 @@ self.connectUncachedPorts(uncached_bus) def addPrivateSplitL1Caches(self, ic, dc, iwc = None, dwc = None): - assert(len(self._cached_ports) < 7) + assert(len(self._mem_master_ports) < 7) self.icache = ic self.dcache = dc - self.icache_port = ic.cpu_side - self.dcache_port = dc.cpu_side - self._cached_ports = ['icache.mem_side', 'dcache.mem_side'] + # master is the CPU, the cache is the slave + self.inst_port = ic.mem_slave + self.data_port = dc.mem_slave + + # left dangling are the memory mapped and cache maintenance ports + # of the caches + self._mem_master_ports = ['icache.mem_master', 'dcache.mem_master'] + self._cache_slave_ports = ['icache.cache_slave', 'dcache.cache_slave'] + self._cache_master_ports = ['icache.cache_down_master', + 'dcache.cache_down_master'] if buildEnv['FULL_SYSTEM']: - if buildEnv['TARGET_ISA'] == 'x86': - self.itb_walker_cache = iwc - self.dtb_walker_cache = dwc - self.itb.walker.port = iwc.cpu_side - self.dtb.walker.port = dwc.cpu_side - self._cached_ports += ["itb_walker_cache.mem_side", \ - "dtb_walker_cache.mem_side"] - elif buildEnv['TARGET_ISA'] == 'arm': - self._cached_ports += ["itb.walker.port", "dtb.walker.port"] + if buildEnv['TARGET_ISA'] == 'arm': + self._mem_master_ports += ["itb.walker.port", "dtb.walker.port"] def addTwoLevelCacheHierarchy(self, ic, dc, l2c, iwc = None, dwc = None): self.addPrivateSplitL1Caches(ic, dc, iwc, dwc) - self.toL2Bus = Bus() + self.toL2Bus = CoherentBus() self.connectCachedPorts(self.toL2Bus) self.l2cache = l2c - self.l2cache.cpu_side = self.toL2Bus.port - self._cached_ports = ['l2cache.mem_side'] + self.toL2Bus.mem_master = self.l2cache.mem_slave + self.l2cache.cache_up_master = self.toL2Bus.cache_slave + self._mem_master_ports = ['l2cache.mem_master'] + self._cache_slave_ports = ['l2cache.cache_slave'] + self._cache_master_ports = ['l2cache.cache_down_master'] diff -r 2a04edb07407 -r 95f365b864dc src/cpu/base.hh --- a/src/cpu/base.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/base.hh Wed Jul 27 16:46:31 2011 +0100 @@ -41,9 +41,9 @@ #include "base/statistics.hh" #include "config/full_system.hh" #include "config/the_isa.hh" -#include "mem/mem_object.hh" #include "sim/eventq.hh" #include "sim/insttracer.hh" +#include "mem/mem_object.hh" #if FULL_SYSTEM #include "arch/interrupts.hh" @@ -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). @@ -239,6 +266,8 @@ */ ThreadID numThreads; + TheISA::CoreSpecific coreParams; //ISA-Specific Params That Set Up State in Core + /** * Vector of per-thread instruction-based event queues. Used for * scheduling events based on number of instructions committed by diff -r 2a04edb07407 -r 95f365b864dc src/cpu/base.cc --- a/src/cpu/base.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/base.cc Wed Jul 27 16:46:31 2011 +0100 @@ -32,24 +32,23 @@ */ #include +#include #include -#include #include "arch/tlb.hh" +#include "base/cprintf.hh" #include "base/loader/symtab.hh" -#include "base/cprintf.hh" #include "base/misc.hh" #include "base/output.hh" #include "base/trace.hh" #include "cpu/base.hh" #include "cpu/cpuevent.hh" +#include "cpu/thread_context.hh" #include "cpu/profile.hh" -#include "cpu/thread_context.hh" -#include "debug/SyscallVerbose.hh" #include "params/BaseCPU.hh" +#include "sim/sim_exit.hh" #include "sim/process.hh" #include "sim/sim_events.hh" -#include "sim/sim_exit.hh" #include "sim/system.hh" // Hack @@ -103,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 @@ -210,6 +215,9 @@ profileEvent = new ProfileEvent(this, params()->profile); #endif tracer = params()->tracer; + + regPort(&instPort); + regPort(&dataPort); } void @@ -227,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 @@ -348,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()); @@ -374,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(); @@ -383,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); } } @@ -410,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 2a04edb07407 -r 95f365b864dc src/cpu/o3/O3CPU.py --- a/src/cpu/o3/O3CPU.py Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/o3/O3CPU.py Wed Jul 27 16:46:31 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") @@ -118,9 +99,6 @@ LQEntries = Param.Unsigned(32, "Number of load queue entries") SQEntries = Param.Unsigned(32, "Number of store queue entries") - LSQDepCheckShift = Param.Unsigned(4, "Number of places to shift addr before check") - LSQCheckLoads = Param.Bool(True, - "Should dependency violations be checked for loads & stores or just stores") LFSTSize = Param.Unsigned(1024, "Last fetched store table size") SSITSize = Param.Unsigned(1024, "Store set ID table size") diff -r 2a04edb07407 -r 95f365b864dc src/cpu/o3/cpu.hh --- a/src/cpu/o3/cpu.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/o3/cpu.hh Wed Jul 27 16:46:31 2011 +0100 @@ -42,20 +42,20 @@ #include "arch/types.hh" #include "base/statistics.hh" +#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" #include "cpu/o3/comm.hh" #include "cpu/o3/cpu_policy.hh" #include "cpu/o3/scoreboard.hh" #include "cpu/o3/thread_state.hh" -#include "cpu/activity.hh" -#include "cpu/base.hh" -#include "cpu/simple_thread.hh" -#include "cpu/timebuf.hh" //#include "cpu/o3/thread_context.hh" +#include "sim/process.hh" + #include "params/DerivO3CPU.hh" -#include "sim/process.hh" template class Checker; @@ -118,6 +118,7 @@ Status _threadStatus[Impl::MaxThreads]; private: + class TickEvent : public Event { private: @@ -278,9 +279,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. */ @@ -311,6 +309,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); @@ -563,6 +564,10 @@ TheISA::ISA isa[Impl::MaxThreads]; + virtual bool recvTimingResp(MemMapPacketPtr resp_pkt, int master_id); + + virtual void recvRetryReq(int master_id); + public: /** Enum to give each stage a specific index, so when calling * activateStage() or deactivateStage(), they can specify which stage @@ -644,16 +649,10 @@ /** 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; +#endif /** Event to call process() on once draining has completed. */ Event *drainEvent; @@ -701,9 +700,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 2a04edb07407 -r 95f365b864dc src/cpu/o3/cpu.cc --- a/src/cpu/o3/cpu.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/o3/cpu.cc Wed Jul 27 16:46:31 2011 +0100 @@ -33,34 +33,25 @@ #include "config/full_system.hh" #include "config/the_isa.hh" -#include "config/use_checker.hh" -#include "cpu/o3/cpu.hh" -#include "cpu/o3/isa_specific.hh" -#include "cpu/o3/thread_context.hh" #include "cpu/activity.hh" #include "cpu/simple_thread.hh" #include "cpu/thread_context.hh" -#include "debug/Activity.hh" -#include "debug/O3CPU.hh" -#include "debug/Quiesce.hh" +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/cpu.hh" +#include "cpu/o3/thread_context.hh" #include "enums/MemoryMode.hh" #include "sim/core.hh" #include "sim/stat_control.hh" -#include "sim/system.hh" #if FULL_SYSTEM #include "cpu/quiesce_event.hh" +#include "sim/system.hh" #else #include "sim/process.hh" #endif -#if USE_CHECKER -#include "cpu/checker/cpu.hh" -#endif - #if THE_ISA == ALPHA_ISA #include "arch/alpha/osfpal.hh" -#include "debug/Activity.hh" #endif class BaseCPUParams; @@ -80,6 +71,26 @@ } template +bool +FullO3CPU::recvTimingResp(MemMapPacketPtr respPkt, int master_id) +{ + if (master_id == DATA_MASTER_ID) + iew.ldstQueue.completeDataAccess(respPkt->req->threadId(), respPkt); + else + fetch.processCacheCompletion(respPkt); + return true; +} + +template +void +FullO3CPU::recvRetryReq(int master_id) { + if (master_id == DATA_MASTER_ID) + iew.ldstQueue.recvRetry(); + else + fetch.recvRetry(); +} + +template FullO3CPU::TickEvent::TickEvent(FullO3CPU *c) : Event(CPU_Tick_Pri), cpu(c) { @@ -204,7 +215,9 @@ params->activity), globalSeqNum(1), +#if FULL_SYSTEM system(params->system), +#endif // FULL_SYSTEM drainCount(0), deferRegistration(params->defer_registration) { @@ -214,18 +227,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); @@ -387,15 +388,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]; @@ -518,18 +510,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() { @@ -600,11 +580,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. @@ -695,6 +682,15 @@ } template +void +FullO3CPU::wakeFromQuiesce(ThreadID tid) +{ + activityRec.activity(); + fetch.wakeFromQuiesce(); + _status = Running; +} + +template bool FullO3CPU::deallocateContext(ThreadID tid, bool remove, int delay) { @@ -810,9 +806,8 @@ } // Squash Throughout Pipeline - DynInstPtr inst = commit.rob->readHeadInst(tid); - InstSeqNum squash_seq_num = inst->seqNum; - fetch.squash(0, squash_seq_num, inst, tid); + InstSeqNum squash_seq_num = commit.rob->readHeadInst(tid)->seqNum; + //fetch.squash(0, squash_seq_num, NULL, tid); decode.squash(tid); rename.squash(squash_seq_num, tid); iew.squash(tid); @@ -1103,7 +1098,9 @@ if (_status == SwitchedOut || _status == Idle) return; +#if FULL_SYSTEM assert(system->getMemoryMode() == Enums::timing); +#endif if (!tickEvent.scheduled()) schedule(tickEvent, nextCycle()); @@ -1144,10 +1141,6 @@ } _status = SwitchedOut; -#if USE_CHECKER - if (checker) - checker->switchOut(); -#endif if (tickEvent.scheduled()) tickEvent.squash(); } @@ -1167,7 +1160,7 @@ activityRec.reset(); - BaseCPU::takeOverFrom(oldCPU, fetch.getIcachePort(), iew.getDcachePort()); + BaseCPU::takeOverFrom(oldCPU, &instPort, &dataPort); fetch.takeOverFrom(); decode.takeOverFrom(); diff -r 2a04edb07407 -r 95f365b864dc src/cpu/simple/TimingSimpleCPU.py --- a/src/cpu/simple/TimingSimpleCPU.py Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/simple/TimingSimpleCPU.py Wed Jul 27 16:46:31 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 2a04edb07407 -r 95f365b864dc src/cpu/simple/timing.hh --- a/src/cpu/simple/timing.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/simple/timing.hh Wed Jul 27 16:46:31 2011 +0100 @@ -33,6 +33,7 @@ #include "cpu/simple/base.hh" #include "cpu/translation.hh" + #include "params/TimingSimpleCPU.hh" class TimingSimpleCPU : public BaseSimpleCPU @@ -63,7 +64,7 @@ { public: int outstanding; - PacketPtr fragments[2]; + MemMapPacketPtr fragments[2]; int getPendingFragment() @@ -81,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 @@ -128,121 +129,49 @@ 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) - { } - - 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 current response packet for the data port. + */ + MemMapPacketPtr dataRespPkt; Tick previousTick; public: - virtual Port *getPort(const std::string &if_name, int idx = -1); + /** + * In line with the master interface, receive a timing response. + * + * @param respPkt the response packet + * @param master_id the id of the port the packet is received on + * @return true if the response packet is accepted + */ + virtual bool recvTimingResp(MemMapPacketPtr resp_pkt, int master_id); + + /** + * Receive a retry for a request that was not yet accepted. + * + * @param master_id the id of the port the retry is received on + */ + virtual void recvRetryReq(int master_id); virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); @@ -256,24 +185,24 @@ virtual void activateContext(int thread_num, int delay); virtual void suspendContext(int thread_num); - Fault readMem(Addr addr, uint8_t *data, unsigned size, unsigned flags); + template + Fault read(Addr addr, T &data, unsigned flags); - Fault writeMem(uint8_t *data, unsigned size, - Addr addr, unsigned flags, uint64_t *res); + Fault readBytes(Addr addr, uint8_t *data, unsigned size, unsigned flags); + + template + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + Fault writeBytes(uint8_t *data, unsigned size, + Addr addr, unsigned flags, uint64_t *res); 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. */ @@ -281,17 +210,21 @@ private: + // The backend for writeBytes and write. It's the same as writeBytes, but + // doesn't make a copy of data. + 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 2a04edb07407 -r 95f365b864dc src/cpu/simple/timing.cc --- a/src/cpu/simple/timing.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/cpu/simple/timing.cc Wed Jul 27 16:46:31 2011 +0100 @@ -41,15 +41,12 @@ */ #include "arch/locked_mem.hh" -#include "arch/mmapped_ipr.hh" +#include "arch/mmaped_ipr.hh" #include "arch/utility.hh" #include "base/bigint.hh" #include "config/the_isa.hh" +#include "cpu/exetrace.hh" #include "cpu/simple/timing.hh" -#include "cpu/exetrace.hh" -#include "debug/Config.hh" -#include "debug/ExecFaulting.hh" -#include "debug/SimpleCPU.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "params/TimingSimpleCPU.hh" @@ -59,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() { @@ -81,57 +67,23 @@ // 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), + 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; } @@ -205,7 +157,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. @@ -264,37 +216,41 @@ } bool -TimingSimpleCPU::handleReadPacket(PacketPtr pkt) +TimingSimpleCPU::handleReadPacket(MemMapPacketPtr pkt) { RequestPtr req = pkt->req; - if (req->isMmappedIpr()) { + 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; - // memory system takes ownership of packet - dcache_pkt = NULL; + if (!dataPort->sendTimingReq(pkt)) { + _status = DcacheRetry; + dataReqPkt = pkt; + } else { + _status = DcacheWaitResponse; + // memory system takes ownership of packet + dataReqPkt = NULL; + } } - 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 { @@ -308,11 +264,12 @@ } if (do_access) { - dcache_pkt = pkt; + dataReqPkt = pkt; handleWritePacket(); } else { _status = DcacheWaitResponse; - completeDataAccess(pkt); + dataRespPkt = pkt; + completeDataAccess(); } } } @@ -321,12 +278,13 @@ 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) { SplitFragmentSenderState * send_state = dynamic_cast(pkt1->senderState); @@ -339,12 +297,12 @@ } } } else { - dcache_pkt = pkt1; + 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()) { @@ -379,7 +337,7 @@ } void -TimingSimpleCPU::buildPacket(PacketPtr &pkt, RequestPtr req, bool read) +TimingSimpleCPU::buildPacket(MemMapPacketPtr& pkt, RequestPtr req, bool read) { MemCmd cmd; if (read) { @@ -394,17 +352,17 @@ 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) { pkt1 = pkt2 = NULL; - assert(!req1->isMmappedIpr() && !req2->isMmappedIpr()); + assert(!req1->isMmapedIpr() && !req2->isMmapedIpr()); if (req->getFlags().isSet(Request::NO_ACCESS)) { buildPacket(pkt1, req, read); @@ -415,8 +373,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); @@ -432,14 +389,14 @@ } Fault -TimingSimpleCPU::readMem(Addr addr, uint8_t *data, - unsigned size, unsigned flags) +TimingSimpleCPU::readBytes(Addr addr, uint8_t *data, + unsigned size, unsigned flags) { Fault fault; 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) { @@ -479,37 +436,91 @@ return NoFault; } +template +Fault +TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) +{ + return readBytes(addr, (uint8_t *)&data, sizeof(T), flags); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +TimingSimpleCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +TimingSimpleCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + +template<> +Fault +TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + bool TimingSimpleCPU::handleWritePacket() { - RequestPtr req = dcache_pkt->req; - if (req->isMmappedIpr()) { + 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)) { + schedule(completeDataAccessEvent, nextCycle(curTick() + delay)); + } else if (!dataPort->sendTimingReq(dataReqPkt)) { _status = DcacheRetry; } else { _status = DcacheWaitResponse; // memory system takes ownership of packet - dcache_pkt = NULL; + dataReqPkt = NULL; } - return dcache_pkt == NULL; + return dataReqPkt == NULL; } Fault -TimingSimpleCPU::writeMem(uint8_t *data, unsigned size, - Addr addr, unsigned flags, uint64_t *res) +TimingSimpleCPU::writeTheseBytes(uint8_t *data, unsigned size, + Addr addr, unsigned flags, uint64_t *res) { - uint8_t *newData = new uint8_t[size]; - memcpy(newData, data, size); - 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) { @@ -529,7 +540,7 @@ req->splitOnVaddr(split_addr, req1, req2); WholeTranslationState *state = - new WholeTranslationState(req, req1, req2, newData, res, mode); + new WholeTranslationState(req, req1, req2, data, res, mode); DataTranslation *trans1 = new DataTranslation(this, state, 0); DataTranslation *trans2 = @@ -539,7 +550,7 @@ thread->dtb->translateTiming(req2, tc, trans2, mode); } else { WholeTranslationState *state = - new WholeTranslationState(req, newData, res, mode); + new WholeTranslationState(req, data, res, mode); DataTranslation *translation = new DataTranslation(this, state); thread->dtb->translateTiming(req, tc, translation, mode); @@ -549,6 +560,84 @@ return NoFault; } +Fault +TimingSimpleCPU::writeBytes(uint8_t *data, unsigned size, + Addr addr, unsigned flags, uint64_t *res) +{ + uint8_t *newData = new uint8_t[size]; + memcpy(newData, data, size); + return writeTheseBytes(newData, size, addr, flags, res); +} + +template +Fault +TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + if (traceData) { + traceData->setData(data); + } + T *dataP = (T*) new uint8_t[sizeof(T)]; + *dataP = TheISA::htog(data); + + return writeTheseBytes((uint8_t *)dataP, sizeof(T), addr, flags, res); +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +TimingSimpleCPU::write(Twin32_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(Twin64_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint64_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint32_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint16_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint8_t data, Addr addr, + unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + + +template<> +Fault +TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + void TimingSimpleCPU::finishTranslation(WholeTranslationState *state) @@ -586,24 +675,18 @@ checkPcEventQueue(); - // We must have just got suspended by a PC event - if (_status == Idle) - return; - TheISA::PCState pcState = thread->pcState(); bool needToFetch = !isRomMicroPC(pcState.microPC()) && !curMacroStaticInst; if (needToFetch) { - _status = Running; Request *ifetch_req = new Request(); ifetch_req->setThreadContext(_cpuId, /* thread ID */ 0); setupFetchRequest(ifetch_req); - DPRINTF(SimpleCPU, "Translating address %#x\n", ifetch_req->getVaddr()); thread->itb->translateTiming(ifetch_req, tc, &fetchTranslation, BaseTLB::Execute); } else { _status = IcacheWaitResponse; - completeIfetch(NULL); + completeIfetch(); numCycles += tickToCycles(curTick() - previousTick); previousTick = curTick(); @@ -615,23 +698,19 @@ TimingSimpleCPU::sendFetch(Fault fault, RequestPtr req, ThreadContext *tc) { if (fault == NoFault) { - DPRINTF(SimpleCPU, "Sending fetch for addr %#x(pa: %#x)\n", - req->getVaddr(), req->getPaddr()); - ifetch_pkt = new Packet(req, MemCmd::ReadReq, Packet::Broadcast); - ifetch_pkt->dataStatic(&inst); - DPRINTF(SimpleCPU, " -- pkt addr: %#x\n", ifetch_pkt->getAddr()); + instReqPkt = new MemMapPacket(req, MemCmd::ReadReq); + instReqPkt->dataStatic(&inst); - if (!icachePort.sendTiming(ifetch_pkt)) { + if (!instPort->sendTimingReq(instReqPkt)) { // 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; + instReqPkt = NULL; } } else { - DPRINTF(SimpleCPU, "Translation of addr %#x faulted\n", req->getVaddr()); delete req; // fetch fault: advance directly to next instruction (fault handler) _status = Running; @@ -646,20 +725,7 @@ void TimingSimpleCPU::advanceInst(Fault fault) { - - if (_status == Faulting) - return; - - if (fault != NoFault) { - advancePC(fault); - DPRINTF(SimpleCPU, "Fault occured, scheduling fetch event\n"); - reschedule(fetchEvent, nextCycle(), true); - _status = Faulting; - return; - } - - - if (!stayAtPC) + if (fault != NoFault || !stayAtPC) advancePC(fault); if (_status == Running) { @@ -672,15 +738,12 @@ void -TimingSimpleCPU::completeIfetch(PacketPtr pkt) +TimingSimpleCPU::completeIfetch() { - DPRINTF(SimpleCPU, "Complete ICache Fetch for addr %#x\n", pkt ? - pkt->getAddr() : 0); + DPRINTF(SimpleCPU, "Complete ICache Fetch\n"); // received a response from the icache: execute the received // instruction - - assert(!pkt || !pkt->isError()); assert(_status == IcacheWaitResponse); _status = Running; @@ -689,11 +752,6 @@ previousTick = curTick(); if (getState() == SimObject::Draining) { - if (pkt) { - delete pkt->req; - delete pkt; - } - completeDrain(); return; } @@ -741,78 +799,112 @@ } else { advanceInst(NoFault); } +} - if (pkt) { - delete pkt->req; - delete pkt; +bool +TimingSimpleCPU::recvTimingResp(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) { + delete resp_pkt->req; + delete 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); + } + + return true; +} + +void +TimingSimpleCPU::recvRetryReq(int master_id) { + // we shouldn't get a retry unless we have a request packet that we're + // waiting to transmit + + if (master_id == INST_MASTER_ID) { + assert(instReqPkt != NULL); + assert(_status == IcacheRetry); + + if (instPort->sendTimingReq(instReqPkt)) { + _status = IcacheWaitResponse; + instReqPkt = NULL; + } else { + // someone told us to retry and then still did not accept the + // request packet, no need to make any changes since we will retry + // next time they call us + } + } else { + assert(dataReqPkt != NULL); + assert(_status == DcacheRetry); + + if (dataReqPkt->senderState) { + // This is a packet from a split access. + SplitFragmentSenderState* send_state = + dynamic_cast( + dataReqPkt->senderState); + assert(send_state); + Packet* big_pkt = send_state->bigPkt; + + SplitMainSenderState * main_send_state = + dynamic_cast(big_pkt->senderState); + assert(main_send_state); + + if (dataPort->sendTimingReq(dataReqPkt)) { + // 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) { + dataReqPkt = main_send_state->fragments[other_index]; + if ((big_pkt->isRead() && handleReadPacket(dataReqPkt)) || + (big_pkt->isWrite() && handleWritePacket())) { + main_send_state->fragments[other_index] = NULL; + } + } else { + _status = DcacheWaitResponse; + // memory system takes ownership of packet + dataReqPkt = NULL; + } + } + } else if (dataPort->sendTimingReq(dataReqPkt)) { + _status = DcacheWaitResponse; + // memory system takes ownership of packet + dataReqPkt = NULL; + } } } void -TimingSimpleCPU::IcachePort::ITickEvent::process() -{ - cpu->completeIfetch(pkt); -} - -bool -TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt) -{ - if (pkt->isResponse() && !pkt->wasNacked()) { - DPRINTF(SimpleCPU, "Received timing response %#x\n", pkt->getAddr()); - // delay processing of returned data until next CPU clock edge - Tick next_tick = cpu->nextCycle(curTick()); - - if (next_tick == curTick()) - cpu->completeIfetch(pkt); - else - tickEvent.schedule(pkt, next_tick); - - 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; - } -} - -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)); 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 = @@ -827,13 +919,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) @@ -846,12 +938,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(); @@ -865,7 +958,6 @@ advanceInst(fault); } - void TimingSimpleCPU::completeDrain() { @@ -874,131 +966,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 2a04edb07407 -r 95f365b864dc src/dev/Device.py --- a/src/dev/Device.py Sun Jul 10 12:56:09 2011 -0500 +++ b/src/dev/Device.py Wed Jul 27 16:46:31 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' @@ -64,8 +58,6 @@ ret_bad_addr = Param.Bool(False, "Return pkt status bad address on access") update_data = Param.Bool(False, "Update the data that is returned on writes") warn_access = Param.String("", "String to print when device is accessed") - fake_mem = Param.Bool(False, - "Is this device acting like a memory and thus may get a cache line sized req") class BadAddr(IsaFake): pio_addr = 0 diff -r 2a04edb07407 -r 95f365b864dc src/dev/io_device.hh --- a/src/dev/io_device.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/dev/io_device.hh Wed Jul 27 16:46:31 2011 +0100 @@ -35,7 +35,8 @@ #include "base/fast_alloc.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/BasicPioDevice.hh" #include "params/DmaDevice.hh" #include "params/PioDevice.hh" @@ -47,32 +48,12 @@ 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. - */ -class PioPort : public SimpleTimingPort +class DmaMaster : public MemMapMasterInterface { - protected: - /** The device that this port serves. */ - PioDevice *device; + private: - virtual Tick recvAtomic(PacketPtr pkt); + MemMapMasterPort masterPort; - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); - - public: - - PioPort(PioDevice *dev, System *s, std::string pname = "-pioport"); -}; - - -class DmaPort : public Port -{ protected: struct DmaReqState : public Packet::SenderState, public FastAlloc { @@ -80,9 +61,6 @@ * complete. */ Event *completionEvent; - /** Where we came from for some sanity checking. */ - Port *outPort; - /** Total number of bytes that this transaction involves. */ Addr totBytes; @@ -93,14 +71,14 @@ Tick delay; - DmaReqState(Event *ce, Port *p, Addr tb, Tick _delay) - : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0), + DmaReqState(Event *ce, Addr tb, Tick _delay) + : completionEvent(ce), totBytes(tb), numBytes(0), delay(_delay) {} }; MemObject *device; - std::list transmitList; + MemMapPacketList transmitList; /** The system that device/port are in. This is used to select which mode * we are currently operating in. */ @@ -120,47 +98,44 @@ * 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 bool recvTimingResp(MemMapPacketPtr respPkt, int master_id); - virtual void recvStatusChange(Status status) - { ; } + virtual void recvRetryReq(int master_id); - virtual void recvRetry() ; + virtual void recvRangeChange(int master_id); - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { resp.clear(); snoop = false; } - - void queueDma(PacketPtr pkt, bool front = false); + void queueDma(MemMapPacketPtr pkt); void sendDma(); /** event to give us a kick every time we backoff time is reached. */ - EventWrapper backoffEvent; + EventWrapper backoffEvent; public: - DmaPort(MemObject *dev, System *s, Tick min_backoff, Tick max_backoff); + + 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 peerBlockSize(); } + 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; }; /** @@ -170,8 +145,17 @@ * mode we are in, etc is handled by the PioPort so the device doesn't have to * bother. */ -class PioDevice : public MemObject +class PioDevice : public MemObject, public MemMapSlaveInterface4Phase { + + public: + + /** + * 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: /** The platform we are in. This is used to decide what type of memory @@ -182,23 +166,32 @@ /** 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; + virtual AddrRangeList getAddrRanges(int slave_id) const = 0; + + virtual void beginReq(MemMapPacketPtr req_pkt, int slave_id); + + virtual void endResp(int slave_id); + + virtual void recvDebug(MemMapPacketPtr pkt, int slave_id); + + virtual Tick recvAtomic(MemMapPacketPtr reqPkt, 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; public: typedef PioDeviceParams Params; @@ -215,23 +208,24 @@ 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; - } + /** + * 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; + friend class PioPort; }; class BasicPioDevice : public PioDevice { + friend class PioDevice; + protected: /** Address that the device listens to. */ Addr pioAddr; @@ -252,23 +246,29 @@ 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; + + unsigned getBlockSize(int slave_id) const; + + virtual void init(); }; class DmaDevice : public PioDevice { protected: - DmaPort *dmaPort; + + DmaMaster dmaMaster; public: typedef DmaDeviceParams Params; DmaDevice(const Params *p); virtual ~DmaDevice(); + virtual void init(); + const Params * params() const { @@ -277,40 +277,21 @@ void dmaWrite(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) { - dmaPort->dmaAction(MemCmd::WriteReq, addr, size, event, data, delay); + dmaMaster.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); + dmaMaster.dmaAction(MemCmd::ReadReq, addr, size, event, data, delay); } - bool dmaPending() { return dmaPort->dmaPending(); } + bool dmaPending() { return dmaMaster.dmaPending(); } virtual unsigned int drain(Event *de); - unsigned cacheBlockSize() const { return dmaPort->cacheBlockSize(); } + unsigned cacheBlockSize() const { return dmaMaster.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; + friend class DmaMaster; }; diff -r 2a04edb07407 -r 95f365b864dc src/dev/io_device.cc --- a/src/dev/io_device.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/dev/io_device.cc Wed Jul 27 16:46:31 2011 +0100 @@ -31,48 +31,67 @@ #include "base/chunk_generator.hh" #include "base/trace.hh" -#include "debug/BusAddrRanges.hh" -#include "debug/DMA.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) -{ } +PioDevice::PioDevice(const Params *p) + : MemObject(p), platform(p->platform), sys(p->system), + pioPort(*this, this, "pio", PIO_SLAVE_ID), + pioRespPktQueue(this, pioPort) +{ + regPort(&pioPort); +} - -Tick -PioPort::recvAtomic(PacketPtr pkt) +PioDevice::~PioDevice() { - return pkt->isRead() ? device->read(pkt) : device->write(pkt); } void -PioPort::getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) +PioDevice::endResp(int slave_id) { - 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); + pioRespPktQueue.endDeferred(); } +void +PioDevice::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + if (req_pkt->memInhibitAsserted()) { + // snooper will supply based on copy of packet + // still target's responsibility to delete packet + delete req_pkt; + } else { + // 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()) { + pioRespPktQueue.insert(req_pkt, curTick() + latency); + } else { + delete req_pkt; + } + } -PioDevice::PioDevice(const Params *p) - : MemObject(p), platform(p->platform), sys(p->system), pioPort(NULL) -{} + // always accepts requests immediately + schedule((*memSlavePorts[slave_id])->endReqEvent, curTick()); +} -PioDevice::~PioDevice() +void +PioDevice::recvDebug(MemMapPacketPtr pkt, int slave_id) { - if (pioPort) - delete pioPort; + // 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::init() { - if (!pioPort) + if (!pioPort.isConnected()) panic("Pio port not connected to anything!"); - pioPort->sendStatusChange(Port::RangeChange); } @@ -80,7 +99,7 @@ PioDevice::drain(Event *de) { unsigned int count; - count = pioPort->drain(de); + count = pioRespPktQueue.drain(de); if (count) changeState(Draining); else @@ -88,74 +107,92 @@ 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(slave_id == PIO_SLAVE_ID); + if (pioSize == 0) { + panic("%s does not have a size", name()); + } assert(pioSize != 0); - range_list.clear(); - DPRINTF(BusAddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize); - range_list.push_back(RangeSize(pioAddr, pioSize)); + AddrRangeList ranges; + ranges.push_back(RangeSize(pioAddr, pioSize)); + return ranges; } +unsigned +BasicPioDevice::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; +} -DmaPort::DmaPort(MemObject *dev, System *s, Tick min_backoff, Tick max_backoff) - : Port(dev->name() + "-dmaport", dev), device(dev), sys(s), +void +BasicPioDevice::init() +{ + PioDevice::init(); +} + +DmaMaster::DmaMaster(MemObject *dev, System *s) + : masterPort(*this, dev, "dma"), + device(dev), sys(s), pendingCount(0), actionInProgress(0), drainEvent(NULL), - backoffTime(0), minBackoffDelay(min_backoff), - maxBackoffDelay(max_backoff), inRetry(false), backoffEvent(this) + backoffTime(0), inRetry(false), backoffEvent(this), + _name(dev->name()+"-dmaMaster") { } +const std::string +DmaMaster::name() const +{ + return _name; +} + bool -DmaPort::recvTiming(PacketPtr pkt) +DmaMaster::recvTimingResp(MemMapPacketPtr resp_pkt, int master_id) { - 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) { + if (resp_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); + resp_pkt->cmdString(), resp_pkt->getAddr(), + resp_pkt->req->getSize()); + state = dynamic_cast(resp_pkt->senderState); pendingCount--; assert(pendingCount >= 0); assert(state); - // We shouldn't ever get a block in ownership state - assert(!(pkt->memInhibitAsserted() && !pkt->sharedAsserted())); - - state->numBytes += pkt->req->getSize(); + state->numBytes += resp_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); + device->schedule(state->completionEvent, + curTick() + state->delay); else state->completionEvent->process(); } delete state; } - delete pkt->req; - delete pkt; + delete resp_pkt->req; + delete resp_pkt; if (pendingCount == 0 && drainEvent) { drainEvent->process(); @@ -168,16 +205,31 @@ return true; } +void +DmaMaster::recvRangeChange(int master_id) +{ +} + DmaDevice::DmaDevice(const Params *p) - : PioDevice(p), dmaPort(NULL) -{ } + : PioDevice(p), dmaMaster(this, sys) +{ + regPort(dmaMaster.getPort()); +} +void +DmaDevice::init() +{ + if (!dmaMaster.getPort()->isConnected()) + panic("DMA port %s not connected!", dmaMaster.getPort()->name()); + + PioDevice::init(); +} unsigned int DmaDevice::drain(Event *de) { unsigned int count; - count = pioPort->drain(de) + dmaPort->drain(de); + count = pioRespPktQueue.drain(de) + dmaMaster.drain(de); if (count) changeState(Draining); else @@ -186,7 +238,7 @@ } unsigned int -DmaPort::drain(Event *de) +DmaMaster::drain(Event *de) { if (pendingCount == 0) return 0; @@ -196,15 +248,15 @@ void -DmaPort::recvRetry() +DmaMaster::recvRetryReq(int master_id) { assert(transmitList.size()); bool result = true; do { - PacketPtr pkt = transmitList.front(); + MemMapPacketPtr pkt = transmitList.front(); DPRINTF(DMA, "Retry on %s addr %#x\n", - pkt->cmdString(), pkt->getAddr()); - result = sendTiming(pkt); + pkt->cmdString(), pkt->getAddr()); + result = masterPort->sendTimingReq(pkt); if (result) { DPRINTF(DMA, "-- Done\n"); transmitList.pop_front(); @@ -218,7 +270,7 @@ if (transmitList.size() && backoffTime && !inRetry) { DPRINTF(DMA, "Scheduling backoff for %d\n", curTick()+backoffTime); if (!backoffEvent.scheduled()) - schedule(backoffEvent, backoffTime + curTick()); + device->schedule(backoffEvent, backoffTime + curTick()); } DPRINTF(DMA, "TransmitList: %d, backoffTime: %d inRetry: %d es: %d\n", transmitList.size(), backoffTime, inRetry, @@ -227,20 +279,20 @@ void -DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, +DmaMaster::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); + DmaReqState *reqState = new DmaReqState(event, 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()); + for (ChunkGenerator gen(addr, size, cacheBlockSize()); !gen.done(); gen.next()) { Request *req = new Request(gen.addr(), gen.size(), flag); - PacketPtr pkt = new Packet(req, cmd, Packet::Broadcast); + MemMapPacketPtr pkt = new MemMapPacket(req, cmd); // Increment the data pointer on a write if (data) @@ -254,29 +306,24 @@ gen.size()); queueDma(pkt); } - } void -DmaPort::queueDma(PacketPtr pkt, bool front) +DmaMaster::queueDma(MemMapPacketPtr pkt) { - - if (front) - transmitList.push_front(pkt); - else - transmitList.push_back(pkt); + transmitList.push_back(pkt); sendDma(); } void -DmaPort::sendDma() +DmaMaster::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(); + MemMapPacketPtr pkt = transmitList.front(); Enums::MemoryMode state = sys->getMemoryMode(); if (state == Enums::timing) { @@ -290,7 +337,7 @@ bool result; do { - result = sendTiming(pkt); + result = masterPort->sendTimingReq(pkt); if (result) { transmitList.pop_front(); DPRINTF(DMA, "-- Done\n"); @@ -304,7 +351,7 @@ !backoffEvent.scheduled()) { DPRINTF(DMA, "-- Scheduling backoff timer for %d\n", backoffTime+curTick()); - schedule(backoffEvent, backoffTime + curTick()); + device->schedule(backoffEvent, backoffTime + curTick()); } } else if (state == Enums::atomic) { transmitList.pop_front(); @@ -312,7 +359,7 @@ Tick lat; DPRINTF(DMA, "--Sending DMA for addr: %#x size: %d\n", pkt->req->getPaddr(), pkt->req->getSize()); - lat = sendAtomic(pkt); + lat = masterPort->sendAtomic(pkt); assert(pkt->senderState); DmaReqState *state = dynamic_cast(pkt->senderState); assert(state); @@ -326,7 +373,8 @@ if (state->totBytes == state->numBytes) { if (state->completionEvent) { assert(!state->completionEvent->scheduled()); - schedule(state->completionEvent, curTick() + lat + state->delay); + device->schedule(state->completionEvent, + curTick() + lat + state->delay); } delete state; delete pkt->req; @@ -346,6 +394,4 @@ DmaDevice::~DmaDevice() { - if (dmaPort) - delete dmaPort; } diff -r 2a04edb07407 -r 95f365b864dc src/dev/pcidev.cc --- a/src/dev/pcidev.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/dev/pcidev.cc Wed Jul 27 16:46:31 2011 +0100 @@ -43,7 +43,6 @@ #include "base/misc.hh" #include "base/str.hh" #include "base/trace.hh" -#include "debug/PCIDEV.hh" #include "dev/alpha/tsunamireg.h" #include "dev/pciconfigall.hh" #include "dev/pcidev.hh" @@ -54,38 +53,80 @@ 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) +PciDev::recvRetryResp(int slave_id) { - snoop = false;; - if (configAddr != ULL(-1)) - resp.push_back(RangeSize(configAddr, PCI_CONFIG_SIZE+1)); + if (slave_id == CONFIG_SLAVE_ID) { + configRespPktQueue.sendDeferredPacket(); + } else { + DmaDevice::recvRetryResp(slave_id); + } } +AddrRangeList +PciDev::getAddrRanges(int slave_id) const +{ + 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; +} + +unsigned +PciDev::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; +} 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); @@ -145,14 +186,15 @@ plat->registerPciDevice(0, p->pci_dev, p->pci_func, letoh(config.interruptLine)); + + regPort(&configPort); } void PciDev::init() { - if (!configPort) + if (!configPort.isConnected()) panic("pci config port not connected to anything!"); - configPort->sendStatusChange(Port::RangeChange); PioDevice::init(); } @@ -160,7 +202,8 @@ PciDev::drain(Event *de) { unsigned int count; - count = pioPort->drain(de) + dmaPort->drain(de) + configPort->drain(de); + count = pioRespPktQueue.drain(de) + dmaMaster.drain(de) + + configRespPktQueue.drain(de); if (count) changeState(Draining); else @@ -169,7 +212,7 @@ } Tick -PciDev::readConfig(PacketPtr pkt) +PciDev::readConfig(MemMapPacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset >= PCI_DEVICE_SPECIFIC) @@ -207,18 +250,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) @@ -301,7 +334,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) | @@ -354,7 +389,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 2a04edb07407 -r 95f365b864dc src/mem/Bridge.py --- a/src/mem/Bridge.py Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/Bridge.py Wed Jul 27 16:46:31 2011 +0100 @@ -31,16 +31,10 @@ 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 2a04edb07407 -r 95f365b864dc src/mem/Bridge4PhaseToClassic.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/Bridge4PhaseToClassic.py Wed Jul 27 16:46:31 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 2a04edb07407 -r 95f365b864dc src/mem/BridgeClassicTo4Phase.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/BridgeClassicTo4Phase.py Wed Jul 27 16:46:31 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 2a04edb07407 -r 95f365b864dc src/mem/CoherentBus.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/CoherentBus.py Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,51 @@ +# 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 + +from m5.defines import buildEnv +from m5.params import * +from m5.proxy 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 2a04edb07407 -r 95f365b864dc src/mem/NonCoherentBus4Phase.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/NonCoherentBus4Phase.py Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,58 @@ +# 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.defines import buildEnv +from m5.params import * +from m5.proxy import * +from MemObject import MemObject + +class NonCoherentBus4Phase(MemObject): + type = 'NonCoherentBus4Phase' + 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 if one isn't set by a device attached to the bus (bytes)") + default_mem_master = \ + Port("MEM_MAP", "MASTER", "Default master port") + use_default_range = \ + Param.Bool(False, "Query default port device for legal range.") diff -r 2a04edb07407 -r 95f365b864dc src/mem/bridge.hh --- a/src/mem/bridge.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/bridge.hh Wed Jul 27 16:46:31 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,136 @@ * * 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 "base/fast_alloc.hh" -#include "base/types.hh" #include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/port.hh" +#include "mem/packet_queue.hh" #include "params/Bridge.hh" #include "sim/eventq.hh" -class Bridge : public MemObject +/** + * 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 { - 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; + public: - /** - * 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; + Bridge(Params *p); - public: - const Params *params() const { return _params; } + /** Slave interface */ + unsigned int getBlockSize(int slave_id) const; - /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1); + AddrRangeList getAddrRanges(int slave_id) const; + + void recvDebug(MemMapPacketPtr pkt, int slave_id); + + Tick recvAtomic(MemMapPacketPtr pkt, int slave_id); + + bool recvTimingReq(MemMapPacketPtr req_pkt, int slave_id); + + void recvRetryResp(int slave_id); + + void sendRetryReq(); + /** slave interface ends here */ + + /** Master interface */ + bool recvTimingResp(MemMapPacketPtr resp_pkt, int master_id); + + void recvRetryReq(int master_id); + + void recvRangeChange(int master_id){}; + /** master interface ends here */ + + void releaseRespSpace(); + + void releaseReqSpace(); virtual void init(); - Bridge(Params *p); + unsigned int drain(Event *de); + + protected: + + 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; + RespPacketQueue slaveRespPktQueue; + + /** The memory-mapped master port and its associated packet queue. */ + MemMapMasterPort masterPort; + ReqPacketQueue masterReqPktQueue; + + /** Minimum delay though this bridge. */ + Tick delay; + + /** Address ranges to pass through the bridge. */ + AddrRangeList ranges; + + /** we need to remember if we have a pending request while blocked */ + bool pendingReq; + + /** Event used to call send retry on unblocking with a pending + * memory request */ + EventWrapper sendRetryReqEvent; + + /** + * 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; + + /** + * 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__ diff -r 2a04edb07407 -r 95f365b864dc src/mem/bridge.cc --- a/src/mem/bridge.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/bridge.cc Wed Jul 27 16:46:31 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,331 +40,177 @@ * * 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 "debug/BusBridge.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), + slaveRespPktQueue(this, slavePort, &releaseRespSpaceEvent), + masterPort(*this, this, "master", MEM_MASTER_ID), + masterReqPktQueue(this, masterPort, &releaseReqSpaceEvent), + delay(p->delay), + pendingReq(false), + sendRetryReqEvent(this), + releaseRespSpaceEvent(this), + releaseReqSpaceEvent(this), + outstandingResponses(0), respQueueLimit(p->resp_size), + queuedRequests(0), reqQueueLimit(p->req_size) { - if (ackWrites) - panic("No support for acknowledging writes\n"); + copy(p->ranges.begin(), p->ranges.end(), back_inserter(ranges)); + 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 ports of bridge must be connected.\n"); } bool -Bridge::BridgePort::respQueueFull() -{ - assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit); - return outstandingResponses >= respQueueLimit; -} - -bool -Bridge::BridgePort::reqQueueFull() +Bridge::reqQueueFull() { assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit); return queuedRequests >= reqQueueLimit; } -/** Function called by the port when the bus is receiving a Timing - * transaction.*/ bool -Bridge::BridgePort::recvTiming(PacketPtr pkt) +Bridge::respQueueFull() { - DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", - pkt->getSrc(), pkt->getDest(), pkt->getAddr()); + assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit); + return outstandingResponses == respQueueLimit; +} - 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); +unsigned int +Bridge::getBlockSize(int slave_id) const +{ + return 0; +} - if (pkt->isRequest() && otherPort->reqQueueFull()) { - DPRINTF(BusBridge, "Remote queue full, nacking\n"); - nackRequest(pkt); +AddrRangeList +Bridge::getAddrRanges(int slave_id) const +{ + return ranges; +} + +void +Bridge::releaseRespSpace() +{ + --outstandingResponses; + // let a potentially pending request know that it should retry + if (pendingReq && !reqQueueFull()) + schedule(sendRetryReqEvent, curTick() + 1); +} + +void +Bridge::releaseReqSpace() +{ + --queuedRequests; + // in essence we should also check if the request that is pending need + // response space + if (pendingReq && !respQueueFull()) + schedule(sendRetryReqEvent, curTick() + 1); +} + +void Bridge::sendRetryReq() +{ + slavePort->sendRetryReq(); +} + +void +Bridge::recvRetryResp(int slave_id) +{ + slaveRespPktQueue.sendDeferredPacket(); +} + +Tick Bridge::recvAtomic(MemMapPacketPtr pkt, int slave_id) +{ + return delay + masterPort->sendAtomic(pkt); +} + +void +Bridge::recvDebug(MemMapPacketPtr pkt, int slave_id) +{ + pkt->pushLabel(name()); + + if (!slaveRespPktQueue.checkFunctional(pkt)) + // fall through if pkt still not satisfied + masterPort->sendDebug(pkt); + + pkt->popLabel(); +} + +bool +Bridge::recvTimingReq(MemMapPacketPtr req_pkt, int slave_id) +{ + DPRINTF(BusBridge, "recvTimingReq: addr 0x%x\n", req_pkt->getAddr()); + + // the master port is responsible for the queueing of requests. + if (reqQueueFull() || (req_pkt->needsResponse() && respQueueFull())) { + pendingReq = true; + + return false; + } else { + pendingReq = false; + + // if we need a response reserve space + if (req_pkt->needsResponse()) { + ++outstandingResponses; + } + + // always reserve space for the request + ++queuedRequests; + + // send it out on the master port after the right delay + masterReqPktQueue.insert(req_pkt, curTick() + delay); + 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; - } - } +bool +Bridge::recvTimingResp(MemMapPacketPtr respPkt, int master_id) +{ + DPRINTF(BusBridge, "recvTimingRest: addr 0x%x\n", respPkt->getAddr()); - otherPort->queueForSendTiming(pkt); + // space is already reserved when we issues the request + + // send it out on the slave port after the right delay + slaveRespPktQueue.insert(respPkt, curTick() + delay); return true; } void -Bridge::BridgePort::nackRequest(PacketPtr pkt) +Bridge::recvRetryReq(int master_id) { - // 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); + masterReqPktQueue.sendDeferredPacket(); } +unsigned int +Bridge::drain(Event *de) +{ + int count = slaveRespPktQueue.drain(de) + masterReqPktQueue.drain(de); -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); -} - -void -Bridge::BridgePort::trySend() -{ - 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(); + if (count) + changeState(Draining); 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); -} - -void -Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) -{ - otherPort->getPeerAddressRanges(resp, snoop); - FilterRangeList(filterRanges, resp); - // we don't allow snooping across bridges - snoop = false; + changeState(Drained); + return count; } Bridge * diff -r 2a04edb07407 -r 95f365b864dc 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 Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,130 @@ +/* + * 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 + */ + +#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 MemMapSlaveInterface4Phase, + public MemMapMasterInterface +{ + 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(int slave_id); + /** slave interface ends here */ + + /** 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: + + /** Temp to store req packet if it needs to be retried */ + MemMapPacketPtr reqPktToRetry; + + /** Used to remember if recvTimingResp needs to be retried */ + bool needRetry; + + void sendRetry(); + EventWrapper sendRetryEvent; +}; + +#endif //__MEM_BRIDGE_4PHASE_TO_CLASSIC_HH__ diff -r 2a04edb07407 -r 95f365b864dc 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 Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,156 @@ +/* + * 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), + reqPktToRetry(NULL), needRetry(false), sendRetryEvent(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) { + needRetry = false; + slavePort->beginResp(resp_pkt); + return true; + } else { + needRetry = true; + return false; + } +} + +void +Bridge4PhaseToClassic::recvRetryReq(int master_id) +{ + assert(reqPktToRetry != NULL); + bool success = masterPort->sendTimingReq(reqPktToRetry); + if (success) { + reqPktToRetry = NULL; + slavePort->endReq(); // Here we can call end directly + } +} + + +// 4PHASE +void +Bridge4PhaseToClassic::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + bool success = masterPort->sendTimingReq(req_pkt); + if (success) { + // You are not allowed to call end directly from begin + schedule(slavePort->endReqEvent, curTick()); + } else { + reqPktToRetry = req_pkt; + } +} + +void +Bridge4PhaseToClassic::endResp(int slave_id) +{ + 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 2a04edb07407 -r 95f365b864dc 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 Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,122 @@ +/* + * 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 "mem/packet.hh" +#include "mem/port.hh" +#include "sim/eventq.hh" +#include "params/BridgeClassicTo4Phase.hh" + +class BridgeClassicTo4Phase : public MemObject, + public MemMapSlaveInterface, + public MemMapMasterInterface4Phase +{ + public: + + typedef BridgeClassicTo4PhaseParams Params; + + /** + * Create a classic to 4phase bridge. + */ + BridgeClassicTo4Phase(Params *p); + + /** 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(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 classic send/recv calls. + */ + MemMapSlavePort slavePort; + + /** + * The master port, using new begin/end calls. + */ + MemMapMasterPort masterPort; + + private: + + /** Temp to store resp packet if it needs to be retried */ + MemMapPacketPtr respPktToRetry; + + /** Used to remember if recvTimingReq needs to be retried */ + bool needRetry; + + void sendRetry(); + EventWrapper sendRetryEvent; +}; + +#endif //__MEM_BRIDGE_CLASSIC_TO_4PHASE_HH___ diff -r 2a04edb07407 -r 95f365b864dc 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 Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,158 @@ +/* + * 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), + respPktToRetry(NULL), needRetry(false), sendRetryEvent(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) { + needRetry = false; + masterPort->beginReq(req_pkt); + return true; + } else { + needRetry = true; + return false; + } +} + +void +BridgeClassicTo4Phase::recvRetryResp(int slave_id) +{ + assert(respPktToRetry != NULL); + bool success = slavePort->sendTimingResp(respPktToRetry); + if (success) { + respPktToRetry = NULL; + masterPort->endResp(); // Here we can call end directly + } +} + + +// 4PHASE +void +BridgeClassicTo4Phase::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + bool success = slavePort->sendTimingResp(resp_pkt); + if (success) { + // You are not allowed to call end directly from begin + schedule(masterPort->endRespEvent, curTick()); + respPktToRetry = NULL; + } else { + respPktToRetry = resp_pkt; + } +} + +void +BridgeClassicTo4Phase::endReq(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 2a04edb07407 -r 95f365b864dc src/mem/coherent_bus.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/coherent_bus.hh Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,132 @@ +/* + * 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 + */ + +/** + * @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, + const MemObject* source_object) const; + + void forwardTimingReq(CacheMntPacketPtr req_pkt, + const 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) { + NonCoherentBus::recvRetryResp(slave_id); + } + + /** + * Function called by the master ports when they are told to retry + * a request. + */ + void recvRetryReq(int master_id) { + NonCoherentBus::recvRetryReq(master_id); + } + +}; + +#endif //__MEM_COHERENT_BUS_HH__ diff -r 2a04edb07407 -r 95f365b864dc src/mem/coherent_bus.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/coherent_bus.cc Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,412 @@ +/* + * 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()) { + 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 to everyone + // for debug accesses + (*p)->sendDebug(pkt); + + // we could have the response at this point + got_snoop_resp = pkt->isResponse(); + } + + // next + ++sm; + } +} + +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]; + + 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 + 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) +{ + // find out where to go, note that this cannot be assumed through + // the retryMasterPortSlaveId as the one calling recvTimingResp is + // allowed to send a different packet on a retry + int slave_id = respPkt->getPath().top(); + respPkt->getPath().pop(); + + // if we created the packet as a snoop then we have to deal with it + // and it should be sent back to a memory-mapped port + + // if it is a cache response that we did not create then we should + // merely forward it + + if (respPkt->wasExpressSnoop()) { + assert(slave_id < cacheSlavePorts.size()); + CacheMntSlavePort* slave_port = cacheSlavePorts[slave_id]; + + CacheMntPacket* fwdRespPkt = new CacheMntPacket(respPkt); + fwdRespPkt->copyData(respPkt); + + fwdRespPkt->firstWordTime = curTick() + 1; + fwdRespPkt->finishTime = curTick() + 1; + (*slave_port)->sendTimingResp(fwdRespPkt); + return true; + } else { + MemMapPacket* memPkt = new MemMapPacket(respPkt); + memPkt->copyData(respPkt); + + MemMapSlavePort* slave_port = memSlavePorts[slave_id]; + calcPacketTiming(memPkt); + + (*slave_port)->sendTimingResp(memPkt); + 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, + const 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); + 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, + const 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 2a04edb07407 -r 95f365b864dc src/mem/mem_object.hh --- a/src/mem/mem_object.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/mem_object.hh Wed Jul 27 16:46:31 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 2a04edb07407 -r 95f365b864dc src/mem/mem_object.cc --- a/src/mem/mem_object.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/mem_object.cc Wed Jul 27 16:46:31 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 2a04edb07407 -r 95f365b864dc src/mem/module_interface.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/module_interface.hh Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,438 @@ +/* + * 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. + */ +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; + + /** + * 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; + + /** + * 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; + + // 4PHASE calls + + /** + * 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. 4Phase call received\n"); + } + + /** + * Called when a request transfer ends, transfering the thread of + * execution to the master again. + * + * @param master_id the identifier of the master port + */ + virtual void endReq(int master_id) + { + panic("This is a classic interface. 4Phase call received\n"); + } + +}; + +/** + * Specialisation of the master interface for the memory-mapped + * protocol. + */ +class MemMapMasterInterface : public MasterInterface +{ + + 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; +}; + +/** + * Specialisation for the cache-maintenance protocol. + */ +class CacheMntMasterInterface : public MasterInterface +{ +}; + +template +class SlaveInterface +{ + + private: + + /** + * Prevent the copy constructor and assignment operator from being + * called. + */ + SlaveInterface(const SlaveInterface&); + SlaveInterface& operator=(const SlaveInterface&); + + protected: + + /** + * Abstract base class + */ + SlaveInterface() {} + virtual ~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; + + /** + * 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; + + /** + * 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; + + // 4PHASE calls + + /** + * 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. 4Phase call received\n"); + } + + /** + * Called when response transfer ends, transfering the thread of + * execution back to the slave. + * + * @param slave_id the identifier of the slave port + */ + virtual void endResp(int slave_id) + { + panic("This is a classic interface. 4Phase call received\n"); + } + +}; + +/** + * Protocol specific slave interface for memory-mapped slaves. Besides + * the basic functionality this also adds the ability to get address + * ranges and block sizes. + */ +class MemMapSlaveInterface : public SlaveInterface +{ + + 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 address ranges + */ + virtual AddrRangeList getAddrRanges(int slave_id) const = 0; + + /** + * Get the block size of the slave port. + * + * @param slave_id the identifier of the slave port + * + * @return the block size in bytes + */ + virtual unsigned getBlockSize(int slave_id) const = 0; + +}; + +/** + * Protocol specific slave interface for cache maintenance slaves. + */ +class CacheMntSlaveInterface : public SlaveInterface +{ +}; + +//////////// 4PHASE interfaces + +class MemMapMasterInterface4Phase : public MemMapMasterInterface +{ + + private: + + /** + * Prevent the copy constructor and assignment operator from being + * called. + */ + MemMapMasterInterface4Phase(const MemMapMasterInterface4Phase&); + MemMapMasterInterface4Phase& operator=(const MemMapMasterInterface4Phase&); + + protected: + + MemMapMasterInterface4Phase() {} + + public: + + /** + * Called by the corresponding slave port when a response packet + * is sent from the slave to the master. + * + * @param respPkt 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"); + } + + /** + * 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"); + } + + // 4PHASE calls + + /** + * 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) = 0; + + /** + * Called when a request transfer ends, transfering the thread of + * execution to the master again. + * + * @param master_id the identifier of the master port + */ + virtual void endReq(int master_id) = 0; +}; + +class MemMapSlaveInterface4Phase : public MemMapSlaveInterface +{ + + private: + + /** + * Prevent the copy constructor and assignment operator from being + * called. + */ + MemMapSlaveInterface4Phase(const MemMapSlaveInterface4Phase&); + MemMapSlaveInterface4Phase& operator=(const MemMapSlaveInterface4Phase&); + + protected: + + MemMapSlaveInterface4Phase() {} + + public: + + /** + * 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"); + } + + /** + * 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"); + } + + // 4PHASE calls + + /** + * 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 slave_id the identifier of the slave port + */ + virtual void endResp(int slave_id) = 0; + +}; + +#endif //__MEM_MODULE_INTERFACE_HH__ diff -r 2a04edb07407 -r 95f365b864dc src/mem/noncoherent_bus_4phase.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/noncoherent_bus_4phase.hh Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,332 @@ +/* + * 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_4PHASE_HH__ +#define __MEM_NON_COHERENT_BUS_4PHASE_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/NonCoherentBus4Phase.hh" +#include "sim/eventq.hh" + +class NonCoherentBus4Phase : public MemObject, + public MemMapSlaveInterface4Phase, + public MemMapMasterInterface4Phase +{ + + public: + + /** + * Create a non-coherent bus based on the parameters passed. + */ + NonCoherentBus4Phase(const NonCoherentBus4PhaseParams *p); + + virtual ~NonCoherentBus4Phase() {}; + + /** + * 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: + + /** + * 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_REQ, BUSY_RESP}; + + /** + * Holding the current global state of the bus + */ + BusState busState; + + /** + * 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. + */ + std::queue pendingReqPackets; + /** + * Queue of slave_id's who originated the pending requests. + */ + std::queue pendingReqPacketSources; + + /** + * Queue of pending responses. + */ + std::queue pendingRespPackets; + /** + * Queue of master_id's who originated the pending responses. + */ + std::queue pendingRespPacketSources; + + /** + * When the bus returns to the IDLE state, this handles any potential + * outstanding requests. + */ + void handlePendingTransfers(); + + /** + * Function called by the master ports when the bus is recieving a + * range change. + */ + void recvRangeChange(int master_id); + + virtual void beginResp(MemMapPacketPtr resp_pkt, int master_id); + virtual void endReq(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 endResp(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; + + 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); + + /** 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; + + EventWrapper + handlePendingTransfersEvent; + + + Tick calcPacketTiming(MemMapPacketPtr pkt); + + /** Mimimum tick, when the bus can become IDLE. (This simulates transfer time) */ + Tick minUnblockTick; + + /** This simulates transfer time by going to IDLE late. */ + void unblock(); + + EventWrapper unblockEvent; +}; + +#endif //__MEM_BUS_HH__ diff -r 2a04edb07407 -r 95f365b864dc src/mem/noncoherent_bus_4phase.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/noncoherent_bus_4phase.cc Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,532 @@ +/* + * 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 non-coherent bus. + */ + +#include +#include + +#include "base/misc.hh" +#include "base/trace.hh" +#include "mem/noncoherent_bus_4phase.hh" + +typedef AddrRangeList::const_iterator AddrRangeIter; + +NonCoherentBus4Phase::NonCoherentBus4Phase(const NonCoherentBus4PhaseParams *p) + : MemObject(p), + busState(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), + handlePendingTransfersEvent(this), unblockEvent(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 +NonCoherentBus4Phase::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); + } +} + +MemMapMasterPort* +NonCoherentBus4Phase::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* +NonCoherentBus4Phase::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 NonCoherentBus4Phase::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 +NonCoherentBus4Phase::recvRangeChange(int master_id) +{ + DPRINTF(BusAddrRanges, "received RangeChange from master port id %d\n", + master_id); + + updateAddrRanges(memMasterPorts[master_id]); +} + +AddrRangeList +NonCoherentBus4Phase::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 +NonCoherentBus4Phase::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 +NonCoherentBus4Phase::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. + unsigned max_bs = 0; + + // iterate over our memory-mapped masters to find out their peers + // (the slave ports) block sizes + for (MemMapMasterConstIter m = memMasterPorts.begin(); + m != memMasterPorts.end(); ++m) { + MemMapMasterPort* master_port = *m; + unsigned tmp_bs = (*master_port)->getBlockSize(); + 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"); + + return max_bs; +} + +void +NonCoherentBus4Phase::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 +NonCoherentBus4Phase::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 +NonCoherentBus4Phase::beginReq(MemMapPacketPtr req_pkt, int slave_id) +{ + if (busState != IDLE) { + // defer this until becomes IDLE + pendingReqPackets.push(req_pkt); + pendingReqPacketSources.push(slave_id); + return; + } + + busState = BUSY_REQ; + + calcPacketTiming(req_pkt); + minUnblockTick = req_pkt->finishTime; + + // push the source port onto the path + req_pkt->getPath().push(slave_id); + + // 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 +NonCoherentBus4Phase::endReq(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(); + + if (minUnblockTick <= curTick()) { + busState = IDLE; + schedule(handlePendingTransfersEvent,curTick()); + } else { + schedule(unblockEvent, minUnblockTick); + } +} + +void +NonCoherentBus4Phase::beginResp(MemMapPacketPtr resp_pkt, int master_id) +{ + if (busState != IDLE) { + // defer this until becomes IDLE + pendingRespPackets.push(resp_pkt); + pendingRespPacketSources.push(master_id); + return; + } + + busState = BUSY_RESP; + + calcPacketTiming(resp_pkt); + minUnblockTick = resp_pkt->finishTime; + + // 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 +NonCoherentBus4Phase::endResp(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 + (*memMasterPorts[sourceMasterId])->endResp(); + + if (minUnblockTick <= curTick()) { + busState = IDLE; + schedule(handlePendingTransfersEvent,curTick()); + } else { + schedule(unblockEvent, minUnblockTick); + } +} + +void +NonCoherentBus4Phase::handlePendingTransfers() +{ + // check if there are pending requests or responses and dispatch them + if (!pendingReqPackets.empty()) + { + MemMapPacketPtr req_pkt = pendingReqPackets.front(); + pendingReqPackets.pop(); + int slave_id = pendingReqPacketSources.front(); + pendingReqPacketSources.pop(); + + beginReq(req_pkt, slave_id); + } + else if (!pendingRespPackets.empty()) + { + MemMapPacketPtr resp_pkt = pendingRespPackets.front(); + pendingRespPackets.pop(); + int master_id = pendingRespPacketSources.front(); + pendingRespPacketSources.pop(); + + beginResp(resp_pkt, master_id); + } + else if (drainEvent != NULL) + { + drainEvent->process(); + drainEvent = NULL; + } +} + +Tick +NonCoherentBus4Phase::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 +NonCoherentBus4Phase::unblock() +{ + busState = IDLE; + handlePendingTransfers(); +} + +unsigned int +NonCoherentBus4Phase::drain(Event * de) +{ + // if we are not idle keep the event until drained + if (busState != IDLE) { + drainEvent = de; + return 1; + } + return 0; +} + +void +NonCoherentBus4Phase::getDotColour(unsigned char& r, unsigned char& g, + unsigned char& b) const +{ + r = 0xF3; + g = 0xB1; + b = 0x7F; +} + +NonCoherentBus4Phase* +NonCoherentBus4PhaseParams::create() +{ + return new NonCoherentBus4Phase(this); +} diff -r 2a04edb07407 -r 95f365b864dc src/mem/packet_queue.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/packet_queue.hh Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,293 @@ +/* + * 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 + */ + +#ifndef __MEM_PACKET_QUEUE_HH__ +#define __MEM_PACKET_QUEUE_HH__ + +/** + * @file + * + * Declaration of a simple packet queue for a slave port + */ +#include "mem/port.hh" +#include "sim/eventq.hh" +#include +#include + +/** + * A packet queue is a class that holds deferred packets and later + * sends them using the associated slave port. Currently the class is + * limited to be used for a slave (as the calls are hard-coded), but + * it could be generalised to look more like the TLM + * payload-event-queues with callbacks. + */ +template +class PacketQueue +{ + + protected: + + /** + * Get the type of the packets. + */ + typedef typename PROT::pkt_t pkt_t; + + /** The manager which is used for the event queue */ + EventManager* em; + + /** A deferred packet, buffered to transmit later. */ + 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. */ + typedef std::list DeferredPacketList; + + /** A list of outgoing timing response packets that haven't been + * serviced yet. */ + DeferredPacketList transmitList; + + void schedSendEvent(Tick when); + + /** 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. + **/ + EventWrapper sendEvent; + + /** If we need to drain, keep the drain event around until we're done + * here.*/ + Event *drainEvent; + + /** + * The only way an owner could know that a packet has left the queue + * is through a call to this event + */ + Event* sendNotificationEvent; + + /** Remember whether we're awaiting a retry from the bus. */ + bool pendingPkt; + + enum Handshake {CLASSIC, PHASE4}; + + Handshake handshake; + + public: + + PacketQueue(EventManager* em, Event* sendNotificationEvent = NULL, + Handshake = CLASSIC); + virtual ~PacketQueue(); + + virtual std::string name() const = 0; + + /** + * Insert a packet in the send queue + * @param pkt packet to send + * @param absolute time (in ticks) to send packet + */ + void insert(pkt_t* 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(); + + /** 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(); + + /** signals the end of the current transfer */ + void endDeferred(); + + EventWrapper beginDeferredEvent; + + /** The actual function used to send the packet. + * This is implemented in the RespPacketQueue and ReqPacketQueue. */ + virtual bool sendFunction(pkt_t* pkt) = 0; + + /** The actual function used to begin the packet transfer. + * This is implemented in the RespPacketQueue4Phase and ReqPacketQueue4Phase. */ + virtual void beginFunction(pkt_t* pkt) = 0; + + /** Check the list of queued packets against the supplied + * request. */ + bool checkFunctional(pkt_t* reqPkt); + + /** + * Hook for draining timing accesses. + * + * @param An event which is used to signal back to the caller + * @returns A number indicating how many times process will be called + */ + unsigned int drain(Event *de); +}; + +template +class RespPacketQueue : public PacketQueue +{ + private: + + /** + * Get the packet type from our base class. + */ + typedef typename PacketQueue::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; + + void beginFunction(pkt_t* pkt) + { + panic("This is a classic packet queue."); + } + + public: + + RespPacketQueue(EventManager* em, slave_port_t& slavePort, + Event* sendRespNotificationEvent = NULL); + virtual ~RespPacketQueue(); + + +}; + +template +class ReqPacketQueue : public PacketQueue +{ + private: + + /** + * Get the packet type from our base class. + */ + typedef typename PacketQueue::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: + + /** The actual function used to send the packet. */ + bool sendFunction(pkt_t* pkt) + { + return masterPort->sendTimingReq(pkt); + } + + void beginFunction(pkt_t* pkt) + { + panic("This is a classic packet queue."); + } + + public: + + std::string name() const; + + ReqPacketQueue(EventManager* em, master_port_t& masterPort, + Event* sendRespNotificationEvent = NULL); + virtual ~ReqPacketQueue(); +}; + +template +class RespPacketQueue4Phase : public PacketQueue +{ + private: + + /** + * Get the packet type from our base class. + */ + typedef typename PacketQueue::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) + { + panic("This is a 4Phase packet queue"); + return false; + } + + 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(); +}; + +#endif // __MEM_PACKET_QUEUE_HH__ diff -r 2a04edb07407 -r 95f365b864dc src/mem/packet_queue.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/packet_queue.cc Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,310 @@ +/* + * 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 + */ + +#include "mem/packet_queue.hh" +#include "mem/mem_object.hh" + +using namespace std; + + +template +PacketQueue::PacketQueue(EventManager* em, Event* sendNotificationEvent, + Handshake handshake) + : em(em), sendEvent(this), drainEvent(NULL), + sendNotificationEvent(sendNotificationEvent), + pendingPkt(false), handshake(handshake), + beginDeferredEvent(this) +{ +} + +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 +void +PacketQueue::insert(pkt_t* pkt, Tick when) +{ + + if (when < curTick()) + panic("%s got insert at time before curTick", name()); + + assert(when >= curTick()); + + // Nothing is on the list: add it and schedule an event + if (transmitList.empty() || when < transmitList.front().tick) { + //if (!transmitList.empty() && when < transmitList.front().tick) { + // pkt->print(std::cerr); + // panic("%s is adding response packet %d to head of queue %d", + // name(), when, transmitList.front().tick); + //} + + transmitList.push_front(DeferredPacket(when, pkt)); + if (handshake == CLASSIC) + schedSendEvent(when); + else + em->schedule(beginDeferredEvent, 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 + typename DeferredPacketList::iterator i = transmitList.begin(); + i++; // already checked for insertion at front + typename DeferredPacketList::iterator end = transmitList.end(); + + for (; i != end; ++i) { + if (when < i->tick) { + //if (!transmitList.empty() && when < transmitList.front().tick) { + // pkt->print(std::cerr); + // panic("%s is adding response packet %d to mid of queue %d", + // name(), when, i->tick); + //} + + transmitList.insert(i, DeferredPacket(when, pkt)); + return; + } + } + assert(false); // should never get here +} + +template +void +PacketQueue::processSendEvent() +{ + assert(!pendingPkt); + sendDeferredPacket(); +} + +template +unsigned int +PacketQueue::drain(Event *de) +{ + if (transmitList.empty() && !sendEvent.scheduled()) + return 0; + drainEvent = de; + return 1; +} + +template +void +PacketQueue::schedSendEvent(Tick when) +{ + if (pendingPkt) { + assert(!sendEvent.scheduled()); + return; + } + + if (!sendEvent.scheduled()) { + em->schedule(sendEvent, when); + } else if (sendEvent.when() > when) { + em->reschedule(sendEvent, when); + } +} + +template +void +PacketQueue::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 = transmitList.front(); + transmitList.pop_front(); + + Packet::SenderState *old_sender_state = dp.pkt->senderState; + + bool success = sendFunction(dp.pkt); + + if (success) { + + if (sendNotificationEvent != NULL) + em->schedule(sendNotificationEvent, curTick() + 1); + + if (!transmitList.empty() && !sendEvent.scheduled()) { + Tick time = transmitList.front().tick; + em->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); + + dp.pkt->senderState = old_sender_state; + } + + pendingPkt = !success; + + if (pendingPkt) { + DPRINTF(Bus, "Send failed, waiting on retry\n"); + } +} + +template +void +PacketQueue::beginDeferred() +{ + // 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(); + + beginFunction(dp.pkt); +} + +template +void +PacketQueue::endDeferred() +{ + if (sendNotificationEvent != NULL) + em->schedule(sendNotificationEvent, curTick() + 1); + + if (!transmitList.empty() && !sendEvent.scheduled()) { + Tick time = transmitList.front().tick; + em->schedule(beginDeferredEvent, time <= curTick() ? curTick()+1 : time); + } + + if (transmitList.empty() && drainEvent && !sendEvent.scheduled()) { + drainEvent->process(); + drainEvent = NULL; + } +} + +template +RespPacketQueue::RespPacketQueue(EventManager* em, + slave_port_t& slavePort, + Event* sendNotificationEvent) : + PacketQueue(em, sendNotificationEvent, PacketQueue::CLASSIC), + slavePort(slavePort) +{ +} + +template +RespPacketQueue::~RespPacketQueue() +{ +} + +template +string +RespPacketQueue::name() const +{ + return slavePort.name() + "-queue"; +} + +template +ReqPacketQueue::ReqPacketQueue(EventManager* em, + master_port_t& masterPort, + Event* sendNotificationEvent) : + PacketQueue(em, sendNotificationEvent, PacketQueue::CLASSIC), + masterPort(masterPort) +{ +} + +template +ReqPacketQueue::~ReqPacketQueue() +{ +} + +template +std::string +ReqPacketQueue::name() const +{ + return masterPort.name() + "-queue"; +} + +template +RespPacketQueue4Phase::RespPacketQueue4Phase(EventManager* em, + slave_port_t& slavePort, + Event* sendNotificationEvent) + : PacketQueue(em, sendNotificationEvent, PacketQueue::PHASE4), + slavePort(slavePort) +{ +} + +template +RespPacketQueue4Phase::~RespPacketQueue4Phase() +{ +} + +template +std::string +RespPacketQueue4Phase::name() const +{ + return slavePort.name() + "-queue"; +} + +/** Packet queue template instantiations. */ +template class PacketQueue; +template class PacketQueue; +template class RespPacketQueue; +template class RespPacketQueue; +template class ReqPacketQueue; +template class ReqPacketQueue; + +template class RespPacketQueue4Phase; diff -r 2a04edb07407 -r 95f365b864dc src/mem/port.hh --- a/src/mem/port.hh Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/port.hh Wed Jul 27 16:46:31 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,412 @@ * (INCLUDING 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 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 + */ + 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. */ + 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 to keep track of the ID of this port to enable the + * interface behind it to distinguish between multiple incoming + * paths. + */ + 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 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 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. +/** Convenience typedef for now. */ +typedef SlavePort MemMapSlavePort; +typedef SlavePort CacheMntSlavePort; + +/** + * 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 slave port. By doing this the master + * is also updating the slave. 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 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 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 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 MasterPort MemMapMasterPort; +typedef MasterPort CacheMntMasterPort; + #endif //__MEM_PORT_HH__ diff -r 2a04edb07407 -r 95f365b864dc src/mem/port.cc --- a/src/mem/port.cc Sun Jul 10 12:56:09 2011 -0500 +++ b/src/mem/port.cc Wed Jul 27 16:46:31 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,76 +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 "debug/Config.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) { } @@ -102,76 +54,151 @@ { } -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) +{ +} + +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 make sure it can access the shared link state, + // as kept by the corresponding master port interface + slavePortInterface.bind(master_port->getInterface(), master_port->getId(), + (*master_port)->bindState()); } + +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) +{ +} + +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)->bindState()); + } 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 2a04edb07407 -r 95f365b864dc src/mem/port_interface.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/port_interface.hh Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,525 @@ +/* + * 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 "sim/eventq.hh" +#include "mem/module_interface.hh" + +/** + * The generic port interface is merely keeping track of the basic + * link state that is currently shared both between the request and + * response path, and at the moment a protocol-agnostic "channel" + * state. + */ +class PortInterface +{ + + protected: + + /** + * Abstract class that should never be instantiated, and a virtual + * destructor to match. + */ + PortInterface() {} + + virtual ~PortInterface() {} + + public: + + /** + * State of link between master and slave ports using begin end + * handshake. This is needed for the interfacing of the 4 phase + * and "classic" gem5 ports, and also useful to poke at the + * current state of ports, e.g. in a bus. + */ + enum LinkState {IDLE, BUSY}; + +}; + +/** + * Protocol agnostic part of the slave port interface. + */ +class BaseSlavePortInterface : public PortInterface +{ + + protected: + + /** + * Default constructible abstract class. + */ + BaseSlavePortInterface(); + + virtual ~BaseSlavePortInterface() {} + + /** + * 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; + + /** + * Pointer to state of the request link, kept by the corresponding + * master port interface. + */ + LinkState* _reqLinkState; + + /** + * State of response link + */ + LinkState _respLinkState; + + public: + + /** + * Crude hack for now to bind the state on the two sides. + */ + LinkState* bindState() { return &_respLinkState; } + + /** + * Return state of resp link. + */ + LinkState respLinkState() const; + +}; + +/** + * 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 BaseSlavePortInterface +{ + public: + + /** + * Give the master 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; + + /** + * Bind this slave port interface to a compatible master + * interface. Note that this member is virtual so that it is + * possible to define protocol-specific binding. + * + * @param master_interface the master interface + * @param master_id the port id on the master interface in question + * @param req_link_state a pointer to the request link state + */ + virtual void bind(master_if_t* master_interface, + unsigned int master_id, + LinkState* req_link_state); + + /** + * Make the event queue happy + */ + std::string name() const { return "SlavePortInterface"; } + + /** + * 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); + } + + /** + * 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. + */ + void beginResp(pkt_t* resp_pkt); + + /** + * 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. + */ + void endReq(); + + /** + * Convenience event used for deferred endReq calls + */ + EventWrapper, + &SlavePortInterface::endReq> endReqEvent; + + protected: + + /** + * Default constructible but abstract. + */ + SlavePortInterface(); + + virtual ~SlavePortInterface() {} + + /** + * The master exported interface that implements the actual + * functionality. + */ + master_if_t* masterInterface; + +}; + +/** + * Specialisation for the memory-mapped slave port interface. This + * interface adds the ability to send an address range change. + */ +class MemMapSlavePortInterface : + public SlavePortInterface +{ + + public: + + /** + * Tell the connected master that our address range has changed + */ + void sendRangeChange() const; + +}; + +/** + * Specialisation for the cache-maintenance slave port interface. This + * interface does not add any functions. + */ +class CacheMntSlavePortInterface : + public SlavePortInterface +{ +}; + +class BaseMasterPortInterface : public PortInterface +{ + + protected: + + /** + * Default constructible abstract class + */ + BaseMasterPortInterface(); + + /** + * Virtual due to the inheritance. + */ + virtual ~BaseMasterPortInterface() {} + + /** + * 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; + + /** + * State of the request link. + */ + LinkState _reqLinkState; + + /** + * Pointer to the state of the response link, kept by the + * corresponding slave port interface. + */ + LinkState* _respLinkState; + + public: + + /** + * Crude hack for now to bind the state on the two sides. + */ + LinkState* bindState() { return &_reqLinkState; } + + /** + * Return state of request link. + */ + LinkState reqLinkState() const; + +}; + +template +class MasterPortInterface : public BaseMasterPortInterface +{ + + 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; + + /** + * Bind this master port interface to a compatible slave + * interface. Note that this member is virtual so that it is + * possible to define protocol-specific binding. + * + * @param slave_interface the slave interface + * @param slave_id the port id on the slave interface in question + * @param resp_link_state a pointer to the response link state + */ + virtual void bind(slave_if_t* slave_interface, + unsigned int slave_id, + LinkState* resp_linkstate); + + /** + * Make the event queue happy + */ + std::string name() const { return "MasterPortInterface"; } + + /** + * 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); + } + + /** + * 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. + */ + void beginReq(pkt_t* req_pkt); + + /** + * 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. + */ + void endResp(); + + /** + * Convenience event used for deferred endResp calls + */ + EventWrapper, + &MasterPortInterface::endResp> endRespEvent; + + protected: + + /** + * Default constructible but abstract. + */ + MasterPortInterface(); + + virtual ~MasterPortInterface() {} + + /** + * The slave exported interface that implements the actual + * functionality. + */ + slave_if_t* slaveInterface; + +}; + +/** + * 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 MasterPortInterface +{ + + private: + + /** + * The maximum burst size as perceived by the connected slave + * port. This is set when this master port interface is bound to a + * slave interface. + */ + unsigned int slaveBlockSize; + + /** + * Internal helper function for read/writeMem(). + */ + void blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) const; + + public: + + /** + * Default constructible + */ + MemMapMasterPortInterface(); + + /** + * Override the binding so that we can get the block size at binding time. + * + * @param slave_interface the slave interface + * @param slave_id the port id on the slave interface in question + * @param resp_link_state a pointer to the response link state + */ + void bind(MemMapSlaveInterface* slave_interface, + unsigned int slave_id, + LinkState* resp_link_state); + + /** + * 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 +{ +}; + +#endif diff -r 2a04edb07407 -r 95f365b864dc src/mem/port_interface.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/port_interface.cc Wed Jul 27 16:46:31 2011 +0100 @@ -0,0 +1,239 @@ +/* + * 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/chunk_generator.hh" +#include "mem/port_interface.hh" + +BaseSlavePortInterface::BaseSlavePortInterface() : + masterId(-1), _reqLinkState(NULL), _respLinkState(IDLE) +{ +} + +PortInterface::LinkState +BaseSlavePortInterface::respLinkState() const +{ + return _respLinkState; +} + +template +SlavePortInterface::SlavePortInterface() : + endReqEvent(this), masterInterface(NULL) +{ +} + +template +void +SlavePortInterface::bind(master_if_t* master_interface, + unsigned int master_id, + LinkState* req_link_state) +{ + masterInterface = master_interface; + masterId = master_id; + _reqLinkState = req_link_state; +} + +template +void +SlavePortInterface::endReq() +{ + DPRINTF(Port4Phase, "endReq ---> %s\n", name()); + assert(*_reqLinkState == BUSY); + masterInterface->endReq(masterId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + *_reqLinkState = IDLE; +} + +template +void +SlavePortInterface::beginResp(pkt_t* resp_pkt) +{ + DPRINTF(Port4Phase, "beginResp ---> %s\n", name()); + assert(_respLinkState == IDLE); + masterInterface->beginResp(resp_pkt, masterId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + _respLinkState = BUSY; +} + +void +MemMapSlavePortInterface::sendRangeChange() const +{ + masterInterface->recvRangeChange(masterId); +} + +BaseMasterPortInterface::BaseMasterPortInterface() : + slaveId(-1), _reqLinkState(IDLE), _respLinkState(NULL) +{ +} + +PortInterface::LinkState +BaseMasterPortInterface::reqLinkState() const +{ + return _reqLinkState; +} + +template +MasterPortInterface::MasterPortInterface() : + endRespEvent(this), slaveInterface(NULL) +{ +} + +template +void +MasterPortInterface::bind(slave_if_t* slave_interface, + unsigned int slave_id, + LinkState* resp_link_state) +{ + slaveInterface = slave_interface; + slaveId = slave_id; + _respLinkState = resp_link_state; +} + +//////////////////////////////////////////////////////////// +// 4PHASE calls +//////////////////////////////////////////////////////////// +template +void +MasterPortInterface::beginReq(pkt_t* req_pkt) +{ + DPRINTF(Port4Phase, "beginReq ---> %s\n", name()); + assert(_reqLinkState == IDLE); + slaveInterface->beginReq(req_pkt, slaveId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + _reqLinkState = BUSY; +} + +template +void +MasterPortInterface::endResp() +{ + DPRINTF(Port4Phase, "endResp ---> %s\n", name()); + assert(*_respLinkState == BUSY); + slaveInterface->endResp(slaveId); + // changing the link state after begin returns + // enforces at least a delta cycle between begin and end + *_respLinkState = IDLE; +} + + +MemMapMasterPortInterface::MemMapMasterPortInterface() : slaveBlockSize(0) +{ +} + +void +MemMapMasterPortInterface::bind(MemMapSlaveInterface* slave_interface, + unsigned int slave_id, + LinkState* resp_link_state) +{ + // call our base class + MasterPortInterface::bind(slave_interface, + slave_id, resp_link_state); + + // store the block size so that we only have to get it once + slaveBlockSize = slave_interface->getBlockSize(slave_id); +} + +AddrRangeList +MemMapMasterPortInterface::getAddrRanges() const +{ + return slaveInterface->getAddrRanges(slaveId); +} + +unsigned +MemMapMasterPortInterface::getBlockSize() const +{ + 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, slaveBlockSize); + !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 MasterPortInterface; +template class SlavePortInterface; +template class SlavePortInterface; diff -r 2a04edb07407 -r 95f365b864dc src/mem/protocol.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mem/protocol.hh Wed Jul 27 16:46:31 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 2a04edb07407 -r 95f365b864dc tests/configs/tsunami-simple-timing.py --- a/tests/configs/tsunami-simple-timing.py Sun Jul 10 12:56:09 2011 -0500 +++ b/tests/configs/tsunami-simple-timing.py Wed Jul 27 16:46:31 2011 +0100 @@ -41,7 +41,7 @@ block_size = 64 mshrs = 4 tgts_per_mshr = 8 - is_top_level = True + forward_snoops = False # ---------------------- # Base L2 Cache @@ -66,7 +66,6 @@ tgts_per_mshr = 12 addr_range=AddrRange(0, size='8GB') forward_snoops = False - is_top_level = True #cpu cpu = TimingSimpleCPU(cpu_id=0) @@ -75,18 +74,25 @@ 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.iocacheBridge4PhaseToClassic = Bridge4PhaseToClassic() +system.iobus.mem_master = system.iocacheBridge4PhaseToClassic.slave +system.iocacheBridge4PhaseToClassic.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),