def packets_write(dut, axis_writer, axilite_writer, axilite_reader, pkts,
                  latencies, inter_packet_times):
    """Apply packets on DuT input."""
    # start the module
    yield axilite_writer.write(CPUREG_OFFSET_CTRL_ACTIVE, 0x1)

    # wait a little bit
    yield wait_n_cycles(dut.clk, 10)

    # iterate over all packets
    for i, pkt in enumerate(pkts):
        # convert packet to AXI4-Stream data
        (tdata, tkeep) = packet_to_axis_data(pkt, AXIS_BIT_WIDTH)

        # include latency and inter-packet time in last TUSER word
        tuser = len(tdata) * [0]
        tuser[-1] = latencies[i] | (1 << 24) | (inter_packet_times[i] << 25)

        # write data
        yield axis_writer.write(tdata, tkeep, tuser)

        # wait random number of cycles before applying the next packet
        yield wait_n_cycles(dut.clk, random.randint(0, 10))

    # stop the module
    yield axilite_writer.write(CPUREG_OFFSET_CTRL_ACTIVE, 0x0)
Example #2
0
def nt_timestamp_test(dut):
    """Main test bench function.

    TODO: no automatic validation of DUT output yet. Look at waveforms.
    """
    # start the clock
    cocotb.fork(clk_gen(dut.clk156, CLK_FREQ_MHZ))

    # reset dut
    yield rstn(dut.clk156, dut.rstn156)

    # create axi lite writer, connect and reset
    axi_writer = AXI_Lite_Writer()
    axi_writer.connect(dut, dut.clk156, AXI_DATA_WIDTH, "ctrl")
    yield axi_writer.rst()

    # run simulation for a while
    yield wait_n_cycles(dut.clk156, 1000)

    # set number of clock cycles which shall pass until counter is incremented
    # to 10
    yield axi_writer.write(CPUREG_OFFSET_CTRL_CYCLES_PER_TICK, 10)

    # run simulation for a while
    yield wait_n_cycles(dut.clk156, 1000)
def apply_input(dut, meta, data):
    """Apply meta data and packet data as DUT stimulus.

    Meta data is written to the input meta data FIFO, packet data is written
    to the input packet data FIFO.
    """
    # data word index
    i = 0

    # iterate over meta data words
    for m in meta:
        # get the capture length
        len_capture = (m >> 64) & 0x7FF

        # calculate the number of 64 bit data words
        if len_capture % 8 == 0:
            len_capture_words = len_capture / 8
        else:
            len_capture_words = len_capture / 8 + 1

        # apply data words at DUT input
        for _ in range(len_capture_words):
            # get data word and increment index
            d = data[i]
            i += 1

            # make sure input data fifo is not full
            check_value("fifo_data_full_i", dut.fifo_data_full_o, 0x0)

            # apply data word
            dut.fifo_data_din_i <= d
            dut.fifo_data_wr_en_i <= 1
            yield RisingEdge(dut.clk)

        # done writing data
        dut.fifo_data_wr_en_i <= 0

        # wait a random number of cycles until applying the meta data word
        yield wait_n_cycles(dut.clk, random.randint(0, 10))

        # make sure input meta data fifo is not full
        check_value("fifo_meta_full_o", dut.fifo_meta_full_o, 0x0)

        # apply meta data word
        dut.fifo_meta_din_i <= m
        dut.fifo_meta_wr_en_i <= 1
        yield RisingEdge(dut.clk)

        # done applying meta data
        dut.fifo_meta_wr_en_i <= 0

        # wait a random number of cycles again
        yield wait_n_cycles(dut.clk, random.randint(0, 10))
Example #4
0
def packets_write(dut, axis_writer, pkts):
    """Apply packets on DuT input."""
    # iterate over all packets
    for pkt in pkts:
        # convert packet to AXI4-Stream data
        (tdata, tkeep) = packet_to_axis_data(pkt, AXIS_BIT_WIDTH)

        # apply data
        yield axis_writer.write(tdata, tkeep)

        # wait a random number of cycles before writing next packet
        yield wait_n_cycles(dut.clk156, randint(0, 10))
Example #5
0
def frames_write(dut, axis_writer, frames):
    """Apply generated frames on DuT input."""
    # iterate over frames
    for frame in frames:
        # convert frame to AXI4-Stream data
        (tdata, tkeep) = packet_to_axis_data(frame, AXIS_BIT_WIDTH)

        # write frame
        yield axis_writer.write(tdata, tkeep, [])

        # wait a random number of cycles before writing next frame
        yield wait_n_cycles(dut.clk, randint(0, 10))
Example #6
0
def perform_test(dut, axis_writer, pkts, latencies, inter_packet_times, active,
                 max_len_capture):
    """Perform a test run for specific capture parameters."""
    # enable/disable capture
    dut.active_i <= active

    # set per-packet capture length
    dut.max_len_capture_i <= max_len_capture

    # wait a few cycles
    yield wait_n_cycles(dut.clk, 5)

    # if capture is enabled, module shall be active now. otherwise it should
    # remain deactivated
    check_value("active_o", dut.active_o, active)

    # start writing packets to DuT input
    coroutine_pkts_write = cocotb.fork(packets_write(dut, axis_writer, pkts,
                                                     latencies,
                                                     inter_packet_times))

    # start coroutine that checks module output
    coroutine_check_output = cocotb.fork(check_output(dut, pkts, latencies,
                                                      inter_packet_times))

    # wait for coroutines to complete
    yield coroutine_pkts_write.join()
    yield coroutine_check_output.join()

    # disable module
    dut.active_i <= 0

    # wait a few cycles
    yield wait_n_cycles(dut.clk, 3)

    # make sure module is now inactive
    check_value("active_o", dut.active_o, 0x0)
