# Copyright (c) 2010-2012, 2015-2019 ARM Limited # Copyright (c) 2020 Barkhausen Institut # 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) 2010-2011 Advanced Micro Devices, Inc. # Copyright (c) 2006-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. import m5 from m5.objects import * from m5.util import * from common.Benchmarks import * from common import ObjectList # Populate to reflect supported os types per target ISA os_types = { 'mips' : [ 'linux' ], 'riscv' : [ 'linux' ], # TODO that's a lie 'sparc' : [ 'linux' ], 'x86' : [ 'linux' ], 'arm' : [ 'linux', 'android-gingerbread', 'android-ics', 'android-jellybean', 'android-kitkat', 'android-nougat', ], } class CowIdeDisk(IdeDisk): image = CowDiskImage(child=RawDiskImage(read_only=True), read_only=False) def childImage(self, ci): self.image.child.image_file = ci class MemBus(SystemXBar): badaddr_responder = BadAddr() default = Self.badaddr_responder.pio def attach_9p(parent, bus): viopci = PciVirtIO() viopci.vio = VirtIO9PDiod() viodir = os.path.realpath(os.path.join(m5.options.outdir, '9p')) viopci.vio.root = os.path.join(viodir, 'share') viopci.vio.socketPath = os.path.join(viodir, 'socket') os.makedirs(viopci.vio.root, exist_ok=True) if os.path.exists(viopci.vio.socketPath): os.remove(viopci.vio.socketPath) parent.viopci = viopci parent.attachPciDevice(viopci, bus) def fillInCmdline(mdesc, template, **kwargs): kwargs.setdefault('rootdev', mdesc.rootdev()) kwargs.setdefault('mem', mdesc.mem()) kwargs.setdefault('script', mdesc.script()) return template % kwargs def makeCowDisks(disk_paths): disks = [] for disk_path in disk_paths: disk = CowIdeDisk(driveID='device0') disk.childImage(disk_path); disks.append(disk) return disks def makeSparcSystem(mem_mode, mdesc=None, cmdline=None): # Constants from iob.cc and uart8250.cc iob_man_addr = 0x9800000000 uart_pio_size = 8 class CowMmDisk(MmDisk): image = CowDiskImage(child=RawDiskImage(read_only=True), read_only=False) def childImage(self, ci): self.image.child.image_file = ci self = System() if not mdesc: # generic system mdesc = SysConfig() self.readfile = mdesc.script() self.iobus = IOXBar() self.membus = MemBus() self.bridge = Bridge(delay='50ns') self.t1000 = T1000() self.t1000.attachOnChipIO(self.membus) self.t1000.attachIO(self.iobus) self.mem_ranges = [AddrRange(Addr('1MB'), size = '64MB'), AddrRange(Addr('2GB'), size ='256MB')] self.bridge.mem_side_port = self.iobus.cpu_side_ports self.bridge.cpu_side_port = self.membus.mem_side_ports self.disk0 = CowMmDisk() self.disk0.childImage(mdesc.disks()[0]) self.disk0.pio = self.iobus.mem_side_ports # The puart0 and hvuart are placed on the IO bus, so create ranges # for them. The remaining IO range is rather fragmented, so poke # holes for the iob and partition descriptors etc. self.bridge.ranges = \ [ AddrRange(self.t1000.puart0.pio_addr, self.t1000.puart0.pio_addr + uart_pio_size - 1), AddrRange(self.disk0.pio_addr, self.t1000.fake_jbi.pio_addr + self.t1000.fake_jbi.pio_size - 1), AddrRange(self.t1000.fake_clk.pio_addr, iob_man_addr - 1), AddrRange(self.t1000.fake_l2_1.pio_addr, self.t1000.fake_ssi.pio_addr + self.t1000.fake_ssi.pio_size - 1), AddrRange(self.t1000.hvuart.pio_addr, self.t1000.hvuart.pio_addr + uart_pio_size - 1) ] workload = SparcFsWorkload() # ROM for OBP/Reset/Hypervisor self.rom = SimpleMemory(image_file=binary('t1000_rom.bin'), range=AddrRange(0xfff0000000, size='8MB')) # nvram self.nvram = SimpleMemory(image_file=binary('nvram1'), range=AddrRange(0x1f11000000, size='8kB')) # hypervisor description self.hypervisor_desc = SimpleMemory(image_file=binary('1up-hv.bin'), range=AddrRange(0x1f12080000, size='8kB')) # partition description self.partition_desc = SimpleMemory(image_file=binary('1up-md.bin'), range=AddrRange(0x1f12000000, size='8kB')) self.rom.port = self.membus.mem_side_ports self.nvram.port = self.membus.mem_side_ports self.hypervisor_desc.port = self.membus.mem_side_ports self.partition_desc.port = self.membus.mem_side_ports self.system_port = self.membus.cpu_side_ports self.workload = workload return self def makeArmSystem(mem_mode, machine_type, num_cpus=1, mdesc=None, dtb_filename=None, bare_metal=False, cmdline=None, external_memory="", ruby=False, vio_9p=None, bootloader=None): assert machine_type pci_devices = [] self = ArmSystem() if not mdesc: # generic system mdesc = SysConfig() self.readfile = mdesc.script() self.iobus = IOXBar() if not ruby: self.bridge = Bridge(delay='50ns') self.bridge.mem_side_port = self.iobus.cpu_side_ports self.membus = MemBus() self.membus.badaddr_responder.warn_access = "warn" self.bridge.cpu_side_port = self.membus.mem_side_ports self.mem_mode = mem_mode platform_class = ObjectList.platform_list.get(machine_type) # Resolve the real platform name, the original machine_type # variable might have been an alias. machine_type = platform_class.__name__ self.realview = platform_class() self._bootmem = self.realview.bootmem # Attach any PCI devices this platform supports self.realview.attachPciDevices() disks = makeCowDisks(mdesc.disks()) # Old platforms have a built-in IDE or CF controller. Default to # the IDE controller if both exist. New platforms expect the # storage controller to be added from the config script. if hasattr(self.realview, "ide"): self.realview.ide.disks = disks elif hasattr(self.realview, "cf_ctrl"): self.realview.cf_ctrl.disks = disks else: self.pci_ide = IdeController(disks=disks) pci_devices.append(self.pci_ide) self.mem_ranges = [] size_remain = int(Addr(mdesc.mem())) for region in self.realview._mem_regions: if size_remain > int(region.size()): self.mem_ranges.append(region) size_remain = size_remain - int(region.size()) else: self.mem_ranges.append(AddrRange(region.start, size=size_remain)) size_remain = 0 break warn("Memory size specified spans more than one region. Creating" \ " another memory controller for that range.") if size_remain > 0: fatal("The currently selected ARM platforms doesn't support" \ " the amount of DRAM you've selected. Please try" \ " another platform") if bare_metal: # EOT character on UART will end the simulation self.realview.uart[0].end_on_eot = True self.workload = ArmFsWorkload(dtb_addr=0) else: workload = ArmFsLinux() if dtb_filename: workload.dtb_filename = binary(dtb_filename) workload.machine_type = \ machine_type if machine_type in ArmMachineType.map else "DTOnly" # Ensure that writes to the UART actually go out early in the boot if not cmdline: cmdline = 'earlyprintk=pl011,0x1c090000 console=ttyAMA0 ' + \ 'lpj=19988480 norandmaps rw loglevel=8 ' + \ 'mem=%(mem)s root=%(rootdev)s' if hasattr(self.realview.gic, 'cpu_addr'): self.gic_cpu_addr = self.realview.gic.cpu_addr # This check is for users who have previously put 'android' in # the disk image filename to tell the config scripts to # prepare the kernel with android-specific boot options. That # behavior has been replaced with a more explicit option per # the error message below. The disk can have any name now and # doesn't need to include 'android' substring. if (mdesc.disks() and os.path.split(mdesc.disks()[0])[-1].lower().count('android')): if 'android' not in mdesc.os_type(): fatal("It looks like you are trying to boot an Android " \ "platform. To boot Android, you must specify " \ "--os-type with an appropriate Android release on " \ "the command line.") # android-specific tweaks if 'android' in mdesc.os_type(): # generic tweaks cmdline += " init=/init" # release-specific tweaks if 'kitkat' in mdesc.os_type(): cmdline += " androidboot.hardware=gem5 qemu=1 qemu.gles=0 " + \ "android.bootanim=0 " elif 'nougat' in mdesc.os_type(): cmdline += " androidboot.hardware=gem5 qemu=1 qemu.gles=0 " + \ "android.bootanim=0 " + \ "vmalloc=640MB " + \ "android.early.fstab=/fstab.gem5 " + \ "androidboot.selinux=permissive " + \ "video=Virtual-1:1920x1080-16" workload.command_line = fillInCmdline(mdesc, cmdline) self.workload = workload self.realview.setupBootLoader(self, binary, bootloader) if external_memory: # I/O traffic enters iobus self.external_io = ExternalMaster(port_data="external_io", port_type=external_memory) self.external_io.port = self.iobus.cpu_side_ports # Ensure iocache only receives traffic destined for (actual) memory. self.iocache = ExternalSlave(port_data="iocache", port_type=external_memory, addr_ranges=self.mem_ranges) self.iocache.port = self.iobus.mem_side_ports # Let system_port get to nvmem and nothing else. self.bridge.ranges = [self.realview.nvmem.range] self.realview.attachOnChipIO(self.iobus) # Attach off-chip devices self.realview.attachIO(self.iobus) elif ruby: self._dma_ports = [ ] self._mem_ports = [ ] self.realview.attachOnChipIO(self.iobus, dma_ports=self._dma_ports, mem_ports=self._mem_ports) self.realview.attachIO(self.iobus, dma_ports=self._dma_ports) else: self.realview.attachOnChipIO(self.membus, self.bridge) # Attach off-chip devices self.realview.attachIO(self.iobus) for dev in pci_devices: self.realview.attachPciDevice( dev, self.iobus, dma_ports=self._dma_ports if ruby else None) self.terminal = Terminal() self.vncserver = VncServer() if vio_9p: attach_9p(self.realview, self.iobus) if not ruby: self.system_port = self.membus.cpu_side_ports if ruby: if buildEnv['PROTOCOL'] == 'MI_example' and num_cpus > 1: fatal("The MI_example protocol cannot implement Load/Store " "Exclusive operations. Multicore ARM systems configured " "with the MI_example protocol will not work properly.") return self def makeLinuxMipsSystem(mem_mode, mdesc=None, cmdline=None): class BaseMalta(Malta): ethernet = NSGigE(pci_bus=0, pci_dev=1, pci_func=0) ide = IdeController(disks=Parent.disks, pci_func=0, pci_dev=0, pci_bus=0) self = System() if not mdesc: # generic system mdesc = SysConfig() self.readfile = mdesc.script() self.iobus = IOXBar() self.membus = MemBus() self.bridge = Bridge(delay='50ns') self.mem_ranges = [AddrRange('1GB')] self.bridge.mem_side_port = self.iobus.cpu_side_ports self.bridge.cpu_side_port = self.membus.mem_side_ports self.disks = makeCowDisks(mdesc.disks()) self.malta = BaseMalta() self.malta.attachIO(self.iobus) self.malta.ide.pio = self.iobus.mem_side_ports self.malta.ide.dma = self.iobus.cpu_side_ports self.malta.ethernet.pio = self.iobus.mem_side_ports self.malta.ethernet.dma = self.iobus.cpu_side_ports self.simple_disk = SimpleDisk(disk=RawDiskImage( image_file = mdesc.disks()[0], read_only = True)) self.mem_mode = mem_mode self.terminal = Terminal() self.console = binary('mips/console') if not cmdline: cmdline = 'root=/dev/hda1 console=ttyS0' self.workload = KernelWorkload(command_line=fillInCmdline(mdesc, cmdline)) self.system_port = self.membus.cpu_side_ports return self def x86IOAddress(port): IO_address_space_base = 0x8000000000000000 return IO_address_space_base + port def connectX86ClassicSystem(x86_sys, numCPUs): # Constants similar to x86_traits.hh IO_address_space_base = 0x8000000000000000 pci_config_address_space_base = 0xc000000000000000 interrupts_address_space_base = 0xa000000000000000 APIC_range_size = 1 << 12; x86_sys.membus = MemBus() # North Bridge x86_sys.iobus = IOXBar() x86_sys.bridge = Bridge(delay='50ns') x86_sys.bridge.mem_side_port = x86_sys.iobus.cpu_side_ports x86_sys.bridge.cpu_side_port = x86_sys.membus.mem_side_ports # Allow the bridge to pass through: # 1) kernel configured PCI device memory map address: address range # [0xC0000000, 0xFFFF0000). (The upper 64kB are reserved for m5ops.) # 2) the bridge to pass through the IO APIC (two pages, already contained in 1), # 3) everything in the IO address range up to the local APIC, and # 4) then the entire PCI address space and beyond. x86_sys.bridge.ranges = \ [ AddrRange(0xC0000000, 0xFFFF0000), AddrRange(IO_address_space_base, interrupts_address_space_base - 1), AddrRange(pci_config_address_space_base, Addr.max) ] # Create a bridge from the IO bus to the memory bus to allow access to # the local APIC (two pages) x86_sys.apicbridge = Bridge(delay='50ns') x86_sys.apicbridge.cpu_side_port = x86_sys.iobus.mem_side_ports x86_sys.apicbridge.mem_side_port = x86_sys.membus.cpu_side_ports x86_sys.apicbridge.ranges = [AddrRange(interrupts_address_space_base, interrupts_address_space_base + numCPUs * APIC_range_size - 1)] # connect the io bus x86_sys.pc.attachIO(x86_sys.iobus) x86_sys.system_port = x86_sys.membus.cpu_side_ports def connectX86RubySystem(x86_sys): # North Bridge x86_sys.iobus = IOXBar() # add the ide to the list of dma devices that later need to attach to # dma controllers x86_sys._dma_ports = [x86_sys.pc.south_bridge.ide.dma] x86_sys.pc.attachIO(x86_sys.iobus, x86_sys._dma_ports) def makeX86System(mem_mode, numCPUs=1, mdesc=None, workload=None, Ruby=False): self = System() self.m5ops_base = 0xffff0000 if workload is None: workload = X86FsWorkload() self.workload = workload if not mdesc: # generic system mdesc = SysConfig() self.readfile = mdesc.script() self.mem_mode = mem_mode # Physical memory # On the PC platform, the memory region 0xC0000000-0xFFFFFFFF is reserved # for various devices. Hence, if the physical memory size is greater than # 3GB, we need to split it into two parts. excess_mem_size = \ convert.toMemorySize(mdesc.mem()) - convert.toMemorySize('3GB') if excess_mem_size <= 0: self.mem_ranges = [AddrRange(mdesc.mem())] else: warn("Physical memory size specified is %s which is greater than " \ "3GB. Twice the number of memory controllers would be " \ "created." % (mdesc.mem())) self.mem_ranges = [AddrRange('3GB'), AddrRange(Addr('4GB'), size = excess_mem_size)] # Platform self.pc = Pc() # Create and connect the busses required by each memory system if Ruby: connectX86RubySystem(self) else: connectX86ClassicSystem(self, numCPUs) # Disks disks = makeCowDisks(mdesc.disks()) self.pc.south_bridge.ide.disks = disks # Add in a Bios information structure. structures = [X86SMBiosBiosInformation()] workload.smbios_table.structures = structures # Set up the Intel MP table base_entries = [] ext_entries = [] madt_records = [] for i in range(numCPUs): bp = X86IntelMPProcessor( local_apic_id = i, local_apic_version = 0x14, enable = True, bootstrap = (i == 0)) base_entries.append(bp) lapic = X86ACPIMadtLAPIC( acpi_processor_id=i, apic_id=i, flags=1) madt_records.append(lapic) 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) madt_records.append(X86ACPIMadtIOAPIC(id=io_apic.id, address=io_apic.address, int_base=0)) # In gem5 Pc::calcPciConfigAddr(), it required "assert(bus==0)", # but linux kernel cannot config PCI device if it was not connected to # PCI bus, so we fix PCI bus id to 0, and ISA bus id to 1. pci_bus = X86IntelMPBus(bus_id = 0, bus_type='PCI ') base_entries.append(pci_bus) isa_bus = X86IntelMPBus(bus_id = 1, bus_type='ISA ') base_entries.append(isa_bus) connect_busses = X86IntelMPBusHierarchy(bus_id=1, subtractive_decode=True, parent_bus=0) ext_entries.append(connect_busses) pci_dev4_inta = X86IntelMPIOIntAssignment( interrupt_type = 'INT', polarity = 'ConformPolarity', trigger = 'ConformTrigger', source_bus_id = 0, source_bus_irq = 0 + (4 << 2), dest_io_apic_id = io_apic.id, dest_io_apic_intin = 16) base_entries.append(pci_dev4_inta) pci_dev4_inta_madt = X86ACPIMadtIntSourceOverride( bus_source = pci_dev4_inta.source_bus_id, irq_source = pci_dev4_inta.source_bus_irq, sys_int = pci_dev4_inta.dest_io_apic_intin, flags = 0 ) madt_records.append(pci_dev4_inta_madt) def assignISAInt(irq, apicPin): assign_8259_to_apic = X86IntelMPIOIntAssignment( interrupt_type = 'ExtInt', polarity = 'ConformPolarity', trigger = 'ConformTrigger', source_bus_id = 1, 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 = 1, source_bus_irq = irq, dest_io_apic_id = io_apic.id, dest_io_apic_intin = apicPin) base_entries.append(assign_to_apic) # acpi assign_to_apic_acpi = X86ACPIMadtIntSourceOverride( bus_source = 1, irq_source = irq, sys_int = apicPin, flags = 0 ) madt_records.append(assign_to_apic_acpi) assignISAInt(0, 2) assignISAInt(1, 1) for i in range(3, 15): assignISAInt(i, i) workload.intel_mp_table.base_entries = base_entries workload.intel_mp_table.ext_entries = ext_entries madt = X86ACPIMadt(local_apic_address=0, records=madt_records, oem_id='madt') workload.acpi_description_table_pointer.rsdt.entries.append(madt) workload.acpi_description_table_pointer.xsdt.entries.append(madt) workload.acpi_description_table_pointer.oem_id = 'gem5' workload.acpi_description_table_pointer.rsdt.oem_id='gem5' workload.acpi_description_table_pointer.xsdt.oem_id='gem5' return self def makeLinuxX86System(mem_mode, numCPUs=1, mdesc=None, Ruby=False, cmdline=None): # Build up the x86 system and then specialize it for Linux self = makeX86System(mem_mode, numCPUs, mdesc, X86FsLinux(), Ruby) # We assume below that there's at least 1MB of memory. We'll require 2 # just to avoid corner cases. phys_mem_size = sum([r.size() for r in self.mem_ranges]) assert(phys_mem_size >= 0x200000) assert(len(self.mem_ranges) <= 2) entries = \ [ # Mark the first megabyte of memory as reserved X86E820Entry(addr = 0, size = '639kB', range_type = 1), X86E820Entry(addr = 0x9fc00, size = '385kB', range_type = 2), # Mark the rest of physical memory as available X86E820Entry(addr = 0x100000, size = '%dB' % (self.mem_ranges[0].size() - 0x100000), range_type = 1), ] # Mark [mem_size, 3GB) as reserved if memory less than 3GB, which force # IO devices to be mapped to [0xC0000000, 0xFFFF0000). Requests to this # specific range can pass though bridge to iobus. if len(self.mem_ranges) == 1: entries.append(X86E820Entry(addr = self.mem_ranges[0].size(), size='%dB' % (0xC0000000 - self.mem_ranges[0].size()), range_type=2)) # Reserve the last 16kB of the 32-bit address space for the m5op interface entries.append(X86E820Entry(addr=0xFFFF0000, size='64kB', range_type=2)) # In case the physical memory is greater than 3GB, we split it into two # parts and add a separate e820 entry for the second part. This entry # starts at 0x100000000, which is the first address after the space # reserved for devices. if len(self.mem_ranges) == 2: entries.append(X86E820Entry(addr = 0x100000000, size = '%dB' % (self.mem_ranges[1].size()), range_type = 1)) self.workload.e820_table.entries = entries # Command line if not cmdline: cmdline = 'earlyprintk=ttyS0 console=ttyS0 lpj=7999923 root=/dev/hda1' self.workload.command_line = fillInCmdline(mdesc, cmdline) return self def makeBareMetalRiscvSystem(mem_mode, mdesc=None, cmdline=None): self = System() if not mdesc: # generic system mdesc = SysConfig() self.mem_mode = mem_mode self.mem_ranges = [AddrRange(mdesc.mem())] self.workload = RiscvBareMetal() self.iobus = IOXBar() self.membus = MemBus() self.bridge = Bridge(delay='50ns') self.bridge.mem_side_port = self.iobus.cpu_side_ports self.bridge.cpu_side_port = self.membus.mem_side_ports # Sv39 has 56 bit physical addresses; use the upper 8 bit for the IO space IO_address_space_base = 0x00FF000000000000 self.bridge.ranges = [AddrRange(IO_address_space_base, Addr.max)] self.system_port = self.membus.cpu_side_ports return self def makeDualRoot(full_system, testSystem, driveSystem, dumpfile): self = Root(full_system = full_system) self.testsys = testSystem self.drivesys = driveSystem self.etherlink = EtherLink() if hasattr(testSystem, 'realview'): self.etherlink.int0 = Parent.testsys.realview.ethernet.interface self.etherlink.int1 = Parent.drivesys.realview.ethernet.interface elif hasattr(testSystem, 'tsunami'): self.etherlink.int0 = Parent.testsys.tsunami.ethernet.interface self.etherlink.int1 = Parent.drivesys.tsunami.ethernet.interface else: fatal("Don't know how to connect these system together") if dumpfile: self.etherdump = EtherDump(file=dumpfile) self.etherlink.dump = Parent.etherdump return self def makeDistRoot(testSystem, rank, size, server_name, server_port, sync_repeat, sync_start, linkspeed, linkdelay, dumpfile): self = Root(full_system = True) self.testsys = testSystem self.etherlink = DistEtherLink(speed = linkspeed, delay = linkdelay, dist_rank = rank, dist_size = size, server_name = server_name, server_port = server_port, sync_start = sync_start, sync_repeat = sync_repeat) if hasattr(testSystem, 'realview'): self.etherlink.int0 = Parent.testsys.realview.ethernet.interface elif hasattr(testSystem, 'tsunami'): self.etherlink.int0 = Parent.testsys.tsunami.ethernet.interface else: fatal("Don't know how to connect DistEtherLink to this system") if dumpfile: self.etherdump = EtherDump(file=dumpfile) self.etherlink.dump = Parent.etherdump return self