<reponame>hchenji/optimsoc
# Copyright (c) 2019 by the author(s)
#
# 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.
#
# =============================================================================
#
# Test for the module 'noc_buffer'
#
# Author(s):
#  - <NAME> <<EMAIL>>
#

import random

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, FallingEdge
from cocotb.result import TestFailure

# Mirroring expected contents of the FIFO
_buffer_flits = []
_buffer_last = []

@cocotb.test()
def test_noc_buffer_fullpacket(dut):
    """
    Test the module noc_buffer with the fullpacket feature enabled.
    This test is hardcoded and only works with WIDTH = 8, DEPTH = 4 and
    FULLPACKET = 1.
    """

    if (dut.FULLPACKET.value.integer != 1 or dut.FLIT_WIDTH.value.integer != 8 or
        dut.DEPTH.value.integer != 4):
        return

    dut._log.info("NoC buffer for a flit width of 8 bits and a depth of 4. " +
                  "FULLPACKET feature is enabled.")

    yield _init_dut(dut)
    yield _fullpacket_test_case(dut)
    dut._log.info("All tests done")


@cocotb.test()
def test_noc_buffer_no_fullpacket(dut):
    """
    Test the module noc_buffer with the fullpacket feature disabled.
    """

    if dut.FULLPACKET.value.integer == 1:
        return

    width = dut.FLIT_WIDTH.value.integer
    depth = dut.DEPTH.value.integer
    dut._log.info("NoC buffer for a flit width of {} bits and a depth of {}. ".format(width, depth) +
                  "FULLPACKET feature is disabled.")

    yield _init_dut(dut)
    # Start read and write processes
    write_thread = cocotb.fork(_write_buffer(dut, dut.clk, width))
    read_thread = cocotb.fork(_read_buffer(dut, dut.clk))
    # Wait for read/write to finish. Read only finishes if all required data
    # has been obtained, i.e. it implicitly waits for write as well.
    yield read_thread.join()
    dut._log.info("All tests done")


@cocotb.coroutine
def _init_dut(dut):
    # Setup clock
    cocotb.fork(Clock(dut.clk, 10, "ns").start())

    # Reset
    dut._log.info("Resetting DUT")
    dut.rst <= 1

    dut.in_flit <= 0
    dut.in_valid <= 0
    dut.in_last <= 0
    dut.out_ready <= 0

    for _ in range(2):
        yield RisingEdge(dut.clk)
    dut.rst <= 0


@cocotb.coroutine
def _write_buffer(dut, clk, width, max_delay=5, write_items=1000, write_random_data=True):
    """
    Before writing wait between 0 and max_delay cycles.
    """

    global _buffer_flits, _buffer_last

    wrcnt = 0
    while True:
        # Insert random wait before the next write
        wr_delay = random.randint(0, max_delay)
        dut._log.debug("WRITE: Wait for {} clock cycles".format(wr_delay))
        for _ in range(wr_delay):
            dut.in_valid <= 0
            yield RisingEdge(clk)

        # Generate and write flit, keep track of it for checking
        if write_random_data:
            flit = random.getrandbits(width)
            last = random.getrandbits(1)
        else:
            flit = wrcnt % (2 ** width)
            last = wrcnt % 2
        dut.in_flit <= flit
        dut.in_last <= last
        dut.in_valid <= 1

        yield RisingEdge(clk)

        # Wait until buffer is ready
        if dut.in_ready != 1:
            while dut.in_ready.value.integer != 1:
                yield RisingEdge(clk)

        _buffer_flits.append(flit)
        _buffer_last.append(last)
        wrcnt += 1
        dut._log.debug("WRITE: Wrote word {} to buffer, value {}, last {}".format(wrcnt, hex(flit), last))

        if wrcnt >= write_items:
            dut.in_valid <= 0
            return