Example #7
0
def packets_write(dut, axis_writer, pkts, latencies, inter_packet_times):
    """Apply packets on DuT input."""
    # iterate over all packets
    for i, pkt in enumerate(pkts):
        # convert packet to AXI4-Stream data
        (tdata, tkeep) = packet_to_axis_data(pkt, DATAPATH_BIT_WIDTH)

        # include latency and inter-packet time in last TUSER word
        tuser = len(tdata) * [0]
        tuser[-1] = latencies[i] | (1 << 24) | (inter_packet_times[i] << 25)

        # write data
        yield axis_writer.write(tdata, tkeep, tuser)

        # wait random number of cycles before writing the next packet
        yield wait_n_cycles(dut.clk, randint(0, 10))
def packets_write(dut, axis_writer, pkts):
    """Apply packets on DuT input."""
    # iterate over all packets
    for pkt in pkts:
        # convert packet to axi stream data
        (tdata, tkeep) = packet_to_axis_data(pkt, AXIS_BIT_WIDTH)

        # if TLAST is low, TUSER input must be zero. for the last AXI4-Stream
        # transfer of the packet, TUSER is set to a predetermined random value
        tuser = len(tdata) * [0]
        tuser[-1] = TUSER_LSB

        # write data on AXI4-Stream slave interface
        yield axis_writer.write(tdata, tkeep, tuser)

        # wait a random number of cycles before applying next packet
        yield wait_n_cycles(dut.clk156, randint(0, 100))
def nt_recv_capture_mem_write_fifo_wrapper_test(dut):
    """Test bench main function."""
    # start the clock
    cocotb.fork(clk_gen(dut.clk, CLK_FREQ_MHZ))

    # do not issue software reset
    dut.rst_sw <= 0

    # initially we do not read or write data to the FIFO
    dut.rd_en_i <= 0
    dut.wr_en_i <= 0

    # initially do not add alignment data
    dut.align_i <= 0

    # reset the dut
    yield rstn(dut.clk, dut.rstn)

    # wait a few cycles
    yield wait_n_cycles(dut.clk, 10)

    for i, n_words in enumerate(N_INPUT_WORDS):
        print("Test %d/%d" % (i + 1, len(N_INPUT_WORDS)))

        # generate 64 bit input data
        data64 = gen_input_data(n_words)

        # convert 64 bit input data to 512 bit output data
        data512 = convert_data_64_to_512(data64)

        # start input coroutine
        coroutine_in = cocotb.fork(apply_input(dut, data64))

        # start ouput coroutine
        coroutine_out = cocotb.fork(check_output(dut, data512))

        # start one coroutine for triggering aligment
        coroutine_align = cocotb.fork(trigger_align(dut, data64))

        # wait for coroutines to complete
        yield coroutine_in.join()
        yield coroutine_out.join()
        yield coroutine_align.join()
def nt_recv_capture_fifo_merge(dut):
    """Test bench main function."""
    # start the clock
    cocotb.fork(clk_gen(dut.clk, CLK_FREQ_MHZ))

    # do not issue software reset
    dut.rst_sw <= 0

    # output fifo is never full
    dut.fifo_full_i <= 0

    # intially not writing to input fifos
    dut.fifo_meta_wr_en_i <= 0
    dut.fifo_data_wr_en_i <= 0

    # reset the dut
    yield rstn(dut.clk, dut.rstn)

    # wait a few cycles
    yield wait_n_cycles(dut.clk, 5)

    for i in range(N_REPEATS):
        # print out some status
        print("Test %d/%d" % (i+1, N_REPEATS))

        # determine random maximum capture length
        max_len_capture = random.randint(0, 1514)

        # generate packet and meta data
        (meta, data) = gen_input_data(max_len_capture)

        # start stimulus coroutine
        cocotb.fork(apply_input(dut, meta, data))

        # start coroutine checking output
        coroutine_chk = cocotb.fork(check_output(dut, meta, data,
                                                 max_len_capture))

        # wat for checking coroutine to complete
        yield coroutine_chk.join()
