diff --git a/tb/pcie_usp.py b/tb/pcie_usp.py new file mode 100644 index 000000000..d49984387 --- /dev/null +++ b/tb/pcie_usp.py @@ -0,0 +1,998 @@ +""" + +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_us import * + + +class UltrascalePlusPCIeFunction(Endpoint, MSICapability, MSIXCapability): + def __init__(self): + super(UltrascalePlusPCIeFunction, self).__init__() + + self.msi_64bit_address_capable = 1 + self.msi_per_vector_mask_capable = 0 + + self.register_capability(PM_CAP_ID, offset=0x10) + self.register_capability(MSI_CAP_ID, offset=0x12) + self.register_capability(MSIX_CAP_ID, offset=0x18) + self.register_capability(PCIE_CAP_ID, offset=0x1c) + + +class UltrascalePlusPCIe(Device): + def __init__(self): + super(UltrascalePlusPCIe, self).__init__() + + self.has_logic = False + + self.default_function = UltrascalePlusPCIeFunction + + self.dw = 256 + + # configuration options + self.pcie_generation = 3 + self.pcie_link_width = 8 + self.user_clk_frequency = 250e6 + self.alignment = "dword" + self.cq_cc_straddle = False + self.rq_rc_straddle = False + self.rc_4tlp_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_type == TLP_CFG_READ_0 or tlp.fmt_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 from self.upstream_send(cpl) + return + elif tlp.dest_id.device == self.device_num: + # capture address information + self.bus_num = tlp.dest_id.bus + + for f in self.functions: + f.bus_num = self.bus_num + + # pass TLP to function + for f in self.functions: + if f.function_num == tlp.dest_id.function: + yield from f.upstream_recv(tlp) + return + + #raise Exception("Function not found") + print("Function not found") + else: + print("Device number mismatch") + + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, (self.bus_num, self.device_num, 0)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield from self.upstream_send(cpl) + elif (tlp.fmt_type == TLP_CPL or tlp.fmt_type == TLP_CPL_DATA or + tlp.fmt_type == TLP_CPL_LOCKED or tlp.fmt_type == TLP_CPL_LOCKED_DATA): + # Completion + + if tlp.requester_id.bus == self.bus_num and tlp.requester_id.device == self.device_num: + for f in self.functions: + if f.function_num == tlp.requester_id.function: + + tlp = TLP_us(tlp) + self.rc_queue.append(tlp) + + return + + print("Function not found") + else: + print("Bus/device number mismatch") + elif (tlp.fmt_type == TLP_IO_READ or tlp.fmt_type == TLP_IO_WRITE): + # IO read/write + + for f in self.functions: + 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 = PcieId(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 from self.upstream_send(cpl) + elif (tlp.fmt_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64 or + tlp.fmt_type == TLP_MEM_WRITE or tlp.fmt_type == TLP_MEM_WRITE_64): + # Memory read/write + + for f in self.functions: + 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 = PcieId(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_type == TLP_MEM_READ or tlp.fmt_type == TLP_MEM_READ_64: + # Unsupported request + cpl = TLP() + cpl.set_ur_completion(tlp, PcieId(self.bus_num, self.device_num, 0)) + # logging + print("[%s] UR Completion: %s" % (highlight(self.get_desc()), repr(cpl))) + yield from self.upstream_send(cpl) + else: + raise Exception("TODO") + + def 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(intbv(1)[2:]), + 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_num0=Signal(intbv(0)[6:]), + pcie_rq_seq_num_vld0=Signal(bool(0)), + pcie_rq_seq_num1=Signal(intbv(0)[6:]), + pcie_rq_seq_num_vld1=Signal(bool(0)), + pcie_rq_tag0=Signal(intbv(0)[8:]), + pcie_rq_tag1=Signal(intbv(0)[8:]), + pcie_rq_tag_av=Signal(intbv(0)[4:]), + pcie_rq_tag_vld0=Signal(bool(0)), + pcie_rq_tag_vld1=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)[4:]), + pcie_tfc_npd_av=Signal(intbv(0)[4:]), + + # Configuration Management Interface + cfg_mgmt_addr=Signal(intbv(0)[10:]), + cfg_mgmt_function_number=Signal(intbv(0)[8:]), + 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_debug_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)[3:]), + cfg_current_speed=Signal(intbv(0)[2:]), + cfg_max_payload=Signal(intbv(0)[2:]), + cfg_max_read_req=Signal(intbv(0)[3:]), + cfg_function_status=Signal(intbv(0)[16:]), + cfg_vf_status=Signal(intbv(0)[504:]), + cfg_function_power_state=Signal(intbv(0)[12:]), + cfg_vf_power_state=Signal(intbv(0)[756:]), + 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_local_err_out=Signal(intbv(0)[5:]), + cfg_local_err_valid=Signal(bool(0)), + cfg_rx_pm_state=Signal(intbv(0)[2:]), + cfg_tx_pm_state=Signal(intbv(0)[2:]), + cfg_ltssm_state=Signal(intbv(0)[6:]), + cfg_rcb_status=Signal(intbv(0)[4:]), + cfg_obff_enable=Signal(intbv(0)[2:]), + cfg_pl_status_change=Signal(bool(0)), + cfg_tph_requester_enable=Signal(intbv(0)[4:]), + cfg_tph_st_mode=Signal(intbv(0)[12:]), + cfg_vf_tph_requester_enable=Signal(intbv(0)[252:]), + cfg_vf_tph_st_mode=Signal(intbv(0)[756:]), + + # 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:]), + + # 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_dsn=Signal(intbv(0)[64:]), + cfg_ds_port_number=Signal(intbv(0)[8:]), + 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)[4:]), + cfg_vf_flr_done=Signal(intbv(0)[1:]), + cfg_flr_in_process=Signal(intbv(0)[4:]), + cfg_vf_flr_in_process=Signal(intbv(0)[252:]), + 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)[4:]), + cfg_interrupt_msi_mmenable=Signal(intbv(0)[12:]), + cfg_interrupt_msi_mask_update=Signal(bool(0)), + cfg_interrupt_msi_data=Signal(intbv(0)[32:]), + cfg_interrupt_msi_select=Signal(intbv(0)[2:]), + cfg_interrupt_msi_int=Signal(intbv(0)[32:]), + cfg_interrupt_msi_pending_status=Signal(intbv(0)[32:]), + cfg_interrupt_msi_pending_status_data_enable=Signal(bool(0)), + cfg_interrupt_msi_pending_status_function_num=Signal(intbv(0)[2:]), + cfg_interrupt_msi_sent=Signal(bool(0)), + cfg_interrupt_msi_fail=Signal(bool(0)), + cfg_interrupt_msix_enable=Signal(intbv(0)[4:]), + cfg_interrupt_msix_mask=Signal(intbv(0)[4:]), + cfg_interrupt_msix_vf_enable=Signal(intbv(0)[252:]), + cfg_interrupt_msix_vf_mask=Signal(intbv(0)[252:]), + 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_vec_pending=Signal(intbv(0)[2:]), + cfg_interrupt_msix_vec_pending_status=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)[8:]), + cfg_interrupt_msi_function_number=Signal(intbv(0)[8:]), + + # 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, + phy_rdy_out=Signal(bool(0)), + + # debugging connections + cq_pause=Signal(bool(0)), + cc_pause=Signal(bool(0)), + rq_pause=Signal(bool(0)), + rc_pause=Signal(bool(0)), + ): + + # validate parameters and widths + self.dw = len(m_axis_cq_tdata) + + assert self.dw in [64, 128, 256, 512] + + 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, 16] + 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 or wider, DWORD-aligned interface + assert not self.cq_cc_straddle + assert not self.rq_rc_straddle + if self.dw != 512: + assert not self.rc_4tlp_straddle + + # TODO change this when support added + assert self.alignment == 'dword' + assert not self.cq_cc_straddle + assert not self.rq_rc_straddle + assert not self.rc_4tlp_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_link_width == 16: + assert self.dw == 128 + assert self.user_clk_frequency == 250e6 + 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_link_width == 16: + assert self.dw == 256 + assert self.user_clk_frequency == 250e6 + 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 + elif self.pcie_link_width == 16: + assert self.dw == 512 + assert self.user_clk_frequency == 250e6 + + # Completer reQuest Interface + assert len(m_axis_cq_tdata) == self.dw + assert len(m_axis_cq_tuser) == 88 + 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) == 2 + 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) == 62 + 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_num0) == 6 + assert len(pcie_rq_seq_num_vld0) == 1 + assert len(pcie_rq_seq_num1) == 6 + assert len(pcie_rq_seq_num_vld1) == 1 + assert len(pcie_rq_tag0) >= 8 + assert len(pcie_rq_tag1) >= 8 + assert len(pcie_rq_tag_av) == 4 + assert len(pcie_rq_tag_vld0) == 1 + assert len(pcie_rq_tag_vld1) == 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) == 4 + assert len(pcie_tfc_npd_av) == 4 + + # Configuration Management Interface + assert len(cfg_mgmt_addr) == 10 + assert len(cfg_mgmt_function_number) == 8 + 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_debug_access) == 1 + + # Configuration Status Interface + assert len(cfg_phy_link_down) == 1 + assert len(cfg_phy_link_status) == 2 + assert len(cfg_negotiated_width) == 3 + assert len(cfg_current_speed) == 2 + assert len(cfg_max_payload) == 2 + assert len(cfg_max_read_req) == 3 + assert len(cfg_function_status) == 16 + assert len(cfg_vf_status) == 504 + assert len(cfg_function_power_state) == 12 + assert len(cfg_vf_power_state) == 756 + 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_local_err_out) == 5 + assert len(cfg_local_err_valid) == 1 + assert len(cfg_rx_pm_state) == 2 + assert len(cfg_tx_pm_state) == 2 + assert len(cfg_ltssm_state) == 6 + assert len(cfg_rcb_status) == 4 + assert len(cfg_obff_enable) == 2 + assert len(cfg_pl_status_change) == 1 + assert len(cfg_tph_requester_enable) == 4 + assert len(cfg_tph_st_mode) == 12 + assert len(cfg_vf_tph_requester_enable) == 252 + assert len(cfg_vf_tph_st_mode) == 756 + + # 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 + + # 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_dsn) == 64 + assert len(cfg_ds_port_number) == 8 + 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) == 4 + assert len(cfg_vf_flr_done) == 1 + assert len(cfg_flr_in_process) == 4 + assert len(cfg_vf_flr_in_process) == 252 + 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) == 4 + assert len(cfg_interrupt_msi_mmenable) == 12 + assert len(cfg_interrupt_msi_mask_update) == 1 + assert len(cfg_interrupt_msi_data) == 32 + assert len(cfg_interrupt_msi_select) == 2 + assert len(cfg_interrupt_msi_int) == 32 + assert len(cfg_interrupt_msi_pending_status) == 32 + assert len(cfg_interrupt_msi_pending_status_data_enable) == 1 + assert len(cfg_interrupt_msi_pending_status_function_num) == 2 + assert len(cfg_interrupt_msi_sent) == 1 + assert len(cfg_interrupt_msi_fail) == 1 + assert len(cfg_interrupt_msix_enable) == 4 + assert len(cfg_interrupt_msix_mask) == 4 + assert len(cfg_interrupt_msix_vf_enable) == 252 + assert len(cfg_interrupt_msix_vf_mask) == 252 + assert len(cfg_interrupt_msix_address) == 64 + assert len(cfg_interrupt_msix_data) == 32 + assert len(cfg_interrupt_msix_vec_pending) == 2 + assert len(cfg_interrupt_msix_vec_pending_status) == 1 + assert len(cfg_interrupt_msix_int) == 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) == 8 + assert len(cfg_interrupt_msi_function_number) == 8 + + # 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(phy_rdy_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', + pause=cq_pause + ) + + 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', + pause=cc_pause + ) + + 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', + pause=rq_pause + ) + + 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', + pause=rc_pause + ) + + 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 + + @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_type == TLP_IO_READ or tlp.fmt_type == TLP_IO_WRITE or + tlp.fmt_type == TLP_MEM_READ or tlp.fmt_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 = PcieId(self.bus_num, self.device_num, tlp.completer_id.function) + + if not tlp.discontinue: + yield from 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 = PcieId(self.bus_num, self.device_num, tlp.requester_id.function) + + if not tlp.discontinue: + if self.functions[tlp.requester_id.function].bus_master_enable: + yield from 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 + # TODO four cycle delay + function = cfg_mgmt_function_number + reg_num = cfg_mgmt_addr + if cfg_mgmt_read_write_done: + cfg_mgmt_read_write_done.next = 0 + elif cfg_mgmt_read: + cfg_mgmt_read_data.next = self.functions[function].read_config_register(reg_num) + cfg_mgmt_read_write_done.next = 1 + elif cfg_mgmt_write: + 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_debug_access + + # configuration status + 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 = min(max((self.functions[0].negotiated_link_width).bit_length()-1, 0), 4) + cfg_current_speed.next = min(max(self.functions[0].current_link_speed-1, 0), 3) + cfg_max_payload.next = self.functions[0].max_payload_size & 3 + cfg_max_read_req.next = self.functions[0].max_read_request_size + + status = 0 + for k in range(len(self.functions)): + if self.functions[k].bus_master_enable: + status |= 0x07 << k*4 + if self.functions[k].interrupt_disable: + status |= 0x08 << k*4 + 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_local_err_out + #cfg_local_err_valid + #cfg_rx_pm_state + #cfg_tx_pm_state + #cfg_ltssm_state + + status = 0 + for k in range(len(self.functions)): + if self.functions[k].read_completion_boundary: + status |= 1 << k + cfg_rcb_status.next = status + + #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 + + # 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_dsn + #cfg_ds_port_number + #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 + val = 0 + if self.functions[0].msi_enable: + val |= 1 + if len(self.functions) > 1: + if self.functions[1].msi_enable: + val |= 2 + cfg_interrupt_msi_enable.next = val + + cfg_interrupt_msi_sent.next = 0 + cfg_interrupt_msi_fail.next = 0 + if (cfg_interrupt_msi_int): + n = int(cfg_interrupt_msi_int) + #bits = [i for i in range(n.bit_length()) if n >> i & 1] + bits = [i for i in range(32) if n >> i & 1] + if len(bits) == 1 and cfg_interrupt_msi_function_number < len(self.functions): + yield self.functions[cfg_interrupt_msi_function_number].issue_msi_interrupt(bits[0], attr=int(cfg_interrupt_msi_attr)) + cfg_interrupt_msi_sent.next = 1 + + val = 0 + val |= self.functions[0].msi_multiple_message_enable & 0x7 + if len(self.functions) > 1: + val |= (self.functions[1].msi_multiple_message_enable & 0x7) << 3 + cfg_interrupt_msi_mmenable.next = val + + #cfg_interrupt_msi_mask_update + + if cfg_interrupt_msi_select == 0b1111: + cfg_interrupt_msi_data.next = 0 + else: + if cfg_interrupt_msi_select < len(self.functions): + cfg_interrupt_msi_data.next = self.functions[cfg_interrupt_msi_select].msi_mask_bits; + else: + cfg_interrupt_msi_data.next = 0 + if cfg_interrupt_msi_pending_status_data_enable: + if cfg_interrupt_msi_pending_status_function_num < len(self.functions): + self.functions[cfg_interrupt_msi_pending_status_function_num].msi_pending_bits = int(cfg_interrupt_msi_pending_status) + + # MSI-X + val = 0 + if self.functions[0].msix_enable: + val |= 1 + if len(self.functions) > 1: + if self.functions[1].msix_enable: + val |= 2 + cfg_interrupt_msix_enable.next = val + val = 0 + if self.functions[0].msix_function_mask: + val |= 1 + if len(self.functions) > 1: + if self.functions[1].msix_function_mask: + val |= 2 + cfg_interrupt_msix_mask.next = val + #cfg_interrupt_msix_vf_enable + #cfg_interrupt_msix_vf_mask + + if cfg_interrupt_msix_int: + if cfg_interrupt_msi_function_number < len(self.functions): + yield self.functions[cfg_interrupt_msi_function_number].issue_msix_interrupt(int(cfg_interrupt_msix_address), int(cfg_interrupt_msix_data), attr=int(cfg_interrupt_msi_attr)) + cfg_interrupt_msi_sent.next = 1 + + # MSI/MSI-X + #cfg_interrupt_msi_tph_present + #cfg_interrupt_msi_tph_type + #cfg_interrupt_msi_tph_st_tag + + # 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_usp.py b/tb/test_pcie_usp.py new file mode 100755 index 000000000..bf24c6ec2 --- /dev/null +++ b/tb/test_pcie_usp.py @@ -0,0 +1,925 @@ +#!/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_usp + +#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)[88:]) + 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(intbv(3)[2:]) + 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)[62:]) + 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_num0=Signal(intbv(0)[6:]) + pcie_rq_seq_num_vld0=Signal(bool(0)) + pcie_rq_seq_num1=Signal(intbv(0)[6:]) + pcie_rq_seq_num_vld1=Signal(bool(0)) + pcie_rq_tag0=Signal(intbv(0)[8:]) + pcie_rq_tag1=Signal(intbv(0)[8:]) + pcie_rq_tag_av=Signal(intbv(0)[4:]) + pcie_rq_tag_vld0=Signal(bool(0)) + pcie_rq_tag_vld1=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)[4:]) + pcie_tfc_npd_av=Signal(intbv(0)[4:]) + + # Configuration Management Interface + cfg_mgmt_addr=Signal(intbv(0)[10:]) + cfg_mgmt_function_number=Signal(intbv(0)[8:]) + 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_debug_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)[3:]) + cfg_current_speed=Signal(intbv(0)[2:]) + cfg_max_payload=Signal(intbv(0)[2:]) + cfg_max_read_req=Signal(intbv(0)[3:]) + cfg_function_status=Signal(intbv(0)[16:]) + cfg_vf_status=Signal(intbv(0)[504:]) + cfg_function_power_state=Signal(intbv(0)[12:]) + cfg_vf_power_state=Signal(intbv(0)[756:]) + 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_local_err_out=Signal(intbv(0)[5:]) + cfg_local_err_valid=Signal(bool(0)) + cfg_rx_pm_state=Signal(intbv(0)[2:]) + cfg_tx_pm_state=Signal(intbv(0)[2:]) + cfg_ltssm_state=Signal(intbv(0)[6:]) + cfg_rcb_status=Signal(intbv(0)[4:]) + cfg_obff_enable=Signal(intbv(0)[2:]) + cfg_pl_status_change=Signal(bool(0)) + cfg_tph_requester_enable=Signal(intbv(0)[4:]) + cfg_tph_st_mode=Signal(intbv(0)[12:]) + cfg_vf_tph_requester_enable=Signal(intbv(0)[252:]) + cfg_vf_tph_st_mode=Signal(intbv(0)[756:]) + + # 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:]) + + # 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_dsn=Signal(intbv(0)[64:]) + cfg_ds_port_number=Signal(intbv(0)[8:]) + 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)[4:]) + cfg_vf_flr_done=Signal(intbv(0)[1:]) + cfg_flr_in_process=Signal(intbv(0)[4:]) + cfg_vf_flr_in_process=Signal(intbv(0)[252:]) + 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)[4:]) + cfg_interrupt_msi_mmenable=Signal(intbv(0)[12:]) + cfg_interrupt_msi_mask_update=Signal(bool(0)) + cfg_interrupt_msi_data=Signal(intbv(0)[32:]) + cfg_interrupt_msi_select=Signal(intbv(0)[2:]) + cfg_interrupt_msi_int=Signal(intbv(0)[32:]) + cfg_interrupt_msi_pending_status=Signal(intbv(0)[32:]) + cfg_interrupt_msi_pending_status_data_enable=Signal(bool(0)) + cfg_interrupt_msi_pending_status_function_num=Signal(intbv(0)[2:]) + cfg_interrupt_msi_sent=Signal(bool(0)) + cfg_interrupt_msi_fail=Signal(bool(0)) + cfg_interrupt_msix_enable=Signal(intbv(0)[4:]) + cfg_interrupt_msix_mask=Signal(intbv(0)[4:]) + cfg_interrupt_msix_vf_enable=Signal(intbv(0)[252:]) + cfg_interrupt_msix_vf_mask=Signal(intbv(0)[252:]) + 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_vec_pending=Signal(intbv(0)[2:]) + cfg_interrupt_msix_vec_pending_status=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)[8:]) + cfg_interrupt_msi_function_number=Signal(intbv(0)[8:]) + + # 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)) + phy_rdy_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_usp.UltrascalePlusPCIe() + + 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].msi_multiple_message_capable = 5 + + 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_num0=pcie_rq_seq_num0, + pcie_rq_seq_num_vld0=pcie_rq_seq_num_vld0, + pcie_rq_seq_num1=pcie_rq_seq_num1, + pcie_rq_seq_num_vld1=pcie_rq_seq_num_vld1, + pcie_rq_tag0=pcie_rq_tag0, + pcie_rq_tag1=pcie_rq_tag1, + pcie_rq_tag_av=pcie_rq_tag_av, + pcie_rq_tag_vld0=pcie_rq_tag_vld0, + pcie_rq_tag_vld1=pcie_rq_tag_vld1, + + # 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_function_number=cfg_mgmt_function_number, + 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_debug_access=cfg_mgmt_debug_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_local_err_out=cfg_local_err_out, + cfg_local_err_valid=cfg_local_err_valid, + cfg_rx_pm_state=cfg_rx_pm_state, + cfg_tx_pm_state=cfg_tx_pm_state, + cfg_ltssm_state=cfg_ltssm_state, + cfg_rcb_status=cfg_rcb_status, + 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, + + # 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_dsn=cfg_dsn, + cfg_ds_port_number=cfg_ds_port_number, + 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_mmenable=cfg_interrupt_msi_mmenable, + cfg_interrupt_msi_mask_update=cfg_interrupt_msi_mask_update, + cfg_interrupt_msi_data=cfg_interrupt_msi_data, + cfg_interrupt_msi_select=cfg_interrupt_msi_select, + cfg_interrupt_msi_int=cfg_interrupt_msi_int, + cfg_interrupt_msi_pending_status=cfg_interrupt_msi_pending_status, + cfg_interrupt_msi_pending_status_data_enable=cfg_interrupt_msi_pending_status_data_enable, + cfg_interrupt_msi_pending_status_function_num=cfg_interrupt_msi_pending_status_function_num, + cfg_interrupt_msi_sent=cfg_interrupt_msi_sent, + cfg_interrupt_msi_fail=cfg_interrupt_msi_fail, + 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_vec_pending=cfg_interrupt_msix_vec_pending, + cfg_interrupt_msix_vec_pending_status=cfg_interrupt_msix_vec_pending_status, + 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, + phy_rdy_out=phy_rdy_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_usp.TLP_us().unpack_us_cq(pkt, dw) + + print(tlp) + + if (tlp.fmt_type == pcie.TLP_IO_READ): + print("IO read") + + cpl = pcie_usp.TLP_us() + cpl.set_completion(tlp, pcie_usp.PcieId(0, 0, 0)) + cpl.fmt_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_type == pcie.TLP_IO_WRITE): + print("IO write") + + cpl = pcie_usp.TLP_us() + cpl.set_completion(tlp, pcie_usp.PcieId(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_type == pcie.TLP_MEM_READ or tlp.fmt_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_usp.TLP_us() + cpl.set_completion(tlp, pcie_usp.PcieId(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_type == pcie.TLP_MEM_WRITE or tlp.fmt_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, configure_msi=True) + + yield delay(100) + + yield clk.posedge + print("test 2: IO and memory read/write") + current_test.next = 2 + + yield from 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 from 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 from 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 from 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_usp.TLP_us() + tlp.fmt_type = pcie.TLP_IO_WRITE + tlp.requester_id = pcie_usp.PcieId(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_usp.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_usp.TLP_us() + tlp.fmt_type = pcie.TLP_IO_READ + tlp.requester_id = pcie_usp.PcieId(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_usp.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_type = pcie.TLP_MEM_WRITE_64 + else: + tlp.fmt_type = pcie.TLP_MEM_WRITE + tlp.requester_id = pcie_usp.PcieId(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_usp.TLP_us() + if addr > 0xffffffff: + tlp.fmt_type = pcie.TLP_MEM_READ_64 + else: + tlp.fmt_type = pcie.TLP_MEM_READ + tlp.requester_id = pcie_usp.PcieId(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_usp.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('