@cocotb.coroutine
def _read_buffer(dut, clk, max_delay=5, read_items=1000):
    """
    Before the read starts a delay between 0 and max_delay cycles is
    waited.
    """

    global _buffer_flits, _buffer_last

    rdcnt = 0
    while True:
        # Determine wait cycles before next read
        rd_delay = random.randint(0, max_delay)
        dut._log.debug("READ: Wait for {} clock cycles".format(rd_delay))
        for _ in range(rd_delay):
            yield RisingEdge(clk)
        else:
            dut.out_ready <= 1
            yield RisingEdge(clk)

        while dut.out_valid.value.integer:
            # Get current flit and flag
            flit = dut.out_flit.value.integer
            last = dut.out_last.value.integer
            flit_expected = _buffer_flits.pop(0)
            last_expected = _buffer_last.pop(0)
            rdcnt += 1
            dut._log.debug("READ: Read flit {} in read {}.".format(hex(flit), rdcnt))

            # Check read flit and flag
            if flit_expected != flit or last_expected != last:
                raise TestFailure("READ: Flit expected {}, read {}, ".format(hex(flit_expected), hex(flit)) +\
                                  "last expected {}, read {}, at read {}.".format(last_expected, last, rdcnt))

            yield RisingEdge(clk)

        else:
            dut._log.debug("READ: Buffer empty, not reading")
            dut.out_ready <= 0

        if rdcnt >= read_items:
            return


@cocotb.coroutine
def _fullpacket_test_case(dut):
    """
    Test case to test FULLPACKET behavior.
    This test is hardcoded to the parameters:
        FLIT_WIDTH = 8
        DEPTH = 4
        FULLPACKET = 1
    """

    yield FallingEdge(dut.clk)

    _set_inputs(dut, 0x01, 0, 0, 0)
    yield FallingEdge(dut.clk)

    # Fill buffer with long packet, then add a second, shorter one while draining
    # 1) 5 flits: 0x02 0x04 0x05 0x06 0x07
    # 2) 2 flits: 0x0a 0x0c
    if dut.out_valid.value.integer:
        raise TestFailure("Out valid is '1', expected '0'!")
    _set_inputs(dut, 0x02, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x03, 0, 0, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x04, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x05, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x06, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x07, 1, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 1, 0, 5)
    _set_inputs(dut, 0x08, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 1, 0, 5)
    _set_inputs(dut, 0x09, 0, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x04, 0, 1, 1, 4)
    _set_inputs(dut, 0x0a, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x04, 0, 1, 0, 4)
    _set_inputs(dut, 0x0b, 0, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x05, 0, 1, 1, 3)
    _set_inputs(dut, 0x0c, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x06, 0, 1, 1, 2)
    _set_inputs(dut, 0x0d, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x07, 1, 1, 1, 1)
    _set_inputs(dut, 0x0e, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x0a, 0, 1, 1, 2)
    _set_inputs(dut, 0x0f, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x0c, 1, 1, 1, 1)
    _set_inputs(dut, 0x10, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x0c, 1, 0, 1, 0)
    _set_inputs(dut, 0x11, 0, 0, 0)
    yield FallingEdge(dut.clk)

    # Insert a series of different sized packets
    # 1) 1 flit: 0x12
    # 2) 3 flits: 0x13 0x14 0x15
    # 3) 1 flit: 0x16
    _assert_outputs(dut, 0x0c, 1, 0, 1, 0)
    _set_inputs(dut, 0x12, 1, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x12, 1, 1, 1, 1)
    _set_inputs(dut, 0x13, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x12, 1, 1, 1, 1)
    _set_inputs(dut, 0x14, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x12, 1, 1, 1, 1)
    _set_inputs(dut, 0x15, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x13, 0, 1, 1, 3)
    _set_inputs(dut, 0x16, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x14, 0, 1, 1, 2)
    _set_inputs(dut, 0x17, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x15, 1, 1, 1, 1)
    _set_inputs(dut, 0x18, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x16, 1, 1, 1, 1)
    _set_inputs(dut, 0x19, 0, 0, 1)
    yield FallingEdge(dut.clk)

    # Insert a series of 3 1-flit packets with 1 idle cycle in between and
    # assure correct timing behavior.
    # 0x1a 0x1c 0x1e
    _assert_outputs(dut, 0x16, 1, 0, 1, 0)
    _set_inputs(dut, 0x1a, 1, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1a, 1, 1, 1, 1)
    _set_inputs(dut, 0x1b, 1, 0, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1a, 1, 1, 1, 1)
    _set_inputs(dut, 0x1c, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1c, 1, 1, 1, 1)
    _set_inputs(dut, 0x1d, 1, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1c, 1, 0, 1, 0)
    _set_inputs(dut, 0x1e, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1e, 1, 1, 1, 1)
    _set_inputs(dut, 0x1f, 1, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1e, 1, 0, 1, 0)
    _set_inputs(dut, 0x1f, 1, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1e, 1, 0, 1, 0)
    _set_inputs(dut, 0x1f, 1, 0, 1)
    yield FallingEdge(dut.clk)