def nt_gen_rate_ctrl_top_test(dut):
    """Test bench main function."""
    # initially module is inactive
    dut.active_i <= 0

    # no software reset
    dut.rst_sw156 <= 0

    # start the clock
    cocotb.fork(clk_gen(dut.clk156, CLK_FREQ_MHZ))

    # reset the DuT
    yield rstn(dut.clk156, dut.rstn156)

    # create AXI4-Stream writer and reader
    axis_writer = AXIS_Writer()
    axis_writer.connect(dut, dut.clk156, AXIS_BIT_WIDTH)
    yield axis_writer.rst()

    axis_reader = AXIS_Reader()
    axis_reader.connect(dut, dut.clk156, AXIS_BIT_WIDTH)
    yield axis_reader.rst()

    # create AXI4-Lite writer and connect to DuT
    axi_lite_writer = AXI_Lite_Writer()
    axi_lite_writer.connect(dut, dut.clk156, AXI_CTRL_BIT_WIDTH, "ctrl")
    yield axi_lite_writer.rst()

    # create AXI4-Lite reader and connect to DuT
    axi_lite_reader = AXI_Lite_Reader()
    axi_lite_reader.connect(dut, dut.clk156, AXI_CTRL_BIT_WIDTH, "ctrl")
    yield axi_lite_reader.rst()

    # initially we are always ready to receive
    dut.m_axis_tready <= 1

    # start a coroutine that counts the number of cycles between two packets
    # on the output axi stream
    cocotb.fork(count_cycles_between_axis_transmission(dut))

    print("Test 1/4")

    # generate some packets and inter-packet times. we start with a constant
    # inter-packet time of 200 cycles, more than enough to transmit each packet
    pkts = []
    for _ in range(N_PACKETS):
        pkts.append((gen_packet(), 200))

    # write packet data
    cocotb.fork(packets_write(dut, axis_writer, pkts))

    # wait a little
    yield wait_n_cycles(dut.clk156, 500)

    # start the module
    dut.active_i <= 1

    # start coroutine that checks output and wait until it completes
    yield cocotb.fork(packets_read(dut, axis_reader, pkts, True))

    # deactive the module
    dut.active_i <= 0

    # make sure no warning was flagged
    status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
    assert status == 0x0

    print("Test 2/4")

    # now generate packets of fixed size of 800 bytes. Since the datapath is
    # 8 byte wide, it will take exactly 100 cycles to send them. Also select
    # a fixed inter-packet time of 100 cycles. packets should be sent
    # back-to-back
    pkts = []
    for _ in range(N_PACKETS):
        pkt = Ether(src="53:00:00:00:00:01", dst="53:00:00:00:00:02")
        pkt /= ''.join(
            chr(random.randint(0, 255)) for _ in range(800 - len(pkt)))
        pkts.append((pkt, 100))

    # apply packet data
    cocotb.fork(packets_write(dut, axis_writer, pkts))

    # wait a little
    yield wait_n_cycles(dut.clk156, 1000)

    # start the module
    dut.active_i <= 1

    # start coroutine that checks output and wait until it completes
    yield cocotb.fork(packets_read(dut, axis_reader, pkts, True))

    # deactive the module
    dut.active_i <= 0

    # make sure no warning was flagged
    status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
    assert status == 0x0

    print("Test 3/4")

    # repeat the experiment, but decrement inter-packet time to 99 cycles.
    # since inter-packet time is smaller than the packet transmit time, we
    # should get a warning
    pkts = []
    for _ in range(N_PACKETS):
        pkt = Ether(src="53:00:00:00:00:01", dst="53:00:00:00:00:02")
        pkt /= ''.join(
            chr(random.randint(0, 255)) for _ in range(800 - len(pkt)))
        pkts.append((pkt, 99))

    # apply packet data
    cocotb.fork(packets_write(dut, axis_writer, pkts))

    # wait a little
    yield wait_n_cycles(dut.clk156, 500)

    # start the module
    dut.active_i <= 1

    # start coroutine that checks output and wait until it completes
    yield cocotb.fork(packets_read(dut, axis_reader, pkts, False))

    # deactive the module
    dut.active_i <= 0

    # make sure a warning was flagged
    status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
    assert status == 0x1

    # perform software reset to clear warning flag
    yield axi_lite_writer.write(CPUREG_OFFSET_RST, 0x1)

    # now start toggeling tready
    cocotb.fork(toggle_signal(dut.clk156, dut.m_axis_tready))

    print("Test 4/4")

    # repeat the experiment, inter-packet time back at 100 cycles. since the
    # slave is not always ready to receive, this should cause a warning
    pkts = []
    for _ in range(N_PACKETS):
        pkt = Ether(src="53:00:00:00:00:01", dst="53:00:00:00:00:02")
        pkt /= ''.join(
            chr(random.randint(0, 255)) for _ in range(800 - len(pkt)))
        pkts.append((pkt, 100))

    # apply packet data
    cocotb.fork(packets_write(dut, axis_writer, pkts))

    # wait a little
    yield wait_n_cycles(dut.clk156, 500)

    # start the module
    dut.active_i <= 1

    # start coroutine that checks output and wait until it completes
    yield cocotb.fork(packets_read(dut, axis_reader, pkts, False))

    # deactive the module
    dut.active_i <= 0

    # make sure no warning was flagged
    status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
    assert status == 0x1
