Esempio n. 1
0
    def __init__(self, dut):
        self.dut = dut

        self.log = SimLog("cocotb.tb")
        self.log.setLevel(logging.DEBUG)

        cocotb.start_soon(Clock(dut.clk_250mhz, 4, units="ns").start())

        # AXI
        self.address_space = AddressSpace()
        self.pool = self.address_space.create_pool(0, 0x8000_0000)

        self.axil_master = AxiLiteMaster(
            AxiLiteBus.from_prefix(dut, "s_axil_ctrl"), dut.clk_250mhz,
            dut.rst_250mhz)
        self.address_space.register_region(self.axil_master, 0x10_0000_0000)
        self.hw_regs = self.address_space.create_window(
            0x10_0000_0000, self.axil_master.size)

        self.axi_slave = AxiSlave(AxiBus.from_prefix(dut,
                                                     "m_axi"), dut.clk_250mhz,
                                  dut.rst_250mhz, self.address_space)

        self.driver = mqnic.Driver()

        # Ethernet
        cocotb.start_soon(Clock(dut.sfp0_rx_clk, 6.4, units="ns").start())
        self.sfp0_source = XgmiiSource(dut.sfp0_rxd, dut.sfp0_rxc,
                                       dut.sfp0_rx_clk, dut.sfp0_rx_rst)
        cocotb.start_soon(Clock(dut.sfp0_tx_clk, 6.4, units="ns").start())
        self.sfp0_sink = XgmiiSink(dut.sfp0_txd, dut.sfp0_txc, dut.sfp0_tx_clk,
                                   dut.sfp0_tx_rst)

        cocotb.start_soon(Clock(dut.sfp1_rx_clk, 6.4, units="ns").start())
        self.sfp1_source = XgmiiSource(dut.sfp1_rxd, dut.sfp1_rxc,
                                       dut.sfp1_rx_clk, dut.sfp1_rx_rst)
        cocotb.start_soon(Clock(dut.sfp1_tx_clk, 6.4, units="ns").start())
        self.sfp1_sink = XgmiiSink(dut.sfp1_txd, dut.sfp1_txc, dut.sfp1_tx_clk,
                                   dut.sfp1_tx_rst)

        cocotb.start_soon(Clock(dut.sfp_drp_clk, 8, units="ns").start())
        dut.sfp_drp_rst.setimmediatevalue(0)
        dut.sfp_drp_do.setimmediatevalue(0)
        dut.sfp_drp_rdy.setimmediatevalue(0)

        dut.sfp0_rx_error_count.setimmediatevalue(0)
        dut.sfp1_rx_error_count.setimmediatevalue(0)

        dut.btnu.setimmediatevalue(0)
        dut.btnl.setimmediatevalue(0)
        dut.btnd.setimmediatevalue(0)
        dut.btnr.setimmediatevalue(0)
        dut.btnc.setimmediatevalue(0)
        dut.sw.setimmediatevalue(0)

        dut.i2c_scl_i.setimmediatevalue(1)
        dut.i2c_sda_i.setimmediatevalue(1)

        self.loopback_enable = False
        cocotb.start_soon(self._run_loopback())