def _assert_outputs(dut, out_flit, out_last, out_valid, in_ready, packet_size):
    """
    Checks if the DUT outputs show the expected values.
    """

    error = False

    r_out_flit = dut.out_flit.value.integer
    r_out_last = dut.out_last.value.integer
    r_out_valid = dut.out_valid.value.integer
    r_in_ready = dut.in_ready.value.integer
    r_packet_size = dut.packet_size.value.integer
    if (r_out_flit != out_flit or r_out_last != out_last or
        r_out_valid != out_valid or r_in_ready != in_ready or
        r_packet_size != packet_size):
        error = True

    dut._log.debug("out_flit: {}; out_last: {}; ".format(hex(r_out_flit), r_out_last) +\
                   "out_valid: {}; in_ready: {}\n".format(r_out_valid, r_in_ready) +\
                   "packet_size: {}".format(r_packet_size))
    if error:
        raise TestFailure("Comparison error!\nValues expected:\n" +\
                          "out_flit: {}; out_last: {}; ".format(hex(out_flit), out_last) +\
                          "out_valid: {}; in_ready: {}\n".format(out_valid, in_ready) +\
                          "packet_size: {}\n".format(packet_size) +\
                          "Values read:\n" +\
                          "out_flit: {}; out_last: {}; ".format(hex(r_out_flit), r_out_last) +\
                          "out_valid: {}; in_ready: {}\n".format(r_out_valid, r_in_ready) +\
                          "packet_size: {}\n".format(r_packet_size))


def _set_inputs(dut, in_flit, in_last, in_valid, out_ready):
    """
    Helper method to set the TDM and BE inputs of the DUT.
    """

    dut.in_flit <= in_flit
    dut.in_last <= in_last
    dut.in_valid <= in_valid
    dut.out_ready <= out_ready

# Copyright (c) 2019 by the author(s)
#
# 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.
#
# =============================================================================
#
# Test for the module 'noc_buffer'
#
# Author(s):
#  - <NAME> <<EMAIL>>
#

import random

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, FallingEdge
from cocotb.result import TestFailure

# Mirroring expected contents of the FIFO
_buffer_flits = []
_buffer_last = []

@cocotb.test()
def test_noc_buffer_fullpacket(dut):
    """
    Test the module noc_buffer with the fullpacket feature enabled.
    This test is hardcoded and only works with WIDTH = 8, DEPTH = 4 and
    FULLPACKET = 1.
    """

    if (dut.FULLPACKET.value.integer != 1 or dut.FLIT_WIDTH.value.integer != 8 or
        dut.DEPTH.value.integer != 4):
        return

    dut._log.info("NoC buffer for a flit width of 8 bits and a depth of 4. " +
                  "FULLPACKET feature is enabled.")

    yield _init_dut(dut)
    yield _fullpacket_test_case(dut)
    dut._log.info("All tests done")