Example #12
0
def nt_recv_capture_mem_write_test(dut):
    """Test bench main function."""
    # open file with random content
    try:
        f = File("files/random.file")
    except IOError:
        raise cocotb.result.TestFailure("Generate input data by calling " +
                                        "'./create_random.py' in 'files' " +
                                        "folder!")

    # file size must be a multiple of AXI data width
    if f.size() % (BIT_WIDTH_MEM_WRITE / 8) != 0:
        raise cocotb.result.TestFailure("invalid input data size")

    # create a ring buffer memory (initially of size 0) and connect it to the
    # DuT
    ring_buff = Mem(0)
    ring_buff.connect(dut)

    # start the clock
    cocotb.fork(clk_gen(dut.clk, CLK_FREQ_MHZ))

    # deassert sw reset
    dut.rst_sw <= 0

    # initially module is disabled
    dut.active_i <= 0

    # initially no FIFO flush
    dut.flush_i <= 0

    # reset DuT
    yield rstn(dut.clk, dut.rstn)

    # start the ring buffer memory main routine
    cocotb.fork(ring_buff.main())

    # wait some more clock cycles
    yield wait_n_cycles(dut.clk, 5)

    # iterate over all ring buffer sizes
    for i, ring_buff_size in enumerate(RING_BUFF_SIZES):

        # set ring buffer size
        ring_buff.set_size(ring_buff_size)

        # iterate over all adderesses where ring buffer shall be located in
        # memory
        for j, ring_buff_addr in enumerate(RING_BUFF_ADDRS):

            # print status
            print("Test %d/%d" % (i * len(RING_BUFF_ADDRS) + j + 1,
                                  len(RING_BUFF_ADDRS) * len(RING_BUFF_SIZES)))

            # we have a total of 8 GByte of memory. Make sure the ring buffer
            # fits at the desired address
            if ring_buff_addr + ring_buff_size > 0x1FFFFFFFF:
                raise cocotb.result.TestFailure("ring buffer is too large")

            # to reduce the simulation memory footprint, provide the memory
            # module the first memory address that we actually care about
            ring_buff.set_offset(ring_buff_addr)

            # apply ring buffer memory location to dut
            dut.mem_addr_hi_i <= ring_buff_addr >> 32
            dut.mem_addr_lo_i <= ring_buff_addr & 0xFFFFFFFF

            # apply ring buffer address range to dut
            dut.mem_range_i <= ring_buff_size - 1

            # reset read address pointer
            dut.addr_rd_i <= 0

            # start a couroutine that applies input data
            cocotb.fork(apply_input(dut, f))

            # wait a few clock cycles
            yield wait_n_cycles(dut.clk, 10)

            # start the ring buffer read coroutine and wait until it completes
            yield ring_buff_read(dut, ring_buff, f)

            # clear the ring buffer contents
            ring_buff.clear()

    # close file
    f.close()
def nt_recv_capture_top_test(dut):
    """Test bench main function."""
    # start the clock
    cocotb.fork(clk_gen(dut.clk, CLK_FREQ_MHZ))

    # no software reset
    dut.rst_sw <= 0

    # reset DuT
    yield rstn(dut.clk, dut.rstn)

    # create AXI4-Lite writer, connect and reset it
    axilite_writer = AXI_Lite_Writer()
    axilite_writer.connect(dut, dut.clk, AXI_CTRL_BIT_WIDTH, "ctrl")
    yield axilite_writer.rst()

    # create AXI4-Lite reader, connect and reset it
    axilite_reader = AXI_Lite_Reader()
    axilite_reader.connect(dut, dut.clk, AXI_CTRL_BIT_WIDTH, "ctrl")
    yield axilite_reader.rst()

    # create AXI4-Stream writer, connect and reset it
    axis_writer = AXIS_Writer()
    axis_writer.connect(dut, dut.clk, AXIS_BIT_WIDTH)
    yield axis_writer.rst()

    # create a ring buffer memory (initially of size 0) and connect it to the
    # DuT
    ring_buff = Mem(0)
    ring_buff.connect(dut, "ddr3")

    # generate a couple of random Ethernet packets. For each packet, generate
    # a 16 bit latency value and a 26 bit inter-packet time value
    pkts = []
    latencies = []
    inter_packet_times = []
    for _ in range(N_PACKETS):
        pkts.append(gen_packet())
        latencies.append(random.randint(0, 2**24 - 1))
        inter_packet_times.append(random.randint(0, 2**28 - 1))

    # start the ring buffer memory main routine
    cocotb.fork(ring_buff.main())

    # wait some more clock cycles
    yield wait_n_cycles(dut.clk, 5)

    # iterate over all ring buffer sizes
    for i, ring_buff_size in enumerate(RING_BUFF_SIZES):

        # set ring buffer size
        ring_buff.set_size(ring_buff_size)

        # iterate over all adderesses where ring buffer shall be located in
        # memory
        for j, ring_buff_addr in enumerate(RING_BUFF_ADDRS):

            # print status
            print("Test %d/%d (this will take a while)" %
                  (i * len(RING_BUFF_ADDRS) + j + 1,
                   len(RING_BUFF_ADDRS) * len(RING_BUFF_SIZES)))

            # we have a total of 8 GByte of memory. Make sure the ring buffer
            # fits at the desired address
            if ring_buff_addr + ring_buff_size > 0x1FFFFFFFF:
                raise cocotb.result.TestFailure("ring buffer is too large")

            # to reduce the simulation memory footprint, provide the memory
            # module the first memory address that we actually care about
            ring_buff.set_offset(ring_buff_addr)

            # write ring buffer memory location and address range
            yield axilite_writer.write(CPUREG_OFFSET_CTRL_MEM_ADDR_HI,
                                       ring_buff_addr >> 32)
            yield axilite_writer.write(CPUREG_OFFSET_CTRL_MEM_ADDR_LO,
                                       ring_buff_addr & 0xFFFFFFFF)
            yield axilite_writer.write(CPUREG_OFFSET_CTRL_MEM_RANGE,
                                       ring_buff_size - 1)

            # itererate over all capture lengths
            for max_len_capture in MAX_CAPTURE_LENS:
                # reset read address pointer
                yield axilite_writer.write(CPUREG_OFFSET_CTRL_ADDR_RD, 0x0)

                # set max capture length
                yield axilite_writer.write(CPUREG_OFFSET_CTRL_MAX_LEN_CAPTURE,
                                           max_len_capture)

                # start couroutine that applies packets at input
                cocotb.fork(
                    packets_write(dut, axis_writer, axilite_writer,
                                  axilite_reader, pkts, latencies,
                                  inter_packet_times))

                # wait a bit
                yield wait_n_cycles(dut.clk, 50)

                # start the ring buffer read coroutine and wait until it
                # completes
                yield ring_buff_read(dut, axilite_writer, axilite_reader,
                                     ring_buff, ring_buff_addr,
                                     max_len_capture, pkts, latencies,
                                     inter_packet_times)

                # make sure no error occured
                errs = yield axilite_reader.read(CPUREG_OFFSET_STATUS_ERRS)
                assert errs == 0x0

                # make sure packet count is correct
                pkt_cnt = \
                    yield axilite_reader.read(CPUREG_OFFSET_STATUS_PKT_CNT)
                assert pkt_cnt == len(pkts)

                # make sure module is deactivated now
                active = yield axilite_reader.read(CPUREG_OFFSET_STATUS_ACTIVE)
                assert active == 0

                # clear the ring buffer contents
                ring_buff.clear()