Esempio n. 2
0
    def __init__(self, dut):
        self.dut = dut

        self.log = SimLog("cocotb.tb")
        self.log.setLevel(logging.DEBUG)

        cocotb.start_soon(Clock(dut.clk, 4, units="ns").start())

        # AXI
        self.address_space = AddressSpace()
        self.pool = self.address_space.create_pool(0, 0x8000_0000)

        self.axil_master = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil_ctrl"), dut.clk, dut.rst)
        self.address_space.register_region(self.axil_master, 0x10_0000_0000)
        self.hw_regs = self.address_space.create_window(0x10_0000_0000, self.axil_master.size)

        self.axi_slave = AxiSlave(AxiBus.from_prefix(dut, "m_axi"), dut.clk, dut.rst, self.address_space)

        self.driver = mqnic.Driver()

        # Ethernet
        self.port_mac = []

        eth_int_if_width = len(dut.core_inst.iface[0].port[0].rx_async_fifo_inst.m_axis_tdata)
        eth_clock_period = 6.4
        eth_speed = 10e9

        if eth_int_if_width == 64:
            # 10G
            eth_clock_period = 6.4
            eth_speed = 10e9
        elif eth_int_if_width == 128:
            # 25G
            eth_clock_period = 2.56
            eth_speed = 25e9
        elif eth_int_if_width == 512:
            # 100G
            eth_clock_period = 3.102
            eth_speed = 100e9

        for iface in dut.core_inst.iface:
            for port in iface.port:
                cocotb.start_soon(Clock(port.port_rx_clk, eth_clock_period, units="ns").start())
                cocotb.start_soon(Clock(port.port_tx_clk, eth_clock_period, units="ns").start())

                port.port_rx_rst.setimmediatevalue(0)
                port.port_tx_rst.setimmediatevalue(0)

                mac = EthMac(
                    tx_clk=port.port_tx_clk,
                    tx_rst=port.port_tx_rst,
                    tx_bus=AxiStreamBus.from_prefix(port, "axis_tx"),
                    tx_ptp_time=port.ptp.tx_ptp_cdc_inst.output_ts,
                    tx_ptp_ts=port.ptp.axis_tx_ptp_ts,
                    tx_ptp_ts_tag=port.ptp.axis_tx_ptp_ts_tag,
                    tx_ptp_ts_valid=port.ptp.axis_tx_ptp_ts_valid,
                    rx_clk=port.port_rx_clk,
                    rx_rst=port.port_rx_rst,
                    rx_bus=AxiStreamBus.from_prefix(port, "axis_rx"),
                    rx_ptp_time=port.ptp.rx_ptp_cdc_inst.output_ts,
                    ifg=12, speed=eth_speed
                )

                self.port_mac.append(mac)

        dut.ctrl_reg_wr_wait.setimmediatevalue(0)
        dut.ctrl_reg_wr_ack.setimmediatevalue(0)
        dut.ctrl_reg_rd_data.setimmediatevalue(0)
        dut.ctrl_reg_rd_wait.setimmediatevalue(0)
        dut.ctrl_reg_rd_ack.setimmediatevalue(0)

        dut.ptp_sample_clk.setimmediatevalue(0)

        dut.s_axis_stat_tdata.setimmediatevalue(0)
        dut.s_axis_stat_tid.setimmediatevalue(0)
        dut.s_axis_stat_tvalid.setimmediatevalue(0)

        self.loopback_enable = False
        cocotb.start_soon(self._run_loopback())
Esempio n. 3
0
    def __init__(self, *args, **kwargs):
        self._pcie_id = PcieId()

        self.log = logging.getLogger(
            f"cocotb.pcie.{type(self).__name__}.{id(self)}")
        self.log.name = f"cocotb.pcie.{type(self).__name__}[{self._pcie_id}]"

        self.upstream_tx_handler = None

        self.current_tag = 0
        self.tag_count = 256
        self.tag_active = [False] * 256
        self.tag_release = Event()

        self.rx_cpl_queues = [Queue() for k in range(256)]
        self.rx_cpl_sync = [Event() for k in range(256)]

        self.rx_tlp_handler = {}

        self.capabilities = PcieCapList()
        self.ext_capabilities = PcieExtCapList()

        # configuration registers
        # Vendor ID
        self.vendor_id = 0
        # Device ID
        self.device_id = 0
        # Command
        self.io_space_enable = False
        self.memory_space_enable = False
        self.bus_master_enable = False
        self.parity_error_response_enable = False
        self.serr_enable = False
        self.interrupt_disable = False
        # Status
        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
        # Revision ID
        self.revision_id = 0
        # Class code
        self.class_code = 0
        # Cache line size
        self.cache_line_size = 0
        # Latency timer
        self.latency_timer = 0
        # Header type
        self.header_layout = 0
        self.multifunction_device = False
        # BIST
        self.bist_capable = False
        self.start_bist = False
        self.bist_completion_code = 0
        # Base Address Registers
        self.bar = []
        self.bar_mask = []
        # Expansion ROM Base Address Register
        self.expansion_rom_addr = 0
        self.expansion_rom_addr_mask = 0
        self.expansion_rom_enable = 0
        # Capabilities pointer
        self.capabilities_ptr = 0
        # Interrupt line
        self.interrupt_line = 0
        # Interrupt pin
        self.interrupt_pin = 0

        self.read_completion_boundary = 128

        self.register_rx_tlp_handler(TlpType.CFG_READ_0,
                                     self.handle_config_0_read_tlp)
        self.register_rx_tlp_handler(TlpType.CFG_WRITE_0,
                                     self.handle_config_0_write_tlp)

        self.pm_cap = PmCapability()
        self.register_capability(self.pm_cap)

        self.pcie_cap = PcieCapability()
        self.register_capability(self.pcie_cap)

        self.mem_address_space = AddressSpace(2**64)
        self.io_address_space = AddressSpace(2**32)

        self.mem_region = MemoryTlpRegion(self)
        self.io_region = IoTlpRegion(self)

        self.mem_address_space.register_region(self.mem_region, 0, 2**64)
        self.io_address_space.register_region(self.io_region, 0, 2**32)

        super().__init__(*args, **kwargs)