@cocotb.test()
def test_noc_buffer_no_fullpacket(dut):
    """
    Test the module noc_buffer with the fullpacket feature disabled.
    """

    if dut.FULLPACKET.value.integer == 1:
        return

    width = dut.FLIT_WIDTH.value.integer
    depth = dut.DEPTH.value.integer
    dut._log.info("NoC buffer for a flit width of {} bits and a depth of {}. ".format(width, depth) +
                  "FULLPACKET feature is disabled.")

    yield _init_dut(dut)
    # Start read and write processes
    write_thread = cocotb.fork(_write_buffer(dut, dut.clk, width))
    read_thread = cocotb.fork(_read_buffer(dut, dut.clk))
    # Wait for read/write to finish. Read only finishes if all required data
    # has been obtained, i.e. it implicitly waits for write as well.
    yield read_thread.join()
    dut._log.info("All tests done")


@cocotb.coroutine
def _init_dut(dut):
    # Setup clock
    cocotb.fork(Clock(dut.clk, 10, "ns").start())

    # Reset
    dut._log.info("Resetting DUT")
    dut.rst <= 1

    dut.in_flit <= 0
    dut.in_valid <= 0
    dut.in_last <= 0
    dut.out_ready <= 0

    for _ in range(2):
        yield RisingEdge(dut.clk)
    dut.rst <= 0


@cocotb.coroutine
def _write_buffer(dut, clk, width, max_delay=5, write_items=1000, write_random_data=True):
    """
    Before writing wait between 0 and max_delay cycles.
    """

    global _buffer_flits, _buffer_last

    wrcnt = 0
    while True:
        # Insert random wait before the next write
        wr_delay = random.randint(0, max_delay)
        dut._log.debug("WRITE: Wait for {} clock cycles".format(wr_delay))
        for _ in range(wr_delay):
            dut.in_valid <= 0
            yield RisingEdge(clk)

        # Generate and write flit, keep track of it for checking
        if write_random_data:
            flit = random.getrandbits(width)
            last = random.getrandbits(1)
        else:
            flit = wrcnt % (2 ** width)
            last = wrcnt % 2
        dut.in_flit <= flit
        dut.in_last <= last
        dut.in_valid <= 1

        yield RisingEdge(clk)

        # Wait until buffer is ready
        if dut.in_ready != 1:
            while dut.in_ready.value.integer != 1:
                yield RisingEdge(clk)

        _buffer_flits.append(flit)
        _buffer_last.append(last)
        wrcnt += 1
        dut._log.debug("WRITE: Wrote word {} to buffer, value {}, last {}".format(wrcnt, hex(flit), last))

        if wrcnt >= write_items:
            dut.in_valid <= 0
            return


@cocotb.coroutine
def _read_buffer(dut, clk, max_delay=5, read_items=1000):
    """
    Before the read starts a delay between 0 and max_delay cycles is
    waited.
    """

    global _buffer_flits, _buffer_last

    rdcnt = 0
    while True:
        # Determine wait cycles before next read
        rd_delay = random.randint(0, max_delay)
        dut._log.debug("READ: Wait for {} clock cycles".format(rd_delay))
        for _ in range(rd_delay):
            yield RisingEdge(clk)
        else:
            dut.out_ready <= 1
            yield RisingEdge(clk)

        while dut.out_valid.value.integer:
            # Get current flit and flag
            flit = dut.out_flit.value.integer
            last = dut.out_last.value.integer
            flit_expected = _buffer_flits.pop(0)
            last_expected = _buffer_last.pop(0)
            rdcnt += 1
            dut._log.debug("READ: Read flit {} in read {}.".format(hex(flit), rdcnt))

            # Check read flit and flag
            if flit_expected != flit or last_expected != last:
                raise TestFailure("READ: Flit expected {}, read {}, ".format(hex(flit_expected), hex(flit)) +\
                                  "last expected {}, read {}, at read {}.".format(last_expected, last, rdcnt))

            yield RisingEdge(clk)

        else:
            dut._log.debug("READ: Buffer empty, not reading")
            dut.out_ready <= 0

        if rdcnt >= read_items:
            return