def ring_buff_read(dut, axilite_writer, axilite_reader, ring_buff,
                   ring_buff_addr, max_len_capture, pkts_ref, latencies_ref,
                   inter_packet_times_ref):
    """Read data from the ring buffer and check it for correctness.

    The coroutines monitors the ring buffer write pointer and reads data from
    the buffer if sufficient data is available. It ensures that the read data
    matches the expected one.
    """
    # get ring buffer size
    ring_buff_size = ring_buff.size()

    # ring buffer must be larger than 16384 bytes
    if ring_buff_size <= 16384:
        raise cocotb.result.TestFailure("ring buffer size too small")

    # ring buffer size must be a multiple of 16384 bytes
    if ring_buff_size % 16384 != 0:
        raise cocotb.result.TestFailure("ring buffer size invalid")

    # transfer size must be smaller than ring buffer
    if RD_TRANSFER_SIZE_MAX >= ring_buff_size:
        raise cocotb.result.TestFailure("transfer size too large")

    # determine the number of bytes that we are expecting to read in total
    size_outstanding = 0

    # iterate over packets
    for pkt in pkts_ref:
        # for each packet we need to read 8 byte of meta information
        size_outstanding += 8

        # determine data capture length
        len_capture = min(len(pkt), max_len_capture)

        # data is captured at the granularity of 8 byte words
        if len_capture % 8 == 0:
            size_outstanding += len_capture
        else:
            size_outstanding += 8 * (len_capture / 8 + 1)

    # total capture data is 64 byte aligned
    if size_outstanding % 64 != 0:
        size_outstanding = 64 * (size_outstanding / 64 + 1)

    # read pointer has been reset and currently is zero
    rd = 0

    data = []

    while True:
        # number of outstanding bytes that still need to be read must never be
        # negative
        assert size_outstanding >= 0

        # abort if there is no more data to be read
        if size_outstanding == 0:
            break

        # read error register
        errs = yield axilite_reader.read(CPUREG_OFFSET_STATUS_ERRS)

        # make sure there was no error
        assert errs == 0x0

        # get the write pointer
        wr = yield axilite_reader.read(CPUREG_OFFSET_CTRL_ADDR_WR)

        # get memory size from current read pointer position until the end of
        # the ring buffer memory location
        ring_buff_size_end = ring_buff_size - rd

        # calculate the desired memory transfer size
        transfer_size = min(ring_buff_size_end,
                            min(size_outstanding, RD_TRANSFER_SIZE_MAX))

        # calculated memory transfer size must always be positive
        assert transfer_size > 0

        # ... and it must always be a multiple of 64 bytes
        assert transfer_size % 64 == 0

        if rd == wr:
            # ring buffer is empty -> nothing to transfer
            do_transfer = False
        elif rd < wr:
            # we can read if the difference between both pointers is at least
            # the desired transfer size
            do_transfer = (wr - rd) >= transfer_size
        elif wr < rd:
            # we can read until the end of the ring buffer
            do_transfer = True

        if not do_transfer:
            # no data transfer shall take place now, do nothing
            continue

        # read data from the ring buffer
        data_ring_buff = ring_buff.read(ring_buff_addr + rd, transfer_size)

        # write data to list in 8 byte words
        for i in range(transfer_size / 8):
            d = data_ring_buff >> (
                (transfer_size / 8 - i - 1) * 64) & 2**64 - 1
            data.append(d)

        # update read pointer
        if (rd + transfer_size) == ring_buff_size:
            # end of memory reached, wrap around
            rd = 0
        else:
            assert (rd + transfer_size) < ring_buff_size
            rd = rd + transfer_size

        # write read pointer to DuT
        yield axilite_writer.write(CPUREG_OFFSET_CTRL_ADDR_RD, rd)

        # decrement number of bytes that still remain to be written to memory
        size_outstanding -= transfer_size

        # wait a little bit
        yield wait_n_cycles(dut.clk, 100)

    # check data for correctness
    check_data(pkts_ref, latencies_ref, inter_packet_times_ref, data,
               max_len_capture)
