text: """
  
  Copyright 2020, The Regents of the University of California.
  All rights reserved.
  
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
  
     1. Redistributions of source code must retain the above copyright notice,
        this list of conditions and the following disclaimer.
  
     2. Redistributions in binary form must reproduce the above copyright notice,
        this list of conditions and the following disclaimer in the documentation
        and/or other materials provided with the distribution.
  
  THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
  OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  OF SUCH DAMAGE.
  
  The views and conclusions contained in the software and documentation are those
  of the authors and should not be interpreted as representing official policies,
  either expressed or implied, of The Regents of the University of California.
  
  """
  
  import logging
  import os
  import sys
  
  import scapy.utils
  from scapy.layers.l2 import Ether
  from scapy.layers.inet import IP, UDP
  
  import cocotb_test.simulator
  
  import cocotb
  from cocotb.log import SimLog
  from cocotb.clock import Clock
  from cocotb.triggers import RisingEdge, FallingEdge, Timer
  
  from cocotbext.pcie.core import RootComplex
  from cocotbext.pcie.xilinx.us import UltraScalePlusPcieDevice
  
  from cocotbext.axi import AxiStreamSource, AxiStreamSink, AxiLiteRam
  
  try:
      import mqnic
  except ImportError:
      # attempt import from current directory
      sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
      try:
          import mqnic
      finally:
          del sys.path[0]
  
  
  class TB(object):
      def __init__(self, dut):
          self.dut = dut
  
          self.BAR0_APERTURE = int(os.getenv("PARAM_BAR0_APERTURE"))
  
          self.log = SimLog("cocotb.tb")
          self.log.setLevel(logging.DEBUG)
  
          # PCIe
          self.rc = RootComplex()
  
          self.rc.max_payload_size = 0x1  # 256 bytes
          self.rc.max_read_request_size = 0x2  # 512 bytes
  
          self.dev = UltraScalePlusPcieDevice(
              # configuration options
              pcie_generation=3,
              pcie_link_width=16,
              user_clk_frequency=250e6,
              alignment="dword",
              cq_cc_straddle=False,
              rq_rc_straddle=False,
              rc_4tlp_straddle=False,
              enable_pf1=False,
              enable_client_tag=True,
              enable_extended_tag=True,
              enable_parity=False,
              enable_rx_msg_interface=False,
              enable_sriov=False,
              enable_extended_configuration=False,
  
              enable_pf0_msi=True,
              enable_pf1_msi=False,
  
              # signals
              # Clock and Reset Interface
              user_clk=dut.clk_250mhz,
              user_reset=dut.rst_250mhz,
              # user_lnk_up
              # sys_clk
              # sys_clk_gt
              # sys_reset
              # phy_rdy_out
  
              # Requester reQuest Interface
              rq_entity=dut,
              rq_name="m_axis_rq",
              pcie_rq_seq_num0=dut.s_axis_rq_seq_num_0,
              pcie_rq_seq_num_vld0=dut.s_axis_rq_seq_num_valid_0,
              pcie_rq_seq_num1=dut.s_axis_rq_seq_num_1,
              pcie_rq_seq_num_vld1=dut.s_axis_rq_seq_num_valid_1,
              # pcie_rq_tag0
              # pcie_rq_tag1
              # pcie_rq_tag_av
              # pcie_rq_tag_vld0
              # pcie_rq_tag_vld1
  
              # Requester Completion Interface
              rc_entity=dut,
              rc_name="s_axis_rc",
  
              # Completer reQuest Interface
              cq_entity=dut,
              cq_name="s_axis_cq",
              # pcie_cq_np_req
              # pcie_cq_np_req_count
  
              # Completer Completion Interface
              cc_entity=dut,
              cc_name="m_axis_cc",
  
              # Transmit Flow Control Interface
              # pcie_tfc_nph_av=dut.pcie_tfc_nph_av,
              # pcie_tfc_npd_av=dut.pcie_tfc_npd_av,
  
              # Configuration Management Interface
              cfg_mgmt_addr=dut.cfg_mgmt_addr,
              cfg_mgmt_function_number=dut.cfg_mgmt_function_number,
              cfg_mgmt_write=dut.cfg_mgmt_write,
              cfg_mgmt_write_data=dut.cfg_mgmt_write_data,
              cfg_mgmt_byte_enable=dut.cfg_mgmt_byte_enable,
              cfg_mgmt_read=dut.cfg_mgmt_read,
              cfg_mgmt_read_data=dut.cfg_mgmt_read_data,
              cfg_mgmt_read_write_done=dut.cfg_mgmt_read_write_done,
              # cfg_mgmt_debug_access
  
              # Configuration Status Interface
              # cfg_phy_link_down
              # cfg_phy_link_status
              # cfg_negotiated_width
              # cfg_current_speed
              cfg_max_payload=dut.cfg_max_payload,
              cfg_max_read_req=dut.cfg_max_read_req,
              # cfg_function_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_error_out
              # cfg_local_error_valid
              # cfg_rx_pm_state
              # cfg_tx_pm_state
              # cfg_ltssm_state
              # cfg_rcb_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 Interface
              # cfg_msg_received
              # cfg_msg_received_data
              # cfg_msg_received_type
  
              # Configuration Transmit Message Interface
              # cfg_msg_transmit
              # cfg_msg_transmit_type
              # cfg_msg_transmit_data
              # cfg_msg_transmit_done
  
              # Configuration Flow Control Interface
              cfg_fc_ph=dut.cfg_fc_ph,
              cfg_fc_pd=dut.cfg_fc_pd,
              cfg_fc_nph=dut.cfg_fc_nph,
              cfg_fc_npd=dut.cfg_fc_npd,
              cfg_fc_cplh=dut.cfg_fc_cplh,
              cfg_fc_cpld=dut.cfg_fc_cpld,
              cfg_fc_sel=dut.cfg_fc_sel,
  
              # Configuration Control Interface
              # cfg_hot_reset_in
              # cfg_hot_reset_out
              # cfg_config_space_enable
              # cfg_dsn
              # cfg_bus_number
              # 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=dut.status_error_cor,
              cfg_err_uncor_in=dut.status_error_uncor,
              # cfg_flr_in_process
              # cfg_flr_done
              # cfg_vf_flr_in_process
              # cfg_vf_flr_func_num
              # cfg_vf_flr_done
              # cfg_pm_aspm_l1_entry_reject
              # cfg_pm_aspm_tx_l0s_entry_disable
              # cfg_req_pm_transition_l23_ready
              # cfg_link_training_enable
  
              # Configuration Interrupt Controller Interface
              # cfg_interrupt_int
              # cfg_interrupt_sent
              # cfg_interrupt_pending
              cfg_interrupt_msi_enable=dut.cfg_interrupt_msi_enable,
              cfg_interrupt_msi_mmenable=dut.cfg_interrupt_msi_mmenable,
              cfg_interrupt_msi_mask_update=dut.cfg_interrupt_msi_mask_update,
              cfg_interrupt_msi_data=dut.cfg_interrupt_msi_data,
              # cfg_interrupt_msi_select=dut.cfg_interrupt_msi_select,
              cfg_interrupt_msi_int=dut.cfg_interrupt_msi_int,
              cfg_interrupt_msi_pending_status=dut.cfg_interrupt_msi_pending_status,
              cfg_interrupt_msi_pending_status_data_enable=dut.cfg_interrupt_msi_pending_status_data_enable,
              # cfg_interrupt_msi_pending_status_function_num=dut.cfg_interrupt_msi_pending_status_function_num,
              cfg_interrupt_msi_sent=dut.cfg_interrupt_msi_sent,
              cfg_interrupt_msi_fail=dut.cfg_interrupt_msi_fail,
              # cfg_interrupt_msix_enable
              # cfg_interrupt_msix_mask
              # cfg_interrupt_msix_vf_enable
              # cfg_interrupt_msix_vf_mask
              # cfg_interrupt_msix_address
              # cfg_interrupt_msix_data
              # cfg_interrupt_msix_int
              # cfg_interrupt_msix_vec_pending
              # cfg_interrupt_msix_vec_pending_status
              cfg_interrupt_msi_attr=dut.cfg_interrupt_msi_attr,
              cfg_interrupt_msi_tph_present=dut.cfg_interrupt_msi_tph_present,
              cfg_interrupt_msi_tph_type=dut.cfg_interrupt_msi_tph_type,
              # cfg_interrupt_msi_tph_st_tag=dut.cfg_interrupt_msi_tph_st_tag,
              # cfg_interrupt_msi_function_number=dut.cfg_interrupt_msi_function_number,
  
              # Configuration Extend Interface
              # 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
          )
  
          # self.dev.log.setLevel(logging.DEBUG)
  
          self.rc.make_port().connect(self.dev)
  
          self.driver = mqnic.Driver(self.rc)
  
          self.dev.functions[0].msi_multiple_message_capable = 5
  
          self.dev.functions[0].configure_bar(0, 2**self.BAR0_APERTURE, ext=True, prefetch=True)
  
          # Ethernet
          cocotb.fork(Clock(dut.qsfp0_rx_clk, 3.102, units="ns").start())
          self.qsfp0_source = AxiStreamSource(dut, "qsfp0_rx_axis", dut.qsfp0_rx_clk, dut.qsfp0_rx_rst)
          cocotb.fork(Clock(dut.qsfp0_tx_clk, 3.102, units="ns").start())
          self.qsfp0_sink = AxiStreamSink(dut, "qsfp0_tx_axis", dut.qsfp0_tx_clk, dut.qsfp0_tx_rst)
  
          cocotb.fork(Clock(dut.qsfp1_rx_clk, 3.102, units="ns").start())
          self.qsfp1_source = AxiStreamSource(dut, "qsfp1_rx_axis", dut.qsfp1_rx_clk, dut.qsfp1_rx_rst)
          cocotb.fork(Clock(dut.qsfp1_tx_clk, 3.102, units="ns").start())
          self.qsfp1_sink = AxiStreamSink(dut, "qsfp1_tx_axis", dut.qsfp1_tx_clk, dut.qsfp1_tx_rst)
  
          dut.qspi_dq_i.setimmediatevalue(0)
  
          self.cms_ram = AxiLiteRam(dut, "m_axil_cms", dut.m_axil_cms_clk, dut.m_axil_cms_rst, size=256*1024)
  
          self.loopback_enable = False
          cocotb.fork(self._run_loopback())
  
      async def init(self):
  
          self.dut.qsfp0_rx_rst.setimmediatevalue(0)
          self.dut.qsfp0_tx_rst.setimmediatevalue(0)
          self.dut.qsfp1_rx_rst.setimmediatevalue(0)
          self.dut.qsfp1_tx_rst.setimmediatevalue(0)
  
          await RisingEdge(self.dut.clk_250mhz)
          await RisingEdge(self.dut.clk_250mhz)
  
          self.dut.qsfp0_rx_rst.setimmediatevalue(1)
          self.dut.qsfp0_tx_rst.setimmediatevalue(1)
          self.dut.qsfp1_rx_rst.setimmediatevalue(1)
          self.dut.qsfp1_tx_rst.setimmediatevalue(1)
  
          await FallingEdge(self.dut.rst_250mhz)
          await Timer(100, 'ns')
  
          await RisingEdge(self.dut.clk_250mhz)
          await RisingEdge(self.dut.clk_250mhz)
  
          self.dut.qsfp0_rx_rst.setimmediatevalue(0)
          self.dut.qsfp0_tx_rst.setimmediatevalue(0)
          self.dut.qsfp1_rx_rst.setimmediatevalue(0)
          self.dut.qsfp1_tx_rst.setimmediatevalue(0)
  
          await self.rc.enumerate(enable_bus_mastering=True, configure_msi=True)
  
      async def _run_loopback(self):
          while True:
              await RisingEdge(self.dut.clk_250mhz)
  
              if self.loopback_enable:
                  if not self.qsfp0_sink.empty():
                      await self.qsfp0_source.send(await self.qsfp0_sink.recv())
                  if not self.qsfp1_sink.empty():
                      await self.qsfp1_source.send(await self.qsfp1_sink.recv())
  
  
  @cocotb.test()
  async def run_test_nic(dut):
  
      tb = TB(dut)
  
      await tb.init()
  
      tb.log.info("Init driver")
      await tb.driver.init_dev(tb.dev.functions[0].pcie_id)
      await tb.driver.interfaces[0].open()
      # await driver.interfaces[1].open()
  
      # enable queues
      tb.log.info("Enable queues")
      await tb.rc.mem_write_dword(tb.driver.interfaces[0].ports[0].hw_addr+mqnic.MQNIC_PORT_REG_SCHED_ENABLE, 0x00000001)
      for k in range(tb.driver.interfaces[0].tx_queue_count):
          await tb.rc.mem_write_dword(tb.driver.interfaces[0].ports[0].schedulers[0].hw_addr+4*k, 0x00000003)
  
      # wait for all writes to complete
      await tb.rc.mem_read(tb.driver.hw_addr, 4)
      tb.log.info("Init complete")
  
      tb.log.info("Send and receive single packet")
  
      data = bytearray([x % 256 for x in range(1024)])
  
      await tb.driver.interfaces[0].start_xmit(data, 0)
  
      pkt = await tb.qsfp0_sink.recv()
      tb.log.info("Packet: %s", pkt)
  
      await tb.qsfp0_source.send(pkt)
  
      pkt = await tb.driver.interfaces[0].recv()
  
      tb.log.info("Packet: %s", pkt)
      assert pkt.rx_checksum == ~scapy.utils.checksum(bytes(pkt.data[14:])) & 0xffff
  
      # await tb.driver.interfaces[1].start_xmit(data, 0)
  
      # pkt = await tb.qsfp1_0_sink.recv()
      # tb.log.info("Packet: %s", pkt)
  
      # await tb.qsfp1_0_source.send(pkt)
  
      # pkt = await tb.driver.interfaces[1].recv()
  
      # tb.log.info("Packet: %s", pkt)
      # assert pkt.rx_checksum == ~scapy.utils.checksum(bytes(pkt.data[14:])) & 0xffff
  
      tb.log.info("RX and TX checksum tests")
  
      payload = bytes([x % 256 for x in range(256)])
      eth = Ether(src='5A:51:52:53:54:55', dst='DA:D1:D2:D3:D4:D5')
      ip = IP(src='192.168.1.100', dst='192.168.1.101')
      udp = UDP(sport=1, dport=2)
      test_pkt = eth / ip / udp / payload
  
      test_pkt2 = test_pkt.copy()
      test_pkt2[UDP].chksum = scapy.utils.checksum(bytes(test_pkt2[UDP]))
  
      await tb.driver.interfaces[0].start_xmit(test_pkt2.build(), 0, 34, 6)
  
      pkt = await tb.qsfp0_sink.recv()
      tb.log.info("Packet: %s", pkt)
  
      await tb.qsfp0_source.send(pkt)
  
      pkt = await tb.driver.interfaces[0].recv()
  
      tb.log.info("Packet: %s", pkt)
      assert pkt.rx_checksum == ~scapy.utils.checksum(bytes(pkt.data[14:])) & 0xffff
      assert Ether(pkt.data).build() == test_pkt.build()
  
      tb.log.info("Multiple small packets")
  
      count = 64
  
      pkts = [bytearray([(x+k) % 256 for x in range(60)]) for k in range(count)]
  
      tb.loopback_enable = True
  
      for p in pkts:
          await tb.driver.interfaces[0].start_xmit(p, 0)
  
      for k in range(count):
          pkt = await tb.driver.interfaces[0].recv()
  
          tb.log.info("Packet: %s", pkt)
          assert pkt.data == pkts[k]
          assert pkt.rx_checksum == ~scapy.utils.checksum(bytes(pkt.data[14:])) & 0xffff
  
      tb.loopback_enable = False
  
      tb.log.info("Multiple large packets")
  
      count = 64
  
      pkts = [bytearray([(x+k) % 256 for x in range(1514)]) for k in range(count)]
  
      tb.loopback_enable = True
  
      for p in pkts:
          await tb.driver.interfaces[0].start_xmit(p, 0)
  
      for k in range(count):
          pkt = await tb.driver.interfaces[0].recv()
  
          tb.log.info("Packet: %s", pkt)
          assert pkt.data == pkts[k]
          assert pkt.rx_checksum == ~scapy.utils.checksum(bytes(pkt.data[14:])) & 0xffff
  
      tb.loopback_enable = False
  
      tb.log.info("Jumbo frames")
  
      count = 64
  
      pkts = [bytearray([(x+k) % 256 for x in range(9014)]) for k in range(count)]
  
      tb.loopback_enable = True
  
      for p in pkts:
          await tb.driver.interfaces[0].start_xmit(p, 0)
  
      for k in range(count):
          pkt = await tb.driver.interfaces[0].recv()
  
          tb.log.info("Packet: %s", pkt)
          assert pkt.data == pkts[k]
          assert pkt.rx_checksum == ~scapy.utils.checksum(bytes(pkt.data[14:])) & 0xffff
  
      tb.loopback_enable = False
  
      await RisingEdge(dut.clk_250mhz)
      await RisingEdge(dut.clk_250mhz)
  
  
  # cocotb-test
  
  tests_dir = os.path.dirname(__file__)
  rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
  lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
  axi_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axi', 'rtl'))
  axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
  eth_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'eth', 'rtl'))
  pcie_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'pcie', 'rtl'))
  
  
  def test_fpga_core(request):
      dut = "fpga_core"
      module = os.path.splitext(os.path.basename(__file__))[0]
      toplevel = dut
  
      verilog_sources = [
          os.path.join(rtl_dir, f"{dut}.v"),
          os.path.join(rtl_dir, "common", "mqnic_interface.v"),
          os.path.join(rtl_dir, "common", "mqnic_port.v"),
          os.path.join(rtl_dir, "common", "cpl_write.v"),
          os.path.join(rtl_dir, "common", "cpl_op_mux.v"),
          os.path.join(rtl_dir, "common", "desc_fetch.v"),
          os.path.join(rtl_dir, "common", "desc_op_mux.v"),
          os.path.join(rtl_dir, "common", "queue_manager.v"),
          os.path.join(rtl_dir, "common", "cpl_queue_manager.v"),
          os.path.join(rtl_dir, "common", "tx_engine.v"),
          os.path.join(rtl_dir, "common", "rx_engine.v"),
          os.path.join(rtl_dir, "common", "tx_checksum.v"),
          os.path.join(rtl_dir, "common", "rx_hash.v"),
          os.path.join(rtl_dir, "common", "rx_checksum.v"),
          os.path.join(rtl_dir, "common", "tx_scheduler_rr.v"),
          os.path.join(rtl_dir, "common", "event_mux.v"),
          os.path.join(rtl_dir, "common", "tdma_scheduler.v"),
          os.path.join(rtl_dir, "common", "tdma_ber.v"),
          os.path.join(rtl_dir, "common", "tdma_ber_ch.v"),
          os.path.join(eth_rtl_dir, "ptp_clock.v"),
          os.path.join(eth_rtl_dir, "ptp_clock_cdc.v"),
          os.path.join(eth_rtl_dir, "ptp_perout.v"),
          os.path.join(eth_rtl_dir, "ptp_ts_extract.v"),
          os.path.join(axi_rtl_dir, "axil_interconnect.v"),
          os.path.join(axi_rtl_dir, "arbiter.v"),
          os.path.join(axi_rtl_dir, "priority_encoder.v"),
          os.path.join(axis_rtl_dir, "axis_adapter.v"),
          os.path.join(axis_rtl_dir, "axis_arb_mux.v"),
          os.path.join(axis_rtl_dir, "axis_async_fifo.v"),
          os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"),
          os.path.join(axis_rtl_dir, "axis_fifo.v"),
          os.path.join(axis_rtl_dir, "axis_register.v"),
          os.path.join(axis_rtl_dir, "axis_pipeline_register.v"),
          os.path.join(pcie_rtl_dir, "pcie_us_axil_master.v"),
          os.path.join(pcie_rtl_dir, "dma_if_pcie_us.v"),
          os.path.join(pcie_rtl_dir, "dma_if_pcie_us_rd.v"),
          os.path.join(pcie_rtl_dir, "dma_if_pcie_us_wr.v"),
          os.path.join(pcie_rtl_dir, "dma_if_mux.v"),
          os.path.join(pcie_rtl_dir, "dma_if_mux_rd.v"),
          os.path.join(pcie_rtl_dir, "dma_if_mux_wr.v"),
          os.path.join(pcie_rtl_dir, "dma_psdpram.v"),
          os.path.join(pcie_rtl_dir, "dma_client_axis_sink.v"),
          os.path.join(pcie_rtl_dir, "dma_client_axis_source.v"),
          os.path.join(pcie_rtl_dir, "pcie_us_cfg.v"),
          os.path.join(pcie_rtl_dir, "pcie_us_msi.v"),
          os.path.join(pcie_rtl_dir, "pcie_tag_manager.v"),
          os.path.join(pcie_rtl_dir, "pulse_merge.v"),
      ]
  
      parameters = {}
  
      parameters['AXIS_PCIE_DATA_WIDTH'] = 512
      parameters['AXIS_PCIE_KEEP_WIDTH'] = parameters['AXIS_PCIE_DATA_WIDTH'] // 32
      parameters['AXIS_PCIE_RQ_USER_WIDTH'] = 62 if parameters['AXIS_PCIE_DATA_WIDTH'] < 512 else 137
      parameters['AXIS_PCIE_RC_USER_WIDTH'] = 75 if parameters['AXIS_PCIE_DATA_WIDTH'] < 512 else 161
      parameters['AXIS_PCIE_CQ_USER_WIDTH'] = 88 if parameters['AXIS_PCIE_DATA_WIDTH'] < 512 else 183
      parameters['AXIS_PCIE_CC_USER_WIDTH'] = 33 if parameters['AXIS_PCIE_DATA_WIDTH'] < 512 else 81
      parameters['RQ_SEQ_NUM_WIDTH'] = 6
      parameters['BAR0_APERTURE'] = 24
      parameters['AXIS_ETH_DATA_WIDTH'] = 512
      parameters['AXIS_ETH_KEEP_WIDTH'] = parameters['AXIS_ETH_DATA_WIDTH'] // 8
  
      extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
  
      sim_build = os.path.join(tests_dir, "sim_build",
          request.node.name.replace('[', '-').replace(']', ''))
  
      cocotb_test.simulator.run(
          python_search=[tests_dir],
          verilog_sources=verilog_sources,
          toplevel=toplevel,
          module=module,
          parameters=parameters,
          sim_build=sim_build,
          extra_env=extra_env,
      )
  