From c57ef057ee56056d5d39e7814ecc421372d8d592 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 25 Sep 2018 19:50:57 -0700 Subject: [PATCH] Initial commit --- AUTHORS | 1 + COPYING | 19 + README | 1 + README.md | 35 + tb/pcie.py | 3834 ++++++++++++++++++++++++++++++++++++++++++++ tb/pcie_us.py | 1552 ++++++++++++++++++ tb/test_pcie.py | 250 +++ tb/test_pcie_us.py | 915 +++++++++++ 8 files changed, 6607 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 120000 README create mode 100644 README.md create mode 100644 tb/pcie.py create mode 100644 tb/pcie_us.py create mode 100755 tb/test_pcie.py create mode 100755 tb/test_pcie_us.py diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..7dab2b3a5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..6923387c7 --- /dev/null +++ b/COPYING @@ -0,0 +1,19 @@ +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. diff --git a/README b/README new file mode 120000 index 000000000..42061c01a --- /dev/null +++ b/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..419bbaea2 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Verilog PCI Express Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/pcie/start + +GitHub repository: https://github.com/alexforencich/verilog-pcie + +## Introduction + +Collection of PCI express related components. Includes full MyHDL testbench +with intelligent bus cosimulation endpoints. + +## Documentation + +### PCIe BFM + +A MyHDL transaction layer PCI Express bus functional model (BFM) is included in pcie.py. This BFM implements an extensive event driven simulation of a complete PCI express system, including root complex, switches, devices, and functions. The BFM includes code to enumerate the bus, initialize configuration space registers and allocate BARs, pass messages between devices, and perform memory read and write operations. Any module can be connected to a cosimulated design, enabling testing of not only isolated components but also communication between multiple components such as device-to-device DMA and message passing. + +### Common signals + +### Common parameters + +### Source Files + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/pcie.py : MyHDL PCI Express BFM + tb/pcie_us.py : MyHDL Xilinx Ultrascale PCIe core model diff --git a/tb/pcie.py b/tb/pcie.py new file mode 100644 index 000000000..0551ad740 --- /dev/null +++ b/tb/pcie.py @@ -0,0 +1,3834 @@ +""" + +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 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 +MSIX_CAP_ID = 0x11 + +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 id2int(id): + return ((id[0] & 0xff) << 8) | ((id[1] & 0x1f) << 3) | (id[2] & 0x7) + + +def int2id(i): + return ((i >> 8) & 0xff, (i >> 3) & 0x1f, i & 0x7) + + +def id_to_str(id): + return "%02x:%02x.%x" % id + + +def highlight(s): + return "\033[32m%s\033[0m" % s + + +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 = (0, 0, 0) + self.status = 0 + self.bcm = 0 + self.byte_count = 0 + self.requester_id = (0, 0, 0) + self.dest_id = (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 + + def check(self): + """Validate TLP""" + 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(tlp)) + raise Exception("TLP validation failed, length field does not match data") + if ((self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64 or + (self.fmt, self.type) == TLP_MEM_READ_LOCKED or (self.fmt, self.type) == TLP_MEM_READ_LOCKED_64 or + (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64): + if tlp.length*4 > 0x1000 - (tlp.address & 0xfff): + print("TLP validation failed, request crosses 4K boundary: %s" % repr(tlp)) + raise Exception("TLP validation failed, request crosses 4K boundary") + return True + + def set_completion(self, tlp, completer_id): + """Prepare completion for TLP""" + self.fmt, self.type = TLP_CPL + self.requester_id = tlp.requester_id + self.completer_id = completer_id + self.status = CPL_STATUS_SC + self.attr = tlp.attr + self.tag = tlp.tag + self.tc = tlp.tc + + def set_ur_completion(self, tlp, completer_id): + """Prepare unsupported request (UR) completion for TLP""" + self.set_completion(tlp, completer_id) + self.status = 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) + self.status = CPL_STATUS_CRS + + def set_ca_completion(self, tlp, completer_id): + """Prepare completer abort (CA) completion for TLP""" + self.set_completion(tlp, completer_id) + self.status = CPL_STATUS_CA + + def set_be(self, addr, length): + """Compute byte enables and DWORD length from byte address and length""" + 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) + if ((self.fmt, self.type) == TLP_CPL or (self.fmt, self.type) == TLP_CPL_DATA or + (self.fmt, self.type) == TLP_CPL_LOCKED or (self.fmt, self.type) == TLP_CPL_LOCKED_DATA): + l = self.byte_count & 0xfff + l |= (self.bcm & 1) << 12 + l |= (self.status & 0x7) << 13 + l |= id2int(self.completer_id) << 16 + pkt.append(l) + l = self.lower_address & 0x7f + l |= (self.tag & 0xff) << 8 + l |= id2int(self.requester_id) << 16 + pkt.append(l) + + 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, self.type) == TLP_CFG_READ_0 or (self.fmt, self.type) == TLP_CFG_WRITE_0 or + (self.fmt, self.type) == TLP_CFG_READ_1 or (self.fmt, self.type) == TLP_CFG_WRITE_1 or + (self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64 or + (self.fmt, self.type) == TLP_MEM_READ_LOCKED or (self.fmt, self.type) == TLP_MEM_READ_LOCKED_64 or + (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64 or + (self.fmt, self.type) == TLP_IO_READ or (self.fmt, self.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 = int2id(pkt[1] >> 16) + + if ((self.fmt, self.type) == TLP_CFG_READ_0 or (self.fmt, self.type) == TLP_CFG_WRITE_0 or + (self.fmt, self.type) == TLP_CFG_READ_1 or (self.fmt, self.type) == TLP_CFG_WRITE_1): + self.register_number = (pkt[2] >> 2) >> 0x3ff + self.dest_id = int2id(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 + if ((self.fmt, self.type) == TLP_CPL or (self.fmt, self.type) == TLP_CPL_DATA or + (self.fmt, self.type) == TLP_CPL_LOCKED or (self.fmt, self.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 = int2id(pkt[1] >> 16) + self.lower_address = pkt[2] & 0x7f + self.tag = (pkt[2] >> 8) & 0xff + self.requester_id = int2id(pkt[2] >> 16) + + 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, ' % repr(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 self.other.ext_recv(tlp) + + def ext_recv(self, tlp): + if self.rx_handler is None: + raise Exception("Receive handler not set") + yield 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 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 MSI32Capability(object): + def __init__(self, *args, **kwargs): + super(MSI32Capability, self).__init__(*args, **kwargs) + + # MSI Capability Registes + self.msi_multiple_message_capable = 0 + self.msi_multiple_message_enable = 0 + self.msi_enable = False + self.msi_message_address = 0 + self.msi_message_data = 0 + + self.register_capability(MSI_CAP_ID, 3, 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 + +---------------------------------+---------------------------------+ + """ + 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 + return val + elif reg == 1: + # Message address + return self.msi_message_address & 0xfffffffc + elif reg == 2: + # Message data + return self.msi_message_data & 0xffff + + def write_msi_cap_register(self, reg, write, offset=None): + 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) & 0xfffffffc + elif reg == 2: + # Message data + self.msi_message_data = byte_mask_update(self.msi_message_data, mask & 0x3, data) & 0xffff + + +class MSI64Capability(object): + def __init__(self, *args, **kwargs): + super(MSI64Capability, self).__init__(*args, **kwargs) + + # MSI Capability Registes + self.msi_multiple_message_capable = 0 + self.msi_multiple_message_enable = 0 + self.msi_enable = False + self.msi_message_address = 0 + self.msi_message_upper_address = 0 + self.msi_message_data = 0 + + self.register_capability(MSI_CAP_ID, 4, self.read_msi_cap_register, self.write_msi_cap_register) + + """ + 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 + +---------------------------------+---------------------------------+ + """ + def read_msi_cap_register(self, reg): + if reg == 0: + # Message control + val = 0x00800000 + if self.msi_enable: val |= 1 << 16 + val |= (self.msi_multiple_message_capable & 0x7) << 17 + val |= (self.msi_multiple_message_enable & 0x7) << 20 + return val + elif reg == 1: + # Message address + return self.msi_message_address & 0xfffffffc + elif reg == 2: + # Message upper address + return self.msi_message_upper_address & 0xffffffff + elif reg == 3: + # Message data + return self.msi_message_data & 0xffff + + def write_msi_cap_register(self, reg, write, offset=None): + 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) & 0xfffffffc + elif reg == 2: + # Message upper address + self.msi_message_upper_address = byte_mask_update(self.msi_message_upper_address, mask, data) & 0xffffffff + elif reg == 3: + # Message data + self.msi_message_data = byte_mask_update(self.msi_message_data, mask & 0x3, data) & 0xffff + + +class MSI64MaskCapability(object): + def __init__(self, *args, **kwargs): + super(MSI64MaskCapability, self).__init__(*args, **kwargs) + + # MSI Capability Registes + self.msi_enable = False + self.msi_multiple_message_capable = 0 + self.msi_multiple_message_enable = 0 + self.msi_message_address = 0 + self.msi_message_upper_address = 0 + self.msi_message_data = 0 + self.msi_mask_bits = 0 + self.msi_pending_bits = 0 + + self.register_capability(MSI_CAP_ID, 6, self.read_msi_cap_register, self.write_msi_cap_register) + + """ + 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 = 0x01800000 + if self.msi_enable: val |= 1 << 16 + val |= (self.msi_multiple_message_capable & 0x7) << 17 + val |= (self.msi_multiple_message_enable & 0x7) << 20 + return val + elif reg == 1: + # Message address + return self.msi_message_address & 0xfffffffc + elif reg == 2: + # Message upper address + return self.msi_message_upper_address & 0xffffffff + elif reg == 3: + # Message data + return self.msi_message_data & 0xffff + elif reg == 4: + # Mask bits + return self.msi_mask_bits & 0xffffffff + elif reg == 5: + # Pending bits + return self.msi_pending_bits & 0xffffffff + + def write_msi_cap_register(self, reg, write, offset=None): + 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) & 0xfffffffc + elif reg == 2: + # Message upper address + self.msi_message_upper_address = byte_mask_update(self.msi_message_upper_address, mask, data) & 0xffffffff + elif reg == 3: + # Message data + self.msi_message_data = byte_mask_update(self.msi_message_data, mask & 0x3, data) & 0xffff + elif reg == 4: + # Mask bits + self.msi_mask_bits = byte_mask_update(self.msi_mask_bits, mask, data) & 0xffffffff + + +class MSIXCapability(object): + def __init__(self, *args, **kwargs): + super(MSIXCapability, self).__init__(*args, **kwargs) + + # MSI-X Capability Registers + self.msix_table_size = 0 + self.msix_function_mask = False + self.msix_enable = False + self.msix_table_bar_indicator_register = 0 + self.msix_table_offset = 0 + self.msix_pba_bar_indicator_register = 0 + self.msix_pba_offset = 0 + + self.register_capability(MSIX_CAP_ID, 3, self.read_msix_cap_register, self.write_msix_cap_register) + + """ + MSI-X Capability + + 31 0 + +---------------------------------+----------------+----------------+ + | Message Control | Next Cap | Cap ID | 0 0x00 + +---------------------------------+----------------+----------+-----+ + | Table Offset | BIR | 1 0x04 + +-------------------------------------------------------------+-----+ + | PBA Offset | BIR | 2 0x08 + +-------------------------------------------------------------+-----+ + """ + def read_msix_cap_register(self, reg): + if reg == 0: + # Message control + val = (self.msix_table_size & 0x7ff) << 16 + if self.msix_function_mask: val |= 1 << 30 + if self.msix_enable: val |= 1 << 31 + return val + elif reg == 1: + # Table offset and BIR + val = self.msix_table_bar_indicator_register & 0x7 + val |= self.msix_table_offset & 0xfffffff8 + return val + elif reg == 2: + # Pending bit array offset and BIR + val = self.msix_pba_bar_indicator_register & 0x7 + val |= self.msix_pba_offset & 0xfffffff8 + return val + + def write_msix_cap_register(self, reg, write, offset=None): + if reg == 0: + # Message control + if mask & 0x8: self.msix_function_mask = (data & 1 << 30 != 0) + if mask & 0x8: self.msix_enable = (data & 1 << 31 != 0) + + +class Function(PMCapability, PCIECapability): + """PCIe function, implements config TLP handling""" + def __init__(self, *args, **kwargs): + self.bus_num = 0 + self.device_num = 0 + self.function_num = 0 + + self.upstream_tx_handler = None + + self.desc = "Function" + + self.current_tag = 0 + + self.rx_cpl_queues = [[]]*256 + self.rx_cpl_sync = [Signal(False)]*256 + self.rx_tlp_handler = {} + + self.capabilities = {} + self.last_capability_id = None + self.ext_capabilities = {} + self.last_ext_capability_id = None + + # configuration registers + self.vendor_id = 0 + self.device_id = 0 + # command register + self.bus_master_enable = False + self.parity_error_response = False + self.serr_enable = False + self.interrupt_disable = False + # status register + self.interrupt_status = False + self.capabilities_list = True + self.master_data_parity_error = False + self.signaled_target_abort = False + self.received_target_abort = False + self.received_master_abort = False + self.signaled_system_error = False + self.detected_parity_error = False + self.rev_id = 0 + self.class_code = 0 + self.cache_ln = 0 + self.lat_timer = 0 + self.header_type = 0 + self.bist = 0 + self.bar = [] + self.bar_mask = [] + self.expansion_rom_addr = 0 + self.cap_ptr = 0 + self.intr_pin = 0 + self.intr_line = 0 + + self.read_completion_boundary = 128 + + self.register_rx_tlp_handler(TLP_CFG_READ_0, self.handle_config_0_tlp) + self.register_rx_tlp_handler(TLP_CFG_WRITE_0, self.handle_config_0_tlp) + + super(Function, self).__init__(*args, **kwargs) + + def get_desc(self): + return "%02x:%02x.%x %s" % (self.bus_num, self.device_num, self.function_num, self.desc) + + """ + Common 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 | + +----------------+----------------+----------------+----------------+ + | | 4 0x10 + +-------------------------------------------------------------------+ + | | 5 0x14 + +-------------------------------------------------------------------+ + | | 6 0x18 + +-------------------------------------------------------------------+ + | | 7 0x1C + +-------------------------------------------------------------------+ + | | 8 0x20 + +-------------------------------------------------------------------+ + | | 9 0x24 + +-------------------------------------------------------------------+ + | | 10 0x28 + +-------------------------------------------------------------------+ + | | 11 0x2C + +-------------------------------------------------------------------+ + | | 12 0x30 + +--------------------------------------------------+----------------+ + | | Cap Ptr | 13 0x34 + +--------------------------------------------------+----------------+ + | | 14 0x38 + +---------------------------------+----------------+----------------+ + | | Int Pin | Int Line | 15 0x3C + +---------------------------------+----------------+----------------+ + """ + def read_config_register(self, reg): + if reg == 0: return (self.device_id << 16) | self.vendor_id + #elif reg == 1: return (self.status << 16) | self.command + elif reg == 1: + val = 0 + # command + if self.bus_master_enable: val |= 1 << 2 + if self.parity_error_response: val |= 1 << 6 + if self.serr_enable: val |= 1 << 8 + if self.interrupt_disable: val |= 1 << 10 + # status + if self.interrupt_status: val |= 1 << 19 + if self.capabilities_list: val |= 1 << 20 + if self.master_data_parity_error: val |= 1 << 24 + if self.signaled_target_abort: val |= 1 << 27 + if self.received_target_abort: val |= 1 << 28 + if self.received_master_abort: val |= 1 << 29 + if self.signaled_system_error: val |= 1 << 30 + if self.detected_parity_error: val |= 1 << 31 + return val + elif reg == 2: return (self.class_code << 8) | self.rev_id + elif reg == 3: return (self.bist << 24) | (self.header_type << 16) | (self.lat_timer << 8) | self.cache_ln + elif reg == 13: return self.cap_ptr + elif reg == 15: return (self.intr_pin << 8) | self.intr_line + elif 16 <= reg < 256: return self.read_capability_register(reg) + elif 256 <= reg < 4096: return self.read_extended_capability_register(reg) + else: return 0 + + def write_config_register(self, reg, data, mask): + if reg == 1: + # command + if mask & 0x1: self.bus_master_enable = (data & 1 << 2 != 0) + if mask & 0x1: self.parity_error_response = (data & 1 << 6 != 0) + if mask & 0x2: self.serr_enable = (data & 1 << 8 != 0) + if mask & 0x2: self.interrupt_disable = (data & 1 << 10 != 0) + # status + if mask & 0x8 and data & 1 << 24: self.master_data_parity_error = False + if mask & 0x8 and data & 1 << 27: self.signaled_target_abort = False + if mask & 0x8 and data & 1 << 28: self.received_target_abort = False + if mask & 0x8 and data & 1 << 29: self.received_master_abort = False + if mask & 0x8 and data & 1 << 30: self.signaled_system_error = False + if mask & 0x8 and data & 1 << 31: self.detected_parity_error = False + elif reg == 3: + self.cache_ln = byte_mask_update(self.cache_ln, mask & 1, data) + self.lat_timer = byte_mask_update(self.lat_timer, (mask >> 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): + for cap in self.capabilities: + if self.capabilities[cap][0] <= reg < self.capabilities[cap][0]+self.capabilities[cap][1]: + print("read cap %d" % cap) + offset = reg-self.capabilities[cap][0] + val = self.capabilities[cap][2](offset) + if offset == 0: + val = (val & 0xffff0000) | (self.capabilities[cap][4] << 8) | cap + return val + return 0 + + def write_capability_register(self, reg, data, mask): + for cap in self.capabilities: + if self.capabilities[cap][0] <= reg < self.capabilities[cap][0]+self.capabilities[cap][1]: + offset = reg-self.capabilities[cap][0] + self.capabilities[cap][3](offset, data, mask) + return + + def register_capability(self, cap_id, length=None, read=None, write=None, offset=None): + if cap_id in self.capabilities: + # re-registering, capture and remove entry + orig_offset = self.capabilities[cap_id][0] + if not offset: + offset = self.capabilities[cap_id][0] + if not length: + length = self.capabilities[cap_id][1] + if not read: + read = self.capabilities[cap_id][2] + if not write: + write = self.capabilities[cap_id][3] + + # remove from linked list + if self.cap_ptr == orig_offset*4: + self.cap_ptr = self.capabilities[cap_id][4] + else: + for cap in self.capabilities: + if self.capabilities[cap][4] == orig_offset*4: + self.capabilities[cap][4] = self.capabilities[cap_id][4] + del self.capabilities[cap_id] + elif not length or not read or not write: + raise Exception("Missing required parameter") + + cl = sorted([(self.capabilities[x][0], self.capabilities[x][1]) for x in self.capabilities]) + + if offset is not None: + for a, l in cl: + if a <= offset+length-1 and offset <= a+l-1: + raise Exception("Invalid offset") + else: + offset = 0x10 + + for a, l in cl: + if a <= offset+length-1 and offset <= a+l-1: + offset = a+l + + self.capabilities[cap_id] = [offset, length, read, write, 0] + + # add to linked list + if self.last_capability_id is not None: + self.capabilities[self.last_capability_id][4] = offset*4 + else: + self.cap_ptr = offset*4 + + self.last_capability_id = cap_id + + def read_extended_capability_register(self, reg): + for cap in self.ext_capabilities: + if self.ext_capabilities[cap][0] <= reg < self.ext_capabilities[cap][0]+self.ext_capabilities[cap][1]: + offset = reg-self.ext_capabilities[cap][0] + if offset == 0: + return (self.ext_capabilities[cap][4] << 20) | cap + return self.ext_capabilities[cap][2](offset) + return 0 + + def write_extended_capability_register(self, reg, data, mask): + for cap in self.ext_capabilities: + if self.ext_capabilities[cap][0] <= reg < self.ext_capabilities[cap][0]+self.ext_capabilities[cap][1]: + offset = reg-self.ext_capabilities[cap][0] + self.ext_capabilities[cap][3](offset, data, mask) + return + + def register_extended_capability(self, cap_id, cap_ver, length=None, read=None, write=None, offset=None): + if cap_id in self.ext_capabilities: + # re-registering, capture and remove entry + orig_offset = self.ext_capabilities[cap_id][0] + if not offset: + offset = self.ext_capabilities[cap_id][0] + if not length: + length = self.ext_capabilities[cap_id][1] + if not read: + read = self.ext_capabilities[cap_id][2] + if not write: + write = self.ext_capabilities[cap_id][3] + if not cap_ver: + cap_ver = self.ext_capabilities[cap_id][5] + + # remove from linked list + for cap in self.ext_capabilities: + if self.ext_capabilities[cap][4] == orig_offset*4: + self.ext_capabilities[cap][4] = self.ext_capabilities[cap_id][4] + del self.ext_capabilities[cap_id] + elif not length or not read or not write: + raise Exception("Missing required parameter") + + cl = sorted([(self.ext_capabilities[x][0], self.ext_capabilities[x][1]) for x in self.ext_capabilities]) + + if offset is not None: + for a, l in cl: + if a <= offset+length-1 and offset <= a+l-1: + raise Exception("Invalid offset") + else: + offset = 0x40 + + for a, l in cl: + if a <= offset+length-1 and offset <= a+l-1: + offset = a+l + + self.ext_capabilities[cap_id] = [offset, length, read, write, 0, cap_ver] + + # add to linked list + if self.last_ext_capability_id is not None: + self.ext_capabilities[self.last_ext_capability_id][4] = offset*4 + else: + raise Exception("TODO") + + self.last_ext_capability_id = cap_id + + 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))) + if self.upstream_tx_handler is None: + raise Exception("Transmit handler not set") + yield self.upstream_tx_handler(tlp) + + def send(self, tlp): + yield self.upstream_send(tlp) + + def upstream_recv(self, tlp): + # logging + print("[%s] Got downstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) + yield self.handle_tlp(tlp) + + def handle_tlp(self, tlp): + if ((tlp.fmt, tlp.type) == TLP_CPL or (tlp.fmt, tlp.type) == TLP_CPL_DATA or + (tlp.fmt, tlp.type) == TLP_CPL_LOCKED or (tlp.fmt, tlp.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, tlp.type) in self.rx_tlp_handler: + yield self.rx_tlp_handler[(tlp.fmt, tlp.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 handle_config_0_tlp(self, tlp): + if tlp.dest_id[1] == self.device_num and tlp.dest_id[2] == self.function_num: + # logging + print("[%s] Config type 0 for me" % (highlight(self.get_desc()))) + + # capture address information + self.bus_num = tlp.dest_id[0] + + # prepare completion TLP + cpl = TLP() + cpl.set_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + + # perform operation + if (tlp.fmt, tlp.type) == TLP_CFG_READ_0: + cpl.fmt, cpl.type = TLP_CPL_DATA + cpl.data = [self.read_config_register(tlp.register_number)] + cpl.byte_count = 4 + cpl.length = 1 + elif (tlp.fmt, tlp.type) == TLP_CFG_WRITE_0: + cpl.fmt, cpl.type = TLP_CPL + 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 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, tlp.type = TLP_IO_READ + tlp.requester_id = (self.bus_num, self.device_num, self.function_num) + tlp.tag = self.current_tag + + first_pad = addr % 4 + byte_length = min(length-n, 4-first_pad) + tlp.set_be(addr, byte_length) + + tlp.address = addr & ~3 + + self.current_tag = (self.current_tag % 31) + 1 + + yield 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: + d = struct.pack(' 0xffffffff: + tlp.fmt, tlp.type = TLP_MEM_READ_64 + else: + tlp.fmt, tlp.type = TLP_MEM_READ + tlp.requester_id = (self.bus_num, self.device_num, self.function_num) + tlp.tag = self.current_tag + + 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) + + tlp.address = addr & ~3 + + self.current_tag = (self.current_tag % 31) + 1 + + yield 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: + d = bytearray() + + for k in range(cpl.length): + d.extend(struct.pack(' 0xffffffff: + tlp.fmt, tlp.type = TLP_MEM_WRITE_64 + else: + tlp.fmt, tlp.type = TLP_MEM_WRITE + tlp.requester_id = (self.bus_num, self.device_num, self.function_num) + tlp.tag = self.current_tag + + 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]) + + tlp.address = addr & ~3 + + self.current_tag = (self.current_tag % 31) + 1 + + yield self.send(tlp) + + n += byte_length + addr += byte_length + + +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()))) + + # prepare completion TLP + cpl = TLP() + cpl.set_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + cpl.fmt, cpl.type = TLP_CPL_DATA + + region = m[0][0] + addr = m[0][1] + offset = 0 + start_offset = None + mask = tlp.first_be + + # perform operation + 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 self.send(cpl) + + else: + # logging + print("IO request did not match any BARs") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield 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()))) + + # prepare completion TLP + cpl = TLP() + cpl.set_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + + region = m[0][0] + addr = m[0][1] + offset = 0 + start_offset = None + mask = tlp.first_be + + # perform operation + 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]) + + # logging + print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield self.send(cpl) + + else: + # logging + print("IO request did not match any BARs") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield 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) + n = 0 + addr = tlp.address + length = tlp.length*4 + + while n < length: + cpl = TLP() + cpl.set_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + + byte_length = length-n + cpl.byte_count = byte_length + if byte_length > 128 << self.max_payload_size: + byte_length = 128 << self.max_payload_size # max payload size + byte_length -= (addr + byte_length) % self.read_completion_boundary # RCB align + byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align + + cpl.lower_address = addr & 0x7f + + cpl.set_data(data[n:n+byte_length]) + + # logging + print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield self.send(cpl) + + n += byte_length + addr += byte_length + + else: + # logging + print("Memory request did not match any BARs") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, self.function_num)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield 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 = BusPort(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): + if self.upstream_tx_handler is None: + raise Exception("Transmit handler not set") + yield 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))) + if (tlp.fmt, tlp.type) == TLP_CFG_READ_0 or (tlp.fmt, tlp.type) == TLP_CFG_WRITE_0: + yield self.handle_tlp(tlp) + elif (tlp.fmt, tlp.type) == TLP_CFG_READ_1 or (tlp.fmt, tlp.type) == TLP_CFG_WRITE_1: + # config type 1 + if self.sec_bus_num <= tlp.dest_id[0] <= self.sub_bus_num: + if tlp.dest_id[0] == self.sec_bus_num: + # targeted to directly connected device; change to type 0 + if (tlp.fmt, tlp.type) == TLP_CFG_READ_1: + (tlp.fmt, tlp.type) = TLP_CFG_READ_0 + elif (tlp.fmt, tlp.type) == TLP_CFG_WRITE_1: + (tlp.fmt, tlp.type) = TLP_CFG_WRITE_0 + yield self.downstream_send(tlp) + else: + # error + pass + elif ((tlp.fmt, tlp.type) == TLP_CPL or (tlp.fmt, tlp.type) == TLP_CPL_DATA or + (tlp.fmt, tlp.type) == TLP_CPL_LOCKED or (tlp.fmt, tlp.type) == TLP_CPL_LOCKED_DATA): + # Completions + if not self.root and tlp.requester_id == (self.bus_num, self.device_num, self.function_num): + # for me + yield self.handle_tlp(tlp) + elif self.sec_bus_num <= tlp.requester_id[0] <= self.sub_bus_num: + yield self.downstream_send(tlp) + else: + # error + pass + elif (tlp.fmt, tlp.type) == TLP_MSG_ID or (tlp.fmt, tlp.type) == TLP_MSG_DATA_ID: + # ID routed message + if not self.root and tlp.dest_id == (self.bus_num, self.device_num, self.function_num): + # for me + yield self.handle_tlp(tlp) + elif self.sec_bus_num <= tlp.dest_id[0] <= self.sub_bus_num: + yield self.downstream_send(tlp) + else: + # error + pass + elif ((tlp.fmt, tlp.type) == TLP_IO_READ or (tlp.fmt, tlp.type) == TLP_IO_WRITE): + # IO read/write + if self.match_bar(tlp.address): + # for me + yield self.handle_tlp(tlp) + elif self.io_base <= tlp.address <= self.io_limit: + yield self.downstream_send(tlp) + else: + # error + pass + elif ((tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.type) == TLP_MEM_READ_64 or + (tlp.fmt, tlp.type) == TLP_MEM_WRITE or (tlp.fmt, tlp.type) == TLP_MEM_WRITE_64): + # Memory read/write + if self.match_bar(tlp.address): + # for me + yield 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 self.downstream_send(tlp) + else: + # error + pass + elif (tlp.fmt, tlp.type) == TLP_MSG_TO_RC or (tlp.fmt, tlp.type) == TLP_MSG_DATA_TO_RC: + # Message to root complex + # error + pass + elif (tlp.fmt, tlp.type) == TLP_MSG_BCAST or (tlp.fmt, tlp.type) == TLP_MSG_DATA_BCAST: + # Message broadcast from root complex + yield self.upstream_send(tlp) + elif (tlp.fmt, tlp.type) == TLP_MSG_LOCAL or (tlp.fmt, tlp.type) == TLP_MSG_DATA_LOCAL: + # Message local to receiver + # error + pass + elif (tlp.fmt, tlp.type) == TLP_MSG_GATHER or (tlp.fmt, tlp.type) == TLP_MSG_DATA_GATHER: + # Message gather to root complex + # error + pass + else: + # logging + raise Exception("Unknown/invalid packet type") + + def downstream_send(self, tlp): + if self.downstream_tx_handler is None: + raise Exception("Transmit handler not set") + yield 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))) + if ((tlp.fmt, tlp.type) == TLP_CFG_READ_0 or (tlp.fmt, tlp.type) == TLP_CFG_WRITE_0 or + (tlp.fmt, tlp.type) == TLP_CFG_READ_1 or (tlp.fmt, tlp.type) == TLP_CFG_WRITE_1): + # error + pass + elif ((tlp.fmt, tlp.type) == TLP_CPL or (tlp.fmt, tlp.type) == TLP_CPL_DATA or + (tlp.fmt, tlp.type) == TLP_CPL_LOCKED or (tlp.fmt, tlp.type) == TLP_CPL_LOCKED_DATA): + # Completions + if not self.root and tlp.requester_id == (self.bus_num, self.device_num, self.function_num): + # for me + yield self.handle_tlp(tlp) + elif self.sec_bus_num <= tlp.requester_id[0] <= self.sub_bus_num: + if self.root and tlp.requester_id[0] == self.pri_bus_num and tlp.requester_id[1] == 0: + yield self.upstream_send(tlp) + else: + yield self.downstream_send(tlp) + else: + yield self.upstream_send(tlp) + elif (tlp.fmt, tlp.type) == TLP_MSG_ID or (tlp.fmt, tlp.type) == TLP_MSG_DATA_ID: + # ID routed messages + if not self.root and tlp.dest_id == (self.bus_num, self.device_num, self.function_num): + # for me + yield self.handle_tlp(tlp) + elif self.sec_bus_num <= tlp.dest_id[0] <= self.sub_bus_num: + if self.root and tlp.dest_id[0] == self.pri_bus_num and tlp.dest_id[1] == 0: + yield self.upstream_send(tlp) + else: + yield self.downstream_send(tlp) + else: + yield self.upstream_send(tlp) + elif ((tlp.fmt, tlp.type) == TLP_IO_READ or (tlp.fmt, tlp.type) == TLP_IO_WRITE): + # IO read/write + if self.match_bar(tlp.address): + # for me + yield self.handle_tlp(tlp) + elif self.io_base <= tlp.address <= self.io_limit: + yield self.downstream_send(tlp) + else: + yield self.upstream_send(tlp) + elif ((tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.type) == TLP_MEM_READ_64 or + (tlp.fmt, tlp.type) == TLP_MEM_WRITE or (tlp.fmt, tlp.type) == TLP_MEM_WRITE_64): + # Memory read/write + if self.match_bar(tlp.address): + # for me + yield 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 self.downstream_send(tlp) + else: + yield self.upstream_send(tlp) + elif (tlp.fmt, tlp.type) == TLP_MSG_TO_RC or (tlp.fmt, tlp.type) == TLP_MSG_DATA_TO_RC: + # Message to root complex + yield self.upstream_send(tlp) + elif (tlp.fmt, tlp.type) == TLP_MSG_BCAST or (tlp.fmt, tlp.type) == TLP_MSG_DATA_BCAST: + # Message broadcast from root complex + # error + pass + elif (tlp.fmt, tlp.type) == TLP_MSG_LOCAL or (tlp.fmt, tlp.type) == TLP_MSG_DATA_LOCAL: + # Message local to receiver + # error + pass + elif (tlp.fmt, tlp.type) == TLP_MSG_GATHER or (tlp.fmt, tlp.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 self.downstream_recv(tlp) + + +class HostBridge(Bridge): + 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(Bridge): + 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 SwitchBridge(Bridge): + def __init__(self, *args, **kwargs): + super(SwitchBridge, self).__init__(*args, **kwargs) + + self.pcie_device_type = 0x6 + + self.desc = "SwitchBridge" + + self.vendor_id = 0x1234 + self.device_id = 0x0003 + + 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))) + if (tlp.fmt, tlp.type) == TLP_CFG_READ_0 or (tlp.fmt, tlp.type) == TLP_CFG_WRITE_0: + # config type 0 + + if tlp.dest_id[1] == self.device_num: + # capture address information + self.bus_num = tlp.dest_id[0] + + 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[2]: + yield 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 self.upstream_send(cpl) + elif ((tlp.fmt, tlp.type) == TLP_CPL or (tlp.fmt, tlp.type) == TLP_CPL_DATA or + (tlp.fmt, tlp.type) == TLP_CPL_LOCKED or (tlp.fmt, tlp.type) == TLP_CPL_LOCKED_DATA): + # Completion + + if tlp.requester_id[0] == self.bus_num and tlp.requester_id[1] == self.device_num: + for f in self.functions: + if f.function_num == tlp.requester_id[2]: + yield f.upstream_recv(tlp) + return + + print("Function not found") + else: + print("Bus/device number mismatch") + elif ((tlp.fmt, tlp.type) == TLP_IO_READ or (tlp.fmt, tlp.type) == TLP_IO_WRITE): + # IO read/write + + for f in self.functions: + if f.match_bar(tlp.address, True): + yield 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 self.upstream_send(cpl) + elif ((tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.type) == TLP_MEM_READ_64 or + (tlp.fmt, tlp.type) == TLP_MEM_WRITE or (tlp.fmt, tlp.type) == TLP_MEM_WRITE_64): + # Memory read/write + + for f in self.functions: + if f.match_bar(tlp.address): + yield f.upstream_recv(tlp) + return + + print("Memory request did not match any BARs") + + if (tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.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 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))) + yield self.upstream_port.send(tlp) + + def send(self, tlp): + yield 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 = SwitchBridge() + self.upstream_bridge.pcie_device_type = 0x5 + + 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 = SwitchBridge() + 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.children = [] + + def find_dev(self, b, d, f): + if (b, d, f) == (self.bus_num, self.device_num, self.function_num): + return self + for c in self.children: + res = c.find_dev(b, d, f) + if res is not None: + return res + 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 (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.min_dev = 1 + + self.current_tag = 0 + + self.downstream_tag_recv_queues = {} + + self.rx_cpl_queues = {} + self.rx_cpl_queues = [[]]*256 + self.rx_cpl_sync = [Signal(False)]*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.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.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: + return 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: + 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: + return region[2](offset, length) + + 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: + region[3](offset, data) + + def make_port(self): + port = RootPort() + 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 downstream_send(self, tlp): + # logging + print("[%s] Sending TLP: %s" % (highlight(self.get_desc()), repr(tlp))) + yield self.upstream_bridge.upstream_recv(tlp) + + def send(self, tlp): + yield self.downstream_send(tlp) + + def downstream_recv(self, tlp): + # logging + print("[%s] Got TLP: %s" % (highlight(self.get_desc()), repr(tlp))) + yield self.handle_tlp(tlp) + + def handle_tlp(self, tlp): + if ((tlp.fmt, tlp.type) == TLP_CPL or (tlp.fmt, tlp.type) == TLP_CPL_DATA or + (tlp.fmt, tlp.type) == TLP_CPL_LOCKED or (tlp.fmt, tlp.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, tlp.type) in self.rx_tlp_handler: + yield self.rx_tlp_handler[(tlp.fmt, tlp.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 handle_io_read_tlp(self, tlp): + if self.find_io_region(tlp.address): + # logging + print("[%s] IO read" % (highlight(self.get_desc()))) + + # prepare completion TLP + cpl = TLP() + cpl.set_completion(tlp, (0, 0, 0)) + cpl.fmt, cpl.type = TLP_CPL_DATA + + addr = tlp.address + offset = 0 + start_offset = None + mask = tlp.first_be + + # perform operation + 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_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] = 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 self.send(cpl) + + else: + # logging + print("IO request did not match any regions") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (0, 0, 0)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield 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()))) + + # prepare completion TLP + cpl = TLP() + cpl.set_completion(tlp, (0, 0, 0)) + + addr = tlp.address + offset = 0 + start_offset = None + mask = tlp.first_be + + # perform operation + 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_io_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_io_region(addr+start_offset, data[start_offset:offset]) + + # logging + print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield self.send(cpl) + + else: + # logging + print("IO request did not match any regions") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (0, 0, 0)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield 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 + 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 read + data = self.read_region(addr, tlp.length*4) + + # prepare completion TLP(s) + n = 0 + addr = tlp.address + length = tlp.length*4 + + while n < length: + cpl = TLP() + cpl.set_completion(tlp, (0, 0, 0)) + + byte_length = length-n + cpl.byte_count = byte_length + if byte_length > 128 << self.max_payload_size: + byte_length = 128 << self.max_payload_size # max payload size + byte_length -= (addr + byte_length) % self.read_completion_boundary # RCB align + byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align + + cpl.lower_address = addr & 0x7f + + cpl.set_data(data[n:n+byte_length]) + + # logging + print("[%s] Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield self.send(cpl) + + n += byte_length + addr += byte_length + + else: + # logging + print("Memory request did not match any regions") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (0, 0, 0)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield 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: + 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: + 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: + 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, dest_id, addr, length, timeout=0): + n = 0 + data = b'' + + while n < length: + tlp = TLP() + tlp.fmt, tlp.type = TLP_CFG_READ_1 + tlp.requester_id = (0, 0, 0) + tlp.tag = self.current_tag + tlp.dest_id = dest_id + + first_pad = addr % 4 + byte_length = min(length-n, 4-first_pad) + tlp.set_be(addr, byte_length) + + tlp.register_number = addr >> 2 + + self.current_tag = (self.current_tag % 31) + 1 + + yield 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: + d = struct.pack('> 2 + + self.current_tag = (self.current_tag % 31) + 1 + + yield self.send(tlp) + cpl = yield from self.recv_cpl(tlp.tag, timeout) + + n += byte_length + addr += byte_length + + def capability_read(self, dest_id, cap_id, addr, length, timeout=0): + ti = self.tree.find_dev(*dest_id) + + if not ti: + raise Exception("Device not found") + + offset = None + + for c in ti.capabilities: + if c[0] == cap_id: + offset = c[1] + + if not offset: + raise Exception("Capability not found") + + return self.config_read(dest_id, addr+offset, length, timeout) + + def capability_write(self, dest_id, cap_id, addr, data, timeout=0): + ti = self.tree.find_dev(*dest_id) + + if not ti: + raise Exception("Device not found") + + offset = None + + for c in ti.capabilities: + if c[0] == cap_id: + offset = c[1] + + if not offset: + raise Exception("Capability not found") + + self.config_write(dest_id, addr+offset, data, timeout) + + def io_read(self, addr, length, timeout=0): + n = 0 + data = b'' + + if self.find_region(addr): + return self.read_io_region(addr, length) + + while n < length: + tlp = TLP() + tlp.fmt, tlp.type = TLP_IO_READ + tlp.requester_id = (0, 0, 0) + tlp.tag = self.current_tag + + first_pad = addr % 4 + byte_length = min(length-n, 4-first_pad) + tlp.set_be(addr, byte_length) + + tlp.address = addr & ~3 + + self.current_tag = (self.current_tag % 31) + 1 + + yield 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: + d = struct.pack(' 0xffffffff: + tlp.fmt, tlp.type = TLP_MEM_READ_64 + else: + tlp.fmt, tlp.type = TLP_MEM_READ + tlp.requester_id = (0, 0, 0) + tlp.tag = self.current_tag + + 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) + + tlp.address = addr & ~3 + + self.current_tag = (self.current_tag % 31) + 1 + + yield 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: + d = bytearray() + + for k in range(cpl.length): + d.extend(struct.pack(' 0xffffffff: + tlp.fmt, tlp.type = TLP_MEM_WRITE_64 + else: + tlp.fmt, tlp.type = TLP_MEM_WRITE + tlp.requester_id = (0, 0, 0) + tlp.tag = self.current_tag + + 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]) + + tlp.address = addr & ~3 + + self.current_tag = (self.current_tag % 31) + 1 + + yield self.send(tlp) + + n += byte_length + addr += byte_length + + def enumerate_segment(self, bus, timeout=1000, tree=None, enable_bus_mastering=False): + sec_bus = bus+1 + sub_bus = bus + + if tree is None: + tree = TreeItem() + + 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((bus, d, 0), 0x000, 4, timeout) + + if val is None or val == b'\xff\xff\xff\xff': + 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((bus, d, 0), 0x00e, 1, timeout) + + if val[0] & 0x80: + # multifunction device + fc = 8 + + for f in range(fc): + # read vendor ID and device ID + val = yield from self.config_read((bus, d, f), 0x000, 4, timeout) + + if val is None or val == b'\xff\xff\xff\xff': + continue + + ti = TreeItem() + 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 self.config_write((bus, d, f), 0x010+bar*4, struct.pack(' 0: + val = yield from self.config_read((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 + + if enable_bus_mastering: + # enable bus mastering + val = yield from self.config_read((bus, d, f), 0x04, 2) + val = struct.unpack('> 8) & 0xf0, (ti.io_limit >> 8) & 0xf0)) + yield self.config_write((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 self.config_write((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 self.config_write((bus, d, f), 0x024, struct.pack('> 16) & 0xfff0, (ti.prefetchable_mem_limit >> 16) & 0xfff0)) + yield self.config_write((bus, d, f), 0x028, struct.pack('> 32)) + yield self.config_write((bus, d, f), 0x02c, struct.pack('> 32)) + + sec_bus = sub_bus+1 + + tree.children.append(ti) + + 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, tree) + + def enumerate(self, timeout=1000, enable_bus_mastering=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 + + sub_bus, self.tree = yield from self.enumerate_segment(bus=0, timeout=timeout, enable_bus_mastering=enable_bus_mastering) + + 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()) + diff --git a/tb/pcie_us.py b/tb/pcie_us.py new file mode 100644 index 000000000..40df13170 --- /dev/null +++ b/tb/pcie_us.py @@ -0,0 +1,1552 @@ +""" + +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 math +import struct +from myhdl import * + +import axis_ep +from pcie import * + + +def dword_parity(d): + d ^= d >> 4 + d ^= d >> 2 + d ^= d >> 1 + p = d & 0x1 + p |= (d & 0x100) >> 7 + p |= (d & 0x10000) >> 14 + p |= (d & 0x1000000) >> 21 + return p + + +class TLP_us(TLP): + def __init__(self, tlp=None): + super(TLP_us, self).__init__(tlp) + self.bar_id = 0 + self.bar_aperture = 0 + self.completer_id_enable = False + self.requester_id_enable = False + self.discontinue = False + + if isinstance(tlp, TLP_us): + self.bar_id = tlp.bar_id + self.bar_aperture = tlp.bar_aperture + self.completer_id_enable = tlp.completer_id_enable + self.requester_id_enable = tlp.requester_id_enable + self.discontinue = tlp.discontinue + + def pack_us_cq(self, dw): + pkt = axis_ep.AXIStreamFrame([]) + + if ((self.fmt, self.type) == TLP_IO_READ or (self.fmt, self.type) == TLP_IO_WRITE or + (self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64 or + (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64): + # Completer Request descriptor + l = self.at & 0x3 + l |= self.address & 0xfffffffc + pkt.data.append(l) + l = (self.address & 0xffffffff00000000) >> 32 + pkt.data.append(l) + l = self.length & 0x7ff + if (self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64: + l |= 0 << 11 + elif (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64: + l |= 1 << 11 + elif (self.fmt, self.type) == TLP_IO_READ: + l |= 2 << 11 + elif (self.fmt, self.type) == TLP_IO_WRITE: + l |= 3 << 11 + elif (self.fmt, self.type) == TLP_FETCH_ADD or (self.fmt, self.type) == TLP_FETCH_ADD_64: + l |= 4 << 11 + elif (self.fmt, self.type) == TLP_SWAP or (self.fmt, self.type) == TLP_SWAP_64: + l |= 5 << 11 + elif (self.fmt, self.type) == TLP_CAS or (self.fmt, self.type) == TLP_CAS_64: + l |= 6 << 11 + elif (self.fmt, self.type) == TLP_MEM_READ_LOCKED or (self.fmt, self.type) == TLP_MEM_READ_LOCKED_64: + l |= 7 << 11 + l |= id2int(self.requester_id) << 16 + pkt.data.append(l) + l = (self.tag & 0xff) + l |= (self.completer_id[2] & 0xff) << 8 + l |= (self.bar_id & 0x7) << 16 + l |= (self.bar_aperture & 0x3f) << 19 + l |= (self.tc & 0x7) << 25 + l |= (self.attr & 0x7) << 28 + pkt.data.append(l) + + # payload data + pkt.data += self.data + + # compute byte enables + byte_en = [0]*4 + + if len(self.data) >= 1: + byte_en += [self.first_be] + if len(self.data) > 2: + byte_en += [0xf] * (len(self.data)-2) + if len(self.data) > 1: + byte_en += [self.last_be] + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # assemble sideband signal + pkt.user = [] + + for k in range(0, len(pkt.data), int(dw/32)): + u = 0 + + for l in range(int(dw/32)): + if k+l < len(byte_en): + u |= byte_en[k+l] << l*4+8 + u |= parity[k+l] << l*4+53 + + if k == 0: + u |= (self.first_be & 0xf) + u |= (self.last_be & 0xf) << 4 + u |= 1 << 40 # SOP + + # TODO tph + + pkt.user.append(u) + + # TODO discontinue? + + else: + raise Exception("Invalid packet type for interface") + + return pkt + + def unpack_us_cq(self, pkt, dw, check_parity=False): + req_type = (pkt.data[2] >> 11) & 0xf + + if req_type == 0: + self.fmt, self.type = TLP_MEM_READ + elif req_type == 1: + self.fmt, self.type = TLP_MEM_WRITE + elif req_type == 2: + self.fmt, self.type = TLP_IO_READ + elif req_type == 3: + self.fmt, self.type = TLP_IO_WRITE + elif req_type == 4: + self.fmt, self.type = TLP_FETCH_ADD + elif req_type == 5: + self.fmt, self.type = TLP_SWAP + elif req_type == 6: + self.fmt, self.type = TLP_CAS + elif req_type == 7: + self.fmt, self.type = TLP_MEM_READ_LOCKED + else: + raise Exception("Invalid packet type") + + self.length = pkt.data[2] & 0x7ff + self.requester_id = int2id(pkt.data[2] >> 16) + self.tag = pkt.data[3] & 0xff + self.tc = (pkt.data[3] >> 25) & 0x7 + self.attr = (pkt.data[3] >> 28) & 0x7 + + if req_type & 8 == 0: + # memory, IO, or atomic operation + self.at = pkt.data[0] & 3 + self.address = (pkt.data[1] << 32) | (pkt.data[0] & 0xfffffffc) + if self.address > 0xffffffff: + if self.fmt == FMT_3DW: + self.fmt = FMT_4DW + elif self.fmt == FMT_3DW_DATA: + self.fmt = FMT_4DW_DATA + self.completer_id = (0, 0, (pkt.data[3] >> 8) & 0xff) + self.bar_id = (pkt.data[3] >> 16) & 7 + self.bar_aperture = (pkt.data[3] >> 19) & 0x3f + + self.first_be = pkt.user[0] & 0xf + self.last_be = (pkt.user[0] >> 4) & 0xf + + self.data = pkt.data[4:] + + # compute byte enables + byte_en = [0]*4 + + if len(self.data) >= 1: + byte_en += [self.first_be] + if len(self.data) > 2: + byte_en += [0xf] * (len(self.data)-2) + if len(self.data) > 1: + byte_en += [self.last_be] + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # check sideband components + assert pkt.user[0] & (1 << 40) # SOP + + for k in range(0, len(pkt.data), int(dw/32)): + for l in range(int(dw/32)): + if k+l < len(byte_en): + assert (pkt.user[int(k/(dw/32))] >> l*4+8) & 0xf == byte_en[k+l] + if check_parity: + assert (pkt.user[int(k/(dw/32))] >> l*4+53) & 0xf == parity[k+l] + + return self + + def pack_us_cc(self, dw): + pkt = axis_ep.AXIStreamFrame([]) + + if ((self.fmt, self.type) == TLP_CPL or (self.fmt, self.type) == TLP_CPL_DATA or + (self.fmt, self.type) == TLP_CPL_LOCKED or (self.fmt, self.type) == TLP_CPL_LOCKED_DATA): + # Requester Completion descriptor + l = self.lower_address & 0x7f + l |= (self.at & 3) << 8 + l |= (self.byte_count & 0x1fff) << 16 + if (self.fmt, self.type) == TLP_CPL_LOCKED or (self.fmt, self.type) == TLP_CPL_LOCKED_DATA: + # TODO only for completions for locked read requests + l |= 1 << 29 + # TODO request completed + pkt.data.append(l) + l = self.length & 0x7ff + l |= (self.status & 0x7) << 11 + # TODO poisoned completion + l |= id2int(self.requester_id) << 16 + pkt.data.append(l) + l = (self.tag & 0xff) + l |= id2int(self.completer_id) << 8 + if self.completer_id_enable: l |= 1 << 24 + l |= (self.tc & 0x7) << 25 + l |= (self.attr & 0x7) << 28 + pkt.data.append(l) + + # payload data + pkt.data += self.data + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # assemble sideband signal + pkt.user = [] + + for k in range(0, len(pkt.data), int(dw/32)): + u = 0 + + if self.discontinue: + u |= 1 + + for l in range(int(dw/32)): + if k+l < len(parity): + u |= parity[k+l] << l*4+1 + + pkt.user.append(u) + + else: + raise Exception("Invalid packet type for interface") + + return pkt + + def unpack_us_cc(self, pkt, dw, check_parity=False): + self.fmt, self.type = TLP_CPL + + self.lower_address = pkt.data[0] & 0x7f + self.at = (pkt.data[0] >> 8) & 3 + self.byte_count = (pkt.data[0] >> 16) & 0x1fff + if pkt.data[0] & (1 << 29): + self.fmt, self.type = TLP_CPL_LOCKED + + self.length = pkt.data[1] & 0x7ff + if self.length > 0: + self.fmt = FMT_3DW_DATA + self.status = (pkt.data[1] >> 11) & 7 + self.requester_id = int2id(pkt.data[1] >> 16) + self.completer_id = int2id(pkt.data[2] >> 8) + self.completer_id_enable = pkt.data[2] >> 24 & 1 != 0 + self.tag = pkt.data[2] & 0xff + self.tc = (pkt.data[2] >> 25) & 0x7 + self.attr = (pkt.data[2] >> 28) & 0x7 + + if self.length > 0: + self.data = pkt.data[3:3+self.length] + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # check sideband components + for k in range(0, len(pkt.data), int(dw/32)): + if pkt.user[int(k/(dw/32))] & 1: + self.discontinue = True + for l in range(int(dw/32)): + if k+l < len(parity): + if check_parity: + assert (pkt.user[int(k/(dw/32))] >> l*4+1) & 0xf == parity[k+l] + + return self + + def pack_us_rq(self, dw): + pkt = axis_ep.AXIStreamFrame([]) + + if ((self.fmt, self.type) == TLP_IO_READ or (self.fmt, self.type) == TLP_IO_WRITE or + (self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64 or + (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64 or + (self.fmt, self.type) == TLP_CFG_READ_0 or (self.fmt, self.type) == TLP_CFG_READ_1 or + (self.fmt, self.type) == TLP_CFG_WRITE_0 or (self.fmt, self.type) == TLP_CFG_WRITE_1): + # Completer Request descriptor + if ((self.fmt, self.type) == TLP_IO_READ or (self.fmt, self.type) == TLP_IO_WRITE or + (self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64 or + (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64): + l = self.at & 0x3 + l |= self.address & 0xfffffffc + pkt.data.append(l) + l = (self.address & 0xffffffff00000000) >> 32 + pkt.data.append(l) + elif ((self.fmt, self.type) == TLP_CFG_READ_0 or (self.fmt, self.type) == TLP_CFG_READ_1 or + (self.fmt, self.type) == TLP_CFG_WRITE_0 or (self.fmt, self.type) == TLP_CFG_WRITE_1): + l = (self.register_number & 0x3ff) << 2 + pkt.data.append(l) + pkt.data.append(0) + l = self.length & 0x7ff + if (self.fmt, self.type) == TLP_MEM_READ or (self.fmt, self.type) == TLP_MEM_READ_64: + l |= 0 << 11 + elif (self.fmt, self.type) == TLP_MEM_WRITE or (self.fmt, self.type) == TLP_MEM_WRITE_64: + l |= 1 << 11 + elif (self.fmt, self.type) == TLP_IO_READ: + l |= 2 << 11 + elif (self.fmt, self.type) == TLP_IO_WRITE: + l |= 3 << 11 + elif (self.fmt, self.type) == TLP_FETCH_ADD or (self.fmt, self.type) == TLP_FETCH_ADD_64: + l |= 4 << 11 + elif (self.fmt, self.type) == TLP_SWAP or (self.fmt, self.type) == TLP_SWAP_64: + l |= 5 << 11 + elif (self.fmt, self.type) == TLP_CAS or (self.fmt, self.type) == TLP_CAS_64: + l |= 6 << 11 + elif (self.fmt, self.type) == TLP_MEM_READ_LOCKED or (self.fmt, self.type) == TLP_MEM_READ_LOCKED_64: + l |= 7 << 11 + elif (self.fmt, self.type) == TLP_CFG_READ_0: + l |= 8 << 11 + elif (self.fmt, self.type) == TLP_CFG_READ_1: + l |= 9 << 11 + elif (self.fmt, self.type) == TLP_CFG_WRITE_0: + l |= 10 << 11 + elif (self.fmt, self.type) == TLP_CFG_WRITE_1: + l |= 11 << 11 + # TODO poisoned + l |= id2int(self.requester_id) << 16 + pkt.data.append(l) + l = (self.tag & 0xff) + l |= id2int(self.completer_id) << 8 + if self.requester_id_enable: l |= 1 << 24 + l |= (self.tc & 0x7) << 25 + l |= (self.attr & 0x7) << 28 + # TODO force ecrc + pkt.data.append(l) + + # payload data + pkt.data += self.data + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # assemble sideband signal + pkt.user = [] + + for k in range(0, len(pkt.data), int(dw/32)): + u = 0 + + if self.discontinue: + u |= 1 << 11 + + for l in range(int(dw/32)): + if k+l < len(parity): + u |= parity[k+l] << l*4+28 + + if k == 0: + u |= (self.first_be & 0xf) + u |= (self.last_be & 0xf) << 4 + + # TODO seq_num + # TODO tph + + # TODO addr_offset + + pkt.user.append(u) + + else: + raise Exception("Invalid packet type for interface") + + return pkt + + def unpack_us_rq(self, pkt, dw, check_parity=False): + req_type = (pkt.data[2] >> 11) & 0xf + + if req_type == 0: + self.fmt, self.type = TLP_MEM_READ + elif req_type == 1: + self.fmt, self.type = TLP_MEM_WRITE + elif req_type == 2: + self.fmt, self.type = TLP_IO_READ + elif req_type == 3: + self.fmt, self.type = TLP_IO_WRITE + elif req_type == 4: + self.fmt, self.type = TLP_FETCH_ADD + elif req_type == 5: + self.fmt, self.type = TLP_SWAP + elif req_type == 6: + self.fmt, self.type = TLP_CAS + elif req_type == 7: + self.fmt, self.type = TLP_MEM_READ_LOCKED + elif req_type == 8: + self.fmt, self.type = TLP_CFG_READ_0 + elif req_type == 9: + self.fmt, self.type = TLP_CFG_READ_1 + elif req_type == 10: + self.fmt, self.type = TLP_CFG_WRITE_0 + elif req_type == 11: + self.fmt, self.type = TLP_CFG_WRITE_1 + else: + raise Exception("Invalid packet type") + + self.length = pkt.data[2] & 0x7ff + # TODO poisoned + self.requester_id = int2id(pkt.data[2] >> 16) + self.tag = pkt.data[3] & 0xff + self.tc = (pkt.data[3] >> 25) & 0x7 + self.attr = (pkt.data[3] >> 28) & 0x7 + + if req_type < 12: + if req_type < 8: + # memory, IO, or atomic operation + self.at = pkt.data[0] & 3 + self.address = (pkt.data[1] << 32) | (pkt.data[0] & 0xfffffffc) + if self.address > 0xffffffff: + if self.fmt == FMT_3DW: + self.fmt = FMT_4DW + elif self.fmt == FMT_3DW_DATA: + self.fmt = FMT_4DW_DATA + else: + self.register_number = (pkt.data[0] >> 2) & 0x3ff + self.completer_id = int2id(pkt.data[3] >> 8) + self.requester_id_enable = pkt.data[3] >> 24 & 1 != 0 + + self.first_be = pkt.user[0] & 0xf + self.last_be = (pkt.user[0] >> 4) & 0xf + + self.data = pkt.data[4:] + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # check sideband components + for k in range(0, len(pkt.data), int(dw/32)): + if pkt.user[int(k/(dw/32))] & (1 << 11): + self.discontinue = True + for l in range(int(dw/32)): + if k+l < len(parity): + if check_parity: + assert (pkt.user[int(k/(dw/32))] >> l*4+28) & 0xf == parity[k+l] + else: + raise Exception("TODO") + + return self + + def pack_us_rc(self, dw): + pkt = axis_ep.AXIStreamFrame([]) + + if ((self.fmt, self.type) == TLP_CPL or (self.fmt, self.type) == TLP_CPL_DATA or + (self.fmt, self.type) == TLP_CPL_LOCKED or (self.fmt, self.type) == TLP_CPL_LOCKED_DATA): + # Requester Completion descriptor + l = self.lower_address & 0xfff + # TODO error code + l |= (self.byte_count & 0x1fff) << 16 + if (self.fmt, self.type) == TLP_CPL_LOCKED or (self.fmt, self.type) == TLP_CPL_LOCKED_DATA: + # TODO only for completions for locked read requests + l |= 1 << 29 + # TODO request completed + pkt.data.append(l) + l = self.length & 0x7ff + l |= (self.status & 0x7) << 11 + # TODO poisoned completion + l |= id2int(self.requester_id) << 16 + pkt.data.append(l) + l |= (self.tag & 0xff) + l |= id2int(self.completer_id) << 8 + l |= (self.tc & 0x7) << 25 + l |= (self.attr & 0x7) << 28 + pkt.data.append(l) + + # payload data + pkt.data += self.data + + # compute byte enables + byte_en = [0]*3 + + if len(self.data) >= 1: + byte_en += [self.first_be] + if len(self.data) > 2: + byte_en += [0xf] * (len(self.data)-2) + if len(self.data) > 1: + byte_en += [self.last_be] + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # assemble sideband signal + pkt.user = [] + + for k in range(0, len(pkt.data), int(dw/32)): + u = 0 + + for l in range(int(dw/32)): + if k+l < len(byte_en): + u |= byte_en[k+l] << l*4 + u |= parity[k+l] << l*4+43 + + if k == 0: + u |= 1 << 32 # is_sof_0 + + # TODO is_sof_1, is_eof (straddle) + + pkt.user.append(u) + + # TODO discontinue + + else: + raise Exception("Invalid packet type for interface") + + return pkt + + def unpack_us_rc(self, pkt, dw, check_parity=False): + self.fmt, self.type = TLP_CPL + + self.lower_address = pkt.data[0] & 0xfff + # error code + self.byte_count = (pkt.data[0] >> 16) & 0x1fff + if pkt.data[0] & (1 << 29): + self.fmt, self.type = TLP_CPL_LOCKED + + self.length = pkt.data[1] & 0x7ff + if self.length > 0: + self.fmt = FMT_3DW_DATA + self.status = (pkt.data[1] >> 11) & 7 + self.requester_id = int2id(pkt.data[1] >> 16) + self.completer_id = int2id(pkt.data[2] >> 8) + self.tag = pkt.data[2] & 0xff + self.tc = (pkt.data[2] >> 25) & 0x7 + self.attr = (pkt.data[2] >> 28) & 0x7 + + if self.length > 0: + self.data = pkt.data[3:3+self.length] + + # compute byte enables + byte_en = [0]*3 + + if len(self.data) >= 1: + byte_en += [self.first_be] + if len(self.data) > 2: + byte_en += [0xf] * (len(self.data)-2) + if len(self.data) > 1: + byte_en += [self.last_be] + + # compute parity + parity = [dword_parity(d) for d in pkt.data] + + # check sideband components + assert pkt.user[0] & (1 << 32) # is_sof_0 + + for k in range(0, len(pkt.data), int(dw/32)): + for l in range(int(dw/32)): + if k+l < len(byte_en): + assert (pkt.user[int(k/(dw/32))] >> l*4) & 0xf == byte_en[k+l] + if check_parity: + assert (pkt.user[int(k/(dw/32))] >> l*4+43) & 0xf == parity[k+l] + + return self + + def __eq__(self, other): + if isinstance(other, TLP_us): + 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_us(data=%s, ' % repr(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 UltrascalePCIeFunction(Endpoint, MSI64MaskCapability, MSIXCapability): + def __init__(self): + super(UltrascalePCIeFunction, self).__init__() + + self.register_capability(PCIE_CAP_ID, offset=48) + self.register_capability(MSIX_CAP_ID, offset=44) + self.register_capability(PM_CAP_ID, offset=32) + self.register_capability(MSI_CAP_ID, offset=36) + + +class UltrascalePCIe(Device): + def __init__(self): + super(UltrascalePCIe, self).__init__() + + self.has_logic = False + + self.default_function = UltrascalePCIeFunction + + self.dw = 256 + + # configuration options + self.pcie_generation = 3 + self.pcie_link_width = 8 + self.user_clk_frequency = 250e6 + self.alignment = "dword" + self.straddle = False + self.enable_pf1 = False + self.enable_client_tag = True + self.enable_extended_tag = False + self.enable_parity = False + self.enable_rx_msg_interface = False + self.enable_sriov = False + self.enable_extended_configuration = False + + self.enable_pf0_msi = False + self.enable_pf1_msi = False + + self.cq_queue = [] + self.cq_np_queue = [] + self.cq_np_req_count = 0 + self.rc_queue = [] + self.msg_queue = [] + + self.config_space_enable = False + + self.cq_source = axis_ep.AXIStreamSource() + self.cc_sink = axis_ep.AXIStreamSink() + self.rq_sink = axis_ep.AXIStreamSink() + self.rc_source = axis_ep.AXIStreamSource() + + self.make_function() + + + def upstream_recv(self, tlp): + # logging + print("[%s] Got downstream TLP: %s" % (highlight(self.get_desc()), repr(tlp))) + if (tlp.fmt, tlp.type) == TLP_CFG_READ_0 or (tlp.fmt, tlp.type) == TLP_CFG_WRITE_0: + # config type 0 + + if not self.config_space_enable: + print("Configuraion space disabled") + + cpl = TLP() + cpl.set_crs_completion(tlp, (self.bus_num, self.device_num, 0)) + # logging + print("[%s] CRS Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield self.upstream_send(cpl) + return + elif tlp.dest_id[1] == self.device_num: + # capture address information + self.bus_num = tlp.dest_id[0] + + 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[2]: + yield 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 self.upstream_send(cpl) + elif ((tlp.fmt, tlp.type) == TLP_CPL or (tlp.fmt, tlp.type) == TLP_CPL_DATA or + (tlp.fmt, tlp.type) == TLP_CPL_LOCKED or (tlp.fmt, tlp.type) == TLP_CPL_LOCKED_DATA): + # Completion + + if tlp.requester_id[0] == self.bus_num and tlp.requester_id[1] == self.device_num: + for f in self.functions: + if f.function_num == tlp.requester_id[2]: + + tlp = TLP_us(tlp) + self.rc_queue.append(tlp) + + return + + print("Function not found") + else: + print("Bus/device number mismatch") + elif ((tlp.fmt, tlp.type) == TLP_IO_READ or (tlp.fmt, tlp.type) == TLP_IO_WRITE): + # IO read/write + + for f in self.functions: + bar = f.match_bar(tlp.address, True) + if len(bar) == 1: + + tlp = TLP_us(tlp) + tlp.bar_id = bar[0][0] + tlp.bar_aperture = int(math.log2((~self.functions[0].bar_mask[bar[0][0]]&0xffffffff)+1)) + tlp.completer_id = (self.bus_num, self.device_num, f.function_num) + self.cq_queue.append(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 self.upstream_send(cpl) + elif ((tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.type) == TLP_MEM_READ_64 or + (tlp.fmt, tlp.type) == TLP_MEM_WRITE or (tlp.fmt, tlp.type) == TLP_MEM_WRITE_64): + # Memory read/write + + for f in self.functions: + bar = f.match_bar(tlp.address) + if len(bar) == 1: + + tlp = TLP_us(tlp) + tlp.bar_id = bar[0][0] + if self.functions[0].bar[bar[0][0]] & 4: + tlp.bar_aperture = int(math.log2((~(self.functions[0].bar_mask[bar[0][0]] | (self.functions[0].bar_mask[bar[0][0]+1]<<32))&0xffffffffffffffff)+1)) + else: + tlp.bar_aperture = int(math.log2((~self.functions[0].bar_mask[bar[0][0]]&0xffffffff)+1)) + tlp.completer_id = (self.bus_num, self.device_num, f.function_num) + self.cq_queue.append(tlp) + + return + + print("Memory request did not match any BARs") + + if (tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.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 self.upstream_send(cpl) + else: + raise Exception("TODO") + + def create_logic(self, + # Completer reQuest Interface + m_axis_cq_tdata=None, + m_axis_cq_tuser=None, + m_axis_cq_tlast=None, + m_axis_cq_tkeep=None, + m_axis_cq_tvalid=None, + m_axis_cq_tready=None, + pcie_cq_np_req=Signal(bool(1)), + pcie_cq_np_req_count=Signal(intbv(0)[6:]), + + # Completer Completion Interface + s_axis_cc_tdata=None, + s_axis_cc_tuser=None, + s_axis_cc_tlast=None, + s_axis_cc_tkeep=None, + s_axis_cc_tvalid=None, + s_axis_cc_tready=None, + + # Requester reQuest Interface + s_axis_rq_tdata=None, + s_axis_rq_tuser=None, + s_axis_rq_tlast=None, + s_axis_rq_tkeep=None, + s_axis_rq_tvalid=None, + s_axis_rq_tready=None, + pcie_rq_seq_num=Signal(intbv(0)[4:]), + pcie_rq_seq_num_vld=Signal(bool(0)), + pcie_rq_tag=Signal(intbv(0)[6:]), + pcie_rq_tag_av=Signal(intbv(0)[2:]), + pcie_rq_tag_vld=Signal(bool(0)), + + # Requester Completion Interface + m_axis_rc_tdata=None, + m_axis_rc_tuser=None, + m_axis_rc_tlast=None, + m_axis_rc_tkeep=None, + m_axis_rc_tvalid=None, + m_axis_rc_tready=None, + + # Transmit Flow Control Interface + pcie_tfc_nph_av=Signal(intbv(0)[2:]), + pcie_tfc_npd_av=Signal(intbv(0)[2:]), + + # Configuration Management Interface + cfg_mgmt_addr=Signal(intbv(0)[19:]), + cfg_mgmt_write=Signal(bool(0)), + cfg_mgmt_write_data=Signal(intbv(0)[32:]), + cfg_mgmt_byte_enable=Signal(intbv(0)[4:]), + cfg_mgmt_read=Signal(bool(0)), + cfg_mgmt_read_data=Signal(intbv(0)[32:]), + cfg_mgmt_read_write_done=Signal(bool(0)), + cfg_mgmt_type1_cfg_reg_access=Signal(bool(0)), + + # Configuration Status Interface + cfg_phy_link_down=Signal(bool(0)), + cfg_phy_link_status=Signal(intbv(0)[2:]), + cfg_negotiated_width=Signal(intbv(0)[4:]), + cfg_current_speed=Signal(intbv(0)[3:]), + cfg_max_payload=Signal(intbv(0)[3:]), + cfg_max_read_req=Signal(intbv(0)[3:]), + cfg_function_status=Signal(intbv(0)[8:]), + cfg_vf_status=Signal(intbv(0)[12:]), + cfg_function_power_state=Signal(intbv(0)[6:]), + cfg_vf_power_state=Signal(intbv(0)[18:]), + cfg_link_power_state=Signal(intbv(0)[2:]), + cfg_err_cor_out=Signal(bool(0)), + cfg_err_nonfatal_out=Signal(bool(0)), + cfg_err_fatal_out=Signal(bool(0)), + cfg_ltr_enable=Signal(bool(0)), + cfg_ltssm_state=Signal(intbv(0)[6:]), + cfg_rcb_status=Signal(intbv(0)[2:]), + cfg_dpa_substate_change=Signal(intbv(0)[2:]), + cfg_obff_enable=Signal(intbv(0)[2:]), + cfg_pl_status_change=Signal(bool(0)), + cfg_tph_requester_enable=Signal(intbv(0)[2:]), + cfg_tph_st_mode=Signal(intbv(0)[6:]), + cfg_vf_tph_requester_enable=Signal(intbv(0)[6:]), + cfg_vf_tph_st_mode=Signal(intbv(0)[18:]), + + # Configuration Received Message Interface + cfg_msg_received=Signal(bool(0)), + cfg_msg_received_data=Signal(intbv(0)[8:]), + cfg_msg_received_type=Signal(intbv(0)[5:]), + + # Configuration Transmit Message Interface + cfg_msg_transmit=Signal(bool(0)), + cfg_msg_transmit_type=Signal(intbv(0)[3:]), + cfg_msg_transmit_data=Signal(intbv(0)[32:]), + cfg_msg_transmit_done=Signal(bool(0)), + + # Configuration Flow Control Interface + cfg_fc_ph=Signal(intbv(0)[8:]), + cfg_fc_pd=Signal(intbv(0)[12:]), + cfg_fc_nph=Signal(intbv(0)[8:]), + cfg_fc_npd=Signal(intbv(0)[12:]), + cfg_fc_cplh=Signal(intbv(0)[8:]), + cfg_fc_cpld=Signal(intbv(0)[12:]), + cfg_fc_sel=Signal(intbv(0)[3:]), + + # Per-Function Status Interface + cfg_per_func_status_control=Signal(intbv(0)[3:]), + cfg_per_func_status_data=Signal(intbv(0)[16:]), + + # Configuration Control Interface + cfg_hot_reset_in=Signal(bool(0)), + cfg_hot_reset_out=Signal(bool(0)), + cfg_config_space_enable=Signal(bool(1)), + cfg_per_function_update_done=Signal(bool(0)), + cfg_per_function_number=Signal(intbv(0)[3:]), + cfg_per_function_output_request=Signal(bool(0)), + cfg_dsn=Signal(intbv(0)[64:]), + cfg_ds_bus_number=Signal(intbv(0)[8:]), + cfg_ds_device_number=Signal(intbv(0)[5:]), + cfg_ds_function_number=Signal(intbv(0)[3:]), + cfg_power_state_change_ack=Signal(bool(0)), + cfg_power_state_change_interrupt=Signal(bool(0)), + cfg_err_cor_in=Signal(bool(0)), + cfg_err_uncor_in=Signal(bool(0)), + cfg_flr_done=Signal(intbv(0)[2:]), + cfg_vf_flr_done=Signal(intbv(0)[6:]), + cfg_flr_in_process=Signal(intbv(0)[2:]), + cfg_vf_flr_in_process=Signal(intbv(0)[6:]), + cfg_req_pm_transition_l23_ready=Signal(bool(0)), + cfg_link_training_enable=Signal(bool(1)), + + # Configuration Interrupt Controller Interface + cfg_interrupt_int=Signal(intbv(0)[4:]), + cfg_interrupt_sent=Signal(bool(0)), + cfg_interrupt_pending=Signal(intbv(0)[2:]), + cfg_interrupt_msi_enable=Signal(intbv(0)[2:]), + cfg_interrupt_msi_vf_enable=Signal(intbv(0)[6:]), + cfg_interrupt_msi_int=Signal(intbv(0)[32:]), + cfg_interrupt_msi_sent=Signal(bool(0)), + cfg_interrupt_msi_fail=Signal(bool(0)), + cfg_interrupt_msi_mmenable=Signal(intbv(0)[6:]), + cfg_interrupt_msi_pending_status=Signal(intbv(0)[64:]), + cfg_interrupt_msi_mask_update=Signal(bool(0)), + cfg_interrupt_msi_select=Signal(intbv(0)[4:]), + cfg_interrupt_msi_data=Signal(intbv(0)[32:]), + cfg_interrupt_msix_enable=Signal(intbv(0)[2:]), + cfg_interrupt_msix_mask=Signal(intbv(0)[2:]), + cfg_interrupt_msix_vf_enable=Signal(intbv(0)[6:]), + cfg_interrupt_msix_vf_mask=Signal(intbv(0)[6:]), + cfg_interrupt_msix_address=Signal(intbv(0)[64:]), + cfg_interrupt_msix_data=Signal(intbv(0)[32:]), + cfg_interrupt_msix_int=Signal(bool(0)), + cfg_interrupt_msix_sent=Signal(bool(0)), + cfg_interrupt_msix_fail=Signal(bool(0)), + cfg_interrupt_msi_attr=Signal(intbv(0)[3:]), + cfg_interrupt_msi_tph_present=Signal(bool(0)), + cfg_interrupt_msi_tph_type=Signal(intbv(0)[2:]), + cfg_interrupt_msi_tph_st_tag=Signal(intbv(0)[9:]), + cfg_interrupt_msi_function_number=Signal(intbv(0)[3:]), + + # Configuration Extend Interface + cfg_ext_read_received=Signal(bool(0)), + cfg_ext_write_received=Signal(bool(0)), + cfg_ext_register_number=Signal(intbv(0)[10:]), + cfg_ext_function_number=Signal(intbv(0)[8:]), + cfg_ext_write_data=Signal(intbv(0)[32:]), + cfg_ext_write_byte_enable=Signal(intbv(0)[4:]), + cfg_ext_read_data=Signal(intbv(0)[32:]), + cfg_ext_read_data_valid=Signal(bool(0)), + + # Clock and Reset Interface + user_clk=Signal(bool(0)), + user_reset=Signal(bool(0)), + user_lnk_up=Signal(bool(0)), + sys_clk=None, + sys_clk_gt=None, + sys_reset=None, + pcie_perstn0_out=Signal(bool(0)), + pcie_perstn1_in=Signal(bool(0)), + pcie_perstn1_out=Signal(bool(0)) + ): + + # validate parameters and widths + self.dw = len(m_axis_cq_tdata) + + assert self.dw in [64, 128, 256] + + if self.user_clk_frequency < 1e6: + self.user_clk_frequency *= 1e6 + + assert self.pcie_generation in [1, 2, 3] + assert self.pcie_link_width in [1, 2, 4, 8] + assert self.user_clk_frequency in [62.5e6, 125e6, 250e6] + assert self.alignment in ["address", "dword"] + + self.upstream_port.max_speed = self.pcie_generation + self.upstream_port.max_width = self.pcie_link_width + + if self.dw != 256 or self.alignment != "dword": + # straddle only supported with 256-bit, DWORD-aligned interface + assert not self.straddle + + # TODO change this when support added + assert self.alignment == 'dword' + assert not self.straddle + + if self.pcie_generation == 1: + if self.pcie_link_width in [1, 2]: + assert self.dw == 64 + assert self.user_clk_frequency in [62.5e6, 125e6, 250e6] + elif self.pcie_link_width == 4: + assert self.dw == 64 + assert self.user_clk_frequency in [125e6, 250e6] + elif self.pcie_link_width == 8: + assert self.dw in [64, 128] + if self.dw == 64: + assert self.user_clk_frequency == 250e6 + elif self.dw == 128: + assert self.user_clk_frequency == 125e6 + elif self.pcie_generation == 2: + if self.pcie_link_width == 1: + assert self.dw == 64 + assert self.user_clk_frequency in [62.5e6, 125e6, 250e6] + elif self.pcie_link_width == 2: + assert self.dw == 64 + assert self.user_clk_frequency in [125e6, 250e6] + elif self.pcie_link_width == 4: + assert self.dw in [64, 128] + if self.dw == 64: + assert self.user_clk_frequency == 250e6 + elif self.dw == 128: + assert self.user_clk_frequency == 125e6 + elif self.pcie_link_width == 8: + assert self.dw in [128, 256] + if self.dw == 128: + assert self.user_clk_frequency == 250e6 + elif self.dw == 256: + assert self.user_clk_frequency == 125e6 + elif self.pcie_generation == 3: + if self.pcie_link_width == 1: + assert self.dw == 64 + assert self.user_clk_frequency in [125e6, 250e6] + elif self.pcie_link_width == 2: + assert self.dw in [64, 128] + if self.dw == 64: + assert self.user_clk_frequency == 250e6 + elif self.dw == 128: + assert self.user_clk_frequency == 125e6 + elif self.pcie_link_width == 4: + assert self.dw in [128, 256] + if self.dw == 128: + assert self.user_clk_frequency == 250e6 + elif self.dw == 256: + assert self.user_clk_frequency == 125e6 + elif self.pcie_link_width == 8: + assert self.dw == 256 + assert self.user_clk_frequency == 250e6 + + # Completer reQuest Interface + assert len(m_axis_cq_tdata) == self.dw + assert len(m_axis_cq_tuser) == 85 + assert len(m_axis_cq_tlast) == 1 + assert len(m_axis_cq_tkeep) == self.dw/32 + assert len(m_axis_cq_tvalid) == 1 + assert len(m_axis_cq_tready) == 1 + assert len(pcie_cq_np_req) == 1 + assert len(pcie_cq_np_req_count) == 6 + + # Completer Completion Interface + assert len(s_axis_cc_tdata) == self.dw + assert len(s_axis_cc_tuser) == 33 + assert len(s_axis_cc_tlast) == 1 + assert len(s_axis_cc_tkeep) == self.dw/32 + assert len(s_axis_cc_tvalid) == 1 + assert len(s_axis_cc_tready) == 1 + + # Requester reQuest Interface + assert len(s_axis_rq_tdata) == self.dw + assert len(s_axis_rq_tuser) == 60 + assert len(s_axis_rq_tlast) == 1 + assert len(s_axis_rq_tkeep) == self.dw/32 + assert len(s_axis_rq_tvalid) == 1 + assert len(s_axis_rq_tready) == 1 + assert len(pcie_rq_seq_num) == 4 + assert len(pcie_rq_seq_num_vld) == 1 + assert len(pcie_rq_tag) >= 6 + assert len(pcie_rq_tag_av) == 2 + assert len(pcie_rq_tag_vld) == 1 + + # Requester Completion Interface + assert len(m_axis_rc_tdata) == self.dw + assert len(m_axis_rc_tuser) == 75 + assert len(m_axis_rc_tlast) == 1 + assert len(m_axis_rc_tkeep) == self.dw/32 + assert len(m_axis_rc_tvalid) == 1 + assert len(m_axis_rc_tready) == 1 + + # Transmit Flow Control Interface + assert len(pcie_tfc_nph_av) == 2 + assert len(pcie_tfc_npd_av) == 2 + + # Configuration Management Interface + assert len(cfg_mgmt_addr) == 19 + assert len(cfg_mgmt_write) == 1 + assert len(cfg_mgmt_write_data) == 32 + assert len(cfg_mgmt_byte_enable) == 4 + assert len(cfg_mgmt_read) == 1 + assert len(cfg_mgmt_read_data) == 32 + assert len(cfg_mgmt_read_write_done) == 1 + assert len(cfg_mgmt_type1_cfg_reg_access) == 1 + + # Configuration Status Interface + assert len(cfg_phy_link_down) == 1 + assert len(cfg_phy_link_status) == 2 + assert len(cfg_negotiated_width) == 4 + assert len(cfg_current_speed) == 3 + assert len(cfg_max_payload) == 3 + assert len(cfg_max_read_req) == 3 + assert len(cfg_function_status) == 8 + assert len(cfg_vf_status) == 12 + assert len(cfg_function_power_state) == 6 + assert len(cfg_vf_power_state) == 18 + assert len(cfg_link_power_state) == 2 + assert len(cfg_err_cor_out) == 1 + assert len(cfg_err_nonfatal_out) == 1 + assert len(cfg_err_fatal_out) == 1 + assert len(cfg_ltr_enable) == 1 + assert len(cfg_ltssm_state) == 6 + assert len(cfg_rcb_status) == 2 + assert len(cfg_dpa_substate_change) == 2 + assert len(cfg_obff_enable) == 2 + assert len(cfg_pl_status_change) == 1 + assert len(cfg_tph_requester_enable) == 2 + assert len(cfg_tph_st_mode) == 6 + assert len(cfg_vf_tph_requester_enable) == 6 + assert len(cfg_vf_tph_st_mode) == 18 + + # Configuration Received Message Interface + assert len(cfg_msg_received) == 1 + assert len(cfg_msg_received_data) == 8 + assert len(cfg_msg_received_type) == 5 + + # Configuration Transmit Message Interface + assert len(cfg_msg_transmit) == 1 + assert len(cfg_msg_transmit_type) == 3 + assert len(cfg_msg_transmit_data) == 32 + assert len(cfg_msg_transmit_done) == 1 + + # Configuration Flow Control Interface + assert len(cfg_fc_ph) == 8 + assert len(cfg_fc_pd) == 12 + assert len(cfg_fc_nph) == 8 + assert len(cfg_fc_npd) == 12 + assert len(cfg_fc_cplh) == 8 + assert len(cfg_fc_cpld) == 12 + assert len(cfg_fc_sel) == 3 + + # Per-Function Status Interface + assert len(cfg_per_func_status_control) == 3 + assert len(cfg_per_func_status_data) == 16 + + # Configuration Control Interface + assert len(cfg_hot_reset_in) == 1 + assert len(cfg_hot_reset_out) == 1 + assert len(cfg_config_space_enable) == 1 + assert len(cfg_per_function_update_done) == 1 + assert len(cfg_per_function_number) == 3 + assert len(cfg_per_function_output_request) == 1 + assert len(cfg_dsn) == 64 + assert len(cfg_ds_bus_number) == 8 + assert len(cfg_ds_device_number) == 5 + assert len(cfg_ds_function_number) == 3 + assert len(cfg_power_state_change_ack) == 1 + assert len(cfg_power_state_change_interrupt) == 1 + assert len(cfg_err_cor_in) == 1 + assert len(cfg_err_uncor_in) == 1 + assert len(cfg_flr_done) == 2 + assert len(cfg_vf_flr_done) == 6 + assert len(cfg_flr_in_process) == 2 + assert len(cfg_vf_flr_in_process) == 6 + assert len(cfg_req_pm_transition_l23_ready) == 1 + assert len(cfg_link_training_enable) == 1 + + # Configuration Interrupt Controller Interface + assert len(cfg_interrupt_int) == 4 + assert len(cfg_interrupt_sent) == 1 + assert len(cfg_interrupt_pending) == 2 + assert len(cfg_interrupt_msi_enable) == 2 + assert len(cfg_interrupt_msi_vf_enable) == 6 + assert len(cfg_interrupt_msi_int) == 32 + assert len(cfg_interrupt_msi_sent) == 1 + assert len(cfg_interrupt_msi_fail) == 1 + assert len(cfg_interrupt_msi_mmenable) == 6 + assert len(cfg_interrupt_msi_pending_status) == 64 + assert len(cfg_interrupt_msi_mask_update) == 1 + assert len(cfg_interrupt_msi_select) == 4 + assert len(cfg_interrupt_msi_data) == 32 + assert len(cfg_interrupt_msix_enable) == 2 + assert len(cfg_interrupt_msix_mask) == 2 + assert len(cfg_interrupt_msix_vf_enable) == 6 + assert len(cfg_interrupt_msix_vf_mask) == 6 + assert len(cfg_interrupt_msix_address) == 64 + assert len(cfg_interrupt_msix_data) == 32 + assert len(cfg_interrupt_msix_int) == 1 + assert len(cfg_interrupt_msix_sent) == 1 + assert len(cfg_interrupt_msix_fail) == 1 + assert len(cfg_interrupt_msi_attr) == 3 + assert len(cfg_interrupt_msi_tph_present) == 1 + assert len(cfg_interrupt_msi_tph_type) == 2 + assert len(cfg_interrupt_msi_tph_st_tag) == 9 + assert len(cfg_interrupt_msi_function_number) == 3 + + # Configuration Extend Interface + assert len(cfg_ext_read_received) == 1 + assert len(cfg_ext_write_received) == 1 + assert len(cfg_ext_register_number) == 10 + assert len(cfg_ext_function_number) == 8 + assert len(cfg_ext_write_data) == 32 + assert len(cfg_ext_write_byte_enable) == 4 + assert len(cfg_ext_read_data) == 32 + assert len(cfg_ext_read_data_valid) == 1 + + # Clock and Reset Interface + assert len(user_clk) == 1 + assert len(user_reset) == 1 + assert len(user_lnk_up) == 1 + assert len(sys_clk) == 1 + assert len(sys_clk_gt) == 1 + assert len(sys_reset) == 1 + assert len(pcie_perstn0_out) == 1 + assert len(pcie_perstn1_in) == 1 + assert len(pcie_perstn1_out) == 1 + + assert not self.has_logic + + self.has_logic = True + + # sources and sinks + cq_source_logic = self.cq_source.create_logic( + user_clk, + user_reset, + tdata=m_axis_cq_tdata, + tuser=m_axis_cq_tuser, + tlast=m_axis_cq_tlast, + tkeep=m_axis_cq_tkeep, + tvalid=m_axis_cq_tvalid, + tready=m_axis_cq_tready, + name='cq_source' + ) + + cc_sink_logic = self.cc_sink.create_logic( + user_clk, + user_reset, + tdata=s_axis_cc_tdata, + tuser=s_axis_cc_tuser, + tlast=s_axis_cc_tlast, + tkeep=s_axis_cc_tkeep, + tvalid=s_axis_cc_tvalid, + tready=s_axis_cc_tready, + name='cc_sink' + ) + + rq_sink_logic = self.rq_sink.create_logic( + user_clk, + user_reset, + tdata=s_axis_rq_tdata, + tuser=s_axis_rq_tuser, + tlast=s_axis_rq_tlast, + tkeep=s_axis_rq_tkeep, + tvalid=s_axis_rq_tvalid, + tready=s_axis_rq_tready, + name='rq_sink' + ) + + rc_source_logic = self.rc_source.create_logic( + user_clk, + user_reset, + tdata=m_axis_rc_tdata, + tuser=m_axis_rc_tuser, + tlast=m_axis_rc_tlast, + tkeep=m_axis_rc_tkeep, + tvalid=m_axis_rc_tvalid, + tready=m_axis_rc_tready, + name='rc_source' + ) + + if self.user_clk_frequency == 62.5e6: + user_clk_period = 8 + elif self.user_clk_frequency == 125e6: + user_clk_period = 4 + else: + user_clk_period = 2 + + @always(delay(user_clk_period)) + def clkgen(): + user_clk.next = not user_clk + + @instance + def reset_logic(): + while True: + yield user_clk.posedge, sys_reset.negedge + + if not sys_reset: + user_reset.next = 1 + yield sys_reset.posedge + yield delay(20) + yield user_clk.posedge + user_reset.next = 0 + + @always_comb + def comb_logic(): + pcie_perstn0_out.next = sys_reset + pcie_perstn1_out.next = pcie_perstn1_in + + @instance + def logic(): + + while True: + yield user_clk.posedge, sys_reset.negedge + + if not sys_reset: + self.cq_np_req_count = 0 + elif pcie_cq_np_req: + if self.cq_np_req_count < 32: + self.cq_np_req_count += 1 + + # handle completer requests + # send any queued non-posted requests first + while self.cq_np_queue and self.cq_np_req_count > 0: + tlp = self.cq_np_queue.pop(0) + self.cq_np_req_count -= 1 + self.cq_source.send(tlp.pack_us_cq(self.dw)) + + # handle new requests + while self.cq_queue: + tlp = self.cq_queue.pop(0) + + if ((tlp.fmt, tlp.type) == TLP_IO_READ or (tlp.fmt, tlp.type) == TLP_IO_WRITE or + (tlp.fmt, tlp.type) == TLP_MEM_READ or (tlp.fmt, tlp.type) == TLP_MEM_READ_64): + # non-posted request + if self.cq_np_req_count > 0: + # have credit, can forward + self.cq_np_req_count -= 1 + self.cq_source.send(tlp.pack_us_cq(self.dw)) + else: + # no credits, put it in the queue + self.cq_np_queue.append(tlp) + else: + # posted request + self.cq_source.send(tlp.pack_us_cq(self.dw)) + + pcie_cq_np_req_count.next = self.cq_np_req_count + + # handle completer completions + while not self.cc_sink.empty(): + pkt = self.cc_sink.recv() + + tlp = TLP_us().unpack_us_cc(pkt, self.dw, self.enable_parity) + + if not tlp.completer_id_enable: + tlp.completer_id = (self.bus_num, self.device_num, tlp.completer_id[2]) + + if not tlp.discontinue: + yield self.send(TLP(tlp)) + + # handle requester requests + while not self.rq_sink.empty(): + pkt = self.rq_sink.recv() + + tlp = TLP_us().unpack_us_rq(pkt, self.dw, self.enable_parity) + + if not tlp.requester_id_enable: + tlp.requester_id = (self.bus_num, self.device_num, tlp.requester_id[2]) + + if not tlp.discontinue: + if self.functions[tlp.requester_id[2]].bus_master_enable: + yield self.send(TLP(tlp)) + else: + print("Bus mastering disabled") + + # TODO: internal response + + # TODO pcie_rq_seq_num + # TODO pcie_rq_tag + + # handle requester completions + while self.rc_queue: + tlp = self.rc_queue.pop(0) + self.rc_source.send(tlp.pack_us_rc(self.dw)) + + # transmit flow control + #pcie_tfc_nph_av + #pcie_tfc_npd_av + + # configuration management + if cfg_mgmt_read_write_done: + cfg_mgmt_read_write_done.next = 0 + elif cfg_mgmt_read: + if cfg_mgmt_addr & (1 << 18): + # internal register access + pass + else: + # PCI configuration register access + function = (cfg_mgmt_addr >> 10) & 0x7f + reg_num = cfg_mgmt_addr & 0x3ff + cfg_mgmt_read_data.next = self.functions[function].read_config_register(reg_num) + cfg_mgmt_read_write_done.next = 1 + elif cfg_mgmt_write: + if cfg_mgmt_addr & (1 << 18): + # internal register access + pass + else: + # PCI configuration register access + function = (cfg_mgmt_addr >> 10) & 0x7f + reg_num = cfg_mgmt_addr & 0x3ff + self.functions[function].write_config_register(reg_num, cfg_mgmt_write_data, cfg_mgmt_byte_enable) + cfg_mgmt_read_write_done.next = 1 + #cfg_mgmt_type1_cfg_reg_access + + # configuration status + #cfg_phy_link_down + if not sys_reset: + cfg_phy_link_down.next = 1 + user_lnk_up.next = 0 + else: + cfg_phy_link_down.next = 0 # TODO + user_lnk_up.next = 1 # TODO + #cfg_phy_link_status + cfg_negotiated_width.next = self.functions[0].negotiated_link_width + cfg_current_speed.next = (1 << (self.functions[0].current_link_speed & 3)) >> 1 + cfg_max_payload.next = self.functions[0].max_payload_size + cfg_max_read_req.next = self.functions[0].max_read_request_size + status = 0 + if self.functions[0].bus_master_enable: + status |= 0x07 + if self.functions[0].interrupt_disable: + status |= 0x08 + if len(self.functions) > 1: + if self.functions[1].bus_master_enable: + status |= 0x70 + if self.functions[1].interrupt_disable: + status |= 0x80 + cfg_function_status.next = status + #cfg_vf_status + #cfg_function_power_state + #cfg_vf_power_state + #cfg_link_power_state + #cfg_err_cor_out + #cfg_err_nonfatal_out + #cfg_err_fatal_out + cfg_ltr_enable.next = self.functions[0].ltr_mechanism_enable + #cfg_ltssm_state + status = 0 + if self.functions[0].read_completion_boundary: + status |= 1 + if len(self.functions) > 1: + if self.functions[0].read_completion_boundary: + status |= 2 + cfg_rcb_status.next = status + #cfg_dpa_substate_change + #cfg_obff_enable + #cfg_pl_status_change + #cfg_tph_requester_enable + #cfg_tph_st_mode + #cfg_vf_tph_requester_enable + #cfg_vf_tph_st_mode + + # configuration received message + #cfg_msg_received + #cfg_msg_received_data + #cfg_msg_received_type + + # configuration transmit message + #cfg_msg_transmit + #cfg_msg_transmit_type + #cfg_msg_transmit_data + #cfg_msg_transmit_done + + # configuration flow control + #cfg_fc_ph + #cfg_fc_pd + #cfg_fc_nph + #cfg_fc_npd + #cfg_fc_cplh + #cfg_fc_cpld + #cfg_fc_sel + + # per-function status + #cfg_per_func_status_control + #cfg_per_func_status_data + + # configuration control + #cfg_hot_reset_in + #cfg_hot_reset_out + if not sys_reset: + self.config_space_enable = False + else: + self.config_space_enable = bool(cfg_config_space_enable) + #cfg_per_function_update_done + #cfg_per_function_number + #cfg_per_function_output_request + #cfg_dsn + #cfg_ds_bus_number + #cfg_ds_device_number + #cfg_ds_function_number + #cfg_power_state_change_ack + #cfg_power_state_change_interrupt + #cfg_err_cor_in + #cfg_err_uncor_in + #cfg_flr_done + #cfg_vf_flr_done + #cfg_flr_in_process + #cfg_vf_flr_in_process + #cfg_req_pm_transition_l23_ready + #cfg_link_training_enable + + # configuration interrupt controller + # INTx + #cfg_interrupt_int + #cfg_interrupt_sent + #cfg_interrupt_pending + # MSI + #cfg_interrupt_msi_enable + #cfg_interrupt_msi_vf_enable + #cfg_interrupt_msi_int + #cfg_interrupt_msi_sent + #cfg_interrupt_msi_fail + #cfg_interrupt_msi_mmenable + #cfg_interrupt_msi_pending_status + #cfg_interrupt_msi_mask_update + #cfg_interrupt_msi_select + #cfg_interrupt_msi_data + # MSI-X + #cfg_interrupt_msix_enable + #cfg_interrupt_msix_mask + #cfg_interrupt_msix_vf_enable + #cfg_interrupt_msix_vf_mask + #cfg_interrupt_msix_address + #cfg_interrupt_msix_data + #cfg_interrupt_msix_int + #cfg_interrupt_msix_sent + #cfg_interrupt_msix_fail + # MSI/MSI-X + #cfg_interrupt_msi_attr + #cfg_interrupt_msi_tph_present + #cfg_interrupt_msi_tph_type + #cfg_interrupt_msi_tph_st_tag + #cfg_interrupt_msi_function_number + + # configuration extend + #cfg_ext_read_received + #cfg_ext_write_received + #cfg_ext_register_number + #cfg_ext_function_number + #cfg_ext_write_data + #cfg_ext_write_byte_enable + #cfg_ext_read_data + #cfg_ext_read_data_valid + + return instances() + diff --git a/tb/test_pcie.py b/tb/test_pcie.py new file mode 100755 index 000000000..7b792f34e --- /dev/null +++ b/tb/test_pcie.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import struct +import os + +import pcie + +class TestEP(pcie.MemoryEndpoint): + def __init__(self, *args, **kwargs): + super(TestEP, self).__init__(*args, **kwargs) + + self.vendor_id = 0x1234 + self.device_id = 0x5678 + + self.add_mem_region(1024) + self.add_prefetchable_mem_region(1024*1024) + self.add_io_region(32) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + # Outputs + + # PCIe devices + rc = pcie.RootComplex() + + ep = TestEP() + dev = pcie.Device(ep) + + rc.make_port().connect(dev) + + sw = pcie.Switch() + + rc.make_port().connect(sw) + + ep2 = TestEP() + dev2 = pcie.Device(ep2) + + sw.make_port().connect(dev2) + + ep3 = TestEP() + dev3 = pcie.Device(ep3) + + sw.make_port().connect(dev3) + + ep4 = TestEP() + dev4 = pcie.Device(ep4) + + rc.make_port().connect(dev4) + + @always(delay(2)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + print("test 1: enumeration") + current_test.next = 1 + + yield rc.enumerate(enable_bus_mastering=True) + + # val = yield from rc.config_read((0, 1, 0), 0x000, 4) + + # print(val) + + # val = yield from rc.config_read((1, 0, 0), 0x000, 4) + + # print(val) + + # yield rc.config_write((1, 0, 0), 0x010, b'\xff'*4*6) + + # val = yield from rc.config_read((1, 0, 0), 0x010, 4*6) + + # print(val) + + for k in range(6): + print("0x%08x / 0x%08x" %(ep.bar[k], ep.bar_mask[k])) + + print(sw.upstream_bridge.pri_bus_num) + print(sw.upstream_bridge.sec_bus_num) + print(sw.upstream_bridge.sub_bus_num) + print("0x%08x" % sw.upstream_bridge.io_base) + print("0x%08x" % sw.upstream_bridge.io_limit) + print("0x%08x" % sw.upstream_bridge.mem_base) + print("0x%08x" % sw.upstream_bridge.mem_limit) + print("0x%016x" % sw.upstream_bridge.prefetchable_mem_base) + print("0x%016x" % sw.upstream_bridge.prefetchable_mem_limit) + + yield delay(100) + + yield clk.posedge + print("test 2: IO and memory read/write") + current_test.next = 2 + + yield rc.io_write(0x80000000, bytearray(range(16)), 1000) + assert ep.read_region(3, 0, 16) == bytearray(range(16)) + + val = yield from rc.io_read(0x80000000, 16, 1000) + assert val == bytearray(range(16)) + + yield rc.mem_write(0x80000000, bytearray(range(16)), 1000) + yield delay(1000) + assert ep.read_region(0, 0, 16) == bytearray(range(16)) + + val = yield from rc.mem_read(0x80000000, 16, 1000) + assert val == bytearray(range(16)) + + yield rc.mem_write(0x8000000000000000, bytearray(range(16)), 1000) + yield delay(1000) + assert ep.read_region(1, 0, 16) == bytearray(range(16)) + + val = yield from rc.mem_read(0x8000000000000000, 16, 1000) + assert val == bytearray(range(16)) + + yield delay(100) + + # yield clk.posedge + # print("test 3: Large read/write") + # current_test.next = 3 + + # yield rc.mem_write(0x8000000000000000, bytearray(range(256))*32, 100) + # yield delay(100) + # assert ep.read_region(1, 0, 256*32) == bytearray(range(256))*32 + + # val = yield from rc.mem_read(0x8000000000000000, 256*32, 100) + # assert val == bytearray(range(256))*32 + + # yield delay(100) + + yield clk.posedge + print("test 4: Root complex memory") + current_test.next = 4 + + mem_base, mem_data = rc.alloc_region(1024*1024) + io_base, io_data = rc.alloc_io_region(1024) + + yield rc.io_write(io_base, bytearray(range(16))) + assert io_data[0:16] == bytearray(range(16)) + + val = yield from rc.io_read(io_base, 16) + assert val == bytearray(range(16)) + + yield rc.mem_write(mem_base, bytearray(range(16))) + assert mem_data[0:16] == bytearray(range(16)) + + val = yield from rc.mem_read(mem_base, 16) + assert val == bytearray(range(16)) + + yield delay(100) + + yield clk.posedge + print("test 5: device-to-device DMA") + current_test.next = 5 + + yield ep.io_write(0x80001000, bytearray(range(16)), 10000) + assert ep2.read_region(3, 0, 16) == bytearray(range(16)) + + val = yield from ep.io_read(0x80001000, 16, 10000) + assert val == bytearray(range(16)) + + yield ep.mem_write(0x80100000, bytearray(range(16)), 10000) + yield delay(1000) + assert ep2.read_region(0, 0, 16) == bytearray(range(16)) + + val = yield from ep.mem_read(0x80100000, 16, 10000) + assert val == bytearray(range(16)) + + yield ep.mem_write(0x8000000000100000, bytearray(range(16)), 10000) + yield delay(1000) + assert ep2.read_region(1, 0, 16) == bytearray(range(16)) + + val = yield from ep.mem_read(0x8000000000100000, 16, 10000) + assert val == bytearray(range(16)) + + yield delay(100) + + yield clk.posedge + print("test 6: device-to-root DMA") + current_test.next = 6 + + yield ep.io_write(io_base, bytearray(range(16)), 1000) + assert io_data[0:16] == bytearray(range(16)) + + val = yield from ep.io_read(io_base, 16, 1000) + assert val == bytearray(range(16)) + + yield ep.mem_write(mem_base, bytearray(range(16)), 1000) + yield delay(1000) + assert mem_data[0:16] == bytearray(range(16)) + + val = yield from ep.mem_read(mem_base, 16, 1000) + assert val == bytearray(range(16)) + + yield delay(100) + + val = yield from rc.capability_read((1, 0, 0), pcie.PCIE_CAP_ID, 0x000, 4) + + raise StopSimulation + + return instances() + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + #sim = Simulation(bench()) + traceSignals.name = os.path.basename(__file__).rsplit('.',1)[0] + sim = Simulation(traceSignals(bench)) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_pcie_us.py b/tb/test_pcie_us.py new file mode 100755 index 000000000..571f40f8b --- /dev/null +++ b/tb/test_pcie_us.py @@ -0,0 +1,915 @@ +#!/usr/bin/env python +""" + +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. + +""" + +from myhdl import * +import struct +import os + +import axis_ep +import pcie +import pcie_us + +#pcie.trace_routing = True + +def bench(): + + # Parameters + dw = 128 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + # Outputs + + + # Completer reQuest Interface + m_axis_cq_tdata=Signal(intbv(0)[dw:]) + m_axis_cq_tuser=Signal(intbv(0)[85:]) + m_axis_cq_tlast=Signal(bool(0)) + m_axis_cq_tkeep=Signal(intbv(0)[int(dw/32):]) + m_axis_cq_tvalid=Signal(bool(0)) + m_axis_cq_tready=Signal(bool(0)) + pcie_cq_np_req=Signal(bool(1)) + pcie_cq_np_req_count=Signal(intbv(0)[6:]) + + # Completer Completion Interface + s_axis_cc_tdata=Signal(intbv(0)[dw:]) + s_axis_cc_tuser=Signal(intbv(0)[33:]) + s_axis_cc_tlast=Signal(bool(0)) + s_axis_cc_tkeep=Signal(intbv(0)[int(dw/32):]) + s_axis_cc_tvalid=Signal(bool(0)) + s_axis_cc_tready=Signal(bool(0)) + + # Requester reQuest Interface + s_axis_rq_tdata=Signal(intbv(0)[dw:]) + s_axis_rq_tuser=Signal(intbv(0)[60:]) + s_axis_rq_tlast=Signal(bool(0)) + s_axis_rq_tkeep=Signal(intbv(0)[int(dw/32):]) + s_axis_rq_tvalid=Signal(bool(0)) + s_axis_rq_tready=Signal(bool(0)) + pcie_rq_seq_num=Signal(intbv(0)[4:]) + pcie_rq_seq_num_vld=Signal(bool(0)) + pcie_rq_tag=Signal(intbv(0)[6:]) + pcie_rq_tag_av=Signal(intbv(0)[2:]) + pcie_rq_tag_vld=Signal(bool(0)) + + # Requester Completion Interface + m_axis_rc_tdata=Signal(intbv(0)[dw:]) + m_axis_rc_tuser=Signal(intbv(0)[75:]) + m_axis_rc_tlast=Signal(bool(0)) + m_axis_rc_tkeep=Signal(intbv(0)[int(dw/32):]) + m_axis_rc_tvalid=Signal(bool(0)) + m_axis_rc_tready=Signal(bool(0)) + + # Transmit Flow Control Interface + pcie_tfc_nph_av=Signal(intbv(0)[2:]) + pcie_tfc_npd_av=Signal(intbv(0)[2:]) + + # Configuration Management Interface + cfg_mgmt_addr=Signal(intbv(0)[19:]) + cfg_mgmt_write=Signal(bool(0)) + cfg_mgmt_write_data=Signal(intbv(0)[32:]) + cfg_mgmt_byte_enable=Signal(intbv(0)[4:]) + cfg_mgmt_read=Signal(bool(0)) + cfg_mgmt_read_data=Signal(intbv(0)[32:]) + cfg_mgmt_read_write_done=Signal(bool(0)) + cfg_mgmt_type1_cfg_reg_access=Signal(bool(0)) + + # Configuration Status Interface + cfg_phy_link_down=Signal(bool(0)) + cfg_phy_link_status=Signal(intbv(0)[2:]) + cfg_negotiated_width=Signal(intbv(0)[4:]) + cfg_current_speed=Signal(intbv(0)[3:]) + cfg_max_payload=Signal(intbv(0)[3:]) + cfg_max_read_req=Signal(intbv(0)[3:]) + cfg_function_status=Signal(intbv(0)[8:]) + cfg_vf_status=Signal(intbv(0)[12:]) + cfg_function_power_state=Signal(intbv(0)[6:]) + cfg_vf_power_state=Signal(intbv(0)[18:]) + cfg_link_power_state=Signal(intbv(0)[2:]) + cfg_err_cor_out=Signal(bool(0)) + cfg_err_nonfatal_out=Signal(bool(0)) + cfg_err_fatal_out=Signal(bool(0)) + cfg_ltr_enable=Signal(bool(0)) + cfg_ltssm_state=Signal(intbv(0)[6:]) + cfg_rcb_status=Signal(intbv(0)[2:]) + cfg_dpa_substate_change=Signal(intbv(0)[2:]) + cfg_obff_enable=Signal(intbv(0)[2:]) + cfg_pl_status_change=Signal(bool(0)) + cfg_tph_requester_enable=Signal(intbv(0)[2:]) + cfg_tph_st_mode=Signal(intbv(0)[6:]) + cfg_vf_tph_requester_enable=Signal(intbv(0)[6:]) + cfg_vf_tph_st_mode=Signal(intbv(0)[18:]) + + # Configuration Received Message Interface + cfg_msg_received=Signal(bool(0)) + cfg_msg_received_data=Signal(intbv(0)[8:]) + cfg_msg_received_type=Signal(intbv(0)[5:]) + + # Configuration Transmit Message Interface + cfg_msg_transmit=Signal(bool(0)) + cfg_msg_transmit_type=Signal(intbv(0)[3:]) + cfg_msg_transmit_data=Signal(intbv(0)[32:]) + cfg_msg_transmit_done=Signal(bool(0)) + + # Configuration Flow Control Interface + cfg_fc_ph=Signal(intbv(0)[8:]) + cfg_fc_pd=Signal(intbv(0)[12:]) + cfg_fc_nph=Signal(intbv(0)[8:]) + cfg_fc_npd=Signal(intbv(0)[12:]) + cfg_fc_cplh=Signal(intbv(0)[8:]) + cfg_fc_cpld=Signal(intbv(0)[12:]) + cfg_fc_sel=Signal(intbv(0)[3:]) + + # Per-Function Status Interface + cfg_per_func_status_control=Signal(intbv(0)[3:]) + cfg_per_func_status_data=Signal(intbv(0)[16:]) + + # Configuration Control Interface + cfg_hot_reset_in=Signal(bool(0)) + cfg_hot_reset_out=Signal(bool(0)) + cfg_config_space_enable=Signal(bool(1)) + cfg_per_function_update_done=Signal(bool(0)) + cfg_per_function_number=Signal(intbv(0)[3:]) + cfg_per_function_output_request=Signal(bool(0)) + cfg_dsn=Signal(intbv(0)[64:]) + cfg_ds_bus_number=Signal(intbv(0)[8:]) + cfg_ds_device_number=Signal(intbv(0)[5:]) + cfg_ds_function_number=Signal(intbv(0)[3:]) + cfg_power_state_change_ack=Signal(bool(0)) + cfg_power_state_change_interrupt=Signal(bool(0)) + cfg_err_cor_in=Signal(bool(0)) + cfg_err_uncor_in=Signal(bool(0)) + cfg_flr_done=Signal(intbv(0)[2:]) + cfg_vf_flr_done=Signal(intbv(0)[6:]) + cfg_flr_in_process=Signal(intbv(0)[2:]) + cfg_vf_flr_in_process=Signal(intbv(0)[6:]) + cfg_req_pm_transition_l23_ready=Signal(bool(0)) + cfg_link_training_enable=Signal(bool(1)) + + # Configuration Interrupt Controller Interface + cfg_interrupt_int=Signal(intbv(0)[4:]) + cfg_interrupt_sent=Signal(bool(0)) + cfg_interrupt_pending=Signal(intbv(0)[2:]) + cfg_interrupt_msi_enable=Signal(intbv(0)[2:]) + cfg_interrupt_msi_vf_enable=Signal(intbv(0)[6:]) + cfg_interrupt_msi_int=Signal(intbv(0)[32:]) + cfg_interrupt_msi_sent=Signal(bool(0)) + cfg_interrupt_msi_fail=Signal(bool(0)) + cfg_interrupt_msi_mmenable=Signal(intbv(0)[6:]) + cfg_interrupt_msi_pending_status=Signal(intbv(0)[64:]) + cfg_interrupt_msi_mask_update=Signal(bool(0)) + cfg_interrupt_msi_select=Signal(intbv(0)[4:]) + cfg_interrupt_msi_data=Signal(intbv(0)[32:]) + cfg_interrupt_msix_enable=Signal(intbv(0)[2:]) + cfg_interrupt_msix_mask=Signal(intbv(0)[2:]) + cfg_interrupt_msix_vf_enable=Signal(intbv(0)[6:]) + cfg_interrupt_msix_vf_mask=Signal(intbv(0)[6:]) + cfg_interrupt_msix_address=Signal(intbv(0)[64:]) + cfg_interrupt_msix_data=Signal(intbv(0)[32:]) + cfg_interrupt_msix_int=Signal(bool(0)) + cfg_interrupt_msix_sent=Signal(bool(0)) + cfg_interrupt_msix_fail=Signal(bool(0)) + cfg_interrupt_msi_attr=Signal(intbv(0)[3:]) + cfg_interrupt_msi_tph_present=Signal(bool(0)) + cfg_interrupt_msi_tph_type=Signal(intbv(0)[2:]) + cfg_interrupt_msi_tph_st_tag=Signal(intbv(0)[9:]) + cfg_interrupt_msi_function_number=Signal(intbv(0)[3:]) + + # Configuration Extend Interface + cfg_ext_read_received=Signal(bool(0)) + cfg_ext_write_received=Signal(bool(0)) + cfg_ext_register_number=Signal(intbv(0)[10:]) + cfg_ext_function_number=Signal(intbv(0)[8:]) + cfg_ext_write_data=Signal(intbv(0)[32:]) + cfg_ext_write_byte_enable=Signal(intbv(0)[4:]) + cfg_ext_read_data=Signal(intbv(0)[32:]) + cfg_ext_read_data_valid=Signal(bool(0)) + + # Clock and Reset Interface + user_clk=Signal(bool(0)) + user_reset=Signal(bool(0)) + user_lnk_up=Signal(bool(0)) + sys_clk=Signal(bool(0)) + sys_reset=Signal(bool(0)) + pcie_perstn0_out=Signal(bool(0)) + pcie_perstn1_in=Signal(bool(0)) + pcie_perstn1_out=Signal(bool(0)) + + # sources and sinks + cq_sink = axis_ep.AXIStreamSink() + + cq_sink_logic = cq_sink.create_logic( + user_clk, + user_reset, + tdata=m_axis_cq_tdata, + tuser=m_axis_cq_tuser, + tlast=m_axis_cq_tlast, + tkeep=m_axis_cq_tkeep, + tvalid=m_axis_cq_tvalid, + tready=m_axis_cq_tready, + name='cq_sink' + ) + + cc_source = axis_ep.AXIStreamSource() + + cc_source_logic = cc_source.create_logic( + user_clk, + user_reset, + tdata=s_axis_cc_tdata, + tuser=s_axis_cc_tuser, + tlast=s_axis_cc_tlast, + tkeep=s_axis_cc_tkeep, + tvalid=s_axis_cc_tvalid, + tready=s_axis_cc_tready, + name='cc_source' + ) + + rq_source = axis_ep.AXIStreamSource() + + rq_source_logic = rq_source.create_logic( + user_clk, + user_reset, + tdata=s_axis_rq_tdata, + tuser=s_axis_rq_tuser, + tlast=s_axis_rq_tlast, + tkeep=s_axis_rq_tkeep, + tvalid=s_axis_rq_tvalid, + tready=s_axis_rq_tready, + name='rq_source' + ) + + rc_sink = axis_ep.AXIStreamSink() + + rc_sink_logic = rc_sink.create_logic( + user_clk, + user_reset, + tdata=m_axis_rc_tdata, + tuser=m_axis_rc_tuser, + tlast=m_axis_rc_tlast, + tkeep=m_axis_rc_tkeep, + tvalid=m_axis_rc_tvalid, + tready=m_axis_rc_tready, + name='rc_sink' + ) + + # PCIe devices + rc = pcie.RootComplex() + + mem_base, mem_data = rc.alloc_region(1024*1024) + io_base, io_data = rc.alloc_io_region(1024) + + dev = pcie_us.UltrascalePCIe() + + dev.pcie_generation = 3 + dev.pcie_link_width = 4 + dev.user_clock_frequency = 256e6 + + regions = [None]*6 + regions[0] = bytearray(1024) + regions[1] = bytearray(1024*1024) + regions[3] = bytearray(1024) + + dev.functions[0].configure_bar(0, len(regions[0])) + dev.functions[0].configure_bar(1, len(regions[1]), True, True) + dev.functions[0].configure_bar(3, len(regions[3]), False, False, True) + + rc.make_port().connect(dev) + + pcie_logic = dev.create_logic( + # Completer reQuest Interface + m_axis_cq_tdata=m_axis_cq_tdata, + m_axis_cq_tuser=m_axis_cq_tuser, + m_axis_cq_tlast=m_axis_cq_tlast, + m_axis_cq_tkeep=m_axis_cq_tkeep, + m_axis_cq_tvalid=m_axis_cq_tvalid, + m_axis_cq_tready=m_axis_cq_tready, + pcie_cq_np_req=pcie_cq_np_req, + pcie_cq_np_req_count=pcie_cq_np_req_count, + + # Completer Completion Interface + s_axis_cc_tdata=s_axis_cc_tdata, + s_axis_cc_tuser=s_axis_cc_tuser, + s_axis_cc_tlast=s_axis_cc_tlast, + s_axis_cc_tkeep=s_axis_cc_tkeep, + s_axis_cc_tvalid=s_axis_cc_tvalid, + s_axis_cc_tready=s_axis_cc_tready, + + # Requester reQuest Interface + s_axis_rq_tdata=s_axis_rq_tdata, + s_axis_rq_tuser=s_axis_rq_tuser, + s_axis_rq_tlast=s_axis_rq_tlast, + s_axis_rq_tkeep=s_axis_rq_tkeep, + s_axis_rq_tvalid=s_axis_rq_tvalid, + s_axis_rq_tready=s_axis_rq_tready, + pcie_rq_seq_num=pcie_rq_seq_num, + pcie_rq_seq_num_vld=pcie_rq_seq_num_vld, + pcie_rq_tag=pcie_rq_tag, + pcie_rq_tag_av=pcie_rq_tag_av, + pcie_rq_tag_vld=pcie_rq_tag_vld, + + # Requester Completion Interface + m_axis_rc_tdata=m_axis_rc_tdata, + m_axis_rc_tuser=m_axis_rc_tuser, + m_axis_rc_tlast=m_axis_rc_tlast, + m_axis_rc_tkeep=m_axis_rc_tkeep, + m_axis_rc_tvalid=m_axis_rc_tvalid, + m_axis_rc_tready=m_axis_rc_tready, + + # Transmit Flow Control Interface + pcie_tfc_nph_av=pcie_tfc_nph_av, + pcie_tfc_npd_av=pcie_tfc_npd_av, + + # Configuration Management Interface + cfg_mgmt_addr=cfg_mgmt_addr, + cfg_mgmt_write=cfg_mgmt_write, + cfg_mgmt_write_data=cfg_mgmt_write_data, + cfg_mgmt_byte_enable=cfg_mgmt_byte_enable, + cfg_mgmt_read=cfg_mgmt_read, + cfg_mgmt_read_data=cfg_mgmt_read_data, + cfg_mgmt_read_write_done=cfg_mgmt_read_write_done, + cfg_mgmt_type1_cfg_reg_access=cfg_mgmt_type1_cfg_reg_access, + + # Configuration Status Interface + cfg_phy_link_down=cfg_phy_link_down, + cfg_phy_link_status=cfg_phy_link_status, + cfg_negotiated_width=cfg_negotiated_width, + cfg_current_speed=cfg_current_speed, + cfg_max_payload=cfg_max_payload, + cfg_max_read_req=cfg_max_read_req, + cfg_function_status=cfg_function_status, + cfg_vf_status=cfg_vf_status, + cfg_function_power_state=cfg_function_power_state, + cfg_vf_power_state=cfg_vf_power_state, + cfg_link_power_state=cfg_link_power_state, + cfg_err_cor_out=cfg_err_cor_out, + cfg_err_nonfatal_out=cfg_err_nonfatal_out, + cfg_err_fatal_out=cfg_err_fatal_out, + cfg_ltr_enable=cfg_ltr_enable, + cfg_ltssm_state=cfg_ltssm_state, + cfg_rcb_status=cfg_rcb_status, + cfg_dpa_substate_change=cfg_dpa_substate_change, + cfg_obff_enable=cfg_obff_enable, + cfg_pl_status_change=cfg_pl_status_change, + cfg_tph_requester_enable=cfg_tph_requester_enable, + cfg_tph_st_mode=cfg_tph_st_mode, + cfg_vf_tph_requester_enable=cfg_vf_tph_requester_enable, + cfg_vf_tph_st_mode=cfg_vf_tph_st_mode, + + # Configuration Received Message Interface + cfg_msg_received=cfg_msg_received, + cfg_msg_received_data=cfg_msg_received_data, + cfg_msg_received_type=cfg_msg_received_type, + + # Configuration Transmit Message Interface + cfg_msg_transmit=cfg_msg_transmit, + cfg_msg_transmit_type=cfg_msg_transmit_type, + cfg_msg_transmit_data=cfg_msg_transmit_data, + cfg_msg_transmit_done=cfg_msg_transmit_done, + + # Configuration Flow Control Interface + cfg_fc_ph=cfg_fc_ph, + cfg_fc_pd=cfg_fc_pd, + cfg_fc_nph=cfg_fc_nph, + cfg_fc_npd=cfg_fc_npd, + cfg_fc_cplh=cfg_fc_cplh, + cfg_fc_cpld=cfg_fc_cpld, + cfg_fc_sel=cfg_fc_sel, + + # Per-Function Status Interface + cfg_per_func_status_control=cfg_per_func_status_control, + cfg_per_func_status_data=cfg_per_func_status_data, + + # Configuration Control Interface + cfg_hot_reset_in=cfg_hot_reset_in, + cfg_hot_reset_out=cfg_hot_reset_out, + cfg_config_space_enable=cfg_config_space_enable, + cfg_per_function_update_done=cfg_per_function_update_done, + cfg_per_function_number=cfg_per_function_number, + cfg_per_function_output_request=cfg_per_function_output_request, + cfg_dsn=cfg_dsn, + cfg_ds_bus_number=cfg_ds_bus_number, + cfg_ds_device_number=cfg_ds_device_number, + cfg_ds_function_number=cfg_ds_function_number, + cfg_power_state_change_ack=cfg_power_state_change_ack, + cfg_power_state_change_interrupt=cfg_power_state_change_interrupt, + cfg_err_cor_in=cfg_err_cor_in, + cfg_err_uncor_in=cfg_err_uncor_in, + cfg_flr_done=cfg_flr_done, + cfg_vf_flr_done=cfg_vf_flr_done, + cfg_flr_in_process=cfg_flr_in_process, + cfg_vf_flr_in_process=cfg_vf_flr_in_process, + cfg_req_pm_transition_l23_ready=cfg_req_pm_transition_l23_ready, + cfg_link_training_enable=cfg_link_training_enable, + + # Configuration Interrupt Controller Interface + cfg_interrupt_int=cfg_interrupt_int, + cfg_interrupt_sent=cfg_interrupt_sent, + cfg_interrupt_pending=cfg_interrupt_pending, + cfg_interrupt_msi_enable=cfg_interrupt_msi_enable, + cfg_interrupt_msi_vf_enable=cfg_interrupt_msi_vf_enable, + cfg_interrupt_msi_int=cfg_interrupt_msi_int, + cfg_interrupt_msi_sent=cfg_interrupt_msi_sent, + cfg_interrupt_msi_fail=cfg_interrupt_msi_fail, + cfg_interrupt_msi_mmenable=cfg_interrupt_msi_mmenable, + cfg_interrupt_msi_pending_status=cfg_interrupt_msi_pending_status, + cfg_interrupt_msi_mask_update=cfg_interrupt_msi_mask_update, + cfg_interrupt_msi_select=cfg_interrupt_msi_select, + cfg_interrupt_msi_data=cfg_interrupt_msi_data, + cfg_interrupt_msix_enable=cfg_interrupt_msix_enable, + cfg_interrupt_msix_mask=cfg_interrupt_msix_mask, + cfg_interrupt_msix_vf_enable=cfg_interrupt_msix_vf_enable, + cfg_interrupt_msix_vf_mask=cfg_interrupt_msix_vf_mask, + cfg_interrupt_msix_address=cfg_interrupt_msix_address, + cfg_interrupt_msix_data=cfg_interrupt_msix_data, + cfg_interrupt_msix_int=cfg_interrupt_msix_int, + cfg_interrupt_msix_sent=cfg_interrupt_msix_sent, + cfg_interrupt_msix_fail=cfg_interrupt_msix_fail, + cfg_interrupt_msi_attr=cfg_interrupt_msi_attr, + cfg_interrupt_msi_tph_present=cfg_interrupt_msi_tph_present, + cfg_interrupt_msi_tph_type=cfg_interrupt_msi_tph_type, + cfg_interrupt_msi_tph_st_tag=cfg_interrupt_msi_tph_st_tag, + cfg_interrupt_msi_function_number=cfg_interrupt_msi_function_number, + + # Configuration Extend Interface + cfg_ext_read_received=cfg_ext_read_received, + cfg_ext_write_received=cfg_ext_write_received, + cfg_ext_register_number=cfg_ext_register_number, + cfg_ext_function_number=cfg_ext_function_number, + cfg_ext_write_data=cfg_ext_write_data, + cfg_ext_write_byte_enable=cfg_ext_write_byte_enable, + cfg_ext_read_data=cfg_ext_read_data, + cfg_ext_read_data_valid=cfg_ext_read_data_valid, + + # Clock and Reset Interface + user_clk=user_clk, + user_reset=user_reset, + user_lnk_up=user_lnk_up, + sys_clk=sys_clk, + sys_clk_gt=sys_clk, + sys_reset=sys_reset, + pcie_perstn0_out=pcie_perstn0_out, + pcie_perstn1_in=pcie_perstn1_in, + pcie_perstn1_out=pcie_perstn1_out + ) + + @always(delay(5)) + def clkgen(): + clk.next = not clk + + @always_comb + def clk_logic(): + sys_clk.next = clk + sys_reset.next = not rst + + @instance + def user_logic(): + while True: + yield clk.posedge + + # handle completer request + if not cq_sink.empty(): + pkt = cq_sink.recv() + + tlp = pcie_us.TLP_us().unpack_us_cq(pkt, dw) + + print(tlp) + + if ((tlp.fmt, tlp.type) == pcie.TLP_IO_READ): + print("IO read") + + cpl = pcie_us.TLP_us() + cpl.set_completion(tlp, (0, 0, 0)) + cpl.fmt, cpl.type = pcie.TLP_CPL_DATA + + region = tlp.bar_id + addr = tlp.address & 0xffff # TODO + offset = 0 + start_offset = None + mask = tlp.first_be + + # perform operation + 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] = regions[region][addr+start_offset:addr+offset] + start_offset = None + + offset += 1 + + if start_offset is not None and offset != start_offset: + data[start_offset:offset] = regions[region][addr+start_offset:addr+offset] + + cpl.set_data(data) + cpl.byte_count = 4 + cpl.length = 1 + + cc_source.send(cpl.pack_us_cc(dw)) + elif ((tlp.fmt, tlp.type) == pcie.TLP_IO_WRITE): + print("IO write") + + cpl = pcie_us.TLP_us() + cpl.set_completion(tlp, (0, 0, 0)) + + region = tlp.bar_id + addr = tlp.address & 0xffff # TODO + offset = 0 + start_offset = None + mask = tlp.first_be + + # perform operation + 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: + regions[region][addr+start_offset:addr+offset] = data[start_offset:offset] + start_offset = None + + offset += 1 + + if start_offset is not None and offset != start_offset: + regions[region][addr+start_offset:addr+offset] = data[start_offset:offset] + + cc_source.send(cpl.pack_us_cc(dw)) + if ((tlp.fmt, tlp.type) == pcie.TLP_MEM_READ or (tlp.fmt, tlp.type) == pcie.TLP_MEM_READ_64): + print("Memory read") + + # perform operation + region = tlp.bar_id + addr = tlp.address & 0xffff # TODO + offset = 0 + length = tlp.length + + # perform read + data = regions[region][addr:addr+length*4] + + # prepare completion TLP(s) + n = 0 + offset = 0 + addr = tlp.address + offset + length = tlp.length*4 + + while n < length: + cpl = pcie_us.TLP_us() + cpl.set_completion(tlp, (0, 0, 0)) + + byte_length = length-n + cpl.byte_count = byte_length + byte_length = min(byte_length, 128 << dev.functions[0].max_payload_size) # max payload size + if byte_length > 128: + byte_length -= (addr + byte_length) % 128 # RCB align + byte_length = min(byte_length, 0x1000 - (addr & 0xfff)) # 4k align + + cpl.lower_address = addr & 0x7f + + cpl.set_data(data[offset+n:offset+n+byte_length]) + + print("Completion: %s" % (repr(cpl))) + cc_source.send(cpl.pack_us_cc(dw)) + + n += byte_length + addr += byte_length + if ((tlp.fmt, tlp.type) == pcie.TLP_MEM_WRITE or (tlp.fmt, tlp.type) == pcie.TLP_MEM_WRITE_64): + print("Memory write") + + # perform operation + region = tlp.bar_id + addr = tlp.address & 0xffff # TODO + offset = 0 + start_offset = None + mask = tlp.first_be + length = tlp.length + + # 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: + regions[region][addr+start_offset:addr+offset] = data[start_offset:offset] + start_offset = None + + offset += 1 + + if length > 1: + # middle dwords + if start_offset is None: + start_offset = offset + offset += length*4 + + # 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: + regions[region][addr+start_offset:addr+offset] = data[start_offset:offset] + start_offset = None + + offset += 1 + + if start_offset is not None and offset != start_offset: + regions[region][addr+start_offset:addr+offset] = data[start_offset:offset] + + # haldle requester completion + #if not rc_sink.empty(): + # pkt = rc_sink.recv() + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + current_tag = 1 + + yield clk.posedge + print("test 1: enumeration") + current_test.next = 1 + + yield rc.enumerate(enable_bus_mastering=True) + + yield delay(100) + + yield clk.posedge + print("test 2: IO and memory read/write") + current_test.next = 2 + + yield rc.io_write(0x80000000, bytearray(range(16)), 100) + assert regions[3][0:16] == bytearray(range(16)) + + val = yield from rc.io_read(0x80000000, 16, 100) + assert val == bytearray(range(16)) + + yield rc.mem_write(0x80000000, bytearray(range(16)), 100) + yield delay(100) + assert regions[0][0:16] == bytearray(range(16)) + + val = yield from rc.mem_read(0x80000000, 16, 100) + assert val == bytearray(range(16)) + + yield rc.mem_write(0x8000000000000000, bytearray(range(16)), 100) + yield delay(100) + assert regions[1][0:16] == bytearray(range(16)) + + val = yield from rc.mem_read(0x8000000000000000, 16, 100) + assert val == bytearray(range(16)) + + yield delay(100) + + # yield clk.posedge + # print("test 3: Large read/write") + # current_test.next = 3 + + # yield rc.mem_write(0x8000000000000000, bytearray(range(256))*32, 100) + # yield delay(100) + # assert ep.read_region(1, 0, 256*32) == bytearray(range(256))*32 + + # val = yield from rc.mem_read(0x8000000000000000, 256*32, 100) + # assert val == bytearray(range(256))*32 + + # yield delay(100) + + yield clk.posedge + print("test 4: DMA") + current_test.next = 4 + + #yield ep.io_write(io_base, bytearray(range(16)), 100) + + data = bytearray(range(16)) + addr = io_base + n = 0 + + while n < len(data): + tlp = pcie_us.TLP_us() + tlp.fmt, tlp.type = pcie.TLP_IO_WRITE + tlp.requester_id = (dev.bus_num, dev.device_num, 0) + tlp.tag = current_tag + + first_pad = addr % 4 + byte_length = min(len(data)-n, 4-first_pad) + tlp.set_be_data(addr, data[n:n+byte_length]) + + tlp.address = addr & ~3 + + current_tag = (current_tag % 31) + 1 + + rq_source.send(tlp.pack_us_rq(dw)) + yield rc_sink.wait(100) + pkt = rc_sink.recv() + + if not pkt: + raise Exception("Timeout") + + cpl = pcie_us.TLP_us().unpack_us_rc(pkt, dw) + + if cpl.status != pcie.CPL_STATUS_SC: + raise Exception("Unsuccessful completion") + + n += byte_length + addr += byte_length + + assert io_data[0:16] == bytearray(range(16)) + + #val = yield from ep.io_read(io_base, 16, 100) + + length = 16 + data = b'' + addr = io_base + n = 0 + + while n < length: + tlp = pcie_us.TLP_us() + tlp.fmt, tlp.type = pcie.TLP_IO_READ + tlp.requester_id = (dev.bus_num, dev.device_num, 0) + tlp.tag = current_tag + + first_pad = addr % 4 + byte_length = min(length-n, 4-first_pad) + tlp.set_be(addr, byte_length) + + tlp.address = addr & ~3 + + current_tag = (current_tag % 31) + 1 + + rq_source.send(tlp.pack_us_rq(dw)) + yield rc_sink.wait(100) + pkt = rc_sink.recv() + + if not pkt: + raise Exception("Timeout") + + cpl = pcie_us.TLP_us().unpack_us_rc(pkt, dw) + + if cpl.status != pcie.CPL_STATUS_SC: + raise Exception("Unsuccessful completion") + else: + d = struct.pack(' 0xffffffff: + tlp.fmt, tlp.type = pcie.TLP_MEM_WRITE_64 + else: + tlp.fmt, tlp.type = pcie.TLP_MEM_WRITE + tlp.requester_id = (dev.bus_num, dev.device_num, 0) + tlp.tag = current_tag + + first_pad = addr % 4 + byte_length = len(data)-n + byte_length = min(byte_length, (128 << dev.functions[0].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]) + + tlp.address = addr & ~3 + + current_tag = (current_tag % 31) + 1 + + rq_source.send(tlp.pack_us_rq(dw)) + + n += byte_length + addr += byte_length + + yield delay(100) + assert mem_data[0:16] == bytearray(range(16)) + + #val = yield from ep.mem_read(mem_base, 16, 100) + + length = 16 + data = b'' + addr = mem_base + n = 0 + + while n < length: + tlp = pcie_us.TLP_us() + if addr > 0xffffffff: + tlp.fmt, tlp.type = pcie.TLP_MEM_READ_64 + else: + tlp.fmt, tlp.type = pcie.TLP_MEM_READ + tlp.requester_id = (dev.bus_num, dev.device_num, 0) + tlp.tag = current_tag + + first_pad = addr % 4 + byte_length = length-n + byte_length = min(byte_length, (128 << dev.functions[0].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) + + tlp.address = addr & ~3 + + current_tag = (current_tag % 31) + 1 + + rq_source.send(tlp.pack_us_rq(dw)) + + m = 0 + + while m < byte_length: + yield rc_sink.wait(100) + pkt = rc_sink.recv() + + if not pkt: + raise Exception("Timeout") + + cpl = pcie_us.TLP_us().unpack_us_rc(pkt, dw) + + if cpl.status != pcie.CPL_STATUS_SC: + raise Exception("Unsuccessful completion") + else: + dw_len = cpl.length + if dw_len == 0: + dw_len = 1024 + d = bytearray() + + for k in range(dw_len): + d.extend(struct.pack('