Example #15
0
def nt_gen_replay_mem_read_test(dut):
    """Test bench main function."""
    # open trace file
    trace = File("files/random.file")

    # get trace file size
    trace_size = trace.size()

    # trace file size must be a multiple of AXI data width
    if trace.size() % (AXI_BIT_WIDTH / 8) != 0:
        raise cocotb.result.TestFailure("invalid trace size")

    # calculate ring buffer sizes
    ring_buff_sizes = []
    for ring_buff_size in RING_BUFF_SIZES:
        # size of ring buffer is determined by multiplying the size factor by
        # the size of the trace
        ring_buff_size = int(ring_buff_size * trace_size)

        # make sure that the ring buffer size is multiple of AXI data width
        if ring_buff_size % (AXI_BIT_WIDTH / 8) != 0:
            ring_buff_size += AXI_BIT_WIDTH/8 - ring_buff_size % \
                             (AXI_BIT_WIDTH/8)
        ring_buff_sizes.append(ring_buff_size)

    # create a ring buffer memory (initially of size 0) and connect it to the
    # DUT
    ring_buff = Mem(0)
    ring_buff.connect(dut)

    # start the clock
    cocotb.fork(clk_gen(dut.clk, CLK_FREQ_MHZ))

    # deassert sw reset
    dut.rst_sw <= 0

    # initially module start is not triggered
    dut.ctrl_start_i <= 0

    # reset dut
    yield rstn(dut.clk, dut.rstn)

    # start the ring buffer memory main routine
    cocotb.fork(ring_buff.main())

    # wait some more clock cycles
    yield wait_n_cycles(dut.clk, 5)

    # randomly toggle fifo_prog_full input signal
    dut.fifo_prog_full_i <= 0
    cocotb.fork(toggle_signal(dut.clk, dut.fifo_prog_full_i))

    # iterate over all ring buffer sizes
    for i, ring_buff_size in enumerate(ring_buff_sizes):

        # set ring buffer size
        ring_buff.set_size(ring_buff_size)

        # iterate over all adderesses where ring buffer shall be located in
        # memory
        for j, ring_buff_addr in enumerate(RING_BUFF_ADDRS):

            # print status
            print("Test %d/%d" % (i * len(RING_BUFF_ADDRS) + j + 1,
                                  len(RING_BUFF_ADDRS) * len(RING_BUFF_SIZES)))

            # we have a total of 8 GByte of memory. Make sure the ring buffer
            # fits at the desired address
            if ring_buff_addr + ring_buff_size > 0x1FFFFFFFF:
                raise cocotb.result.TestFailure("ring buffer is too large")

            # to reduce the simulation memory footprint, provide the memory
            # module the first memory address that we actually care about
            ring_buff.set_offset(ring_buff_addr)

            # apply ring buffer memory location to dut
            dut.ctrl_mem_addr_hi_i <= ring_buff_addr >> 32
            dut.ctrl_mem_addr_lo_i <= ring_buff_addr & 0xFFFFFFFF

            # apply ring buffer address range to dut
            dut.ctrl_mem_range_i <= ring_buff_size - 1

            # apply trace size to dut
            dut.ctrl_trace_size_hi_i <= trace_size >> 32
            dut.ctrl_trace_size_lo_i <= trace_size & 0xFFFFFFFF

            # reset write address pointer
            dut.ctrl_addr_wr_i <= 0

            # start reading from the ring buffer
            dut.ctrl_start_i <= 1
            yield RisingEdge(dut.clk)
            dut.ctrl_start_i <= 0
            yield RisingEdge(dut.clk)

            # start writing the ring buffer
            cocotb.fork(ring_buff_write(dut, ring_buff, trace))

            # start checking dut output and wait until it completes
            yield cocotb.fork(check_output(dut, trace)).join()

            # clear the ring buffer contents
            ring_buff.clear()

    # close trace file
    trace.close()