@cocotb.coroutine
def _fullpacket_test_case(dut):
    """
    Test case to test FULLPACKET behavior.
    This test is hardcoded to the parameters:
        FLIT_WIDTH = 8
        DEPTH = 4
        FULLPACKET = 1
    """

    yield FallingEdge(dut.clk)

    _set_inputs(dut, 0x01, 0, 0, 0)
    yield FallingEdge(dut.clk)

    # Fill buffer with long packet, then add a second, shorter one while draining
    # 1) 5 flits: 0x02 0x04 0x05 0x06 0x07
    # 2) 2 flits: 0x0a 0x0c
    if dut.out_valid.value.integer:
        raise TestFailure("Out valid is '1', expected '0'!")
    _set_inputs(dut, 0x02, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x03, 0, 0, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x04, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x05, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x06, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 0, 1, 0)
    _set_inputs(dut, 0x07, 1, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 1, 0, 5)
    _set_inputs(dut, 0x08, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x02, 0, 1, 0, 5)
    _set_inputs(dut, 0x09, 0, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x04, 0, 1, 1, 4)
    _set_inputs(dut, 0x0a, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x04, 0, 1, 0, 4)
    _set_inputs(dut, 0x0b, 0, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x05, 0, 1, 1, 3)
    _set_inputs(dut, 0x0c, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x06, 0, 1, 1, 2)
    _set_inputs(dut, 0x0d, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x07, 1, 1, 1, 1)
    _set_inputs(dut, 0x0e, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x0a, 0, 1, 1, 2)
    _set_inputs(dut, 0x0f, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x0c, 1, 1, 1, 1)
    _set_inputs(dut, 0x10, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x0c, 1, 0, 1, 0)
    _set_inputs(dut, 0x11, 0, 0, 0)
    yield FallingEdge(dut.clk)

    # Insert a series of different sized packets
    # 1) 1 flit: 0x12
    # 2) 3 flits: 0x13 0x14 0x15
    # 3) 1 flit: 0x16
    _assert_outputs(dut, 0x0c, 1, 0, 1, 0)
    _set_inputs(dut, 0x12, 1, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x12, 1, 1, 1, 1)
    _set_inputs(dut, 0x13, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x12, 1, 1, 1, 1)
    _set_inputs(dut, 0x14, 0, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x12, 1, 1, 1, 1)
    _set_inputs(dut, 0x15, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x13, 0, 1, 1, 3)
    _set_inputs(dut, 0x16, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x14, 0, 1, 1, 2)
    _set_inputs(dut, 0x17, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x15, 1, 1, 1, 1)
    _set_inputs(dut, 0x18, 0, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x16, 1, 1, 1, 1)
    _set_inputs(dut, 0x19, 0, 0, 1)
    yield FallingEdge(dut.clk)

    # Insert a series of 3 1-flit packets with 1 idle cycle in between and
    # assure correct timing behavior.
    # 0x1a 0x1c 0x1e
    _assert_outputs(dut, 0x16, 1, 0, 1, 0)
    _set_inputs(dut, 0x1a, 1, 1, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1a, 1, 1, 1, 1)
    _set_inputs(dut, 0x1b, 1, 0, 0)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1a, 1, 1, 1, 1)
    _set_inputs(dut, 0x1c, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1c, 1, 1, 1, 1)
    _set_inputs(dut, 0x1d, 1, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1c, 1, 0, 1, 0)
    _set_inputs(dut, 0x1e, 1, 1, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1e, 1, 1, 1, 1)
    _set_inputs(dut, 0x1f, 1, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1e, 1, 0, 1, 0)
    _set_inputs(dut, 0x1f, 1, 0, 1)
    yield FallingEdge(dut.clk)

    _assert_outputs(dut, 0x1e, 1, 0, 1, 0)
    _set_inputs(dut, 0x1f, 1, 0, 1)
    yield FallingEdge(dut.clk)


