""" Copyright (c) 2018 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import inspect import math import struct from myhdl import * # TLP formats FMT_3DW = 0x0 FMT_4DW = 0x1 FMT_3DW_DATA = 0x2 FMT_4DW_DATA = 0x3 FMT_TLP_PREFIX = 0x4 TLP_MEM_READ = (FMT_3DW, 0x00) TLP_MEM_READ_64 = (FMT_4DW, 0x00) TLP_MEM_READ_LOCKED = (FMT_3DW, 0x01) TLP_MEM_READ_LOCKED_64 = (FMT_4DW, 0x01) TLP_MEM_WRITE = (FMT_3DW_DATA, 0x00) TLP_MEM_WRITE_64 = (FMT_4DW_DATA, 0x00) TLP_IO_READ = (FMT_3DW, 0x02) TLP_IO_WRITE = (FMT_3DW_DATA, 0x02) TLP_CFG_READ_0 = (FMT_3DW, 0x04) TLP_CFG_WRITE_0 = (FMT_3DW_DATA, 0x04) TLP_CFG_READ_1 = (FMT_3DW, 0x05) TLP_CFG_WRITE_1 = (FMT_3DW_DATA, 0x05) TLP_MSG_TO_RC = (FMT_4DW, 0x10) TLP_MSG_ADDR = (FMT_4DW, 0x11) TLP_MSG_ID = (FMT_4DW, 0x12) TLP_MSG_BCAST = (FMT_4DW, 0x13) TLP_MSG_LOCAL = (FMT_4DW, 0x14) TLP_MSG_GATHER = (FMT_4DW, 0x15) TLP_MSG_DATA_TO_RC = (FMT_4DW_DATA, 0x10) TLP_MSG_DATA_ADDR = (FMT_4DW_DATA, 0x11) TLP_MSG_DATA_ID = (FMT_4DW_DATA, 0x12) TLP_MSG_DATA_BCAST = (FMT_4DW_DATA, 0x13) TLP_MSG_DATA_LOCAL = (FMT_4DW_DATA, 0x14) TLP_MSG_DATA_GATHER = (FMT_4DW_DATA, 0x15) TLP_CPL = (FMT_3DW, 0x0A) TLP_CPL_DATA = (FMT_3DW_DATA, 0x0A) TLP_CPL_LOCKED = (FMT_3DW, 0x0B) TLP_CPL_LOCKED_DATA = (FMT_3DW_DATA, 0x0B) TLP_FETCH_ADD = (FMT_3DW_DATA, 0x0C) TLP_FETCH_ADD_64 = (FMT_4DW_DATA, 0x0C) TLP_SWAP = (FMT_3DW_DATA, 0x0D) TLP_SWAP_64 = (FMT_4DW_DATA, 0x0D) TLP_CAS = (FMT_3DW_DATA, 0x0E) TLP_CAS_64 = (FMT_4DW_DATA, 0x0E) TLP_PREFIX_MRIOV = (FMT_TLP_PREFIX, 0x00) TLP_PREFIX_VENDOR_L0 = (FMT_TLP_PREFIX, 0x0E) TLP_PREFIX_VENDOR_L1 = (FMT_TLP_PREFIX, 0x0F) TLP_PREFIX_EXT_TPH = (FMT_TLP_PREFIX, 0x10) TLP_PREFIX_VENDOR_E0 = (FMT_TLP_PREFIX, 0x1E) TLP_PREFIX_VENDOR_E1 = (FMT_TLP_PREFIX, 0x1F) # Message types MSG_UNLOCK = 0x00 MSG_INVALIDATE_REQ = 0x01 MSG_INVALIDATE_CPL = 0x02 MSG_PAGE_REQ = 0x04 MSG_PRG_RESP = 0x05 MSG_LTR = 0x10 MSG_OBFF = 0x12 MSG_PM_AS_NAK = 0x14 MSG_PM_PME = 0x18 MSG_PME_TO = 0x19 MSG_PME_TO_ACK = 0x1A MSG_ASSERT_INTA = 0x20 MSG_ASSERT_INTB = 0x21 MSG_ASSERT_INTC = 0x22 MSG_ASSERT_INTD = 0x23 MSG_DEASSERT_INTA = 0x24 MSG_DEASSERT_INTB = 0x25 MSG_DEASSERT_INTC = 0x26 MSG_DEASSERT_INTD = 0x27 MSG_ERR_COR = 0x30 MSG_ERR_NONFATAL = 0x31 MSG_ERR_FATAL = 0x32 MSG_SET_SPL = 0x50 MSG_VENDOR_0 = 0x7e MSG_VENDOR_1 = 0x7f AT_DEFAULT = 0x0 AT_TRANSLATE_REQ = 0x1 AT_TRANSLATED = 0x2 CPL_STATUS_SC = 0x0 # successful completion CPL_STATUS_UR = 0x1 # unsupported request CPL_STATUS_CRS = 0x2 # configuration request retry status CPL_STATUS_CA = 0x4 # completer abort # PCIe capabilities MSI_CAP_ID = 0x05 MSI_CAP_LEN = 6 MSIX_CAP_ID = 0x11 MSIX_CAP_LEN = 3 PM_CAP_ID = 0x01 PM_CAP_LEN = 2 PCIE_CAP_ID = 0x10 PCIE_CAP_LEN = 15 SEC_PCIE_EXT_CAP_ID = 0x0019 SEC_PCIE_EXT_CAP_LEN = 3 PCIE_GEN_RATE = { 1: 2.5*8/10, 2: 5*8/10, 3: 8*128/130, 4: 16*128/130, 5: 32*128/130, } # debugging trace_routing = False def align(val, mask): if val & mask: return val + mask + 1 - (val & mask) else: return val def byte_mask_update(old, mask, new, bitmask=-1): new = (new & bitmask) | (old & ~bitmask) m1 = 1 m2 = 0xff while mask >= m1: if mask & m1: old = (old & ~m2) | (new & m2) m1 <<= 1 m2 <<= 8 return old def highlight(s): return "\033[32m%s\033[0m" % s class PcieId(object): def __init__(self, bus=0, device=0, function=0): self.bus = 0 self.device = 0 self.function = 0 if isinstance(bus, PcieId): self.bus = bus.bus self.device = bus.device self.function = bus.function elif isinstance(bus, tuple): self.bus, self.device, self.function = bus else: self.bus = bus self.device = device self.function = function @classmethod def from_int(cls, val): return cls((val >> 8) & 0xff, (val >> 3) & 0x1f, val & 0x7) def __eq__(self, other): if isinstance(other, PcieId): return self.bus == other.bus and self.device == other.device and self.function == other.function return False def __int__(self): return ((self.bus & 0xff) << 8) | ((self.device & 0x1f) << 3) | (self.function & 0x7) def __str__(self): return "%02x:%02x.%x" % (self.bus, self.device, self.function) def __repr__(self): return "PcieId(%d, %d, %d)" % (self.bus, self.device, self.function) class PcieCap(object): def __init__(self, cap_id, cap_ver=None, length=None, read=None, write=None, offset=None, next_cap=None): self.cap_id = cap_id self.cap_ver = cap_ver self.length = length self.read = read self.write = write self.offset = offset self.next_cap = next_cap def read_register(self, reg): val = self.read(reg) if reg == 0: val = (val & 0xffff0000) | ((self.next_cap & 0xff) << 8) | (self.cap_id & 0xff) return val def write_register(self, reg, data, mask): self.write(reg, data, mask) def __repr__(self): return "PcieCap(cap_id={:#x}, cap_ver={}, length={}, read={}, write={}, offset={}, next_cap={})".format(self.cap_id, repr(self.cap_ver), repr(self.length), repr(self.read), repr(self.write), repr(self.offset), repr(self.next_cap)) class PcieExtCap(PcieCap): def read_register(self, reg): if reg == 0: return ((self.next_cap & 0xfff) << 20) | ((self.cap_ver & 0xf) << 16) | (self.cap_id & 0xffff) return self.read(reg) class PcieCapList(object): def __init__(self): self.cap_type = PcieCap self.list = [] self.start = 0x10 self.end = 0x3f def find_by_id(self, cap_id): for cap in self.list: if cap.cap_id == cap_id: return cap return None def find_by_reg(self, reg): for cap in self.list: if cap.offset <= reg < cap.offset+cap.length: return cap return None def read_register(self, reg): cap = self.find_by_reg(reg) if cap: return cap.read_register(reg-cap.offset) return 0 def write_register(self, reg, data, mask): cap = self.find_by_reg(reg) if cap: cap.write_register(reg-cap.offset, data, mask) def register(self, cap_id, cap_ver=None, length=None, read=None, write=None, offset=None): if isinstance(cap_id, self.cap_type): new_cap = cap_id else: new_cap = self.find_by_id(cap_id) if new_cap: # re-registering cap # remove from list self.list.remove(new_cap) # update parameters if cap_ver is not None: new_cap.cap_ver = cap_ver if length: new_cap.length = length if read: new_cap.read = read if write: new_cap.write = write if offset: new_cap.offset = offset if not new_cap: new_cap = self.cap_type(cap_id, cap_ver, length, read, write, offset) if not new_cap.length or not new_cap.read or not new_cap.write: raise Exception("Missing required parameter") bump_list = [] if new_cap.offset: for cap in self.list: if cap.offset <= new_cap.offset+new_cap.length-1 and new_cap.offset <= cap.offset+cap.length-1: bump_list.append(cap) for cap in bump_list: self.list.remove(cap) else: new_cap.offset = self.start for cap in self.list: if cap.offset < new_cap.offset+new_cap.length-1 and new_cap.offset <= cap.offset+cap.length-1: new_cap.offset = cap.offset+cap.length self.list.append(new_cap) # sort list by offset self.list.sort(key=lambda x: x.offset) # update list next cap pointers for k in range(1, len(self.list)): self.list[k-1].next_cap = self.list[k].offset*4 self.list[k].next_cap = 0 # re-insert bumped caps for cap in bump_list: cap.offset = None self.register(cap) class PcieExtCapList(PcieCapList): def __init__(self): super(PcieExtCapList, self).__init__() self.cap_type = PcieExtCap self.start = 0x40 self.end = 0x3ff class TLP(object): def __init__(self, tlp=None): self.fmt = 0 self.type = 0 self.tc = 0 self.th = 0 self.td = 0 self.ep = 0 self.attr = 0 self.at = 0 self.length = 0 self.completer_id = PcieId(0, 0, 0) self.status = 0 self.bcm = 0 self.byte_count = 0 self.requester_id = PcieId(0, 0, 0) self.dest_id = PcieId(0, 0, 0) self.tag = 0 self.first_be = 0 self.last_be = 0 self.lower_address = 0 self.address = 0 self.register_number = 0 self.data = [] if isinstance(tlp, TLP): self.fmt = tlp.fmt self.type = tlp.type self.tc = tlp.tc self.td = tlp.td self.ep = tlp.ep self.attr = tlp.attr self.at = tlp.at self.length = tlp.length self.completer_id = tlp.completer_id self.status = tlp.status self.bcm = tlp.bcm self.byte_count = tlp.byte_count self.requester_id = tlp.requester_id self.dest_id = tlp.dest_id self.tag = tlp.tag self.first_be = tlp.first_be self.last_be = tlp.last_be self.lower_address = tlp.lower_address self.address = tlp.address self.register_number = tlp.register_number self.data = tlp.data @property def fmt_type(self): return (self.fmt, self.type) @fmt_type.setter def fmt_type(self, val): self.fmt, self.type = val @property def completer_id(self): return self._completer_id @completer_id.setter def completer_id(self, val): self._completer_id = PcieId(val) @property def requester_id(self): return self._requester_id @requester_id.setter def requester_id(self, val): self._requester_id = PcieId(val) @property def dest_id(self): return self._dest_id @dest_id.setter def dest_id(self, val): self._dest_id = PcieId(val) def check(self): """Validate TLP""" ret = True if self.fmt == FMT_3DW_DATA or self.fmt == FMT_4DW_DATA: if self.length != len(self.data): print("TLP validation failed, length field does not match data: %s" % repr(self)) ret = False if 0 > self.length > 1024: print("TLP validation failed, length out of range: %s" % repr(self)) ret = False if (self.fmt_type == TLP_MEM_READ or self.fmt_type == TLP_MEM_READ_64 or self.fmt_type == TLP_MEM_READ_LOCKED or self.fmt_type == TLP_MEM_READ_LOCKED_64 or self.fmt_type == TLP_MEM_WRITE or self.fmt_type == TLP_MEM_WRITE_64): if self.length*4 > 0x1000 - (self.address & 0xfff): print("TLP validation failed, request crosses 4K boundary: %s" % repr(self)) ret = False if (self.fmt_type == TLP_IO_READ or self.fmt_type == TLP_IO_WRITE): if self.length != 1: print("TLP validation failed, invalid length for IO request: %s" % repr(self)) ret = False if self.last_be != 0: print("TLP validation failed, invalid last BE for IO request: %s" % repr(self)) ret = False if (self.fmt_type == TLP_CPL_DATA): if (self.byte_count + (self.lower_address&3) + 3) < self.length*4: print("TLP validation failed, completion byte count too small: %s" % repr(self)) ret = False return ret def set_completion(self, tlp, completer_id, has_data=False, status=CPL_STATUS_SC): """Prepare completion for TLP""" if has_data: self.fmt_type = TLP_CPL_DATA else: self.fmt_type = TLP_CPL self.requester_id = tlp.requester_id self.completer_id = completer_id self.status = status self.attr = tlp.attr self.tag = tlp.tag self.tc = tlp.tc def set_completion_data(self, tlp, completer_id): """Prepare completion with data for TLP""" self.set_completion(tlp, completer_id, True) def set_ur_completion(self, tlp, completer_id): """Prepare unsupported request (UR) completion for TLP""" self.set_completion(tlp, completer_id, False, CPL_STATUS_UR) def set_crs_completion(self, tlp, completer_id): """Prepare configuration request retry status (CRS) completion for TLP""" self.set_completion(tlp, completer_id, False, CPL_STATUS_CRS) def set_ca_completion(self, tlp, completer_id): """Prepare completer abort (CA) completion for TLP""" self.set_completion(tlp, completer_id, False, CPL_STATUS_CA) def set_be(self, addr, length): """Compute byte enables, DWORD address, and DWORD length from byte address and length""" self.address = addr & ~3 first_pad = addr % 4 last_pad = 3 - (addr+length-1) % 4 self.length = math.ceil((length+first_pad+last_pad)/4) self.first_be = (0xf << first_pad) & 0xf self.last_be = (0xf >> last_pad) if self.length == 1: self.first_be &= self.last_be self.last_be = 0 return (first_pad, last_pad) def set_data(self, data): """Set DWORD data from byte data""" self.data = [] for k in range(0, len(data), 4): self.data.append(struct.unpack('> 32) & 0xffffffff pkt.append(l) l |= self.address & 0xfffffffc pkt.append(l) elif (self.fmt_type == TLP_CPL or self.fmt_type == TLP_CPL_DATA or self.fmt_type == TLP_CPL_LOCKED or self.fmt_type == TLP_CPL_LOCKED_DATA): l = self.byte_count & 0xfff l |= (self.bcm & 1) << 12 l |= (self.status & 0x7) << 13 l |= int(self.completer_id) << 16 pkt.append(l) l = self.lower_address & 0x7f l |= (self.tag & 0xff) << 8 l |= int(self.requester_id) << 16 pkt.append(l) else: raise Exception("Unknown TLP type") if self.fmt == FMT_3DW_DATA or self.fmt == FMT_4DW_DATA: pkt.extend(self.data) return pkt def unpack(self, pkt): """Unpack TLP from DWORD array""" self.length = pkt[0] & 0x3ff self.at = (pkt[0] >> 10) & 0x3 self.attr = (pkt[0] >> 12) & 0x3 self.ep = (pkt[0] >> 14) & 1 self.td = (pkt[0] >> 15) & 1 self.th = (pkt[0] >> 16) & 1 self.attr |= (pkt[0] >> 16) & 0x4 self.tc = (pkt[0] >> 20) & 0x7 self.type = (pkt[0] >> 24) & 0x1f self.fmt = (pkt[0] >> 29) & 0x7 if self.fmt == FMT_3DW_DATA or self.fmt == FMT_4DW_DATA: if self.length == 0: self.length = 1024 if (self.fmt_type == TLP_CFG_READ_0 or self.fmt_type == TLP_CFG_WRITE_0 or self.fmt_type == TLP_CFG_READ_1 or self.fmt_type == TLP_CFG_WRITE_1 or self.fmt_type == TLP_MEM_READ or self.fmt_type == TLP_MEM_READ_64 or self.fmt_type == TLP_MEM_READ_LOCKED or self.fmt_type == TLP_MEM_READ_LOCKED_64 or self.fmt_type == TLP_MEM_WRITE or self.fmt_type == TLP_MEM_WRITE_64 or self.fmt_type == TLP_IO_READ or self.fmt_type == TLP_IO_WRITE): self.first_be = pkt[1] & 0xf self.last_be = (pkt[1] >> 4) & 0xf self.tag = (pkt[1] >> 8) & 0xff self.requester_id = PcieId.from_int(pkt[1] >> 16) if (self.fmt_type == TLP_CFG_READ_0 or self.fmt_type == TLP_CFG_WRITE_0 or self.fmt_type == TLP_CFG_READ_1 or self.fmt_type == TLP_CFG_WRITE_1): self.register_number = (pkt[2] >> 2) >> 0x3ff self.dest_id = PcieId.from_int(pkt[2] >> 16) elif self.fmt == FMT_3DW or self.fmt == FMT_3DW_DATA: self.address = pkt[3] & 0xfffffffc elif self.fmt == FMT_4DW or self.fmt == FMT_4DW_DATA: self.address = (pkt[4] & 0xffffffff) << 32 | pkt[4] & 0xfffffffc elif (self.fmt_type == TLP_CPL or self.fmt_type == TLP_CPL_DATA or self.fmt_type == TLP_CPL_LOCKED or self.fmt_type == TLP_CPL_LOCKED_DATA): self.byte_count = pkt[1] & 0xfff self.bcm = (pkt[1] >> 12) & 1 self.status = (pkt[1] >> 13) & 0x7 self.completer_id = PcieId.from_int(pkt[1] >> 16) self.lower_address = pkt[2] & 0x7f self.tag = (pkt[2] >> 8) & 0xff self.requester_id = PcieId.from_int(pkt[2] >> 16) if self.byte_count == 0: self.byte_count = 4096 else: raise Exception("Unknown TLP type") if self.fmt == FMT_3DW_DATA: self.data = pkt[3:] elif self.fmt == FMT_4DW_DATA: self.data = pkt[4:] return self def __eq__(self, other): if isinstance(other, TLP): return ( self.data == other.data and self.fmt == other.fmt and self.type == other.type and self.tc == other.tc and self.td == other.td and self.ep == other.ep and self.attr == other.attr and self.at == other.at and self.length == other.length and self.completer_id == other.completer_id and self.status == other.status and self.bcm == other.bcm and self.byte_count == other.byte_count and self.requester_id == other.requester_id and self.dest_id == other.dest_id and self.tag == other.tag and self.first_be == other.first_be and self.last_be == other.last_be and self.lower_address == other.lower_address and self.address == other.address and self.register_number == other.register_number ) return False def __repr__(self): return ( ('TLP(data=[%s], ' % ', '.join(hex(x) for x in self.data)) + ('fmt=0x%x, ' % self.fmt) + ('type=0x%x, ' % self.type) + ('tc=0x%x, ' % self.tc) + ('th=0x%x, ' % self.th) + ('td=0x%x, ' % self.td) + ('ep=0x%x, ' % self.ep) + ('attr=0x%x, ' % self.attr) + ('at=0x%x, ' % self.at) + ('length=0x%x, ' % self.length) + ('completer_id=%s, ' % repr(self.completer_id)) + ('status=0x%x, ' % self.status) + ('bcm=0x%x, ' % self.bcm) + ('byte_count=0x%x, ' % self.byte_count) + ('requester_id=%s, ' % repr(self.requester_id)) + ('dest_id=%s, ' % repr(self.dest_id)) + ('tag=0x%x, ' % self.tag) + ('first_be=0x%x, ' % self.first_be) + ('last_be=0x%x, ' % self.last_be) + ('lower_address=0x%x, ' % self.lower_address) + ('address=0x%x, ' % self.address) + ('register_number=0x%x)' % self.register_number) ) class Port(object): """Basic port""" def __init__(self, parent=None, rx_handler=None): self.parent = parent self.other = None self.rx_handler = rx_handler self.tx_queue = [] self.tx_scheduled = False self.max_speed = 3 self.max_width = 16 self.port_delay = 5 self.cur_speed = 1 self.cur_width = 1 self.link_delay = 0 def connect(self, port): if isinstance(port, Port): self._connect(port) else: port.connect(self) def _connect(self, port): if self.other is not None: raise Exception("Already connected") port._connect_int(self) self._connect_int(port) def _connect_int(self, port): if self.other is not None: raise Exception("Already connected") self.other = port self.cur_speed = min(self.max_speed, port.max_speed) self.cur_width = min(self.max_width, port.max_width) self.link_delay = self.port_delay + port.port_delay def send(self, tlp): self.tx_queue.append(tlp) if not self.tx_scheduled: # schedule transmit yield self.transmit(), None self.tx_scheduled = True def transmit(self): if self.tx_queue: # schedule transmit tlp = self.tx_queue.pop(0) d = tlp.get_wire_size()*8/(PCIE_GEN_RATE[self.cur_speed]*self.cur_width) yield delay(int(d)) yield self.transmit(), None yield delay(int(self.link_delay)) yield self._transmit(tlp) else: self.tx_scheduled = False def _transmit(self, tlp): if self.other is None: raise Exception("Port not connected") yield from self.other.ext_recv(tlp) def ext_recv(self, tlp): if self.rx_handler is None: raise Exception("Receive handler not set") yield from self.rx_handler(tlp) class BusPort(Port): """Port for root of bus interconnection, broadcasts TLPs to all connected ports""" def __init__(self, parent=None, rx_handler=None): super(BusPort, self).__init__(parent, rx_handler) self.other = [] def _connect(self, port): if port in self.other: raise Exception("Already connected") port._connect_int(self) self._connect_int(port) def _connect_int(self, port): if port in self.other: raise Exception("Already connected") self.other.append(port) self.cur_speed = min(self.max_speed, port.max_speed) self.cur_width = min(self.max_width, port.max_width) self.link_delay = self.port_delay + port.port_delay def _transmit(self, tlp): if not self.other: raise Exception("Port not connected") for p in self.other: yield from p.ext_recv(TLP(tlp)) class PMCapability(object): """Power Management capability""" def __init__(self, *args, **kwargs): super(PMCapability, self).__init__(*args, **kwargs) # Power management capability registers self.pm_capabilities = 0 self.pm_control_status = 0 self.pm_data = 0 self.register_capability(PM_CAP_ID, PM_CAP_LEN, self.read_pm_cap_register, self.write_pm_cap_register) """ PCI Power Management Capability 31 0 +---------------------------------+----------------+----------------+ | PM Capabilities | Next Cap | PM Cap | 0 0x00 +----------------+----------------+----------------+----------------+ | PM Data | | PM Control/Status | 1 0x04 +----------------+----------------+---------------------------------+ """ def read_pm_cap_register(self, reg): if reg == 0: return self.pm_capabilities << 16 elif reg == 1: return (self.pm_data << 24) | self.pm_control_status def write_pm_cap_register(self, reg, data, mask): # TODO pass class PCIECapability(object): """PCI Express capability""" def __init__(self, *args, **kwargs): super(PCIECapability, self).__init__(*args, **kwargs) # PCIe capability registers # PCIe capabilities self.pcie_capability_version = 2 self.pcie_device_type = 0 self.pcie_slot_implemented = False self.interrupt_message_number = 0 # Device capabilities self.max_payload_size_supported = 0x5 self.phantom_functions_supported = 0 self.extended_tag_supported = True self.endpoint_l0s_acceptable_latency = 0x7 self.endpoint_l1_acceptable_latency = 0x7 self.role_based_error_reporting = True # TODO check ECN self.captured_slot_power_limit_value = 0 self.captured_slot_power_limit_scale = 0 self.function_level_reset_capability = False # Device control self.correctable_error_reporting_enable = False self.non_fatal_error_reporting_enable = False self.fatal_error_reporting_enable = False self.unsupported_request_reporting_enable = False self.enable_relaxed_ordering = True self.max_payload_size = 0x0 self.extended_tag_field_enable = False self.phantom_functions_enable = False self.aux_power_pm_enable = False self.enable_no_snoop = True self.max_read_request_size = 0x2 # Device status self.correctable_error_detected = False self.nonfatal_error_detected = False self.fatal_error_detected = False self.unsupported_request_detected = False self.aux_power_detected = False self.transactions_pending = False # Link capabilities self.max_link_speed = 0 self.max_link_width = 0 self.aspm_support = 0 self.l0s_exit_latency = 0 self.l1_exit_latency = 0 self.clock_power_management = False self.surprise_down_error_reporting_capability = False self.data_link_layer_link_active_reporting_capable = False self.link_bandwidth_notification_capability = False self.aspm_optionality_compliance = False self.port_number = 0 # Link control self.aspm_control = 0 self.read_completion_boundary = False self.link_disable = False self.common_clock_configuration = False self.extended_synch = False self.enable_clock_power_management = False self.hardware_autonomous_width_disable = False self.link_bandwidth_management_interrupt_enable = False self.link_autonomous_bandwidth_interrupt_enable = False # Link status self.current_link_speed = 0 self.negotiated_link_width = 0 self.link_training = False self.slot_clock_configuration = False self.data_link_layer_link_active = False self.link_bandwidth_management_status = False self.link_autonomous_bandwidth_status = False # Slot capabilities self.attention_button_present = False self.power_controller_present = False self.mrl_sensor_present = False self.attention_indicator_present = False self.power_indicator_present = False self.hot_plug_surprise = False self.hot_plug_capable = False self.slot_power_limit_value = 0 self.slot_power_limit_scale = 0 self.electromechanical_interlock_present = False self.no_command_completed_support = False self.physical_slot_number = 0 # Slot control self.attention_button_pressed_enable = False self.power_fault_detected_enable = False self.mrl_sensor_changed_enable = False self.presence_detect_changed_enable = False self.command_completed_interrupt_enable = False self.hot_plug_interrupt_enable = False self.attention_indicator_control = 0 self.power_indicator_control = 0 self.power_controller_control = False self.electromechanical_interlock_control = False self.data_link_layer_state_changed_enable = False # Slot status self.attention_button_pressed = False self.power_fault_detected = False self.mrl_sensor_changed = False self.presence_detect_changed = False self.command_completed = False self.mrl_sensor_state = False self.presence_detect_state = False self.electromechanical_interlock_status = False self.data_link_layer_state_changed = False # Root control self.system_error_on_correctable_error_enable = False self.system_error_on_non_fatal_error_enable = False self.system_error_on_fatal_error_enable = False self.pme_interrupt_enable = False self.crs_software_visibility_enable = False # Root capabilities self.crs_software_visibility = False # Root status self.pme_requester_id = 0 self.pme_status = False self.pme_pending = False # Device capabilities 2 self.completion_timeout_ranges_supported = 0 self.completion_timeout_disable_supported = False self.ari_forwarding_supported = False self.atomic_op_forwarding_supported = False self.atomic_op_32_bit_completer_supported = False self.atomic_op_64_bit_completer_supported = False self.cas_128_bit_completer_supported = False self.no_ro_enabled_pr_pr_passing = False self.ltr_mechanism_supported = False self.tph_completer_supported = 0 self.obff_supported = 0 self.extended_fmt_field_supported = False self.end_end_tlp_prefix_supported = False self.max_end_end_tlp_prefix = 0 # Device control 2 self.completion_timeout_value = 0 self.completion_timeout_disable = False self.ari_forwarding_enable = False self.atomic_op_requester_enable = False self.atomic_op_egress_blocking = False self.ido_request_enable = False self.ido_completion_enable = False self.ltr_mechanism_enable = False self.obff_enable = 0 self.end_end_tlp_prefix_blocking = False # Device status 2 # Link capabilities 2 self.supported_link_speeds = 0 self.crosslink_supported = False # Link control 2 self.target_link_speed = 0 self.enter_compliance = False self.hardware_autonomous_speed_disable = False self.selectable_de_emphasis = False self.transmit_margin = 0 self.enter_modified_compliance = False self.compliance_sos = False self.compliance_preset_de_emphasis = 0 # Link status 2 self.current_de_emphasis_level = False self.equalization_complete = False self.equalization_phase_1_successful = False self.equalization_phase_2_successful = False self.equalization_phase_3_successful = False self.link_equalization_request = False # Slot capabilities 2 # Slot control 2 # Slot status 2 self.register_capability(PCIE_CAP_ID, PCIE_CAP_LEN, self.read_pcie_cap_register, self.write_pcie_cap_register) """ PCIe Capability 31 0 +---------------------------------+----------------+----------------+ | PCIe Capabilities | Next Cap | PCIe Cap | 0 0x00 +---------------------------------+----------------+----------------+ | Device Capabilities | 1 0x04 +---------------------------------+---------------------------------+ | Device Status | Device Control | 2 0x08 +---------------------------------+----------------+----------------+ | Link Capabilities | 3 0x0C +---------------------------------+---------------------------------+ | Link Status | Link Control | 4 0x10 +---------------------------------+---------------------------------+ | Slot Capabilities | 5 0x14 +---------------------------------+---------------------------------+ | Slot Status | Slot Control | 6 0x18 +---------------------------------+---------------------------------+ | Root Capabilities | Root Control | 7 0x1C +---------------------------------+---------------------------------+ | Root status | 8 0x20 +---------------------------------+---------------------------------+ | Device Capabilities 2 | 9 0x24 +---------------------------------+---------------------------------+ | Device Status 2 | Device Control 2 | 10 0x28 +---------------------------------+----------------+----------------+ | Link Capabilities 2 | 11 0x2C +---------------------------------+---------------------------------+ | Link Status 2 | Link Control 2 | 12 0x30 +---------------------------------+---------------------------------+ | Slot Capabilities 2 | 13 0x34 +---------------------------------+---------------------------------+ | Slot Status 2 | Slot Control 2 | 14 0x38 +---------------------------------+---------------------------------+ """ def read_pcie_cap_register(self, reg): if reg == 0: # PCIe capabilities val = 2 << 16 val |= (self.pcie_device_type & 0xf) << 20 if self.pcie_slot_implemented: val |= 1 << 24 val |= (self.interrupt_message_number & 0x1f) << 25 return val elif reg == 1: # Device capabilities val = self.max_payload_size_supported & 0x7 val |= (self.phantom_functions_supported & 0x3) << 3 if self.extended_tag_supported: val |= 1 << 5 val |= (self.endpoint_l0s_acceptable_latency & 0x7) << 6 val |= (self.endpoint_l1_acceptable_latency & 7) << 9 if self.role_based_error_reporting: val |= 1 << 15 val |= (self.captured_slot_power_limit_value & 0xff) << 18 val |= (self.captured_slot_power_limit_scale & 0x3) << 26 if self.function_level_reset_capability: val |= 1 << 28 return val elif reg == 2: val = 0 # Device control if self.correctable_error_reporting_enable: val |= 1 << 0 if self.non_fatal_error_reporting_enable: val |= 1 << 1 if self.fatal_error_reporting_enable: val |= 1 << 2 if self.unsupported_request_reporting_enable: val |= 1 << 3 if self.enable_relaxed_ordering: val |= 1 << 4 val |= (self.max_payload_size & 0x7) << 5 if self.extended_tag_field_enable: val |= 1 << 8 if self.phantom_functions_enable: val |= 1 << 9 if self.aux_power_pm_enable: val |= 1 << 10 if self.enable_no_snoop: val |= 1 << 11 val |= (self.max_read_request_size & 0x7) << 12 # Device status if self.correctable_error_detected: val |= 1 << 16 if self.nonfatal_error_detected: val |= 1 << 17 if self.fatal_error_detected: val |= 1 << 18 if self.unsupported_request_detected: val |= 1 << 19 if self.aux_power_detected: val |= 1 << 20 if self.transactions_pending: val |= 1 << 21 return val elif reg == 3: # Link capabilities val = self.max_link_speed & 0xf val |= (self.max_link_width & 0x3f) >> 4 val |= (self.aspm_support & 0x3) >> 10 val |= (self.l0s_exit_latency & 0x7) >> 12 val |= (self.l1_exit_latency & 0x7) >> 15 if self.clock_power_management: val |= 1 << 18 if self.surprise_down_error_reporting_capability: val |= 1 << 19 if self.data_link_layer_link_active_reporting_capable: val |= 1 << 20 if self.link_bandwidth_notification_capability: val |= 1 << 21 if self.aspm_optionality_compliance: val |= 1 << 22 val |= (self.port_number & 0xff) << 24 return val elif reg == 4: # Link control val = self.aspm_control & 0x3 if self.read_completion_boundary: val |= 1 << 3 if self.link_disable: val |= 1 << 4 if self.common_clock_configuration: val |= 1 << 6 if self.extended_synch: val |= 1 << 7 if self.enable_clock_power_management: val |= 1 << 8 if self.hardware_autonomous_width_disable: val |= 1 << 9 if self.link_bandwidth_management_interrupt_enable: val |= 1 << 10 if self.link_autonomous_bandwidth_interrupt_enable: val |= 1 << 11 # Link status val |= (self.current_link_speed & 0xf) << 16 val |= (self.negotiated_link_width & 0x3f) << 20 if self.link_training: val |= 1 << 27 if self.slot_clock_configuration: val |= 1 << 28 if self.data_link_layer_link_active: val |= 1 << 29 if self.link_bandwidth_management_status: val |= 1 << 30 if self.link_autonomous_bandwidth_status: val |= 1 << 31 return val elif reg == 5: # Slot capabilities val = 0 if self.attention_button_present: val |= 1 if self.power_controller_present: val |= 1 << 1 if self.mrl_sensor_present: val |= 1 << 2 if self.attention_indicator_present: val |= 1 << 3 if self.power_indicator_present: val |= 1 << 4 if self.hot_plug_surprise: val |= 1 << 5 if self.hot_plug_capable: val |= 1 << 6 val |= (self.slot_power_limit_value & 0xff) << 7 val |= (self.slot_power_limit_scale & 0x3) << 15 if self.electromechanical_interlock_present: val |= 1 << 17 if self.no_command_completed_support: val |= 1 << 18 val |= (self.physical_slot_number & 0x1fff) << 19 return val elif reg == 6: # Slot control val = 0 if self.attention_button_pressed_enable: val |= 1 << 0 if self.power_fault_detected_enable: val |= 1 << 1 if self.mrl_sensor_changed_enable: val |= 1 << 2 if self.presence_detect_changed_enable: val |= 1 << 3 if self.command_completed_interrupt_enable: val |= 1 << 4 if self.hot_plug_interrupt_enable: val |= 1 << 5 val |= (self.attention_indicator_control & 0x3) << 6 val |= (self.power_indicator_control & 0x3) << 8 if self.power_controller_control: val |= 1 << 10 if self.electromechanical_interlock_control: val |= 1 << 11 if self.data_link_layer_state_changed_enable: val |= 1 << 12 # Slot status if self.attention_button_pressed: val |= 1 << 16 if self.power_fault_detected: val |= 1 << 17 if self.mrl_sensor_changed: val |= 1 << 18 if self.presence_detect_changed: val |= 1 << 19 if self.command_completed: val |= 1 << 20 if self.mrl_sensor_state: val |= 1 << 21 if self.presence_detect_state: val |= 1 << 22 if self.electromechanical_interlock_status: val |= 1 << 23 if self.data_link_layer_state_changed: val |= 1 << 24 return val elif reg == 7: # Root control val = 0 if self.system_error_on_correctable_error_enable: val |= 1 << 0 if self.system_error_on_non_fatal_error_enable: val |= 1 << 1 if self.system_error_on_fatal_error_enable: val |= 1 << 2 if self.pme_interrupt_enable: val |= 1 << 3 if self.crs_software_visibility_enable: val |= 1 << 4 # Root capabilities if self.crs_software_visibility: val |= 1 << 16 return val elif reg == 8: # Root status val = self.pme_requester_id & 0xffff if self.pme_status: val |= 1 << 16 if self.pme_pending: val |= 1 << 17 return val elif reg == 9: # Device capabilities 2 val = self.completion_timeout_ranges_supported & 0xf if self.completion_timeout_disable_supported: val |= 1 << 4 if self.ari_forwarding_supported: val |= 1 << 5 if self.atomic_op_forwarding_supported: val |= 1 << 6 if self.atomic_op_32_bit_completer_supported: val |= 1 << 7 if self.atomic_op_64_bit_completer_supported: val |= 1 << 8 if self.cas_128_bit_completer_supported: val |= 1 << 9 if self.no_ro_enabled_pr_pr_passing: val |= 1 << 10 if self.ltr_mechanism_supported: val |= 1 << 11 val |= (self.tph_completer_supported & 0x3) << 12 val |= (self.obff_supported & 0x3) << 18 if self.extended_fmt_field_supported: val |= 1 << 20 if self.end_end_tlp_prefix_supported: val |= 1 << 21 val |= (self.max_end_end_tlp_prefix & 0x3) << 22 return val elif reg == 10: # Device control 2 val = self.completion_timeout_value & 0xf if self.completion_timeout_disable: val |= 1 << 4 if self.ari_forwarding_enable: val |= 1 << 5 if self.atomic_op_requester_enable: val |= 1 << 6 if self.atomic_op_egress_blocking: val |= 1 << 7 if self.ido_request_enable: val |= 1 << 8 if self.ido_completion_enable: val |= 1 << 9 if self.ltr_mechanism_enable: val |= 1 << 10 val |= (self.obff_enable & 0x3) << 13 if self.end_end_tlp_prefix_blocking: val |= 1 << 15 # Device status 2 return val elif reg == 11: # Link capabilities 2 val = (self.supported_link_speeds & 0x7f) << 1 if self.crosslink_supported: val |= 1 << 8 return val elif reg == 12: # Link control 2 val = self.target_link_speed & 0xf if self.enter_compliance: val |= 1 << 4 if self.hardware_autonomous_speed_disable: val |= 1 << 5 if self.selectable_de_emphasis: val |= 1 << 6 val |= (self.transmit_margin & 0x7) << 7 if self.enter_modified_compliance: val |= 1 << 10 if self.compliance_sos: val |= 1 << 11 val |= (self.compliance_preset_de_emphasis & 0xf) << 12 # Link status 2 if self.current_de_emphasis_level: val |= 1 << 16 if self.equalization_complete: val |= 1 << 17 if self.equalization_phase_1_successful: val |= 1 << 18 if self.equalization_phase_2_successful: val |= 1 << 19 if self.equalization_phase_3_successful: val |= 1 << 20 if self.link_equalization_request: val |= 1 << 21 return val else: return 0 def write_pcie_cap_register(self, reg, data, mask): if reg == 2: # Device control if mask & 0x1: self.correctable_error_reporting_enable = (data & 1 << 0 != 0) if mask & 0x1: self.non_fatal_error_reporting_enable = (data & 1 << 1 != 0) if mask & 0x1: self.fatal_error_reporting_enable = (data & 1 << 2 != 0) if mask & 0x1: self.unsupported_request_reporting_enable = (data & 1 << 3 != 0) if mask & 0x1: self.enable_relaxed_ordering = (data & 1 << 4 != 0) if mask & 0x1: self.max_payload_size = (data >> 5) & 0x7 if mask & 0x2: self.extended_tag_field_enable = (data & 1 << 8 != 0) if mask & 0x2: self.phantom_functions_enable = (data & 1 << 9 != 0) if mask & 0x2: self.aux_power_pm_enable = (data & 1 << 10 != 0) if mask & 0x2: self.enable_no_snoop = (data & 1 << 11 != 0) if mask & 0x2: self.max_read_request_size = (data >> 12) & 0x7 if mask & 0x2 and data & 1 << 15: self.initiate_function_level_reset() # Device status if mask & 0x4 and data & 1 << 16: self.correctable_error_detected = False if mask & 0x4 and data & 1 << 17: self.nonfatal_error_detected = False if mask & 0x4 and data & 1 << 18: self.fatal_error_detected = False if mask & 0x4 and data & 1 << 19: self.unsupported_request_detected = False if mask & 0x4 and data & 1 << 20: self.aux_power_detected = False if mask & 0x4 and data & 1 << 21: self.transactions_pending = False elif reg == 4: # Link control if mask & 0x1: self.aspm_control = data & 3 if mask & 0x1: self.read_completion_boundary = (data & 1 << 4 != 0) if mask & 0x1 and data & 1 << 5: self.initiate_retrain_link() if mask & 0x1: self.common_clock_configuration = (data & 1 << 6 != 0) if mask & 0x1: self.extended_synch = (data & 1 << 7 != 0) if mask & 0x2: self.enable_clock_power_management = (data & 1 << 8 != 0) if mask & 0x2: self.hardware_autonomous_width_disable = (data & 1 << 9 != 0) if mask & 0x2: self.link_bandwidth_management_interrupt_enable = (data & 1 << 10 != 0) if mask & 0x2: self.link_autonomous_bandwidth_interrupt_enable = (data & 1 << 11 != 0) # Link status if mask & 0x8 and data & 1 << 30: self.link_bandwidth_management_status = False if mask & 0x8 and data & 1 << 31: self.link_autonomous_bandwidth_status = False elif reg == 6: # Slot control if mask & 0x1: self.attention_button_pressed_enable = (data & 1 << 0 != 0) if mask & 0x1: self.power_fault_detected_enable = (data & 1 << 1 != 0) if mask & 0x1: self.mrl_sensor_changed_enable = (data & 1 << 2 != 0) if mask & 0x1: self.presence_detect_changed_enable = (data & 1 << 3 != 0) if mask & 0x1: self.command_completed_interrupt_enable = (data & 1 << 4 != 0) if mask & 0x1: self.hot_plug_interrupt_enable = (data & 1 << 5 != 0) if mask & 0x1: self.attention_indicator_control = (data >> 6) & 0x3 if mask & 0x2: self.power_indicator_control = (data >> 8) & 0x3 if mask & 0x2: self.power_controller_control = (data & 1 << 10 != 0) if mask & 0x2: self.electromechanical_interlock_control = (data & 1 << 11 != 0) if mask & 0x2: self.data_link_layer_state_changed_enable = (data & 1 << 12 != 0) # Slot status if mask & 0x4 and data & 1 << 16: self.attention_button_pressed = False if mask & 0x4 and data & 1 << 17: self.power_fault_detected = False if mask & 0x4 and data & 1 << 18: self.mrl_sensor_changed = False if mask & 0x4 and data & 1 << 19: self.presence_detect_changed = False if mask & 0x4 and data & 1 << 20: self.command_completed = False if mask & 0x8 and data & 1 << 24: self.data_link_layer_state_changed = False elif reg == 7: # Root control if mask & 0x1: self.system_error_on_correctable_error_enable = (data & 1 << 0 != 0) if mask & 0x1: self.system_error_on_non_fatal_error_enable = (data & 1 << 1 != 0) if mask & 0x1: self.system_error_on_fatal_error_enable = (data & 1 << 2 != 0) if mask & 0x1: self.pme_interrupt_enable = (data & 1 << 3 != 0) if mask & 0x1: self.crs_software_visibility_enable = (data & 1 << 4 != 0) elif reg == 8: # Root status if mask & 0x4 and data & 1 << 16: self.pme_status = False elif reg == 10: # Device control 2 if mask & 0x1: self.completion_timeout_value = data & 0xf if mask & 0x1: self.completion_timeout_disable = (data & 1 << 4 != 0) if mask & 0x1: self.ari_forwarding_enable = (data & 1 << 5 != 0) if mask & 0x1: self.atomic_op_requester_enable = (data & 1 << 6 != 0) if mask & 0x1: self.atomic_op_egress_blocking = (data & 1 << 7 != 0) if mask & 0x2: self.ido_request_enable = (data & 1 << 8 != 0) if mask & 0x2: self.ido_completion_enable = (data & 1 << 9 != 0) if mask & 0x2: self.ltr_mechanism_enable = (data & 1 << 10 != 0) if mask & 0x2: self.obff_enable = (data >> 13) & 0x3 if mask & 0x2: self.end_end_tlp_prefix_blocking = (data & 1 << 15 != 0) # Device status 2 elif reg == 12: # Link control 2 if mask & 0x1: self.target_link_speed = data & 0xf if mask & 0x1: self.enter_compliance = (data & 1 << 4 != 0) if mask & 0x1: self.hardware_autonomous_speed_disable = (data & 1 << 5 != 0) if mask & 0x1: self.transmit_margin = self.transmit_margin & 0x6 | (data >> 7) & 0x1 if mask & 0x2: self.transmit_margin = self.transmit_margin & 0x1 | (data >> 7) & 0x6 if mask & 0x2: self.enter_modified_compliance = (data & 1 << 10 != 0) if mask & 0x2: self.compliance_sos = (data & 1 << 11 != 0) if mask & 0x2: self.compliance_preset_de_emphasis = (data >> 12) & 0xff # Link status 2 if self.link_equalization_request: val |= 1 << 21 def initiate_function_level_reset(self): pass def initiate_retrain_link(self): pass class MSICapability(object): def __init__(self, *args, **kwargs): super(MSICapability, self).__init__(*args, **kwargs) # MSI Capability Registers self.msi_enable = False self.msi_multiple_message_capable = 0 self.msi_multiple_message_enable = 0 self.msi_64bit_address_capable = 0 self.msi_per_vector_mask_capable = 0 self.msi_message_address = 0 self.msi_message_data = 0 self.msi_mask_bits = 0 self.msi_pending_bits = 0 self.register_capability(MSI_CAP_ID, MSI_CAP_LEN, self.read_msi_cap_register, self.write_msi_cap_register) """ MSI Capability (32 bit) 31 0 +---------------------------------+----------------+----------------+ | Message Control | Next Cap | Cap ID | 0 0x00 +---------------------------------+----------------+----------------+ | Message Address | 1 0x04 +---------------------------------+---------------------------------+ | | Message Data | 2 0x08 +---------------------------------+---------------------------------+ MSI Capability (64 bit) 31 0 +---------------------------------+----------------+----------------+ | Message Control | Next Cap | Cap ID | 0 0x00 +---------------------------------+----------------+----------------+ | Message Address | 1 0x04 +-------------------------------------------------------------------+ | Message Upper Address | 2 0x08 +---------------------------------+---------------------------------+ | | Message Data | 3 0x0C +---------------------------------+---------------------------------+ MSI Capability (32 bit with per-vector masking) 31 0 +---------------------------------+----------------+----------------+ | Message Control | Next Cap | Cap ID | 0 0x00 +---------------------------------+----------------+----------------+ | Message Address | 1 0x04 +-------------------------------------------------------------------+ | | Message Data | 2 0x08 +---------------------------------+---------------------------------+ | Mask Bits | 3 0x0C +-------------------------------------------------------------------+ | Pending Bits | 4 0x10 +-------------------------------------------------------------------+ MSI Capability (64 bit with per-vector masking) 31 0 +---------------------------------+----------------+----------------+ | Message Control | Next Cap | Cap ID | 0 0x00 +---------------------------------+----------------+----------------+ | Message Address | 1 0x04 +-------------------------------------------------------------------+ | Message Upper Address | 2 0x08 +---------------------------------+---------------------------------+ | | Message Data | 3 0x0C +---------------------------------+---------------------------------+ | Mask Bits | 4 0x10 +-------------------------------------------------------------------+ | Pending Bits | 5 0x14 +-------------------------------------------------------------------+ """ def read_msi_cap_register(self, reg): if reg == 0: # Message control val = 0x00000000 if self.msi_enable: val |= 1 << 16 val |= (self.msi_multiple_message_capable & 0x7) << 17 val |= (self.msi_multiple_message_enable & 0x7) << 20 if self.msi_64bit_address_capable: val |= 1 << 23 if self.msi_per_vector_mask_capable: val |= 1 << 24 return val elif reg == 1: # Message address return self.msi_message_address & 0xfffffffc elif reg == 2 and self.msi_64bit_address_capable: # Message upper address return (self.msi_message_address >> 32) & 0xffffffff elif reg == (3 if self.msi_64bit_address_capable else 2): # Message data return self.msi_message_data & 0xffff elif reg == (4 if self.msi_64bit_address_capable else 3) and self.msi_per_vector_mask_capable: # Mask bits return self.msi_mask_bits & 0xffffffff elif reg == (5 if self.msi_64bit_address_capable else 4) and self.msi_per_vector_mask_capable: # Pending bits return self.msi_pending_bits & 0xffffffff def write_msi_cap_register(self, reg, data, mask): if reg == 0: # Message control if mask & 0x4: self.msi_enable = (data & 1 << 16 != 0) if mask & 0x4: self.msi_multiple_message_enable = (data >> 20) & 0x7 elif reg == 1: # Message address self.msi_message_address = byte_mask_update(self.msi_message_address, mask, data) & 0xfffffffffffffffc elif reg == 2 and self.msi_64bit_address_capable: # Message upper address self.msi_message_address = byte_mask_update(self.msi_message_address, mask << 4, data << 32) & 0xfffffffffffffffc elif reg == (3 if self.msi_64bit_address_capable else 2): # Message data self.msi_message_data = byte_mask_update(self.msi_message_data, mask & 0x3, data) & 0xffff elif reg == (4 if self.msi_64bit_address_capable else 3) and self.msi_per_vector_mask_capable: # Mask bits self.msi_mask_bits = byte_mask_update(self.msi_mask_bits, mask, data) & 0xffffffff def issue_msi_interrupt(self, number=0, attr=0, tc=0): if not self.msi_enable: print("MSI disabled") return if number < 0 or number >= 2**self.msi_multiple_message_enable or number >= 2**self.msi_multiple_message_capable: print("MSI message number out of range") return data = self.msi_message_data & ~(2**self.msi_multiple_message_enable-1) | number yield from self.mem_write(self.msi_message_address, struct.pack('> 1) & 1, data >> 8) self.bist = byte_mask_update(self.bist, (mask >> 3) & 1, data >> 24) elif reg == 15: self.intr_line = byte_mask_update(self.intr_line, mask & 1, data) self.intr_pin = byte_mask_update(self.intr_pin, (mask >> 1) & 1, data >> 8) elif 16 <= reg < 256: self.write_capability_register(reg, data, mask) elif 256 <= reg < 4096: self.write_extended_capability_register(reg, data, mask) def read_capability_register(self, reg): return self.capabilities.read_register(reg) def write_capability_register(self, reg, data, mask): self.capabilities.write_register(reg, data, mask) def register_capability(self, cap_id, length=None, read=None, write=None, offset=None): self.capabilities.register(cap_id, 0, length, read, write, offset) if self.capabilities.list: self.cap_ptr = self.capabilities.list[0].offset*4 else: self.cap_ptr = 0 def read_extended_capability_register(self, reg): return self.ext_capabilities.read_register(reg) def write_extended_capability_register(self, reg, data, mask): self.ext_capabilities.write_register(reg, data, mask) def register_extended_capability(self, cap_id, cap_ver, length=None, read=None, write=None, offset=None): self.ext_capabilities.register(cap_id, cap_ver, length, read, write, offset) def configure_bar(self, idx, size, ext=False, prefetch=False, io=False): mask = 2**math.ceil(math.log(size, 2))-1 if idx >= len(self.bar) or (ext and idx+1 >= len(self.bar)): raise Exception("BAR index out of range") if io: self.bar[idx] = 1 self.bar_mask[idx] = 0xfffffffc & ~mask else: self.bar[idx] = 0 self.bar_mask[idx] = 0xfffffff0 & ~mask if ext: self.bar[idx] |= 4 self.bar[idx+1] = 0 self.bar_mask[idx+1] = 0xffffffff & (~mask >> 32) if prefetch: self.bar[idx] |= 8 def match_bar(self, addr, io=False): m = [] bar = 0 while bar < len(self.bar): bar_val = self.bar[bar] bar_mask = self.bar_mask[bar] orig_bar = bar bar += 1 if bar_mask == 0: # unimplemented BAR continue if bar_val & 1: # IO BAR if io and addr & bar_mask == bar_val & bar_mask: m.append((orig_bar, addr & ~bar_mask)) else: # Memory BAR if bar_val & 4: # 64 bit BAR if bar >= len(self.bar): raise Exception("Final BAR marked as 64 bit, but no extension BAR available") bar_val |= self.bar[bar] << 32 bar_mask |= self.bar_mask[bar] << 32 bar += 1 if not io and addr & bar_mask == bar_val & bar_mask: m.append((orig_bar, addr & ~bar_mask)) return m def upstream_send(self, tlp): # logging print("[%s] Sending upstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() if self.upstream_tx_handler is None: raise Exception("Transmit handler not set") yield from self.upstream_tx_handler(tlp) def send(self, tlp): yield from self.upstream_send(tlp) def upstream_recv(self, tlp): # logging print("[%s] Got downstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() yield from self.handle_tlp(tlp) def handle_tlp(self, tlp): if (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): self.rx_cpl_queues[tlp.tag].append(tlp) self.rx_cpl_sync[tlp.tag].next = not self.rx_cpl_sync[tlp.tag] elif tlp.fmt_type in self.rx_tlp_handler: yield self.rx_tlp_handler[tlp.fmt_type](tlp) else: raise Exception("Unhandled TLP") def register_rx_tlp_handler(self, fmt_type, func): self.rx_tlp_handler[fmt_type] = func def recv_cpl(self, tag, timeout=0): queue = self.rx_cpl_queues[tag] sync = self.rx_cpl_sync[tag] if timeout: yield sync, delay(timeout) else: yield sync if queue: return queue.pop(0) return None def get_free_tag(self): tag = None tag_count = 256 if self.extended_tag_field_enable else 32 for k in range(tag_count): if not self.rx_cpl_queues[self.current_tag]: tag = self.current_tag self.current_tag = (self.current_tag + 1) % tag_count return tag def handle_config_0_tlp(self, tlp): if tlp.dest_id.device == self.device_num and tlp.dest_id.function == self.function_num: # logging print("[%s] Config type 0 for me" % (highlight(self.get_desc()))) # capture address information self.bus_num = tlp.dest_id.bus # prepare completion TLP cpl = TLP() # perform operation if tlp.fmt_type == TLP_CFG_READ_0: cpl.set_completion_data(tlp, self.get_id()) cpl.data = [self.read_config_register(tlp.register_number)] cpl.byte_count = 4 cpl.length = 1 elif tlp.fmt_type == TLP_CFG_WRITE_0: cpl.set_completion(tlp, self.get_id()) self.write_config_register(tlp.register_number, tlp.data[0], tlp.first_be) # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.upstream_send(cpl) else: # error pass def io_read(self, addr, length, timeout=0): n = 0 data = b'' if not self.bus_master_enable: print("Bus mastering not enabled") return None while n < length: tlp = TLP() tlp.fmt_type = TLP_IO_READ tlp.requester_id = self.get_id() tlp.tag = self.get_free_tag() first_pad = addr % 4 byte_length = min(length-n, 4-first_pad) tlp.set_be(addr, byte_length) yield from self.send(tlp) cpl = yield from self.recv_cpl(tlp.tag, timeout) if not cpl: raise Exception("Timeout") if cpl.status != CPL_STATUS_SC: raise Exception("Unsuccessful completion") else: assert cpl.length == 1 d = struct.pack(' 0xffffffff: tlp.fmt_type = TLP_MEM_READ_64 else: tlp.fmt_type = TLP_MEM_READ tlp.requester_id = self.get_id() tlp.tag = self.get_free_tag() tlp.attr = attr tlp.tc = tc first_pad = addr % 4 byte_length = length-n byte_length = min(byte_length, (128 << self.max_read_request_size)-first_pad) # max read request size byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align tlp.set_be(addr, length) yield from self.send(tlp) m = 0 while m < byte_length: cpl = yield from self.recv_cpl(tlp.tag, timeout) if not cpl: raise Exception("Timeout") if cpl.status != CPL_STATUS_SC: raise Exception("Unsuccessful completion") else: assert cpl.byte_count+3+(cpl.lower_address&3) >= cpl.length*4 assert cpl.byte_count == byte_length - m d = bytearray() for k in range(cpl.length): d.extend(struct.pack(' 0xffffffff: tlp.fmt_type = TLP_MEM_WRITE_64 else: tlp.fmt_type = TLP_MEM_WRITE tlp.requester_id = self.get_id() tlp.attr = attr tlp.tc = tc first_pad = addr % 4 byte_length = len(data)-n byte_length = min(byte_length, (128 << self.max_payload_size)-first_pad) # max payload size byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align tlp.set_be_data(addr, data[n:n+byte_length]) yield from self.send(tlp) n += byte_length addr += byte_length def mem_write_words(self, addr, data, ws=2, timeout=0, attr=0, tc=0): assert ws in (1, 2, 4, 8) words = data data = b'' for w in words: data += w.to_bytes(ws, 'little') yield from self.mem_write(addr, data, timeout, attr, tc) def mem_write_dwords(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_words(addr, data, 4, timeout, attr, tc) def mem_write_qwords(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_words(addr, data, 8, timeout, attr, tc) def mem_write_byte(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write(addr, [data], timeout, attr, tc) def mem_write_word(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_words(addr, [data], timeout=timeout, attr=attr, tc=tc) def mem_write_dword(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_dwords(addr, [data], timeout=timeout, attr=attr, tc=tc) def mem_write_qword(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_qwords(addr, [data], timeout=timeout, attr=attr, tc=tc) class Endpoint(Function): """PCIe endpoint function, implements endpoint config space""" def __init__(self, *args, **kwargs): super(Endpoint, self).__init__(*args, **kwargs) # configuration registers self.header_type = 0 self.bar = [0]*6 self.bar_mask = [0]*6 self.cardbus_cis = 0 self.subsystem_vendor_id = 0 self.subsystem_id = 0 self.pcie_device_type = 0 """ Endpoint (type 0) config space 31 0 +---------------------------------+---------------------------------+ | Device ID | Vendor ID | 0 0x00 +---------------------------------+---------------------------------+ | Status | Command | 1 0x04 +---------------------------------+----------------+----------------+ | Class Code | Revision ID | 2 0x08 +----------------+----------------+----------------+----------------+ | BIST | Header Type | Primary | Cache Line | 3 0x0C | | | Latency Timer | Size | +----------------+----------------+----------------+----------------+ | Base Address Register 0 | 4 0x10 +-------------------------------------------------------------------+ | Base Address Register 1 | 5 0x14 +-------------------------------------------------------------------+ | Base Address Register 2 | 6 0x18 +-------------------------------------------------------------------+ | Base Address Register 3 | 7 0x1C +-------------------------------------------------------------------+ | Base Address Register 4 | 8 0x20 +-------------------------------------------------------------------+ | Base Address Register 5 | 9 0x24 +-------------------------------------------------------------------+ | Cardbus CIS pointer | 10 0x28 +---------------------------------+---------------------------------+ | Subsystem ID | Subsystem Vendor ID | 11 0x2C +---------------------------------+---------------------------------+ | Expansion ROM Base Address | 12 0x30 +--------------------------------------------------+----------------+ | Reserved | Cap Ptr | 13 0x34 +--------------------------------------------------+----------------+ | Reserved | 14 0x38 +----------------+----------------+----------------+----------------+ | Max Lat | Min Gnt | Int Pin | Int Line | 15 0x3C +----------------+----------------+----------------+----------------+ """ def read_config_register(self, reg): if reg == 4: return self.bar[0] elif reg == 5: return self.bar[1] elif reg == 6: return self.bar[2] elif reg == 7: return self.bar[3] elif reg == 8: return self.bar[4] elif reg == 9: return self.bar[5] elif reg == 10: return self.cardbus_cis elif reg == 11: return (self.subsystem_id << 16) | self.subsystem_vendor_id elif reg == 12: return self.expansion_rom_addr elif reg == 13: return self.cap_ptr elif reg == 14: return 0 # reserved elif reg == 15: return (self.intr_pin << 8) | self.intr_line else: return super(Endpoint, self).read_config_register(reg) def write_config_register(self, reg, data, mask): if reg == 4: self.bar[0] = byte_mask_update(self.bar[0], mask, data, self.bar_mask[0]) elif reg == 5: self.bar[1] = byte_mask_update(self.bar[1], mask, data, self.bar_mask[1]) elif reg == 6: self.bar[2] = byte_mask_update(self.bar[2], mask, data, self.bar_mask[2]) elif reg == 7: self.bar[3] = byte_mask_update(self.bar[3], mask, data, self.bar_mask[3]) elif reg == 8: self.bar[4] = byte_mask_update(self.bar[4], mask, data, self.bar_mask[4]) elif reg == 9: self.bar[5] = byte_mask_update(self.bar[5], mask, data, self.bar_mask[5]) elif reg == 12: self.expansion_rom_addr = byte_mask_update(self.expansion_rom_addr, mask, data) elif reg == 15: self.intr_line = byte_mask_update(self.intr_line, mask & 1, data) self.intr_pin = byte_mask_update(self.intr_pin, (mask >> 1) & 1, data >> 8) else: super(Endpoint, self).write_config_register(reg, data, mask) class MemoryEndpoint(Endpoint): """PCIe endpoint function, implements BARs pointing to internal memory""" def __init__(self, *args, **kwargs): super(MemoryEndpoint, self).__init__(*args, **kwargs) self.regions = [None]*6 self.bar_ptr = 0 self.register_rx_tlp_handler(TLP_IO_READ, self.handle_io_read_tlp) self.register_rx_tlp_handler(TLP_IO_WRITE, self.handle_io_write_tlp) self.register_rx_tlp_handler(TLP_MEM_READ, self.handle_mem_read_tlp) self.register_rx_tlp_handler(TLP_MEM_READ_64, self.handle_mem_read_tlp) self.register_rx_tlp_handler(TLP_MEM_WRITE, self.handle_mem_write_tlp) self.register_rx_tlp_handler(TLP_MEM_WRITE_64, self.handle_mem_write_tlp) def add_region(self, size, read=None, write=None, ext=False, prefetch=False, io=False): if self.bar_ptr > 5 or (ext and self.bar_ptr > 4): raise Exception("No more BARs available") arr = None self.configure_bar(self.bar_ptr, size, ext, prefetch, io) if not read and not write: arr = bytearray(size) self.regions[self.bar_ptr] = arr else: self.regions[self.bar_ptr] = (read, write) if ext: self.bar_ptr += 2 else: self.bar_ptr += 1 return arr def add_io_region(self, size, read=None, write=None): return self.add_region(size, read, write, False, False, True) def add_mem_region(self, size, read=None, write=None): return self.add_region(size, read, write) def add_prefetchable_mem_region(self, size, read=None, write=None): return self.add_region(size, read, write, True, True) def read_region(self, region, addr, length): if not self.regions[region]: raise Exception("Invalid region") if type(self.regions[region]) is tuple: return self.regions[region][0](addr, length) else: return self.regions[region][addr:addr+length] def write_region(self, region, addr, data): if not self.regions[region]: raise Exception("Invalid region") if type(self.regions[region]) is tuple: self.regions[region][1](addr, length) else: self.regions[region][addr:addr+len(data)] = data def handle_io_read_tlp(self, tlp): m = self.match_bar(tlp.address, True) if len(m) == 1: # logging print("[%s] IO read" % (highlight(self.get_desc()))) assert tlp.length == 1 # prepare completion TLP cpl = TLP() cpl.set_completion_data(tlp, self.get_id()) region = m[0][0] addr = m[0][1] offset = 0 start_offset = None mask = tlp.first_be # perform read data = bytearray(4) for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: data[start_offset:offset] = self.read_region(region, addr+start_offset, offset-start_offset) start_offset = None offset += 1 if start_offset is not None and offset != start_offset: data[start_offset:offset] = self.read_region(region, addr+start_offset, offset-start_offset) cpl.set_data(data) cpl.byte_count = 4 cpl.length = 1 # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) else: # logging print("IO request did not match any BARs") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, self.get_id()) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) def handle_io_write_tlp(self, tlp): m = self.match_bar(tlp.address, True) if len(m) == 1: # logging print("[%s] IO write" % (highlight(self.get_desc()))) assert tlp.length == 1 # prepare completion TLP cpl = TLP() cpl.set_completion(tlp, self.get_id()) region = m[0][0] addr = m[0][1] offset = 0 start_offset = None mask = tlp.first_be # perform write data = tlp.get_data() for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: self.write_region(region, addr+start_offset, data[start_offset:offset]) start_offset = None offset += 1 if start_offset is not None and offset != start_offset: self.write_region(region, addr+start_offset, data[start_offset:offset]) cpl.byte_count = 4 # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) else: # logging print("IO request did not match any BARs") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, self.get_id()) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) def handle_mem_read_tlp(self, tlp): m = self.match_bar(tlp.address) if len(m) == 1: print("[%s] Memory read" % (highlight(self.get_desc()))) # perform operation region = m[0][0] addr = m[0][1] # check for 4k boundary crossing if tlp.length*4 > 0x1000 - (addr & 0xfff): print("Request crossed 4k boundary, discarding request") return # perform read data = bytearray(self.read_region(region, addr, tlp.length*4)) # prepare completion TLP(s) m = 0 n = 0 addr = tlp.address+tlp.get_first_be_offset() dw_length = tlp.length byte_length = tlp.get_be_byte_count() while m < dw_length: cpl = TLP() cpl.set_completion_data(tlp, self.get_id()) cpl_dw_length = dw_length - m cpl_byte_length = byte_length - n cpl.byte_count = cpl_byte_length if cpl_dw_length > 32 << self.max_payload_size: cpl_dw_length = 32 << self.max_payload_size # max payload size cpl_dw_length -= (addr & 0x7c) >> 2 # RCB align cpl.lower_address = addr & 0x7f cpl.set_data(data[m*4:(m+cpl_dw_length)*4]) # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) m += cpl_dw_length; n += cpl_dw_length*4 - (addr&3) addr += cpl_dw_length*4 - (addr&3) else: # logging print("Memory request did not match any BARs") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, self.get_id()) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) def handle_mem_write_tlp(self, tlp): m = self.match_bar(tlp.address) if len(m) == 1: # logging print("[%s] Memory write" % (highlight(self.get_desc()))) # perform operation region = m[0][0] addr = m[0][1] offset = 0 start_offset = None mask = tlp.first_be # check for 4k boundary crossing if tlp.length*4 > 0x1000 - (addr & 0xfff): print("Request crossed 4k boundary, discarding request") return # perform write data = tlp.get_data() # first dword for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: self.write_region(region, addr+start_offset, data[start_offset:offset]) start_offset = None offset += 1 if tlp.length > 2: # middle dwords if start_offset is None: start_offset = offset offset += (tlp.length-2)*4 if tlp.length > 1: # last dword mask = tlp.last_be for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: self.write_region(region, addr+start_offset, data[start_offset:offset]) start_offset = None offset += 1 if start_offset is not None and offset != start_offset: self.write_region(region, addr+start_offset, data[start_offset:offset]) # memory writes are posted, so don't send a completion else: # logging print("Memory request did not match any BARs") class Bridge(Function): """PCIe bridge function, implements bridge config space and TLP routing""" def __init__(self, *args, **kwargs): super(Bridge, self).__init__(*args, **kwargs) # configuration registers self.header_type = 1 self.bar = [0]*2 self.bar_mask = [0]*2 self.pri_bus_num = 0 self.sec_bus_num = 0 self.sub_bus_num = 0 self.sec_lat_timer = 0 self.io_base = 0x0000 self.io_limit = 0x0fff self.sec_status = 0 self.mem_base = 0x00000000 self.mem_limit = 0x000fffff self.prefetchable_mem_base = 0x00000000 self.prefetchable_mem_limit = 0x000fffff self.bridge_control = 0 self.pcie_device_type = 0x6 self.root = False self.upstream_port = Port(self, self.upstream_recv) self.upstream_tx_handler = self.upstream_port.send self.downstream_port = Port(self, self.downstream_recv) self.downstream_tx_handler = self.downstream_port.send """ Bridge (type 1) config space 31 0 +---------------------------------+---------------------------------+ | Device ID | Vendor ID | 0 0x00 +---------------------------------+---------------------------------+ | Status | Command | 1 0x04 +---------------------------------+----------------+----------------+ | Class Code | Revision ID | 2 0x08 +----------------+----------------+----------------+----------------+ | BIST | Header Type | Primary | Cache Line | 3 0x0C | | | Latency Timer | Size | +----------------+----------------+----------------+----------------+ | Base Address Register 0 | 4 0x10 +-------------------------------------------------------------------+ | Base Address Register 1 | 5 0x14 +----------------+----------------+----------------+----------------+ | Secondary | Subordinate | Secondary | Primary | 6 0x18 | Latency Timer | Bus Number | Bus Number | Bus Number | +----------------+----------------+----------------+----------------+ | Secondary Status | IO Limit | IO Base | 7 0x1C +---------------------------------+----------------+----------------+ | Memory Limit | Memory Base | 8 0x20 +---------------------------------+---------------------------------+ | Prefetchable Memory Limit | Prefetchable Memory Base | 9 0x24 +---------------------------------+---------------------------------+ | Prefetchable Base Upper 32 | 10 0x28 +-------------------------------------------------------------------+ | Prefetchable Limit Upper 32 | 11 0x2C +---------------------------------+---------------------------------+ | IO Lim Upper 16 | IO Base Lower 16 | 12 0x30 +---------------------------------+----------------+----------------+ | Reserved | Cap Ptr | 13 0x34 +--------------------------------------------------+----------------+ | Expansion ROM Base Address | 14 0x38 +---------------------------------+----------------+----------------+ | Bridge Control | Int Pin | Int Line | 15 0x3C +---------------------------------+----------------+----------------+ """ def read_config_register(self, reg): if reg == 4: return self.bar[0] elif reg == 5: return self.bar[1] elif reg == 6: return (self.sec_lat_timer << 24) | (self.sub_bus_num << 16) | (self.sec_bus_num << 8) | self.pri_bus_num elif reg == 7: return (self.sec_status << 16) | (self.io_limit & 0xf000) | ((self.io_base & 0xf000) >> 8) elif reg == 8: return (self.mem_limit & 0xfff00000) | ((self.mem_base & 0xfff00000) >> 16) elif reg == 9: return (self.prefetchable_mem_limit & 0xfff00000) | ((self.prefetchable_mem_base & 0xfff00000) >> 16) elif reg == 10: return self.prefetchable_mem_base >> 32 elif reg == 11: return self.prefetchable_mem_limit >> 32 elif reg == 12: return (self.io_limit & 0xffff0000) | ((self.io_base & 0xffff0000) >> 16) elif reg == 13: return self.cap_ptr elif reg == 14: return self.expansion_rom_addr elif reg == 15: return (self.bridge_control << 16) | (self.intr_pin << 8) | self.intr_line else: return super(Bridge, self).read_config_register(reg) def write_config_register(self, reg, data, mask): if reg == 4: self.bar[0] = byte_mask_update(self.bar[0], mask, data, self.bar_mask[0]) if reg == 5: self.bar[1] = byte_mask_update(self.bar[1], mask, data, self.bar_mask[1]) elif reg == 6: self.pri_bus_num = byte_mask_update(self.pri_bus_num, mask & 0x1, data) self.sec_bus_num = byte_mask_update(self.sec_bus_num, (mask >> 1) & 1, data >> 8) self.sub_bus_num = byte_mask_update(self.sub_bus_num, (mask >> 2) & 1, data >> 16) self.sec_lat_timer = byte_mask_update(self.sec_lat_timer, (mask >> 3) & 1, data >> 24) elif reg == 7: self.io_base = byte_mask_update(self.io_base, (mask & 0x1) << 1, data << 8, 0xf000) self.io_limit = byte_mask_update(self.io_limit, (mask & 0x2), data, 0xf000) | 0xfff self.sec_status = byte_mask_update(self.sec_status, (mask >> 2) & 1, 0x0000, (data >> 16) & 0xf900) elif reg == 8: self.mem_base = byte_mask_update(self.mem_base, (mask & 0x3) << 2, data << 16, 0xfff00000) self.mem_limit = byte_mask_update(self.mem_limit, (mask & 0xc), data, 0xfff00000) | 0xfffff elif reg == 9: self.prefetchable_mem_base = byte_mask_update(self.prefetchable_mem_base, (mask & 0x3) << 2, data << 16, 0xfff00000) self.prefetchable_mem_limit = byte_mask_update(self.prefetchable_mem_limit, (mask & 0xc), data, 0xfff00000) | 0xfffff elif reg == 10: self.prefetchable_mem_base = byte_mask_update(self.prefetchable_mem_base, mask << 4, data << 32) elif reg == 11: self.prefetchable_mem_limit = byte_mask_update(self.prefetchable_mem_limit, mask << 4, data << 32) elif reg == 12: self.io_base = byte_mask_update(self.io_base, (mask & 0x3) << 2, data << 16) self.io_limit = byte_mask_update(self.io_limit, (mask & 0xc), data) elif reg == 14: self.expansion_rom_addr = byte_mask_update(self.expansion_rom_addr, mask, data) elif reg == 15: self.intr_line = byte_mask_update(self.intr_line, mask & 0x1, data) self.intr_pin = byte_mask_update(self.intr_pin, (mask >> 1) & 1, data >> 8) self.bridge_control = byte_mask_update(self.min_gnt, (mask >> 2) & 3, data >> 16, 0x0043) else: super(Bridge, self).write_config_register(reg, data, mask) def upstream_send(self, tlp): assert tlp.check() if self.upstream_tx_handler is None: raise Exception("Transmit handler not set") yield from self.upstream_tx_handler(tlp) def upstream_recv(self, tlp): # logging if trace_routing: print("[%s] Routing downstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() if tlp.fmt_type == TLP_CFG_READ_0 or tlp.fmt_type == TLP_CFG_WRITE_0: yield from self.handle_tlp(tlp) elif tlp.fmt_type == TLP_CFG_READ_1 or tlp.fmt_type == TLP_CFG_WRITE_1: # config type 1 if self.sec_bus_num <= tlp.dest_id.bus <= self.sub_bus_num: if tlp.dest_id.bus == self.sec_bus_num: # targeted to directly connected device; change to type 0 if tlp.fmt_type == TLP_CFG_READ_1: tlp.fmt_type = TLP_CFG_READ_0 elif tlp.fmt_type == TLP_CFG_WRITE_1: tlp.fmt_type = TLP_CFG_WRITE_0 yield from self.route_downstream_tlp(tlp, False) else: # error pass elif (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): # Completions if not self.root and tlp.requester_id == self.get_id(): # for me yield from self.handle_tlp(tlp) elif self.sec_bus_num <= tlp.requester_id.bus <= self.sub_bus_num: yield from self.route_downstream_tlp(tlp, False) else: # error pass elif tlp.fmt_type == TLP_MSG_ID or tlp.fmt_type == TLP_MSG_DATA_ID: # ID routed message if not self.root and tlp.dest_id == self.get_id(): # for me yield from self.handle_tlp(tlp) elif self.sec_bus_num <= tlp.dest_id.bus <= self.sub_bus_num: yield from self.route_downstream_tlp(tlp, False) else: # error pass elif (tlp.fmt_type == TLP_IO_READ or tlp.fmt_type == TLP_IO_WRITE): # IO read/write if self.match_bar(tlp.address, io=True): # for me yield from self.handle_tlp(tlp) elif self.io_base <= tlp.address <= self.io_limit: yield from self.route_downstream_tlp(tlp, False) else: # error pass elif (tlp.fmt_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64 or tlp.fmt_type == TLP_MEM_WRITE or tlp.fmt_type == TLP_MEM_WRITE_64): # Memory read/write if self.match_bar(tlp.address): # for me yield from self.handle_tlp(tlp) elif self.mem_base <= tlp.address <= self.mem_limit or self.prefetchable_mem_base <= tlp.address <= self.prefetchable_mem_limit: yield from self.route_downstream_tlp(tlp, False) else: # error pass elif tlp.fmt_type == TLP_MSG_TO_RC or tlp.fmt_type == TLP_MSG_DATA_TO_RC: # Message to root complex # error pass elif tlp.fmt_type == TLP_MSG_BCAST or tlp.fmt_type == TLP_MSG_DATA_BCAST: # Message broadcast from root complex yield from self.route_downstream_tlp(tlp, False) elif tlp.fmt_type == TLP_MSG_LOCAL or tlp.fmt_type == TLP_MSG_DATA_LOCAL: # Message local to receiver # error pass elif tlp.fmt_type == TLP_MSG_GATHER or tlp.fmt_type == TLP_MSG_DATA_GATHER: # Message gather to root complex # error pass else: # logging raise Exception("Unknown/invalid packet type") def route_downstream_tlp(self, tlp, from_downstream=False): yield from self.downstream_send(tlp) def downstream_send(self, tlp): assert tlp.check() if self.downstream_tx_handler is None: raise Exception("Transmit handler not set") yield from self.downstream_tx_handler(tlp) def downstream_recv(self, tlp): # logging if trace_routing: print("[%s] Routing upstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() if (tlp.fmt_type == TLP_CFG_READ_0 or tlp.fmt_type == TLP_CFG_WRITE_0 or tlp.fmt_type == TLP_CFG_READ_1 or tlp.fmt_type == TLP_CFG_WRITE_1): # error pass elif (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): # Completions if not self.root and tlp.requester_id == self.get_id(): # for me yield from self.handle_tlp(tlp) elif self.sec_bus_num <= tlp.requester_id.bus <= self.sub_bus_num: if self.root and tlp.requester_id.bus == self.pri_bus_num and tlp.requester_id.device == 0: yield from self.upstream_send(tlp) else: yield from self.route_downstream_tlp(tlp, True) else: yield from self.upstream_send(tlp) elif tlp.fmt_type == TLP_MSG_ID or tlp.fmt_type == TLP_MSG_DATA_ID: # ID routed messages if not self.root and tlp.dest_id == self.get_id(): # for me yield from self.handle_tlp(tlp) elif self.sec_bus_num <= tlp.dest_id.bus <= self.sub_bus_num: if self.root and tlp.dest_id.bus == self.pri_bus_num and tlp.dest_id.device == 0: yield from self.upstream_send(tlp) else: yield from self.route_downstream_tlp(tlp, True) else: yield from self.upstream_send(tlp) elif (tlp.fmt_type == TLP_IO_READ or tlp.fmt_type == TLP_IO_WRITE): # IO read/write if self.match_bar(tlp.address, io=True): # for me yield from self.handle_tlp(tlp) elif self.io_base <= tlp.address <= self.io_limit: yield from self.route_downstream_tlp(tlp, True) else: yield from self.upstream_send(tlp) elif (tlp.fmt_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64 or tlp.fmt_type == TLP_MEM_WRITE or tlp.fmt_type == TLP_MEM_WRITE_64): # Memory read/write if self.match_bar(tlp.address): # for me yield from self.handle_tlp(tlp) elif self.mem_base <= tlp.address <= self.mem_limit or self.prefetchable_mem_base <= tlp.address <= self.prefetchable_mem_limit: yield from self.route_downstream_tlp(tlp, True) else: yield from self.upstream_send(tlp) elif tlp.fmt_type == TLP_MSG_TO_RC or tlp.fmt_type == TLP_MSG_DATA_TO_RC: # Message to root complex yield from self.upstream_send(tlp) elif tlp.fmt_type == TLP_MSG_BCAST or tlp.fmt_type == TLP_MSG_DATA_BCAST: # Message broadcast from root complex # error pass elif tlp.fmt_type == TLP_MSG_LOCAL or tlp.fmt_type == TLP_MSG_DATA_LOCAL: # Message local to receiver # error pass elif tlp.fmt_type == TLP_MSG_GATHER or tlp.fmt_type == TLP_MSG_DATA_GATHER: # Message gather to root complex raise Exception("TODO") else: raise Exception("Unknown/invalid packet type") def send(self, tlp): # route local transmissions as if they came in via downstream port yield from self.downstream_recv(tlp) class SwitchUpstreamPort(Bridge): def __init__(self, *args, **kwargs): super(SwitchUpstreamPort, self).__init__(*args, **kwargs) self.pcie_device_type = 0x5 self.downstream_port = BusPort(self, self.downstream_recv) self.downstream_tx_handler = None self.desc = "SwitchUpstreamPort" self.vendor_id = 0x1234 self.device_id = 0x0003 def route_downstream_tlp(self, tlp, from_downstream=False): assert tlp.check() # route downstream packet ok = False for p in self.downstream_port.other: dev = p.parent if tlp.fmt_type == TLP_CFG_READ_0 or tlp.fmt_type == TLP_CFG_WRITE_0: # config type 0 if tlp.dest_id.device == dev.device_num and tlp.dest_id.function == dev.function_num: yield from p.ext_recv(TLP(tlp)) return elif tlp.fmt_type == TLP_CFG_READ_1 or tlp.fmt_type == TLP_CFG_WRITE_1: # config type 1 if isinstance(dev, Bridge) and dev.sec_bus_num <= tlp.dest_id.bus <= dev.sub_bus_num: yield from p.ext_recv(TLP(tlp)) return elif (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): # Completions if tlp.requester_id == dev.get_id(): yield from p.ext_recv(TLP(tlp)) return elif isinstance(dev, Bridge) and dev.sec_bus_num <= tlp.requester_id.bus <= dev.sub_bus_num: yield from p.ext_recv(TLP(tlp)) return elif tlp.fmt_type == TLP_MSG_ID or tlp.fmt_type == TLP_MSG_DATA_ID: # ID routed message if tlp.dest_id == dev.get_id(): yield from p.ext_recv(TLP(tlp)) return elif isinstance(dev, Bridge) and dev.sec_bus_num <= tlp.requester_id.bus <= dev.sub_bus_num: yield from p.ext_recv(TLP(tlp)) return elif (tlp.fmt_type == TLP_IO_READ or tlp.fmt_type == TLP_IO_WRITE): # IO read/write if dev.match_bar(tlp.address, True): yield from p.ext_recv(TLP(tlp)) return elif isinstance(dev, Bridge) and dev.io_base <= tlp.address <= dev.io_limit: yield from p.ext_recv(TLP(tlp)) return elif (tlp.fmt_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64 or tlp.fmt_type == TLP_MEM_WRITE or tlp.fmt_type == TLP_MEM_WRITE_64): # Memory read/write if dev.match_bar(tlp.address): yield from p.ext_recv(TLP(tlp)) return elif isinstance(dev, Bridge) and (dev.mem_base <= tlp.address <= dev.mem_limit or dev.prefetchable_mem_base <= tlp.address <= dev.prefetchable_mem_limit): yield from p.ext_recv(TLP(tlp)) return elif tlp.fmt_type == TLP_MSG_TO_RC or tlp.fmt_type == TLP_MSG_DATA_TO_RC: # Message to root complex # error pass elif tlp.fmt_type == TLP_MSG_BCAST or tlp.fmt_type == TLP_MSG_DATA_BCAST: # Message broadcast from root complex yield from p.ext_recv(TLP(tlp)) ok = True elif tlp.fmt_type == TLP_MSG_LOCAL or tlp.fmt_type == TLP_MSG_DATA_LOCAL: # Message local to receiver # error pass elif tlp.fmt_type == TLP_MSG_GATHER or tlp.fmt_type == TLP_MSG_DATA_GATHER: # Message gather to root complex # error pass else: # logging raise Exception("Unknown/invalid packet type") if not ok: # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) if from_downstream: yield from self.route_downstream_tlp(cpl, False) else: yield from self.upstream_send(cpl) class SwitchDownstreamPort(Bridge): def __init__(self, *args, **kwargs): super(SwitchDownstreamPort, self).__init__(*args, **kwargs) self.pcie_device_type = 0x6 self.desc = "SwitchDownstreamPort" self.vendor_id = 0x1234 self.device_id = 0x0004 def connect(self, port): self.downstream_port.connect(port) class HostBridge(SwitchUpstreamPort): def __init__(self, *args, **kwargs): super(HostBridge, self).__init__(*args, **kwargs) self.desc = "HostBridge" self.vendor_id = 0x1234 self.device_id = 0x0001 self.pri_bus_num = 0 self.sec_bus_num = 0 self.sub_bus_num = 255 class RootPort(SwitchDownstreamPort): def __init__(self, *args, **kwargs): super(RootPort, self).__init__(*args, **kwargs) self.pcie_device_type = 0x4 self.desc = "RootPort" self.vendor_id = 0x1234 self.device_id = 0x0002 def connect(self, port): self.downstream_port.connect(port) class Device(object): """PCIe device, container for multiple functions""" def __init__(self, eps=None): self.bus_num = 0 self.device_num = 0 self.desc = "Device" self.default_function = Endpoint self.functions = [] self.upstream_port = Port(self, self.upstream_recv) if eps: try: for ep in eps: self.append_function(ep) except: self.append_function(eps) def get_desc(self): return "%02x:%02x %s" % (self.bus_num, self.device_num, self.desc) def next_free_function_number(self): self.functions.sort(key=lambda x: x.function_num) if not self.functions: return 0 for x in range(len(self.functions)): if self.functions[x].function_num != x: return x if len(self.functions) < 8: return len(self.functions) return None def add_function(self, function): for f in self.functions: if f.function_num == function.function_num: raise Exception("Function number already in use") function.upstream_tx_handler = self.upstream_send self.functions.append(function) self.functions.sort(key=lambda x: x.function_num) if len(self.functions) > 1: for f in self.functions: f.header_type |= 0x80 return function def append_function(self, function): function.function_num = self.next_free_function_number() return self.add_function(function) def make_function(self): return self.append_function(self.default_function()) def connect(self, port): self.upstream_port.connect(port) def upstream_recv(self, tlp): # logging print("[%s] Got downstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() if tlp.fmt_type == TLP_CFG_READ_0 or tlp.fmt_type == TLP_CFG_WRITE_0: # config type 0 if tlp.dest_id.device == self.device_num: # capture address information self.bus_num = tlp.dest_id.bus for f in self.functions: f.bus_num = self.bus_num # pass TLP to function for f in self.functions: if f.function_num == tlp.dest_id.function: yield from f.upstream_recv(tlp) return #raise Exception("Function not found") print("Function not found") else: print("Device number mismatch") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.upstream_send(cpl) elif (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): # Completion if tlp.requester_id.bus == self.bus_num and tlp.requester_id.device == self.device_num: for f in self.functions: if f.function_num == tlp.requester_id.function: yield from f.upstream_recv(tlp) return print("Function not found") else: print("Bus/device number mismatch") elif (tlp.fmt_type == TLP_IO_READ or tlp.fmt_type == TLP_IO_WRITE): # IO read/write for f in self.functions: if f.match_bar(tlp.address, True): yield from f.upstream_recv(tlp) return print("IO request did not match any BARs") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.upstream_send(cpl) elif (tlp.fmt_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64 or tlp.fmt_type == TLP_MEM_WRITE or tlp.fmt_type == TLP_MEM_WRITE_64): # Memory read/write for f in self.functions: if f.match_bar(tlp.address): yield from f.upstream_recv(tlp) return print("Memory request did not match any BARs") if tlp.fmt_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64: # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.upstream_send(cpl) else: raise Exception("TODO") def upstream_send(self, tlp): # logging print("[%s] Sending upstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() yield from self.upstream_port.send(tlp) def send(self, tlp): yield from self.upstream_send(tlp) class Switch(object): """Switch object, container for switch bridges and associated interconnect""" def __init__(self, *args, **kwargs): super(Switch, self).__init__(*args, **kwargs) self.upstream_bridge = SwitchUpstreamPort() self.default_switch_port = SwitchDownstreamPort self.min_dev = 1 self.endpoints = [] def next_free_device_number(self): self.endpoints.sort(key=lambda x: (x.device_num, x.function_num)) d = self.min_dev if not self.endpoints: return d for ep in self.endpoints: if ep.device_num > d: return d d = ep.device_num + 1 if d < 32: return d return None def append_endpoint(self, ep): ep.upstream_tx_handler = self.upstream_bridge.downstream_recv self.endpoints.append(ep) self.endpoints.sort(key=lambda x: (x.device_num, x.function_num)) return ep def add_endpoint(self, ep): ep.bus_num = 0 ep.device_num = self.next_free_device_number() ep.function_num = 0 return self.append_endpoint(ep) def make_port(self): port = self.default_switch_port() self.upstream_bridge.downstream_port.connect(port.upstream_port) port.pri_bus_num = 0 port.sec_bus_num = 0 port.sub_bus_num = 0 return self.add_endpoint(port) def connect(self, port): self.upstream_bridge.upstream_port.connect(port) class TreeItem(object): def __init__(self): self.bus_num = 0 self.device_num = 0 self.function_num = 0 self.vendor_id = 0 self.device_id = 0 self.desc = "(Unknown)" self.sec_bus_num = 0 self.sub_bus_num = 0 self.bar = [None]*6 self.bar_size = [None]*6 self.io_base = 0 self.io_limit = 0 self.mem_base = 0 self.mem_limit = 0 self.prefetchable_mem_base = 0 self.prefetchable_mem_limit = 0 self.capabilities = [] self.ext_capabilities = [] self.msi_addr = None self.msi_data = None self.children = [] def find_dev(self, dev_id): if dev_id == self.get_id(): return self for c in self.children: res = c.find_dev(dev_id) if res is not None: return res return None def get_capability_offset(self, cap_id): for c in self.capabilities: if c[0] == cap_id: return c[1] return None def to_str(self, prefix=''): s = '' if self.sub_bus_num > self.sec_bus_num: s += '[%02x-%02x]-' % (self.sec_bus_num, self.sub_bus_num) prefix += ' '*8 else: s += '[%02x]-' % (self.sec_bus_num) prefix += ' '*5 for i in range(len(self.children)): c = self.children[i] if i > 0: s += prefix if len(self.children) == 1: s += '-' elif len(self.children)-1 == i: s += '\\' else: s += '+' s += '-%02x.%x' % (c.device_num, c.function_num) if c.children: if i < len(self.children)-1: s += '-'+c.to_str(prefix+'|'+' '*6).strip() else: s += '-'+c.to_str(prefix+' '*7).strip() s += '\n' return s def get_id(self): return PcieId(self.bus_num, self.device_num, self.function_num) def __bool__(self): return True def __getitem__(self, key): return self.children[key] def __iter__(self): return self.children.__iter__() def __len__(self): return len(self.children) class RootComplex(Switch): def __init__(self, *args, **kwargs): super(RootComplex, self).__init__(*args, **kwargs) self.default_switch_port = RootPort self.min_dev = 1 self.current_tag = 0 self.downstream_tag_recv_queues = {} self.rx_cpl_queues = [[] for k in range(256)] self.rx_cpl_sync = [Signal(False) for k in range(256)] self.rx_tlp_handler = {} self.upstream_bridge = HostBridge() self.upstream_bridge.root = True self.upstream_bridge.upstream_tx_handler = self.downstream_recv self.tree = TreeItem() self.io_base = 0x80000000 self.io_limit = self.io_base self.mem_base = 0x80000000 self.mem_limit = self.mem_base self.prefetchable_mem_base = 0x8000000000000000 self.prefetchable_mem_limit = self.prefetchable_mem_base self.upstream_bridge.io_base = self.io_base self.upstream_bridge.io_limit = self.io_limit self.upstream_bridge.mem_base = self.mem_base self.upstream_bridge.mem_limit = self.mem_limit self.upstream_bridge.prefetchable_mem_base = self.prefetchable_mem_base self.upstream_bridge.prefetchable_mem_limit = self.prefetchable_mem_limit self.max_payload_size = 0 self.max_read_request_size = 2 self.read_completion_boundary = 128 self.extended_tag_field_enable = True self.region_base = 0 self.region_limit = self.region_base self.io_region_base = 0 self.io_region_limit = self.io_region_base self.regions = [] self.io_regions = [] self.msi_addr = None self.msi_msg_limit = 0 self.msi_signals = {} self.msi_callbacks = {} self.register_rx_tlp_handler(TLP_IO_READ, self.handle_io_read_tlp) self.register_rx_tlp_handler(TLP_IO_WRITE, self.handle_io_write_tlp) self.register_rx_tlp_handler(TLP_MEM_READ, self.handle_mem_read_tlp) self.register_rx_tlp_handler(TLP_MEM_READ_64, self.handle_mem_read_tlp) self.register_rx_tlp_handler(TLP_MEM_WRITE, self.handle_mem_write_tlp) self.register_rx_tlp_handler(TLP_MEM_WRITE_64, self.handle_mem_write_tlp) def get_desc(self): #return "%02x:%02x.%x %s" % (self.bus_num, self.device_num, self.function_num, self.desc) return "RootComplex" def alloc_region(self, size, read=None, write=None): addr = 0 arr = None addr = align(self.region_limit, 2**math.ceil(math.log(size, 2))-1) self.region_limit = addr+size-1 if not read and not write: arr = bytearray(size) self.regions.append((addr, size, arr)) else: self.regions.append((addr, size, read, write)) return addr, arr def alloc_io_region(self, size, read=None, write=None): addr = 0 arr = None addr = align(self.io_region_limit, 2**math.ceil(math.log(size, 2))-1) self.io_region_limit = addr+size-1 if not read and not write: arr = bytearray(size) self.io_regions.append((addr, size, arr)) else: self.io_regions.append((addr, size, read, write)) return addr, arr def find_region(self, addr): for region in self.regions: if region[0] <= addr < region[0]+region[1]: return region return None def find_io_region(self, addr): for region in self.io_regions: if region[0] <= addr < region[0]+region[1]: return region return None def read_region(self, addr, length): region = self.find_region(addr) if not region: raise Exception("Invalid address") offset = addr - region[0] if len(region) == 3: return region[2][offset:offset+length] elif len(region) == 4: if inspect.isgeneratorfunction(region[2]): yield from region[2](offset, length) else: region[2](offset, length) def write_region(self, addr, data): region = self.find_region(addr) if not region: raise Exception("Invalid address") offset = addr - region[0] if len(region) == 3: region[2][offset:offset+len(data)] = data elif len(region) == 4: if inspect.isgeneratorfunction(region[3]): yield from region[3](offset, data) else: region[3](offset, data) def read_io_region(self, addr, length): region = self.find_io_region(addr) if not region: raise Exception("Invalid address") offset = addr - region[0] if len(region) == 3: return region[2][offset:offset+length] elif len(region) == 4: if inspect.isgeneratorfunction(region[2]): yield from region[2](offset, data) else: region[2](offset, data) def write_io_region(self, addr, data): region = self.find_io_region(addr) if not region: raise Exception("Invalid address") offset = addr - region[0] if len(region) == 3: region[2][offset:offset+len(data)] = data elif len(region) == 4: if inspect.isgeneratorfunction(region[3]): yield from region[3](offset, data) else: region[3](offset, data) def downstream_send(self, tlp): # logging print("[%s] Sending TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() yield from self.upstream_bridge.upstream_recv(tlp) def send(self, tlp): yield from self.downstream_send(tlp) def downstream_recv(self, tlp): # logging print("[%s] Got TLP: %s" % (highlight(self.get_desc()), repr(tlp))) assert tlp.check() yield from self.handle_tlp(tlp) def handle_tlp(self, tlp): if (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): self.rx_cpl_queues[tlp.tag].append(tlp) self.rx_cpl_sync[tlp.tag].next = not self.rx_cpl_sync[tlp.tag] elif tlp.fmt_type in self.rx_tlp_handler: yield self.rx_tlp_handler[tlp.fmt_type](tlp) else: raise Exception("Unhandled TLP") def register_rx_tlp_handler(self, fmt_type, func): self.rx_tlp_handler[fmt_type] = func def recv_cpl(self, tag, timeout=0): queue = self.rx_cpl_queues[tag] sync = self.rx_cpl_sync[tag] if timeout: yield sync, delay(timeout) else: yield sync if queue: return queue.pop(0) return None def get_free_tag(self): tag = None tag_count = 32 for k in range(tag_count): if not self.rx_cpl_queues[self.current_tag]: tag = self.current_tag self.current_tag = (self.current_tag + 1) % tag_count return tag def handle_io_read_tlp(self, tlp): if self.find_io_region(tlp.address): # logging print("[%s] IO read" % (highlight(self.get_desc()))) assert tlp.length == 1 # prepare completion TLP cpl = TLP() cpl.set_completion_data(tlp, PcieId(0, 0, 0)) addr = tlp.address offset = 0 start_offset = None mask = tlp.first_be # perform read data = bytearray(4) for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: data[start_offset:offset] = yield from self.read_io_region(addr+start_offset, offset-start_offset) start_offset = None offset += 1 if start_offset is not None and offset != start_offset: data[start_offset:offset] = yield from self.read_io_region(addr+start_offset, offset-start_offset) cpl.set_data(data) cpl.byte_count = 4 cpl.length = 1 # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) else: # logging print("IO request did not match any regions") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, PcieId(0, 0, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) def handle_io_write_tlp(self, tlp): if self.find_io_region(tlp.address): # logging print("[%s] IO write" % (highlight(self.get_desc()))) assert tlp.length == 1 # prepare completion TLP cpl = TLP() cpl.set_completion(tlp, PcieId(0, 0, 0)) addr = tlp.address offset = 0 start_offset = None mask = tlp.first_be # perform write data = tlp.get_data() for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: yield from self.write_io_region(addr+start_offset, data[start_offset:offset]) start_offset = None offset += 1 if start_offset is not None and offset != start_offset: yield from self.write_io_region(addr+start_offset, data[start_offset:offset]) cpl.byte_count = 4 # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) else: # logging print("IO request did not match any regions") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, PcieId(0, 0, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) def handle_mem_read_tlp(self, tlp): if self.find_region(tlp.address): # logging print("[%s] Memory read" % (highlight(self.get_desc()))) # perform operation addr = tlp.address offset = 0 # check for 4k boundary crossing if tlp.length*4 > 0x1000 - (addr & 0xfff): print("Request crossed 4k boundary, discarding request") return # perform read data = yield from self.read_region(addr, tlp.length*4) # prepare completion TLP(s) m = 0 n = 0 addr = tlp.address+tlp.get_first_be_offset() dw_length = tlp.length byte_length = tlp.get_be_byte_count() while m < dw_length: cpl = TLP() cpl.set_completion_data(tlp, PcieId(0, 0, 0)) cpl_dw_length = dw_length - m cpl_byte_length = byte_length - n cpl.byte_count = cpl_byte_length if cpl_dw_length > 32 << self.max_payload_size: cpl_dw_length = 32 << self.max_payload_size # max payload size cpl_dw_length -= (addr & 0x7c) >> 2 # RCB align cpl.lower_address = addr & 0x7f cpl.set_data(data[m*4:(m+cpl_dw_length)*4]) # logging print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) m += cpl_dw_length; n += cpl_dw_length*4 - (addr&3) addr += cpl_dw_length*4 - (addr&3) else: # logging print("Memory request did not match any regions") # Unsupported request cpl = TLP() cpl.set_ur_completion(tlp, PcieId(0, 0, 0)) # logging print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) yield from self.send(cpl) def handle_mem_write_tlp(self, tlp): if self.find_region(tlp.address): # logging print("[%s] Memory write" % (highlight(self.get_desc()))) # perform operation addr = tlp.address offset = 0 start_offset = None mask = tlp.first_be # check for 4k boundary crossing if tlp.length*4 > 0x1000 - (addr & 0xfff): print("Request crossed 4k boundary, discarding request") return # perform write data = tlp.get_data() # first dword for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: yield from self.write_region(addr+start_offset, data[start_offset:offset]) start_offset = None offset += 1 if tlp.length > 2: # middle dwords if start_offset is None: start_offset = offset offset += (tlp.length-2)*4 if tlp.length > 1: # last dword mask = tlp.last_be for k in range(4): if mask & (1 << k): if start_offset is None: start_offset = offset else: if start_offset is not None and offset != start_offset: yield from self.write_region(addr+start_offset, data[start_offset:offset]) start_offset = None offset += 1 if start_offset is not None and offset != start_offset: yield from self.write_region(addr+start_offset, data[start_offset:offset]) # memory writes are posted, so don't send a completion else: # logging print("Memory request did not match any regions") def config_read(self, dev, addr, length, timeout=0): n = 0 data = b'' while n < length: tlp = TLP() tlp.fmt_type = TLP_CFG_READ_1 tlp.requester_id = PcieId(0, 0, 0) tlp.tag = self.get_free_tag() tlp.dest_id = dev first_pad = addr % 4 byte_length = min(length-n, 4-first_pad) tlp.set_be(addr, byte_length) tlp.register_number = addr >> 2 yield from self.send(tlp) cpl = yield from self.recv_cpl(tlp.tag, timeout) if not cpl or cpl.status != CPL_STATUS_SC: d = b'\xff\xff\xff\xff' else: assert cpl.length == 1 d = struct.pack('> 2 yield from self.send(tlp) cpl = yield from self.recv_cpl(tlp.tag, timeout) n += byte_length addr += byte_length def config_write_words(self, dev, addr, data, ws=2, timeout=0): assert ws in (1, 2, 4, 8) words = data data = b'' for w in words: data += w.to_bytes(ws, 'little') yield from self.config_write(dev, addr, data, timeout) def config_write_dwords(self, dev, addr, data, timeout=0): yield from self.config_write_words(dev, addr, data, 4, timeout) def config_write_qwords(self, dev, addr, data, timeout=0): yield from self.config_write_words(dev, addr, data, 8, timeout) def config_write_byte(self, dev, addr, data, timeout=0): yield from self.config_write(dev, addr, [data], timeout) def config_write_word(self, dev, addr, data, timeout=0): yield from self.config_write_words(dev, addr, [data], timeout=timeout) def config_write_dword(self, dev, addr, data, timeout=0): yield from self.config_write_dwords(dev, addr, [data], timeout=timeout) def config_write_qword(self, dev, addr, data, timeout=0): yield from self.config_write_qwords(dev, addr, [data], timeout=timeout) def capability_read(self, dev, cap_id, addr, length, timeout=0): ti = self.tree.find_dev(dev) if not ti: raise Exception("Device not found") offset = ti.get_capability_offset(cap_id) if not offset: raise Exception("Capability not found") val = yield from self.config_read(dev, addr+offset, length, timeout) return val def capability_read_words(self, dev, cap_id, addr, count, ws=2, timeout=0): assert ws in (1, 2, 4, 8) data = yield from self.capability_read(dev, cap_id, addr, count*ws, timeout) words = [] for k in range(count): words.append(int.from_bytes(data[ws*k:ws*(k+1)], 'little')) return words def capability_read_dwords(self, dev, cap_id, addr, count, timeout=0): data = yield from self.capability_read_words(dev, cap_id, addr, count, 4, timeout) return data def capability_read_qwords(self, dev, cap_id, addr, count, timeout=0): data = yield from self.capability_read_words(dev, cap_id, addr, count, 8, timeout) return data def capability_read_byte(self, dev, cap_id, addr, timeout=0): data = yield from self.capability_read(dev, cap_id, addr, 1, timeout) return data[0] def capability_read_word(self, dev, cap_id, addr, timeout=0): data = yield from self.capability_read_words(dev, cap_id, addr, 1, timeout=timeout) return data[0] def capability_read_dword(self, dev, cap_id, addr, timeout=0): data = yield from self.capability_read_dwords(dev, cap_id, addr, 1, timeout=timeout) return data[0] def capability_read_qword(self, dev, cap_id, addr, timeout=0): data = yield from self.capability_read_qwords(dev, cap_id, addr, 1, timeout=timeout) return data[0] def capability_write(self, dev, cap_id, addr, data, timeout=0): ti = self.tree.find_dev(dev) if not ti: raise Exception("Device not found") offset = ti.get_capability_offset(cap_id) if not offset: raise Exception("Capability not found") yield from self.config_write(dev, addr+offset, data, timeout) def capability_write_words(self, dev, cap_id, addr, data, ws=2, timeout=0): assert ws in (1, 2, 4, 8) words = data data = b'' for w in words: data += w.to_bytes(ws, 'little') yield from self.capability_write(dev, cap_id, addr, data, timeout) def capability_write_dwords(self, dev, cap_id, addr, data, timeout=0): yield from self.capability_write_words(dev, cap_id, addr, data, 4, timeout) def capability_write_qwords(self, dev, cap_id, addr, data, timeout=0): yield from self.capability_write_words(dev, cap_id, addr, data, 8, timeout) def capability_write_byte(self, dev, cap_id, addr, data, timeout=0): yield from self.capability_write(dev, cap_id, addr, [data], timeout) def capability_write_word(self, dev, cap_id, addr, data, timeout=0): yield from self.capability_write_words(dev, cap_id, addr, [data], timeout=timeout) def capability_write_dword(self, dev, cap_id, addr, data, timeout=0): yield from self.capability_write_dwords(dev, cap_id, addr, [data], timeout=timeout) def capability_write_qword(self, dev, cap_id, addr, data, timeout=0): yield from self.capability_write_qwords(dev, cap_id, addr, [data], timeout=timeout) def io_read(self, addr, length, timeout=0): n = 0 data = b'' if self.find_region(addr): val = yield from self.read_io_region(addr, length) return val while n < length: tlp = TLP() tlp.fmt_type = TLP_IO_READ tlp.requester_id = PcieId(0, 0, 0) tlp.tag = self.get_free_tag() first_pad = addr % 4 byte_length = min(length-n, 4-first_pad) tlp.set_be(addr, byte_length) yield from self.send(tlp) cpl = yield from self.recv_cpl(tlp.tag, timeout) if not cpl: raise Exception("Timeout") if cpl.status != CPL_STATUS_SC: raise Exception("Unsuccessful completion") else: assert cpl.length == 1 d = struct.pack(' 0xffffffff: tlp.fmt_type = TLP_MEM_READ_64 else: tlp.fmt_type = TLP_MEM_READ tlp.requester_id = PcieId(0, 0, 0) tlp.tag = self.get_free_tag() tlp.attr = attr tlp.tc = tc first_pad = addr % 4 byte_length = length-n byte_length = min(byte_length, (128 << self.max_read_request_size)-first_pad) # max read request size byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align tlp.set_be(addr, byte_length) yield from self.send(tlp) m = 0 while m < byte_length: cpl = yield from self.recv_cpl(tlp.tag, timeout) if not cpl: raise Exception("Timeout") if cpl.status != CPL_STATUS_SC: raise Exception("Unsuccessful completion") else: assert cpl.byte_count+3+(cpl.lower_address&3) >= cpl.length*4 assert cpl.byte_count == byte_length - m d = bytearray() for k in range(cpl.length): d.extend(struct.pack(' 0xffffffff: tlp.fmt_type = TLP_MEM_WRITE_64 else: tlp.fmt_type = TLP_MEM_WRITE tlp.requester_id = PcieId(0, 0, 0) tlp.attr = attr tlp.tc = tc first_pad = addr % 4 byte_length = len(data)-n byte_length = min(byte_length, (128 << self.max_payload_size)-first_pad) # max payload size byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align tlp.set_be_data(addr, data[n:n+byte_length]) yield from self.send(tlp) n += byte_length addr += byte_length def mem_write_words(self, addr, data, ws=2, timeout=0, attr=0, tc=0): assert ws in (1, 2, 4, 8) words = data data = b'' for w in words: data += w.to_bytes(ws, 'little') yield from self.mem_write(addr, data, timeout, attr, tc) def mem_write_dwords(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_words(addr, data, 4, timeout, attr, tc) def mem_write_qwords(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_words(addr, data, 8, timeout, attr, tc) def mem_write_byte(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write(addr, [data], timeout, attr, tc) def mem_write_word(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_words(addr, [data], timeout=timeout, attr=attr, tc=tc) def mem_write_dword(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_dwords(addr, [data], timeout=timeout, attr=attr, tc=tc) def mem_write_qword(self, addr, data, timeout=0, attr=0, tc=0): yield from self.mem_write_qwords(addr, [data], timeout=timeout, attr=attr, tc=tc) def msi_region_read(self, addr, length): return b'\x00'*length def msi_region_write(self, addr, data): assert addr == 0 assert len(data) == 4 number = struct.unpack('> 23 & 1 msi_mmcap = msg_ctrl >> 17 & 7 # message address yield from self.capability_write_dword(dev, MSI_CAP_ID, 4, self.msi_addr & 0xfffffffc) if msi_64bit: # 64 bit message address # message upper address yield from self.capability_write_dword(dev, MSI_CAP_ID, 8, (self.msi_addr >> 32) & 0xffffffff) # message data yield from self.capability_write_dword(dev, MSI_CAP_ID, 12, self.msi_msg_limit) else: # 32 bit message address # message data yield from self.capability_write_dword(dev, MSI_CAP_ID, 8, self.msi_msg_limit) # enable and set enabled messages yield from self.capability_write_dword(dev, MSI_CAP_ID, 0, (msg_ctrl & ~(7 << 20)) | 1 << 16 | msi_mmcap << 20) ti.msi_addr = self.msi_addr ti.msi_data = self.msi_msg_limit for k in range(32): self.msi_signals[self.msi_msg_limit] = [Signal(bool(0))] self.msi_callbacks[self.msi_msg_limit] = [] self.msi_msg_limit += 1 return True def msi_get_signal(self, dev, number=0): if not self.tree: return None ti = self.tree.find_dev(dev) if not ti: return None if ti.msi_data is None: return None if ti.msi_data+number not in self.msi_signals: return None return self.msi_signals[ti.msi_data+number][0] def msi_register_signal(self, dev, sig, number=0): if not self.tree: return ti = self.tree.find_dev(dev) if not ti: return if ti.msi_data is None: return if ti.msi_data+number not in self.msi_signals: return self.msi_signals[ti.msi_data+number].append(sig) def msi_register_callback(self, dev, callback, number=0): if not self.tree: return ti = self.tree.find_dev(dev) if not ti: return if ti.msi_data is None: return if ti.msi_data+number not in self.msi_callbacks: return self.msi_callbacks[ti.msi_data+number].append(callback) def enumerate_segment(self, tree, bus, timeout=1000, enable_bus_mastering=False, configure_msi=False): sec_bus = bus+1 sub_bus = bus tree.sec_bus_num = bus # align limits against bridge registers self.io_limit = align(self.io_limit, 0xfff) self.mem_limit = align(self.mem_limit, 0xfffff) self.prefetchable_mem_limit = align(self.prefetchable_mem_limit, 0xfffff) tree.io_base = self.io_limit tree.io_limit = self.io_limit tree.mem_base = self.mem_limit tree.mem_limit = self.mem_limit tree.prefetchable_mem_base = self.prefetchable_mem_limit tree.prefetchable_mem_limit = self.prefetchable_mem_limit # logging print("[%s] Enumerating bus %d" % (highlight(self.get_desc()), bus)) for d in range(32): if bus == 0 and d == 0: continue # read vendor ID and device ID val = yield from self.config_read_dword(PcieId(bus, d, 0), 0x000, timeout) if val is None or val == 0xffffffff: continue # valid vendor ID # logging print("[%s] Found device at %02x:%02x.%x" % (highlight(self.get_desc()), bus, d, 0)) fc = 1 # read type val = yield from self.config_read_byte(PcieId(bus, d, 0), 0x00e, timeout) if val & 0x80: # multifunction device fc = 8 for f in range(fc): # read vendor ID and device ID val = yield from self.config_read(PcieId(bus, d, f), 0x000, 4, timeout) if val is None or val == b'\xff\xff\xff\xff': continue ti = TreeItem() tree.children.append(ti) ti.bus_num = bus ti.device_num = d ti.function_num = f ti.vendor_id, ti.device_id = struct.unpack('> 32) & 0xffffffff) bar += 2 else: # 32 bit BAR mask = (~val & 0xffffffff) | 15 size = mask + 1 # logging print("[%s] %02x:%02x.%x (32-bit) Mem BAR%d raw: %08x, mask: %08x, size: %d" % (highlight(self.get_desc()), bus, d, f, bar, val, mask, size)) if val & 8: # prefetchable # logging print("[%s] %02x:%02x.%x (32-bit) Mem BAR%d marked prefetchable, but allocating as non-prefetchable" % (highlight(self.get_desc()), bus, d, f, bar)) # align self.mem_limit = align(self.mem_limit, mask) val = val & 15 | self.mem_limit ti.bar[bar] = val ti.bar_size[bar] = size # logging print("[%s] %02x:%02x.%x (32-bit) Mem BAR%d Allocation: %08x, size: %d" % (highlight(self.get_desc()), bus, d, f, bar, val, size)) self.mem_limit += size # write BAR yield from self.config_write_dword(PcieId(bus, d, f), 0x010+bar*4, val) bar += 1 # logging print("[%s] Walk capabilities of %02x:%02x.%x" % (highlight(self.get_desc()), bus, d, f)) # walk capabilities ptr = yield from self.config_read_byte(PcieId(bus, d, f), 0x34) ptr = ptr & 0xfc while ptr > 0: val = yield from self.config_read(PcieId(bus, d, f), ptr, 2) # logging print("[%s] Found capability 0x%02x at offset 0x%02x, next ptr 0x%02x" % (highlight(self.get_desc()), val[0], ptr, val[1] & 0xfc)) ti.capabilities.append((val[0], ptr)) ptr = val[1] & 0xfc # walk extended capabilities # TODO # set max payload size, max read request size, and extended tag enable dev_cap = yield from self.capability_read_dword(PcieId(bus, d, f), PCIE_CAP_ID, 4) dev_ctrl_sta = yield from self.capability_read_dword(PcieId(bus, d, f), PCIE_CAP_ID, 8) max_payload = min(0x5, min(self.max_payload_size, dev_cap & 7)) ext_tag = bool(self.extended_tag_field_enable and (dev_cap & (1 << 5))) max_read_req = min(0x5, self.max_read_request_size) new_dev_ctrl = dev_ctrl_sta & 0x00008e1f | (max_payload << 5) | (ext_tag << 8) | (max_read_req << 12) yield from self.capability_write_dword(PcieId(bus, d, f), PCIE_CAP_ID, 8, new_dev_ctrl) if enable_bus_mastering: # enable bus mastering val = yield from self.config_read_word(PcieId(bus, d, f), 0x04) yield from self.config_write_word(PcieId(bus, d, f), 0x04, val | 4) if configure_msi: # configure MSI yield from self.configure_msi(PcieId(bus, d, f)) if bridge: # set bridge registers for enumeration # logging print("[%s] Set pri %d, sec %d, sub %d" % (highlight(self.get_desc()), bus, sec_bus, 255)) yield from self.config_write(PcieId(bus, d, f), 0x018, bytearray([bus, sec_bus, 255])) # enumerate secondary bus sub_bus = yield from self.enumerate_segment(tree=ti, bus=sec_bus, timeout=timeout, enable_bus_mastering=enable_bus_mastering, configure_msi=configure_msi) # finalize bridge configuration # logging print("[%s] Set pri %d, sec %d, sub %d" % (highlight(self.get_desc()), bus, sec_bus, sub_bus)) yield from self.config_write(PcieId(bus, d, f), 0x018, bytearray([bus, sec_bus, sub_bus])) # set base/limit registers # logging print("[%s] Set IO base: %08x, limit: %08x" % (highlight(self.get_desc()), ti.io_base, ti.io_limit)) yield from self.config_write(PcieId(bus, d, f), 0x01C, struct.pack('BB', (ti.io_base >> 8) & 0xf0, (ti.io_limit >> 8) & 0xf0)) yield from self.config_write(PcieId(bus, d, f), 0x030, struct.pack('> 16, ti.io_limit >> 16)) # logging print("[%s] Set mem base: %08x, limit: %08x" % (highlight(self.get_desc()), ti.mem_base, ti.mem_limit)) yield from self.config_write(PcieId(bus, d, f), 0x020, struct.pack('> 16) & 0xfff0, (ti.mem_limit >> 16) & 0xfff0)) # logging print("[%s] Set prefetchable mem base: %016x, limit: %016x" % (highlight(self.get_desc()), ti.prefetchable_mem_base, ti.prefetchable_mem_limit)) yield from self.config_write(PcieId(bus, d, f), 0x024, struct.pack('> 16) & 0xfff0, (ti.prefetchable_mem_limit >> 16) & 0xfff0)) yield from self.config_write(PcieId(bus, d, f), 0x028, struct.pack('> 32)) yield from self.config_write(PcieId(bus, d, f), 0x02c, struct.pack('> 32)) sec_bus = sub_bus+1 tree.sub_bus_num = sub_bus # align limits against bridge registers self.io_limit = align(self.io_limit, 0xfff) self.mem_limit = align(self.mem_limit, 0xfffff) self.prefetchable_mem_limit = align(self.prefetchable_mem_limit, 0xfffff) tree.io_limit = self.io_limit-1 tree.mem_limit = self.mem_limit-1 tree.prefetchable_mem_limit = self.prefetchable_mem_limit-1 # logging print("[%s] Enumeration of bus %d complete" % (highlight(self.get_desc()), bus)) return sub_bus def enumerate(self, timeout=1000, enable_bus_mastering=False, configure_msi=False): # logging print("[%s] Enumerating bus" % (highlight(self.get_desc()))) self.io_limit = self.io_base self.mem_limit = self.mem_base self.prefetchable_mem_limit = self.prefetchable_mem_base self.tree = TreeItem() yield from self.enumerate_segment(tree=self.tree, bus=0, timeout=timeout, enable_bus_mastering=enable_bus_mastering, configure_msi=configure_msi) self.upstream_bridge.io_base = self.io_base self.upstream_bridge.io_limit = self.io_limit self.upstream_bridge.mem_base = self.mem_base self.upstream_bridge.mem_limit = self.mem_limit self.upstream_bridge.prefetchable_mem_base = self.prefetchable_mem_base self.upstream_bridge.prefetchable_mem_limit = self.prefetchable_mem_limit # logging print("[%s] Enumeration complete" % (highlight(self.get_desc()))) # logging print("Device tree:") print(self.tree.to_str().strip())