Example #16
0
def ring_buff_write(dut, ring_buff, trace, ring_buff_addr, axi_lite_reader,
                    axi_lite_writer):
    """Coroutine writes trace data to the ring buffer in memory.

    The coroutine monitors the ring buffer read pointer (set by the DUT) and
    writes data to the buffer when a sufficient amount of storage is available.
    """
    # get the ring buffer size
    ring_buff_size = ring_buff.size()

    # get trace size
    trace_size = trace.size()

    # transfer size must be smaller than ring buffer size
    if WR_TRANSFER_SIZE_MAX >= ring_buff_size:
        raise cocotb.result.TestFailure("transfer size too large")

    # initialize number of bytes that still need to be transfered to memory
    trace_size_outstanding = trace_size

    # initialize write pointer
    wr = 0x0

    while True:
        # number of outstanding bytes for transfer must never be negative
        assert trace_size_outstanding >= 0

        # abort if there is no more trace data to be transfered
        if trace_size_outstanding == 0:
            break

        # get the current read pointer
        rd = yield axi_lite_reader.read(CPUREG_OFFSET_CTRL_ADDR_RD)

        # get memory size from current write pointer position until the end
        # of the ring buffer memory location
        ring_buff_size_end = ring_buff_size - wr

        # calculate the desired transfer size
        transfer_size = \
            min(ring_buff_size_end,
                min(trace_size_outstanding, WR_TRANSFER_SIZE_MAX))

        # calculated memory transfer size must always be positive
        assert transfer_size > 0

        if rd == wr:
            # ring buffer is empty --> write data
            do_transfer = True
        elif rd < wr:
            # as long as ring buffer contains valid data, read and write
            # pointers must never become equal. If the read pointer is smaller
            # than the write pointer, we may fill up the memory until the end.
            # This means that the write pointer will may wrap around and have a
            # value of 0. Now if the read pointer is currently 0 as well, this
            # would result in an error situation in which the memory would be
            # assumed to be empty. Thus, special attention is necessary here.
            do_transfer = (rd != 0) or (wr + transfer_size) != ring_buff_size
        elif rd > wr:
            # to make sure that the read pointer does not have the same value
            # as the write pointer (which would mean that ring buffer is
            # empty), only transfer data if difference between both pointer is
            # larger than the transfer size
            do_transfer = (rd - wr) > transfer_size
        if not do_transfer:
            # no data transfer shall take place now, do nothing
            continue

        # read trace file data
        data = trace.read(trace_size - trace_size_outstanding, transfer_size)

        # write data to the ring buffer
        ring_buff.write(ring_buff_addr + wr, data, transfer_size)

        # update the write pointer
        if (wr + transfer_size) == ring_buff_size:
            # end of memory reached, wrap around
            wr = 0x0
        else:
            assert (wr + transfer_size) < ring_buff_size
            wr += transfer_size

        # write the write pointer to the DUT
        yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_ADDR_WR, wr)

        # decrement number of bytes that still remain to be written to memory
        trace_size_outstanding -= transfer_size

        # wait a little bit
        yield wait_n_cycles(dut.clk, 100)
Example #17
0
def nt_gen_replay_top_test(dut):
    """Test bench main function."""
    # start the clock
    cocotb.fork(clk_gen(dut.clk, CLK_FREQ_MHZ))

    # no software reset
    dut.rst_sw <= 0

    # reset dut
    yield rstn(dut.clk, dut.rstn)

    # open trace file
    trace = File("files/random.file")

    # get trace file size
    trace_size = trace.size()

    # trace file must be a multiple of the AXI data width
    if trace.size() % (AXI_MEM_BIT_WIDTH / 8) != 0:
        raise cocotb.result.TestFailure("invalid trace size")

    # calculate ring buffer sizes
    ring_buff_sizes = []
    for ring_buff_size in RING_BUFF_SIZES:
        # size of ring buffer is determined by multiplying the size factor by
        # the size of the trace
        ring_buff_size = int(ring_buff_size * trace_size)

        # make sure that the ring buffer size is multiple of AXI data width
        if ring_buff_size % (AXI_MEM_BIT_WIDTH / 8) != 0:
            ring_buff_size += AXI_MEM_BIT_WIDTH/8 - \
                    ring_buff_size % (AXI_MEM_BIT_WIDTH/8)
        ring_buff_sizes.append(ring_buff_size)

    # create a ring buffer memory (initially of size 0) and connect it to the
    # DUT
    ring_buff = Mem(0)
    ring_buff.connect(dut, "ddr3")

    # create axi lite writer, connect and reset
    axi_lite_writer = AXI_Lite_Writer()
    axi_lite_writer.connect(dut, dut.clk, AXI_LITE_BIT_WIDTH, "ctrl")
    yield axi_lite_writer.rst()

    # create axi lite reader, connect and reset
    axi_lite_reader = AXI_Lite_Reader()
    axi_lite_reader.connect(dut, dut.clk, AXI_LITE_BIT_WIDTH, "ctrl")
    yield axi_lite_reader.rst()

    # create axi stream reader, connect and reset
    axis_reader = AXIS_Reader()
    axis_reader.connect(dut, dut.clk, AXIS_BIT_WIDTH)
    yield axis_reader.rst()

    # start the ring buffer memory main routine
    cocotb.fork(ring_buff.main())

    # toggle m_axis_tready
    cocotb.fork(toggle_signal(dut.clk, dut.m_axis_tready))

    # iterate over all ring buffer sizes
    for i, ring_buff_size in enumerate(ring_buff_sizes):

        # set ring buffer size
        ring_buff.set_size(ring_buff_size)

        # iterate over all addresses where ring buffer shall be located in
        # memory
        for j, ring_buff_addr in enumerate(RING_BUFF_ADDRS):

            # print status
            print("Test %d/%d" % (i * len(RING_BUFF_ADDRS) + j + 1,
                                  len(RING_BUFF_ADDRS) * len(RING_BUFF_SIZES)))

            print("Ring Buff Addr: 0x%x, Size: %d" %
                  (ring_buff_addr, ring_buff_size))

            # we have a total of 8 GByte of memory. Make sure the ring buffer
            # fits at the desired address
            if ring_buff_addr + ring_buff_size > 0x1FFFFFFFF:
                raise cocotb.result.TestFailure("ring buffer is too large")

            # to reduce the simulation memory footprint, provide the memory
            # module the first memory address that we acutally care about
            ring_buff.set_offset(ring_buff_addr)

            # configure ring buffer memory location
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_MEM_ADDR_HI,
                                        ring_buff_addr >> 32)
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_MEM_ADDR_LO,
                                        ring_buff_addr & 0xFFFFFFFF)

            # configure ring buffer address range
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_MEM_RANGE,
                                        ring_buff_size - 1)

            # configure trace size
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_TRACE_SIZE_HI,
                                        trace_size >> 32)
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_TRACE_SIZE_LO,
                                        trace_size & 0xFFFFFFFF)

            # reset write address pointer
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_ADDR_WR, 0x0)

            # make sure module initially is inactive
            status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
            if status & 0x3 != 0:
                raise cocotb.reset.TestFailure("module is active")

            # start the module
            yield axi_lite_writer.write(CPUREG_OFFSET_CTRL_START, 0x1)

            # wait a few cycles
            yield wait_n_cycles(dut.clk, 10)

            # start writing the ring buffer
            cocotb.fork(
                ring_buff_write(dut, ring_buff, trace, ring_buff_addr,
                                axi_lite_reader, axi_lite_writer))

            # start coroutine that checks dut output
            coroutine_chk_out = cocotb.fork(
                check_output(dut, trace, axis_reader))

            # wait a few cycles and make sure module is active
            yield wait_n_cycles(dut.clk, 10)
            status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
            if status & 0x1 == 0x0:
                raise cocotb.result.TestFailure("mem read not active")
            if status & 0x2 == 0x0:
                raise cocotb.result.TestFailure("packet assembly not active")

            # wait for output check to complete
            yield coroutine_chk_out.join()

            # wait a few cycles
            yield wait_n_cycles(dut.clk, 10)

            # make sure module is now inactive
            status = yield axi_lite_reader.read(CPUREG_OFFSET_STATUS)
            if status & 0x3 != 0x0:
                raise cocotb.result.TestFailure("module does not become " +
                                                "inactive")

            # clear the ring buffer contents
            ring_buff.clear()

    # close the trace file
    trace.close()