Esempio n. 4
0
class Function:
    """PCIe function, implements config TLP handling"""
    def __init__(self, *args, **kwargs):
        self._pcie_id = PcieId()

        self.log = logging.getLogger(
            f"cocotb.pcie.{type(self).__name__}.{id(self)}")
        self.log.name = f"cocotb.pcie.{type(self).__name__}[{self._pcie_id}]"

        self.upstream_tx_handler = None

        self.current_tag = 0
        self.tag_count = 256
        self.tag_active = [False] * 256
        self.tag_release = Event()

        self.rx_cpl_queues = [Queue() for k in range(256)]
        self.rx_cpl_sync = [Event() for k in range(256)]

        self.rx_tlp_handler = {}

        self.capabilities = PcieCapList()
        self.ext_capabilities = PcieExtCapList()

        # configuration registers
        # Vendor ID
        self.vendor_id = 0
        # Device ID
        self.device_id = 0
        # Command
        self.io_space_enable = False
        self.memory_space_enable = False
        self.bus_master_enable = False
        self.parity_error_response_enable = False
        self.serr_enable = False
        self.interrupt_disable = False
        # Status
        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
        # Revision ID
        self.revision_id = 0
        # Class code
        self.class_code = 0
        # Cache line size
        self.cache_line_size = 0
        # Latency timer
        self.latency_timer = 0
        # Header type
        self.header_layout = 0
        self.multifunction_device = False
        # BIST
        self.bist_capable = False
        self.start_bist = False
        self.bist_completion_code = 0
        # Base Address Registers
        self.bar = []
        self.bar_mask = []
        # Expansion ROM Base Address Register
        self.expansion_rom_addr = 0
        self.expansion_rom_addr_mask = 0
        self.expansion_rom_enable = 0
        # Capabilities pointer
        self.capabilities_ptr = 0
        # Interrupt line
        self.interrupt_line = 0
        # Interrupt pin
        self.interrupt_pin = 0

        self.read_completion_boundary = 128

        self.register_rx_tlp_handler(TlpType.CFG_READ_0,
                                     self.handle_config_0_read_tlp)
        self.register_rx_tlp_handler(TlpType.CFG_WRITE_0,
                                     self.handle_config_0_write_tlp)

        self.pm_cap = PmCapability()
        self.register_capability(self.pm_cap)

        self.pcie_cap = PcieCapability()
        self.register_capability(self.pcie_cap)

        self.mem_address_space = AddressSpace(2**64)
        self.io_address_space = AddressSpace(2**32)

        self.mem_region = MemoryTlpRegion(self)
        self.io_region = IoTlpRegion(self)

        self.mem_address_space.register_region(self.mem_region, 0, 2**64)
        self.io_address_space.register_region(self.io_region, 0, 2**32)

        super().__init__(*args, **kwargs)

    @property
    def pcie_id(self):
        return self._pcie_id

    @pcie_id.setter
    def pcie_id(self, val):
        val = PcieId(val)
        if self._pcie_id != val:
            self.log.info("Assigned PCIe ID %s", val)
            self._pcie_id = val
            self.log.name = f"cocotb.pcie.{type(self).__name__}[{self._pcie_id}]"

    @property
    def bus_num(self):
        return self._pcie_id.bus

    @property
    def device_num(self):
        return self._pcie_id.device

    @property
    def function_num(self):
        return self._pcie_id.function

    """
    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
    +---------------------------------+----------------+----------------+
    """

    async def read_config_register(self, reg):
        if reg == 0:
            # Vendor ID
            val = self.vendor_id & 0xffff
            # Device ID
            val |= (self.device_id & 0xffff) << 16
            return val
        elif reg == 1:
            val = 0
            # Command
            val |= bool(self.io_space_enable) << 0
            val |= bool(self.memory_space_enable) << 1
            val |= bool(self.bus_master_enable) << 2
            val |= bool(self.parity_error_response_enable) << 6
            val |= bool(self.serr_enable) << 8
            val |= bool(self.interrupt_disable) << 10
            # Status
            val |= bool(self.interrupt_status) << 19
            val |= bool(self.capabilities_list) << 20
            val |= bool(self.master_data_parity_error) << 24
            val |= bool(self.signaled_target_abort) << 27
            val |= bool(self.received_target_abort) << 28
            val |= bool(self.received_master_abort) << 29
            val |= bool(self.signaled_system_error) << 30
            val |= bool(self.detected_parity_error) << 31
            return val
        elif reg == 2:
            # Revision ID
            val = self.revision_id & 0xff
            # Class code
            val |= (self.class_code & 0xffffff) << 8
            return val
        elif reg == 3:
            # Cache line size
            val = self.cache_line_size & 0xff
            # Latency timer
            val |= (self.latency_timer & 0xff) << 8
            # Header type
            val |= (self.header_layout & 0x7f) << 16
            val |= bool(self.multifunction_device) << 23
            # BIST
            val |= (self.bist_completion_code & 0xf) << 24
            val |= bool(self.start_bist) << 30
            val |= bool(self.bist_capable) << 31
            return val
        elif reg == 13:
            # Capabilities pointer
            return self.capabilities_ptr & 0xff
        elif reg == 15:
            # Interrupt line
            val = self.interrupt_line & 0xff
            # Interrupt pin
            val |= (self.interrupt_pin & 0xff) << 8
            return val
        elif 16 <= reg < 64:
            # PCIe capabilities
            return await self.read_capability_register(reg)
        elif 64 <= reg < 1024:
            # PCIe extended capabilities
            return await self.read_extended_capability_register(reg)
        else:
            return 0

    async def write_config_register(self, reg, data, mask):
        if reg == 1:
            # command
            if mask & 0x1:
                self.io_space_enable = (data & 1 << 0 != 0)
                self.memory_space_enable = (data & 1 << 1 != 0)
                self.bus_master_enable = (data & 1 << 2 != 0)
                self.parity_error_response_enable = (data & 1 << 6 != 0)
            if mask & 0x2:
                self.serr_enable = (data & 1 << 8 != 0)
                self.interrupt_disable = (data & 1 << 10 != 0)
            # status
            if mask & 0x8:
                if data & 1 << 24:
                    self.master_data_parity_error = False
                if data & 1 << 27:
                    self.signaled_target_abort = False
                if data & 1 << 28:
                    self.received_target_abort = False
                if data & 1 << 29:
                    self.received_master_abort = False
                if data & 1 << 30:
                    self.signaled_system_error = False
                if data & 1 << 31:
                    self.detected_parity_error = False
        elif reg == 3:
            if mask & 1:
                # Cache line size
                self.cache_line_size = data & 0xff
            if mask & 4:
                # BIST
                self.start_bist = bool(data & 1 << 30)
        elif reg == 15:
            # Interrupt line
            if mask & 1:
                self.interrupt_line = data & 0xff
        elif 16 <= reg < 64:
            # PCIe capabilities
            await self.write_capability_register(reg, data, mask)
        elif 64 <= reg < 1024:
            # PCIe extended capabilities
            await self.write_extended_capability_register(reg, data, mask)

    async def read_capability_register(self, reg):
        return await self.capabilities.read_register(reg)

    async def write_capability_register(self, reg, data, mask):
        await self.capabilities.write_register(reg, data, mask)

    def register_capability(self, cap, offset=None):
        cap.parent = self
        self.capabilities.register(cap, offset)
        if self.capabilities.list:
            self.capabilities_ptr = self.capabilities.list[0].offset * 4
        else:
            self.capabilities_ptr = 0

    async def read_extended_capability_register(self, reg):
        return await self.ext_capabilities.read_register(reg)

    async def write_extended_capability_register(self, reg, data, mask):
        await self.ext_capabilities.write_register(reg, data, mask)

    def register_extended_capability(self, cap, offset=None):
        cap.parent = self
        self.ext_capabilities.register(cap, offset)

    def configure_bar(self, idx, size, ext=False, prefetch=False, io=False):
        mask = 2**((size - 1).bit_length()) - 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 configure_io_bar(self, idx, size):
        self.configure_bar(idx, size, io=True)

    def match_bar(self, addr, io=False):
        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_val) & bar_mask == 0:
                    return (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_val) & bar_mask == 0:
                    return (orig_bar, addr & ~bar_mask)

        return None

    def match_io_bar(self, addr):
        return self.match_bar(addr, io=True)

    def match_tlp(self, tlp):
        if tlp.fmt_type in {TlpType.CFG_READ_0, TlpType.CFG_WRITE_0}:
            # Config type 0
            return self.device_num == tlp.dest_id.device and self.function_num == tlp.dest_id.function
        elif tlp.fmt_type in {TlpType.CFG_READ_1, TlpType.CFG_WRITE_1}:
            # Config type 1
            return False
        elif tlp.fmt_type in {
                TlpType.CPL, TlpType.CPL_DATA, TlpType.CPL_LOCKED,
                TlpType.CPL_LOCKED_DATA
        }:
            # Completion
            return self.pcie_id == tlp.requester_id
        elif tlp.fmt_type in {TlpType.IO_READ, TlpType.IO_WRITE}:
            # IO read/write
            return bool(self.match_bar(tlp.address, True))
        elif tlp.fmt_type in {
                TlpType.MEM_READ, TlpType.MEM_READ_64, TlpType.MEM_WRITE,
                TlpType.MEM_WRITE_64
        }:
            # Memory read/write
            return bool(self.match_bar(tlp.address))
        else:
            raise Exception("TODO")
        return False

    async def upstream_send(self, tlp):
        self.log.debug("Sending upstream TLP: %r", tlp)
        assert tlp.check()
        if self.parity_error_response_enable and tlp.ep:
            self.log.warning(
                "Sending poisoned TLP, reporting master data parity error")
            self.master_data_parity_error = True
        if self.upstream_tx_handler is None:
            raise Exception("Transmit handler not set")
        await self.upstream_tx_handler(tlp)

    async def send(self, tlp):
        if tlp.is_completion() and tlp.status == CplStatus.CA:
            self.log.warning(
                "Sending completion with CA status, reporting target abort")
            self.signaled_target_abort = True
        await self.upstream_send(tlp)

    async def upstream_recv(self, tlp):
        self.log.debug("Got downstream TLP: %r", tlp)
        assert tlp.check()
        if tlp.is_completion():
            if tlp.status == CplStatus.CA:
                self.log.warning(
                    "Received completion with CA status, reporting target abort"
                )
                self.received_target_abort = True
            elif tlp.status == CplStatus.UR:
                self.log.warning(
                    "Received completion with UR status, reporting master abort"
                )
                self.received_master_abort = True
        if self.parity_error_response_enable and tlp.ep:
            self.log.warning(
                "Received poisoned TLP, reporting master data parity error")
            self.master_data_parity_error = True
        await self.handle_tlp(tlp)

    async def handle_tlp(self, tlp):
        if tlp.fmt_type in {
                TlpType.CPL, TlpType.CPL_DATA, TlpType.CPL_LOCKED,
                TlpType.CPL_LOCKED_DATA
        }:
            # completion
            self.rx_cpl_queues[tlp.tag].put_nowait(tlp)
            self.rx_cpl_sync[tlp.tag].set()
        elif tlp.fmt_type in self.rx_tlp_handler:
            # call registered handler
            tlp.release_fc()
            await self.rx_tlp_handler[tlp.fmt_type](tlp)
        else:
            # no handler registered for TLP type
            tlp.release_fc()
            raise Exception("Unhandled TLP")

    def register_rx_tlp_handler(self, fmt_type, func):
        self.rx_tlp_handler[fmt_type] = func

    async def recv_cpl(self, tag, timeout=0, timeout_unit='ns'):
        queue = self.rx_cpl_queues[tag]
        sync = self.rx_cpl_sync[tag]

        if not queue.empty():
            cpl = queue.get_nowait()
            cpl.release_fc()
            return cpl

        sync.clear()
        if timeout:
            await First(sync.wait(), Timer(timeout, timeout_unit))
        else:
            await sync.wait()

        if not queue.empty():
            cpl = queue.get_nowait()
            cpl.release_fc()
            return cpl

        return None

    async def alloc_tag(self):
        tag_count = min(256 if self.pcie_cap.extended_tag_field_enable else 32,
                        self.tag_count)

        while True:
            tag = self.current_tag
            for k in range(tag_count):
                tag = (tag + 1) % tag_count
                if not self.tag_active[tag]:
                    self.tag_active[tag] = True
                    self.current_tag = tag
                    return tag

            self.tag_release.clear()
            await self.tag_release.wait()

    def release_tag(self, tag):
        assert self.tag_active[tag]
        self.tag_active[tag] = False
        self.tag_release.set()

    async def handle_config_0_read_tlp(self, tlp):
        if tlp.dest_id.device == self.device_num and tlp.dest_id.function == self.function_num:
            self.log.info("Config type 0 read, reg 0x%03x",
                          tlp.register_number)

            # capture address information
            if self.bus_num != tlp.dest_id.bus:
                self.log.info("Capture bus number %d", tlp.dest_id.bus)
                self.pcie_id = self.pcie_id._replace(bus=tlp.dest_id.bus)

            # perform operation
            data = await self.read_config_register(tlp.register_number)

            # prepare completion TLP
            cpl = Tlp.create_completion_data_for_tlp(tlp, self.pcie_id)
            cpl.set_data(struct.pack('<L', data))
            cpl.byte_count = 4

            self.log.debug("Completion: %r", cpl)
            await self.upstream_send(cpl)
        else:
            self.log.warning(
                "Type 0 configuration read request device and function number mismatch: %r",
                tlp)

            # Unsupported request
            cpl = Tlp.create_ur_completion_for_tlp(tlp, self.pcie_id)
            self.log.debug("UR Completion: %r", cpl)
            await self.upstream_send(cpl)

    async def handle_config_0_write_tlp(self, tlp):
        if tlp.dest_id.device == self.device_num and tlp.dest_id.function == self.function_num:
            self.log.info("Config type 0 write, reg 0x%03x data 0x%08x",
                          tlp.register_number,
                          struct.unpack('<L', tlp.get_data())[0])

            # capture address information
            if self.bus_num != tlp.dest_id.bus:
                self.log.info("Capture bus number %d", tlp.dest_id.bus)
                self.pcie_id = self.pcie_id._replace(bus=tlp.dest_id.bus)

            data, = struct.unpack('<L', tlp.get_data())

            # perform operation
            await self.write_config_register(tlp.register_number, data,
                                             tlp.first_be)

            # prepare completion TLP
            cpl = Tlp.create_completion_for_tlp(tlp, self.pcie_id)

            self.log.debug("Completion: %r", cpl)
            await self.upstream_send(cpl)
        else:
            self.log.warning(
                "Type 0 configuration write request device and function number mismatch: %r",
                tlp)

            # Unsupported request
            cpl = Tlp.create_ur_completion_for_tlp(tlp, self.pcie_id)
            self.log.debug("UR Completion: %r", cpl)
            await self.upstream_send(cpl)

    async def io_read(self, addr, length, timeout=0, timeout_unit='ns'):
        return await self.io_address_space.read(addr,
                                                length,
                                                timeout=timeout,
                                                timeout_unit=timeout_unit)

    async def io_read_words(self,
                            addr,
                            count,
                            byteorder='little',
                            ws=2,
                            timeout=0,
                            timeout_unit='ns'):
        data = await self.io_read(addr, count * ws, timeout, timeout_unit)
        words = []
        for k in range(count):
            words.append(int.from_bytes(data[ws * k:ws * (k + 1)], byteorder))
        return words

    async def io_read_dwords(self,
                             addr,
                             count,
                             byteorder='little',
                             timeout=0,
                             timeout_unit='ns'):
        return await self.io_read_words(addr, count, byteorder, 4, timeout,
                                        timeout_unit)

    async def io_read_qwords(self,
                             addr,
                             count,
                             byteorder='little',
                             timeout=0,
                             timeout_unit='ns'):
        return await self.io_read_words(addr, count, byteorder, 8, timeout,
                                        timeout_unit)

    async def io_read_byte(self, addr, timeout=0, timeout_unit='ns'):
        return (await self.io_read(addr, 1, timeout, timeout_unit))[0]

    async def io_read_word(self,
                           addr,
                           byteorder='little',
                           ws=2,
                           timeout=0,
                           timeout_unit='ns'):
        return (await self.io_read_words(addr, 1, byteorder, ws, timeout,
                                         timeout_unit))[0]

    async def io_read_dword(self,
                            addr,
                            byteorder='little',
                            timeout=0,
                            timeout_unit='ns'):
        return (await self.io_read_dwords(addr, 1, byteorder, timeout,
                                          timeout_unit))[0]

    async def io_read_qword(self,
                            addr,
                            byteorder='little',
                            timeout=0,
                            timeout_unit='ns'):
        return (await self.io_read_qwords(addr, 1, byteorder, timeout,
                                          timeout_unit))[0]

    async def io_write(self, addr, data, timeout=0, timeout_unit='ns'):
        await self.io_address_space.write(addr,
                                          data,
                                          timeout=timeout,
                                          timeout_unit=timeout_unit)

    async def io_write_words(self,
                             addr,
                             data,
                             byteorder='little',
                             ws=2,
                             timeout=0,
                             timeout_unit='ns'):
        words = data
        data = bytearray()
        for w in words:
            data.extend(w.to_bytes(ws, byteorder))
        await self.io_write(addr, data, timeout, timeout_unit)

    async def io_write_dwords(self,
                              addr,
                              data,
                              byteorder='little',
                              timeout=0,
                              timeout_unit='ns'):
        await self.io_write_words(addr, data, byteorder, 4, timeout,
                                  timeout_unit)

    async def io_write_qwords(self,
                              addr,
                              data,
                              byteorder='little',
                              timeout=0,
                              timeout_unit='ns'):
        await self.io_write_words(addr, data, byteorder, 8, timeout,
                                  timeout_unit)

    async def io_write_byte(self, addr, data, timeout=0, timeout_unit='ns'):
        await self.io_write(addr, [data], timeout, timeout_unit)

    async def io_write_word(self,
                            addr,
                            data,
                            byteorder='little',
                            ws=2,
                            timeout=0,
                            timeout_unit='ns'):
        await self.io_write_words(addr, [data], byteorder, ws, timeout,
                                  timeout_unit)

    async def io_write_dword(self,
                             addr,
                             data,
                             byteorder='little',
                             timeout=0,
                             timeout_unit='ns'):
        await self.io_write_dwords(addr, [data], byteorder, timeout,
                                   timeout_unit)

    async def io_write_qword(self,
                             addr,
                             data,
                             byteorder='little',
                             timeout=0,
                             timeout_unit='ns'):
        await self.io_write_qwords(addr, [data], byteorder, timeout,
                                   timeout_unit)

    async def mem_read(self,
                       addr,
                       length,
                       timeout=0,
                       timeout_unit='ns',
                       attr=TlpAttr(0),
                       tc=TlpTc.TC0):
        return await self.mem_address_space.read(addr,
                                                 length,
                                                 timeout=timeout,
                                                 timeout_unit=timeout_unit,
                                                 attr=attr,
                                                 tc=tc)

    async def mem_read_words(self,
                             addr,
                             count,
                             byteorder='little',
                             ws=2,
                             timeout=0,
                             timeout_unit='ns',
                             attr=TlpAttr(0),
                             tc=TlpTc.TC0):
        data = await self.mem_read(addr, count * ws, timeout, timeout_unit,
                                   attr, tc)
        words = []
        for k in range(count):
            words.append(int.from_bytes(data[ws * k:ws * (k + 1)], byteorder))
        return words

    async def mem_read_dwords(self,
                              addr,
                              count,
                              byteorder='little',
                              timeout=0,
                              timeout_unit='ns',
                              attr=TlpAttr(0),
                              tc=TlpTc.TC0):
        return await self.mem_read_words(addr, count, byteorder, 4, timeout,
                                         timeout_unit, attr, tc)

    async def mem_read_qwords(self,
                              addr,
                              count,
                              byteorder='little',
                              timeout=0,
                              timeout_unit='ns',
                              attr=TlpAttr(0),
                              tc=TlpTc.TC0):
        return await self.mem_read_words(addr, count, byteorder, 8, timeout,
                                         timeout_unit, attr, tc)

    async def mem_read_byte(self,
                            addr,
                            timeout=0,
                            timeout_unit='ns',
                            attr=TlpAttr(0),
                            tc=TlpTc.TC0):
        return (await self.mem_read(addr, 1, timeout, timeout_unit, attr,
                                    tc))[0]

    async def mem_read_word(self,
                            addr,
                            byteorder='little',
                            ws=2,
                            timeout=0,
                            timeout_unit='ns',
                            attr=TlpAttr(0),
                            tc=TlpTc.TC0):
        return (await self.mem_read_words(addr, 1, byteorder, ws, timeout,
                                          timeout_unit, attr, tc))[0]

    async def mem_read_dword(self,
                             addr,
                             byteorder='little',
                             timeout=0,
                             timeout_unit='ns',
                             attr=TlpAttr(0),
                             tc=TlpTc.TC0):
        return (await self.mem_read_dwords(addr, 1, byteorder, timeout,
                                           timeout_unit, attr, tc))[0]

    async def mem_read_qword(self,
                             addr,
                             byteorder='little',
                             timeout=0,
                             timeout_unit='ns',
                             attr=TlpAttr(0),
                             tc=TlpTc.TC0):
        return (await self.mem_read_qwords(addr, 1, byteorder, timeout,
                                           timeout_unit, attr, tc))[0]

    async def mem_write(self,
                        addr,
                        data,
                        timeout=0,
                        timeout_unit='ns',
                        attr=TlpAttr(0),
                        tc=TlpTc.TC0):
        await self.mem_address_space.write(addr,
                                           data,
                                           timeout=timeout,
                                           timeout_unit=timeout_unit,
                                           attr=attr,
                                           tc=tc)

    async def mem_write_words(self,
                              addr,
                              data,
                              byteorder='little',
                              ws=2,
                              timeout=0,
                              timeout_unit='ns',
                              attr=TlpAttr(0),
                              tc=TlpTc.TC0):
        words = data
        data = bytearray()
        for w in words:
            data.extend(w.to_bytes(ws, byteorder))
        await self.mem_write(addr, data, timeout, timeout_unit, attr, tc)

    async def mem_write_dwords(self,
                               addr,
                               data,
                               byteorder='little',
                               timeout=0,
                               timeout_unit='ns',
                               attr=TlpAttr(0),
                               tc=TlpTc.TC0):
        await self.mem_write_words(addr, data, byteorder, 4, timeout,
                                   timeout_unit, attr, tc)

    async def mem_write_qwords(self,
                               addr,
                               data,
                               byteorder='little',
                               timeout=0,
                               timeout_unit='ns',
                               attr=TlpAttr(0),
                               tc=TlpTc.TC0):
        await self.mem_write_words(addr, data, byteorder, 8, timeout,
                                   timeout_unit, attr, tc)

    async def mem_write_byte(self,
                             addr,
                             data,
                             timeout=0,
                             timeout_unit='ns',
                             attr=TlpAttr(0),
                             tc=TlpTc.TC0):
        await self.mem_write(addr, [data], timeout, timeout_unit, attr, tc)

    async def mem_write_word(self,
                             addr,
                             data,
                             byteorder='little',
                             ws=2,
                             timeout=0,
                             timeout_unit='ns',
                             attr=TlpAttr(0),
                             tc=TlpTc.TC0):
        await self.mem_write_words(addr, [data], byteorder, ws, timeout,
                                   timeout_unit, attr, tc)

    async def mem_write_dword(self,
                              addr,
                              data,
                              byteorder='little',
                              timeout=0,
                              timeout_unit='ns',
                              attr=TlpAttr(0),
                              tc=TlpTc.TC0):
        await self.mem_write_dwords(addr, [data], byteorder, timeout,
                                    timeout_unit, attr, tc)

    async def mem_write_qword(self,
                              addr,
                              data,
                              byteorder='little',
                              timeout=0,
                              timeout_unit='ns',
                              attr=TlpAttr(0),
                              tc=TlpTc.TC0):
        await self.mem_write_qwords(addr, [data], byteorder, timeout,
                                    timeout_unit, attr, tc)