def _assert_outputs(dut, out_flit, out_last, out_valid, in_ready, packet_size):
    """
    Checks if the DUT outputs show the expected values.
    """

    error = False

    r_out_flit = dut.out_flit.value.integer
    r_out_last = dut.out_last.value.integer
    r_out_valid = dut.out_valid.value.integer
    r_in_ready = dut.in_ready.value.integer
    r_packet_size = dut.packet_size.value.integer
    if (r_out_flit != out_flit or r_out_last != out_last or
        r_out_valid != out_valid or r_in_ready != in_ready or
        r_packet_size != packet_size):
        error = True

    dut._log.debug("out_flit: {}; out_last: {}; ".format(hex(r_out_flit), r_out_last) +\
                   "out_valid: {}; in_ready: {}\n".format(r_out_valid, r_in_ready) +\
                   "packet_size: {}".format(r_packet_size))
    if error:
        raise TestFailure("Comparison error!\nValues expected:\n" +\
                          "out_flit: {}; out_last: {}; ".format(hex(out_flit), out_last) +\
                          "out_valid: {}; in_ready: {}\n".format(out_valid, in_ready) +\
                          "packet_size: {}\n".format(packet_size) +\
                          "Values read:\n" +\
                          "out_flit: {}; out_last: {}; ".format(hex(r_out_flit), r_out_last) +\
                          "out_valid: {}; in_ready: {}\n".format(r_out_valid, r_in_ready) +\
                          "packet_size: {}\n".format(r_packet_size))


def _set_inputs(dut, in_flit, in_last, in_valid, out_ready):
    """
    Helper method to set the TDM and BE inputs of the DUT.
    """

    dut.in_flit <= in_flit
    dut.in_last <= in_last
    dut.in_valid <= in_valid
    dut.out_ready <= out_ready
# Copyright (c) 2019 by the author(s) # # 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. # # ============================================================================= # # Test for the module 'noc_buffer' # # Author(s): #  - <NAME> <<EMAIL>> # # Mirroring expected contents of the FIFO Test the module noc_buffer with the fullpacket feature enabled.     This test is hardcoded and only works with WIDTH = 8, DEPTH = 4 and     FULLPACKET = 1. Test the module noc_buffer with the fullpacket feature disabled. # Start read and write processes # Wait for read/write to finish. Read only finishes if all required data # has been obtained, i.e. it implicitly waits for write as well. # Setup clock # Reset Before writing wait between 0 and max_delay cycles. # Insert random wait before the next write # Generate and write flit, keep track of it for checking # Wait until buffer is ready Before the read starts a delay between 0 and max_delay cycles is     waited. # Determine wait cycles before next read # Get current flit and flag # Check read flit and flag Test case to test FULLPACKET behavior.     This test is hardcoded to the parameters:         FLIT_WIDTH = 8         DEPTH = 4         FULLPACKET = 1 # Fill buffer with long packet, then add a second, shorter one while draining # 1) 5 flits: 0x02 0x04 0x05 0x06 0x07 # 2) 2 flits: 0x0a 0x0c # Insert a series of different sized packets # 1) 1 flit: 0x12 # 2) 3 flits: 0x13 0x14 0x15 # 3) 1 flit: 0x16 # Insert a series of 3 1-flit packets with 1 idle cycle in between and # assure correct timing behavior. # 0x1a 0x1c 0x1e Checks if the DUT outputs show the expected values. Helper method to set the TDM and BE inputs of the DUT.