Example #18
0
def ring_buff_read(dut, ring_buff, f):
    """Read data from the ring buffer and check it for correctness.

    The coroutines monitors the ring buffer write pointer and reads data from
    the buffer if sufficient data is available. It ensures that the read data
    matches the data that has originally been written from the input file to
    the input FIFO.
    """
    # get ring buffer size
    ring_buff_size = ring_buff.size()

    # get file size
    f_size = f.size()

    # ring buffer must be larger than 16384 bytes
    if ring_buff_size <= 16384:
        raise cocotb.result.TestFailure("ring buffer size too small")

    # ring buffer size must be a multiple of 16384 bytes
    if ring_buff_size % 16384 != 0:
        raise cocotb.result.TestFailure("ring buffer size invalid")

    # transfer size must be smaller than ring buffer
    if RD_TRANSFER_SIZE_MAX >= ring_buff_size:
        raise cocotb.result.TestFailure("transfer size too large")

    # memory address at which ring buffer is located
    ring_buff_addr = (int(dut.mem_addr_hi_i) << 32) | int(dut.mem_addr_lo_i)

    # initialize number of bytes that still need to be read from memory
    size_outstanding = f_size

    # make sure module is active
    if int(dut.active_o) == 0:
        raise cocotb.result.TestFailure("DuT became inactive")

    while True:
        # number of outstanding bytes that still need to be read must never be
        # negative
        assert size_outstanding >= 0

        # abort if there is no more data to be read
        if size_outstanding == 0:
            break

        yield RisingEdge(dut.clk)

        # get read and write pointers
        rd = int(dut.addr_rd_i)
        wr = int(dut.addr_wr_o)

        # get memory size from current read pointer position until the end of
        # the ring buffer memory location
        ring_buff_size_end = ring_buff_size - rd

        # calculate the desired memory transfer size
        transfer_size = min(ring_buff_size_end,
                            min(size_outstanding, RD_TRANSFER_SIZE_MAX))

        # calculated memory transfer size must always be positive
        assert transfer_size > 0

        if rd == wr:
            # ring buffer is empty -> nothing to transfer
            do_transfer = False
        elif rd < wr:
            # we can read if the difference between both pointers is at least
            # the desired transfer size
            do_transfer = (wr - rd) >= transfer_size
        elif wr < rd:
            # we can read until the end of the ring buffer
            do_transfer = True

        if not do_transfer:
            # no data transfer shall take place now, do nothing
            continue

        # read data from the ring buffer
        data_ring_buff = ring_buff.read(ring_buff_addr + rd, transfer_size)

        # read data from file
        data_file = f.read(f_size - size_outstanding, transfer_size)

        # make sure that data read from ring buffer matches the data we are
        # expecting
        if data_ring_buff != data_file:
            raise cocotb.result.TestFailure("ring buffer data does not " +
                                            "match expected data")

        # update read pointer
        if (rd + transfer_size) == ring_buff_size:
            # end of memory reached, wrap around
            dut.addr_rd_i <= 0
        else:
            assert (rd + transfer_size) < ring_buff_size
            dut.addr_rd_i <= rd + transfer_size

        # decrement number of bytes that still remain to be written to memory
        size_outstanding -= transfer_size

        # wait a little bit
        yield wait_n_cycles(dut.clk, 100)

    # make sure module is now inactive
    if int(dut.active_o) != 0:
        raise cocotb.result.TestFailure("DuT does not become inactive")