diff --git a/applications/rdma_demo/libraries/rdma_packetiser/cocotb/dp_bus.py b/applications/rdma_demo/libraries/rdma_packetiser/cocotb/dp_bus.py new file mode 100644 index 0000000000000000000000000000000000000000..1b627f866b1bbe80e0327711b30e040ba8021fe3 --- /dev/null +++ b/applications/rdma_demo/libraries/rdma_packetiser/cocotb/dp_bus.py @@ -0,0 +1,354 @@ + +from typing import Iterable, Union, Optional + +import cocotb +from cocotb.utils import hexdump +from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.binary import BinaryValue +from cocotb.result import TestError +from cocotb_bus.drivers import ValidatedBusDriver +from cocotb_bus.monitors import BusMonitor + + +class SosiDriver(ValidatedBusDriver): + _optional_signals = ['valid', 'sop', 'eop', 'sync', 'bsn', 'data', 're', 'im', 'empty', 'channel', 'err'] + _signals = [] + _default_config = { + "dataBitsPerSymbol" : 8, + "firstSymbolInHighOrderBits" : True, + "maxChannel" : 1, + "readyLatency" : 1 + } + def __init__(self, dut, name, clk, data_w = None): + ValidatedBusDriver.__init__(self, dut, name, clk, bus_separator='.') + self.config = self._default_config.copy() + self.use_empty = True + self.config["useEmpty"] = self.use_empty + if data_w == None: + self.data_w = len(self.bus.data) + else: + self.data_w = data_w + + async def _wait_ready(self): + """Wait for a ready cycle on the bus before continuing. + + Can no longer drive values this cycle... + + FIXME assumes readyLatency of 0 + """ + await ReadOnly() + while False: #not self.bus.ready.value: + await RisingEdge(self.clock) + await ReadOnly() + + async def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] = None) -> None: + """Args: + string: A string of bytes to send over the bus. + channel: Channel to send the data on. + """ + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + firstword = True + + # FIXME: buses that aren't an integer numbers of bytes + bus_width = int(self.data_w / 8) + + word = BinaryValue(n_bits=len(self.bus.data), + bigEndian=self.config["firstSymbolInHighOrderBits"]) + + single = BinaryValue(n_bits=1, bigEndian=False) + if self.use_empty: + empty = BinaryValue(n_bits=len(self.bus.empty), bigEndian=False) + + # Drive some defaults since we don't know what state we're in + if self.use_empty: + self.bus.empty.value = 0 + self.bus.sop.value = 0 + self.bus.eop.value = 0 + self.bus.valid.value = 0 + if hasattr(self.bus, 'err'): + self.bus.err.value = 0 + + if hasattr(self.bus, 'channel'): + self.bus.channel.value = 0 + elif channel is not None: + raise TestError("%s does not have a channel signal" % self.name) + + while string: + if not firstword or (firstword and sync): + await clkedge + + # Insert a gap where valid is low + if not self.on: + self.bus.valid.value = 0 + for _ in range(self.off): + await clkedge + + # Grab the next set of on/off values + self._next_valids() + + # Consume a valid cycle + if self.on is not True and self.on: + self.on -= 1 + + self.bus.valid.value = 1 + if hasattr(self.bus, 'channel'): + if channel is None: + self.bus.channel.value = 0 + elif channel > self.config['maxChannel'] or channel < 0: + raise TestError("%s: Channel value %d is outside range 0-%d" % + (self.name, channel, self.config['maxChannel'])) + else: + self.bus.channel.value = channel + + if firstword: + self.bus.sop.value = 1 + firstword = False + else: + self.bus.sop.value = 0 + + nbytes = min(len(string), bus_width) + data = string[:nbytes] + + # set unused bits of dp_sosi.data field to 0 + data = bytes(word.n_bits // 8 - bus_width) + data + word.buff = data + + if len(string) <= bus_width: + self.bus.eop.value = 1 + if self.use_empty: + self.bus.empty.value = bus_width - len(string) + string = b"" + else: + string = string[bus_width:] + + self.bus.data.value = word + + # If this is a bus with a ready signal, wait for this word to + # be acknowledged + if hasattr(self.bus, "ready"): + await self._wait_ready() + + await clkedge + self.bus.valid.value = 0 + self.bus.eop.value = 0 + word.binstr = "x" * self.data_w + single.binstr = "x" + self.bus.data.value = word + self.bus.sop.value = single + self.bus.eop.value = single + + if self.use_empty: + empty.binstr = "x" * len(self.bus.empty) + self.bus.empty.value = empty + if hasattr(self.bus, 'channel'): + channel_value = BinaryValue(n_bits=len(self.bus.channel), bigEndian=False, + value="x" * len(self.bus.channel)) + self.bus.channel.value = channel_value + + async def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: + """Args: + pkt: Will yield objects with attributes matching the + signal names for each individual bus cycle. + """ + clkedge = RisingEdge(self.clock) + firstword = True + + for word in pkt: + if not firstword or (firstword and sync): + await clkedge + + firstword = False + + # Insert a gap where valid is low + if not self.on: + self.bus.valid.value = 0 + for _ in range(self.off): + await clkedge + + # Grab the next set of on/off values + self._next_valids() + + # Consume a valid cycle + if self.on is not True and self.on: + self.on -= 1 + + if not hasattr(word, "valid"): + self.bus.valid.value = 1 + else: + self.bus.value = word + + # Wait for valid words to be acknowledged + if not hasattr(word, "valid") or word.valid: + if hasattr(self.bus, "ready"): + await self._wait_ready() + + await clkedge + self.bus.valid.value = 0 + + async def _driver_send(self, pkt: Union[bytes, Iterable], sync: bool = True, channel: Optional[int] = None): + """Send a packet over the bus. + + Args: + pkt: Packet to drive onto the bus. + channel: Channel attributed to the packet. + + If ``pkt`` is a string, we simply send it word by word + + If ``pkt`` is an iterable, it's assumed to yield objects with + attributes matching the signal names. + """ + + # Avoid spurious object creation by recycling + if isinstance(pkt, bytes): + self.log.debug("Sending packet of length %d bytes", len(pkt)) + self.log.debug(hexdump(pkt)) + await self._send_string(pkt, sync=sync, channel=channel) + self.log.debug("Successfully sent packet of length %d bytes", len(pkt)) + elif isinstance(pkt, str): + raise TypeError("pkt must be a bytestring, not a unicode string") + else: + if channel is not None: + self.log.warning("%s is ignoring channel=%d because pkt is an iterable", self.name, channel) + await self._send_iterable(pkt, sync=sync) + +class SisoDriver(ValidatedBusDriver): + _optional_signals = ['ready', 'xon'] + _signals = [] + def __init__(self, dut, name, clk): + ValidatedBusDriver.__init__(self, dut, name, clk, bus_separator='.') + +class SosiMonitor(BusMonitor): + _optional_signals = ['valid', 'sop', 'eop', 'sync', 'bsn', 'data', 're', 'im', 'empty', 'channel', 'err'] + _signals = [] + + _default_config = { + "dataBitsPerSymbol" : 8, + "firstSymbolInHighOrderBits" : True, + "maxChannel" : 1, + "readyLatency" : 1, + "invalidTimeout" : 0, + } + def __init__(self, dut, name, clk, data_w=None, reset=None, reset_n=None, report_channel=False): + BusMonitor.__init__(self, dut, name, clk, reset=reset, reset_n=reset_n, bus_separator='.') + self.config = self._default_config.copy() + self.use_empty = True + self.config["useEmpty"] = self.use_empty + self.report_channel = report_channel + self.data_w = data_w + async def _monitor_recv(self): + """Watch the pins and reconstruct transactions.""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + pkt = b"" + in_pkt = False + invalid_cyclecount = 0 + channel = None + + def valid(): + if hasattr(self.bus, 'ready'): + return self.bus.valid.value and self.bus.ready.value + return self.bus.valid.value + + while True: + await clkedge + + if self.in_reset: + continue + + if valid(): + invalid_cyclecount = 0 + + if self.bus.sop.value: + if pkt: + raise Exception("Duplicate start-of-packet received on %s" % + str(self.bus.sop)) + pkt = b"" + in_pkt = True + + if not in_pkt: + raise Exception("Data transfer outside of " + "packet") + + # Handle empty and X's in empty / data + vec = BinaryValue() + if not self.bus.eop.value: + value = self.bus.data.value.get_binstr()[-1*self.data_w:] + vec.assign(value) + else: + value = self.bus.data.value.get_binstr()[-1*self.data_w:] + if self.config["useEmpty"] and self.bus.empty.value.integer: + empty = self.bus.empty.value.integer * self.config["dataBitsPerSymbol"] + if self.config["firstSymbolInHighOrderBits"]: + value = value[:-empty] + else: + value = value[empty:] + + vec.assign(value) + if not vec.is_resolvable: + raise Exception("After empty masking value is still bad? " + "Had empty {:d}, got value {:s}".format(empty, + self.bus.data.value.get_binstr())) + vec.big_endian = self.config['firstSymbolInHighOrderBits'] + pkt += vec.buff + + if hasattr(self.bus, 'channel'): + if channel is None: + channel = self.bus.channel.value.integer + if channel > self.config["maxChannel"]: + raise Exception("Channel value (%d) is greater than maxChannel (%d)" % + (channel, self.config["maxChannel"])) + elif self.bus.channel.value.integer != channel: + raise Exception("Channel value changed during packet") + + if self.bus.eop.value: + self.log.info("Received a packet of %d bytes", len(pkt)) + self.log.debug(hexdump(pkt)) + self.channel = channel + if self.report_channel: + self._recv({"data": pkt, "channel": channel}) + else: + self._recv(pkt) + pkt = b"" + in_pkt = False + channel = None + else: + if in_pkt: + invalid_cyclecount += 1 + if self.config["invalidTimeout"]: + if invalid_cyclecount >= self.config["invalidTimeout"]: + raise Exception( + "In-Packet Timeout. Didn't receive any valid data for %d cycles!" % + invalid_cyclecount) + +class SisoMonitor(BusMonitor): + _optional_signals = ['ready', 'xon'] + _signals = [] + def __init__(self, dut, name, clk, reset=None, reset_n=None): + BusMonitor.__init__(self, dut, name, clk, reset=reset, reset_n=reset_n, bus_separator='.') + self.clk = clk + + async def _monitor_recv(self): + """Watch the pins and reconstruct transactions.""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + + # NB could await on valid here more efficiently? + while True: + await clkedge + self._recv({"ready": self.bus.ready.value, "xon": self.bus.xon.value}) + +class DpStream: + # at the time, cocotb_bus does not officially support VHDL record type bus. + # It does work in the following way: + # 1. Only use _optional_signals, no _signals. + # 2. Use bus_separator='.' + def __init__(self, dut, sosi_name, siso_name, clk, rst, data_w = None): + self.sosi_drv = SosiDriver(dut, sosi_name, clk, data_w) + self.siso_drv = SisoDriver(dut, siso_name, clk) + self.sosi_mon = SosiMonitor(dut, sosi_name, clk, data_w, reset=rst) + self.siso_mon = SisoMonitor(dut, siso_name, clk, reset=rst) + cocotb.start_soon(self.siso_mon._monitor_recv()) + diff --git a/applications/rdma_demo/libraries/rdma_packetiser/cocotb/mm_bus.py b/applications/rdma_demo/libraries/rdma_packetiser/cocotb/mm_bus.py new file mode 100644 index 0000000000000000000000000000000000000000..a25d6c79d4a83e0d05555f79b18a5ae1eb6cff0e --- /dev/null +++ b/applications/rdma_demo/libraries/rdma_packetiser/cocotb/mm_bus.py @@ -0,0 +1,183 @@ + +from typing import Iterable, Union, Optional +import cocotb.binary +from cocotb.log import SimLog +from cocotb.utils import hexdump +from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.binary import BinaryValue +from cocotb.result import TestError +from cocotb_bus.drivers import BusDriver +from cocotb.decorators import coroutine + +class CopiDriver(BusDriver): + _signals = [] + _optional_signals = ["rd", "wr", "wrdata", "address"] + + def __init__(self, entity, name, clock, **kwargs): + BusDriver.__init__(self, entity, name, clock, **kwargs) + self._can_read = False + self._can_write = False + + # Drive some sensible defaults (setimmediatevalue to avoid x asserts) + if hasattr(self.bus, "rd"): + self.bus.rd.setimmediatevalue(0) + self._can_read = True + + if hasattr(self.bus, "wr"): + self.bus.wr.setimmediatevalue(0) + v = self.bus.wrdata.value + v.binstr = "x" * len(self.bus.wrdata) + self.bus.wrdata.value = v + self._can_write = True + + if hasattr(self.bus, "address"): + v = self.bus.address.value + v.binstr = "x" * len(self.bus.address) + self.bus.address.setimmediatevalue(v) + + def read(self, address): + pass + + def write(self, address, value): + pass + + +class CipoDriver(BusDriver): + _signals = [] + _optional_signals = ["rddata", "waitrequest","rdval"] + + def __init__(self, entity, name, clock, **kwargs): + BusDriver.__init__(self, entity, name, clock, **kwargs) + self._can_read = True + self._can_write = False + + # Drive some sensible defaults (setimmediatevalue to avoid x asserts) + if hasattr(self.bus, "rdval"): + self.bus.rdval.setimmediatevalue(0) + + if hasattr(self.bus, "rddata"): + v = self.bus.rddata.value + v.binstr = "x" * len(self.bus.rddata) + self.bus.rddata.value = v + + if hasattr(self.bus, "waitrequest"): + self.bus.waitrequest.setimmediatevalue(0) + + def read(self, address): + pass + + def write(self, address, value): + pass + +class MMController(): + """Memory Mapped Interface (MM) Controller.""" + + def __init__(self, entity, copi_name, cipo_name, clock): + self._log = SimLog("cocotb.%s.%s" % (entity._name, copi_name)) + self._log.debug("Memory Mapped Controller created") + self.copi = CopiDriver(entity, copi_name, clock, bus_separator='.') + self.cipo = CipoDriver(entity, cipo_name, clock, bus_separator='.') + self.clock = clock + + def __len__(self): + return 2**len(self.copi.bus.address) + + @coroutine + async def read(self, address: int, sync: bool = True) -> BinaryValue: + """Issue a request to the bus and block until this comes back. + + Simulation time still progresses + but syntactically it blocks. + + Args: + address: The address to read from. + sync: Wait for rising edge on clock initially. + Defaults to True. + + Returns: + The read data value. + + Raises: + :any:`TestError`: If controller is write-only. + """ + if not self.copi._can_read: + self._log.error("Cannot read - have no read signal") + raise TestError("Attempt to read on a write-only MM Controller") + + await self.copi._acquire_lock() + + # Apply values for next clock edge + if sync: + await RisingEdge(self.clock) + self.copi.bus.address.value = address + self.copi.bus.rd.value = 1 + + + # Wait for waitrequest to be low + if hasattr(self.cipo.bus, "waitrequest"): + await self.cipo._wait_for_nsignal(self.cipo.bus.waitrequest) + await RisingEdge(self.clock) + + # Deassert read + self.copi.bus.rd.value = 0 + #v = self.copi.bus.address.value + #v.binstr = "x" * len(self.copi.bus.address) + #self.copi.bus.address.value = v + + if hasattr(self.cipo.bus, "rdval"): + while True: + await ReadOnly() + if int(self.cipo.bus.rdval): + break + await RisingEdge(self.clock) + else: + # Assume readLatency = 1 if no readdatavalid + # FIXME need to configure this, + # should take a dictionary of Avalon properties. + await ReadOnly() + + # Get the data + data = self.cipo.bus.rddata.value + + self.copi._release_lock() + return data + + @coroutine + async def write(self, address: int, value: int) -> None: + """Issue a write to the given address with the specified + value. + + Args: + address: The address to write to. + value: The data value to write. + + Raises: + :any:`TestError`: If controller is read-only. + """ + if not self.copi._can_write: + self._log.error("Cannot write - have no write signal") + raise TestError("Attempt to write on a read-only MM Controller") + + await self.copi._acquire_lock() + + # Apply values to bus + await RisingEdge(self.clock) + self.copi.bus.address.value = address + self.copi.bus.wrdata.value = value + self.copi.bus.wr.value = 1 + + # Wait for waitrequest to be low + if hasattr(self.cipo.bus, "waitrequest"): + await self.cipo._wait_for_nsignal(self.cipo.bus.waitrequest) + + # Deassert write + await RisingEdge(self.clock) + self.copi.bus.wr.value = 0 + #v = self.copi.bus.address.value + #v.binstr = "x" * len(self.copi.bus.address) + #self.copi.bus.address.value = v + + #v = self.copi.bus.wrdata.value + #v.binstr = "x" * len(self.copi.bus.wrdata) + #self.copi.bus.wrdata.value = v + self.copi._release_lock() diff --git a/applications/rdma_demo/libraries/rdma_packetiser/cocotb/tb_rdma_packetiser.py b/applications/rdma_demo/libraries/rdma_packetiser/cocotb/tb_rdma_packetiser.py new file mode 100644 index 0000000000000000000000000000000000000000..a92c8b4d271c89df17c726f4db266612d17a556a --- /dev/null +++ b/applications/rdma_demo/libraries/rdma_packetiser/cocotb/tb_rdma_packetiser.py @@ -0,0 +1,156 @@ +import cocotb +from cocotb.utils import hexdump +from cocotb.triggers import FallingEdge, Timer, ReadOnly +from cocotb.clock import Clock +from cocotb.binary import BinaryValue + +from dp_bus import DpStream +from mm_bus import MMController + + + +async def perform_rst(rst, clk, cycles): + rst.value = 1 + await Timer(cycles * clk.period) + rst.value = 0 + +async def read_mm_dict(mm: MMController, read_dict: dict) -> dict: + # remove all write-only entries + d = {k: v for k, v in read_dict.items() if 'r' in v['access'].lower()} + return_dict = {} + for k, v in d.items(): + os = v['offset'] + rddata = [(await mm.read(addr)).binstr for addr in range(os, os + v['size'])] + # concat to 32 bits + rddata = [x[-32:] for x in rddata] + # reverse data order to match the write order + rddata = [x for x in rddata[::-1]] + rddata = BinaryValue(''.join(rddata)) + return_dict[k] = rddata + return return_dict + +async def write_mm_dict(mm: MMController, write_dict: dict) -> None: + # remove all read-only entries + d = {k: v for k, v in write_dict.items() if 'w' in v['access'].lower()} + for k, v in d.items(): + # get data to write from dictionary + os = v['offset'] + data_w = 32 + n_bits = v['size'] * data_w + val = BinaryValue(v['value'], n_bits, False).binstr + wrdata = [BinaryValue(val[i:i+data_w]).integer for i in range(0, n_bits, data_w)] + # reverse data order to match the write order + wrdata = [x for x in wrdata[::-1]] + # write data + for i in range(v['size']): + await mm.write(v['offset'] + i, wrdata[i]) + +async def send_multi_dp_packet(dp_stream: DpStream, data, n): + for i in range(n): + await cocotb.start_soon(dp_stream.sosi_drv._driver_send(data)) + + +@cocotb.test() +async def tb_rdma_packetiser(dut): + """Try accessing the design. run with < run -a >""" + + n_bytes = dut.c_nof_byte.value + c_data_w = dut.c_data_w.value + n_words = 120 # = 7680 bytes + c_block_len = n_words * n_bytes + c_nof_packets_in_msg = 5 + c_dma_len = c_block_len * c_nof_packets_in_msg + n_hdr_regs = 46 + + hdr_dict = { + "eth_dst_mac": {"access": "RW", "size": 2, "offset": 44, "value": 0xCAFEBABE1996}, + "eth_src_mac": {"access": "RW", "size": 2, "offset": 42, "value": 0x1DECAFC0FFEE}, + "eth_type": {"access": "RO", "size": 1, "offset": 41, "value": 0x0800}, + + "ip_version": {"access": "RO", "size": 1, "offset": 40, "value": 4}, + "ip_header_length": {"access": "RO", "size": 1, "offset": 39, "value": 5}, + "ip_services": {"access": "RO", "size": 1, "offset": 38, "value": 0}, + "ip_total_length": {"access": "RO", "size": 1, "offset": 37, "value": -1}, + "ip_identification": {"access": "RO", "size": 1, "offset": 36, "value": 0}, + "ip_flags": {"access": "RO", "size": 1, "offset": 35, "value": 2}, + "ip_fragment_offset": {"access": "RO", "size": 1, "offset": 34, "value": 0}, + "ip_time_to_live": {"access": "RO", "size": 1, "offset": 33, "value": 127}, + "ip_protocol": {"access": "RO", "size": 1, "offset": 32, "value": 17}, + "ip_header_checksum": {"access": "RO", "size": 1, "offset": 31, "value": -1}, + "ip_src_addr": {"access": "RW", "size": 1, "offset": 30, "value": 0xFACE0FF}, + "ip_dst_addr": {"access": "RW", "size": 1, "offset": 29, "value": 0x7DECADE}, + + "udp_src_port": {"access": "RW", "size": 1, "offset": 28, "value": 1234}, + "udp_dst_port": {"access": "RW", "size": 1, "offset": 27, "value": 4321}, + "udp_total_length": {"access": "RO", "size": 1, "offset": 26, "value": -1}, + "udp_checksum": {"access": "RO", "size": 1, "offset": 25, "value": 0}, + + "bth_opcode": {"access": "RO", "size": 1, "offset": 24, "value": -1}, + "bth_se": {"access": "RW", "size": 1, "offset": 23, "value": 0}, + "bth_m": {"access": "RW", "size": 1, "offset": 22, "value": 0}, + "bth_pad": {"access": "RW", "size": 1, "offset": 21, "value": 0}, + "bth_tver": {"access": "RW", "size": 1, "offset": 20, "value": 0}, + "bth_partition_key": {"access": "RW", "size": 1, "offset": 19, "value": 65535}, + "bth_fres": {"access": "RW", "size": 1, "offset": 18, "value": 0}, + "bth_bres": {"access": "RW", "size": 1, "offset": 17, "value": 0}, + "bth_reserved_a": {"access": "RO", "size": 1, "offset": 16, "value": 0}, + "bth_dest_qp": {"access": "RW", "size": 1, "offset": 15, "value": 0}, + "bth_ack_req": {"access": "RW", "size": 1, "offset": 14, "value": 0}, + "bth_reserved_b": {"access": "RO", "size": 1, "offset": 13, "value": 0}, + "bth_psn": {"access": "RO", "size": 1, "offset": 12, "value": -1}, + + "reth_virtual_address": {"access": "RO", "size": 2, "offset": 10, "value": -1}, + "reth_r_key": {"access": "RW", "size": 1, "offset": 9, "value": 0}, + "reth_dma_length": {"access": "RW", "size": 1, "offset": 8, "value": c_dma_len}, + + "immediate_data": {"access": "RW", "size": 1, "offset": 7, "value": 0xABADCAFE}, + + "config_reserved": {"access": "RO", "size": 1, "offset": 6, "value": 0}, + "config_use_immediate": {"access": "RW", "size": 1, "offset": 5, "value": 1}, + "config_use_msg_cnt_as_immediate": {"access": "RW", "size": 1, "offset": 4, "value": 0}, + "config_nof_packets_in_msg": {"access": "RW", "size": 1, "offset": 3, "value": c_nof_packets_in_msg}, + "config_nof_msg": {"access": "RW", "size": 1, "offset": 2, "value": 3}, + "config_start_address": {"access": "RW", "size": 2, "offset": 0, "value": 1000000}, + } + + + # simple counter value per byte + snk_in_data = b''.join([(i % 2**8).to_bytes(1, 'little') for i in range(n_words * n_bytes)]) + + # Create clocks + dpClock = Clock(dut.dp_clk, 5, units="ns") + mmClock = Clock(dut.mm_clk, 1, units="ns") + + # DP streams + in_stream = DpStream(dut, 'snk_in', 'snk_out', dut.dp_clk, dut.dp_rst, c_data_w) + out_stream = DpStream(dut, 'src_out', 'src_in', dut.dp_clk, dut.dp_rst, c_data_w) + + # MM busses + reg_hdr_dat = MMController(dut, 'reg_hdr_dat_copi', 'reg_hdr_dat_cipo', dut.mm_clk) + + cocotb.start_soon(dpClock.start()) # run the dp clock "in the background" + cocotb.start_soon(mmClock.start()) # run the mm clock "in the background" + cocotb.start_soon(perform_rst(dut.dp_rst, dpClock, 7)) + cocotb.start_soon(perform_rst(dut.mm_rst, mmClock, 7)) + + await Timer(dpClock.period * 10) # wait a bit for resets to occur + await FallingEdge(dut.dp_clk) # wait for falling edge/"negedge" + await write_mm_dict(reg_hdr_dat, hdr_dict) + #print(snk_in_data) + await Timer(dpClock.period * 10) # wait a bit for resets to occur + await FallingEdge(dut.dp_clk) # wait for falling edge/"negedge" + mm_rd = await read_mm_dict(reg_hdr_dat, hdr_dict) + for k, v in mm_rd.items(): + print(f'{k} = {hex(v.integer)} in hex, {v.integer} in dec') + + await FallingEdge(dut.dp_clk) # wait for falling edge/"negedge" + cocotb.start_soon(send_multi_dp_packet(in_stream, snk_in_data, 10)) + #cocotb.start_soon(in_stream.sosi_drv._driver_send(snk_in_data)) + + # wait for packet to arrive on src_out + for i in range(10): + data = await out_stream.sosi_mon.wait_for_recv() + dut._log.info("src_out data = \n%s", hexdump(data)) + + + diff --git a/applications/rdma_demo/libraries/rdma_packetiser/hdllib.cfg b/applications/rdma_demo/libraries/rdma_packetiser/hdllib.cfg index 70a9f1437ef434bb1c44fe5f959ffb0cf9e57f7f..8de4a7e73f9eaf591a7f6b7bce173f33189bff6d 100644 --- a/applications/rdma_demo/libraries/rdma_packetiser/hdllib.cfg +++ b/applications/rdma_demo/libraries/rdma_packetiser/hdllib.cfg @@ -16,6 +16,8 @@ test_bench_files = regression_test_vhdl = tb/vhdl/tb_tb_rdma_packetiser_assemble_header.vhd +cocotb_files = + cocotb/tb_rdma_packetiser.py # DUT is whatever comes after tb_ (so work.rdma_packetiser) [modelsim_project_file]