Ejemplo n.º 1
0
def write_to_controller(dut):
    """
    Description:
        Write a 16-bit valute to the controller

    Test ID: 0

    Expected Results:
        A value is successfully written to the
        the register of the controller.

        This value should be readable from the test bench
    """
    dut.rst <= 1
    dut.test_id <= 0
    axim = AXI4LiteMaster(dut, "AXIML", dut.clk)
    video_out = VideoOutBus(dut,
                            "VIDEO",
                            dut.clk,
                            width=WIDTH,
                            height=HEIGHT,
                            hblank=H_BLANK,
                            vblank=V_BLANK)

    setup_dut(dut)
    yield Timer(CLK_PERIOD * 10)
    dut.rst <= 0

    dut.log.info("Ready")
    yield Timer(CLK_PERIOD * 10)

    dut.rst <= 1
    axim = AXI4LiteMaster(dut, "AXIML", dut.clk)
    video_out = VideoOutBus(dut,
                            "VIDEO",
                            dut.clk,
                            width=WIDTH,
                            height=HEIGHT,
                            hblank=H_BLANK,
                            vblank=V_BLANK)

    setup_dut(dut)
    yield Timer(CLK_PERIOD * 10)
    dut.rst <= 0

    dut.log.info("Ready")
    yield Timer(CLK_PERIOD * 10)

    control = 0x00
    control |= 1 << BIT_CONTROL_CHIP_SELECT
    control |= 1 << BIT_CONTROL_RESET_DISPLAY
    control |= 1 << BIT_CONTROL_ENABLE
    control |= 1 << BIT_CONTROL_BACKLIGHT_ENABLE
    control |= 1 << BIT_CONTROL_WRITE_OVERRIDE

    #Reset the LCD
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control &= ~(1 << BIT_CONTROL_RESET_DISPLAY)
    control &= ~(1 << BIT_CONTROL_WRITE_OVERRIDE)

    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control &= ~(1 << BIT_CONTROL_CHIP_SELECT)

    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control |= 1 << BIT_CONTROL_CHIP_SELECT

    ##################################################
    #Write a 0xAA55 to address 0xB8

    #First set up the correct mode
    control |= 1 << BIT_CONTROL_COMMAND_MODE
    control &= ~(1 << BIT_CONTROL_COMMAND_PARAMETER)
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #Set The Address to write to
    WRITE_ADDR = 0xB8
    yield axim.write(REG_COMMAND_DATA, WRITE_ADDR)
    yield Timer(CLK_PERIOD * 10)

    #Write the command
    control |= 1 << BIT_CONTROL_COMMAND_WRITE
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #Write a parameter
    WRITE_PARAMETER_1 = 0xAA  # Arbitrary Data
    WRITE_PARAMETER_2 = 0x55  # Arbitrary Data

    # Write Parameter 1
    yield axim.write(REG_COMMAND_DATA, WRITE_PARAMETER_1)
    yield Timer(CLK_PERIOD * 10)

    control |= 1 << BIT_CONTROL_COMMAND_PARAMETER
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    # Write Parameter 2
    yield axim.write(REG_COMMAND_DATA, WRITE_PARAMETER_2)
    yield Timer(CLK_PERIOD * 10)

    control |= 1 << BIT_CONTROL_COMMAND_PARAMETER
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #yield FallingEdge(dut.w_write_n)
    #yield ReadOnly()

    data = dut.r_write_parameter
    value = (WRITE_PARAMETER_1 << 8) | WRITE_PARAMETER_2
    if data != value:
        raise TestFailure("Data written to register should have been: 0x02X, \
                            but is 0x%02X" % (data, value))
    yield Timer(CLK_PERIOD * 100)
Ejemplo n.º 2
0
 def raise_inner():
     yield Timer(10, "ns")
     raise ValueError('It is soon now')
Ejemplo n.º 3
0
 def fire_events():
     """ fire the events in order """
     for e in events:
         yield Timer(1)
         e.set()
Ejemplo n.º 4
0
def access_ulogic(dut):
    """Access a std_ulogic as enum"""
    tlog = logging.getLogger("cocotb.test")
    yield Timer(10)
    constant_integer = dut.stream_in_valid
Ejemplo n.º 5
0
def ipython_embed(dut):
    yield Timer(0)
    import IPython
    IPython.embed()
Ejemplo n.º 6
0
def genClockAndReset(dut):
    dut.reset = 1
    dut.clk   = 0
    yield Timer(1000)
Ejemplo n.º 7
0
def reset_dut(reset_n, clk_i, duration):
    reset_n <= 1
    yield Timer(duration)
    yield RisingEdge(clk_i)
    reset_n <= 0
    reset_n._log.debug("Reset complete")
Ejemplo n.º 8
0
def test_failure(dut):
    yield Timer(1, units='ns')
    raise TestFailure("Mensaje descriptivo sobre la falla (failure)")
Ejemplo n.º 9
0
 def reset_dut(self, duration):
     self.dut.rstn <= 0
     yield Timer(duration)
     self.dut.rstn <= 1
     self.dut._log.info("reset complete")
Ejemplo n.º 10
0
def test_exception(dut):
    yield Timer(1, units='ns')
    raise Exception("Mensaje descriptivo sobre el error (error)")
Ejemplo n.º 11
0
def test_assert(dut):
    yield Timer(1, units='ns')
    assert 1 == 0
Ejemplo n.º 12
0
def test_success(dut):
    yield Timer(1, units='ns')
    raise TestSuccess("Mensaje descriptivo sobre la condición de exito (success)")
Ejemplo n.º 13
0
def test_error(dut):
    yield Timer(1, units='ns')
    raise TestError("Mensaje descriptivo sobre el error (error)")
Ejemplo n.º 14
0
 def tick(self):
     yield Timer(self.halfPeriod)
     self.lastTdo = int(self.jtag.tdo)
     self.jtag.tck <= 1
     yield Timer(self.halfPeriod)
     self.jtag.tck <= 0
Ejemplo n.º 15
0
def test(dut):
    dut.stream_in_data.setimmediatevalue(value)
    yield Timer(1)
    assert dut.stream_in_data.value == 0
    yield ReadOnly()
Ejemplo n.º 16
0
def clock_gen(signal, num):
    for x in range(num):
        signal <= 0
        yield Timer(500)
        signal <= 1
        yield Timer(500)
Ejemplo n.º 17
0
def testMD5CoreStd(dut):

    dut.log.info("Cocotb test MD5 Core Std")
    from cocotblib.misc import cocotbXHack
    cocotbXHack()

    helperMD5 = MD5CoreStdHelper(dut)
    clockDomain = ClockDomain(helperMD5.io.clk, 200, helperMD5.io.resetn,
                              RESET_ACTIVE_LEVEL.LOW)

    # Start clock
    cocotb.fork(clockDomain.start())

    # Init IO and wait the end of the reset
    helperMD5.io.initIO()
    yield clockDomain.event_endReset.wait()

    # start monitoring rsp
    helperMD5.io.rsp.startMonitoringValid(helperMD5.io.clk)
    helperMD5.io.cmd.startMonitoringReady(helperMD5.io.clk)

    # Fix patterns
    #
    msgPattern = [randomword(100 - size) for size in range(1, 100)]
    #msgPattern = ["11111111222222223333333344444444555555556666666677777777"]

    for tmpMsg in msgPattern:

        hexMsg = "".join([format(ord(c), "x") for c in tmpMsg])

        # Init MD5
        yield RisingEdge(helperMD5.io.clk)
        helperMD5.io.init <= 1
        yield RisingEdge(helperMD5.io.clk)
        helperMD5.io.init <= 0
        yield RisingEdge(helperMD5.io.clk)

        block = 0
        rtlHash = 0

        while (hexMsg != None):

            isLast = 0
            sizeLast = 0

            if len(hexMsg) > 8:
                block = endianessWord(hexMsg[:8])
                hexMsg = hexMsg[8:]
                isLast = 0
            else:
                block = endianessWord(hexMsg + "0" * (8 - len(hexMsg)))
                isLast = 1
                sizeLast = (len(hexMsg) / 2) - 1
                hexMsg = None

            helperMD5.io.cmd.valid <= 1
            helperMD5.io.cmd.payload.fragment_msg <= int(block, 16)
            helperMD5.io.cmd.payload.fragment_size <= sizeLast
            helperMD5.io.cmd.payload.last <= isLast

            if isLast == 1:
                yield helperMD5.io.rsp.event_valid.wait()
                tmp = hex(int(helperMD5.io.rsp.event_valid.data.digest))[2:-1]
                if (len(tmp) != 32):
                    tmp = "0" * (32 - len(tmp)) + tmp
            else:
                yield helperMD5.io.cmd.event_ready.wait()

            helperMD5.io.cmd.valid <= 0

            yield RisingEdge(helperMD5.io.clk)

        rtlHash = endianess(tmp)

        # Check result
        m = hashlib.md5(tmpMsg)
        modelHash = m.hexdigest()

        assertEquals(int(rtlHash, 16), int(modelHash, 16),
                     "Wrong MD5 hash value computed ")

        #print("hash-model: ", int(rtlHash, 16) == int(modelHash, 16)  , " :" , rtlHash, " - ", modelHash , " -- : ", tmpMsg)

        yield Timer(50000)
Ejemplo n.º 18
0
def typosyntax_error():
    yield Timer(100)a
Ejemplo n.º 19
0
def iteration_loop(dut):
    for thing in dut:
        thing._log.info("Found something: %s" % thing._fullname)
        yield Timer(1)
Ejemplo n.º 20
0
 def always(self):
     if self.has_checks is True:
         while True:
             yield RisingEdge(self.sig_reset)
     else:
         yield Timer(0, "NS")
Ejemplo n.º 21
0
def test1(dut):
    dut.log.info("Cocotb test boot")
    random.seed(0)

    cocotb.fork(ClockDomainAsyncReset(dut.clk, dut.reset))
    cocotb.fork(simulationSpeedPrinter(dut.clk))

    axiMasters = [Axi4(dut, "axiMasters_" + str(i)) for i in range(3)]
    axiSlaves = [Axi4(dut, "axiSlaves_" + str(i)) for i in range(4)]

    masterHandles = []
    idToWrites = [[] for i in xrange(16)]

    # Instanciate master side
    for idx, axiMaster in enumerate(axiMasters):
        masterHandle = MasterHandle(idx, idToWrites)
        masterHandles.append(masterHandle)

        # Read
        StreamDriverMaster(axiMaster.ar, masterHandle.genReadCmd, dut.clk,
                           dut.reset)
        StreamDriverSlave(axiMaster.r, dut.clk, dut.reset)
        StreamMonitor(axiMaster.r, masterHandle.onReadRsp, dut.clk, dut.reset)

        # Write
        StreamDriverMaster(axiMaster.aw, masterHandle.genWriteCmd, dut.clk,
                           dut.reset)
        StreamDriverMaster(axiMaster.w, masterHandle.genWriteData, dut.clk,
                           dut.reset)
        StreamDriverSlave(axiMaster.b, dut.clk, dut.reset)
        StreamMonitor(axiMaster.b, masterHandle.onWriteRsp, dut.clk, dut.reset)

    # instanciate slave side
    for idx, axiSlave in enumerate(axiSlaves):
        axiSlave.r.payload.hid <= 0
        axiSlave.b.payload.hid <= 0
        slaveHandle = SlaveHandle(idx, idToWrites)

        # Read
        StreamDriverSlave(axiSlave.ar, dut.clk, dut.reset)
        StreamDriverMaster(axiSlave.r, slaveHandle.genReadRsp, dut.clk,
                           dut.reset)
        StreamMonitor(axiSlave.ar, slaveHandle.onReadCmd, dut.clk, dut.reset)

        # Write
        StreamMonitor(axiSlave.aw, slaveHandle.onWriteCmd, dut.clk, dut.reset)
        StreamDriverSlave(axiSlave.aw, dut.clk, dut.reset)
        StreamMonitor(axiSlave.w, slaveHandle.onWriteData, dut.clk, dut.reset)
        StreamDriverSlave(axiSlave.w, dut.clk, dut.reset)
        StreamDriverMaster(axiSlave.b, slaveHandle.genWriteRsp, dut.clk,
                           dut.reset)

    # Run until completion
    while True:
        yield RisingEdge(dut.clk)
        done = True
        for handle in masterHandles:
            if not handle.isCompleted():
                done = False

        for l in idToWrites:
            if l:
                done = False
        if done:
            break

    yield Timer(1000 * 10)

    dut.log.info("Cocotb test done")
Ejemplo n.º 22
0
 def post_write(self, rw):
     yield Timer(0, "NS")
Ejemplo n.º 23
0
def access_string_vhdl(dut):
    """Access to a string, both constant and signal."""
    tlog = logging.getLogger("cocotb.test")
    yield Timer(10)
    constant_string = dut.isample_module1.EXAMPLE_STRING
    tlog.info("%r is %s" % (constant_string, str(constant_string)))
    if not isinstance(constant_string, ConstantObject):
        raise TestFailure("EXAMPLE_STRING was not constant")
    if constant_string != b"TESTING":
        raise TestFailure("EXAMPLE_STRING was not == \'TESTING\'")

    tlog.info("Test writing under size")

    test_string = b"cocotb"
    dut.stream_in_string.setimmediatevalue(test_string)

    variable_string = dut.stream_out_string
    if variable_string != b'':
        raise TestFailure("%r not \'\'" % variable_string)

    yield Timer(10)

    if variable_string != test_string:
        raise TestFailure("%r %s != '%s'" %
                          (variable_string, str(variable_string), test_string))

    test_string = b"longer_than_the_array"
    tlog.info("Test writing over size with '%s'" % test_string)

    dut.stream_in_string.setimmediatevalue(test_string)
    variable_string = dut.stream_out_string

    yield Timer(10)

    test_string = test_string[:len(variable_string)]

    if variable_string != test_string:
        raise TestFailure("%r %s != '%s'" %
                          (variable_string, str(variable_string), test_string))

    tlog.info("Test read access to a string character")

    yield Timer(10)

    idx = 3

    result_slice = variable_string[idx]

    # String is defined as string(1 to 8) so idx=3 will access the 3rd character
    if result_slice != test_string[idx - 1]:
        raise TestFailure("Single character did not match {} != {}".format(
            result_slice, test_string[idx - 1]))

    tlog.info("Test write access to a string character")

    yield Timer(10)

    for i in variable_string:
        lower = chr(i)
        upper = lower.upper()
        i.setimmediatevalue(ord(upper))

    yield Timer(10)

    test_string = test_string.upper()

    result = str(variable_string)
    tlog.info("After setting bytes of string value is %s" % result)
    if variable_string != test_string:
        raise TestFailure("%r %s != '%s'" %
                          (variable_string, result, test_string))
Ejemplo n.º 24
0
 def pre_read(self, rw):
     yield Timer(0, "NS")
Ejemplo n.º 25
0
def discover_value_not_in_dut(dut):
    """Try and get a value from the DUT that is not there"""
    yield Timer(0)
    fake_signal = dut.fake_signal
    yield Timer(0)
Ejemplo n.º 26
0
 def pre_write(self, rw):
     yield Timer(0, "NS")
Ejemplo n.º 27
0
def i2c_start(dut):
    yield Timer(I2C_PERIOD / 2)
    dut.sdo_sda = 0
    yield Timer(I2C_PERIOD / 2)
    dut.sck_scl = 0
    yield Timer(I2C_PERIOD / 2)
Ejemplo n.º 28
0
class Scheduler:
    """The main scheduler.

    Here we accept callbacks from the simulator and schedule the appropriate
    coroutines.

    A callback fires, causing the :any:`react` method to be called, with the
    trigger that caused the callback as the first argument.

    We look up a list of coroutines to schedule (indexed by the trigger) and
    schedule them in turn.

    .. attention::

       Implementors should not depend on the scheduling order!

    Some additional management is required since coroutines can return a list
    of triggers, to be scheduled when any one of the triggers fires.  To
    ensure we don't receive spurious callbacks, we have to un-prime all the
    other triggers when any one fires.

    Due to the simulator nuances and fun with delta delays we have the
    following modes:

    Normal mode
        - Callbacks cause coroutines to be scheduled
        - Any pending writes are cached and do not happen immediately

    ReadOnly mode
        - Corresponds to ``cbReadOnlySynch`` (VPI) or ``vhpiCbRepEndOfTimeStep``
          (VHPI).  In this state we are not allowed to perform writes.

    Write mode
        - Corresponds to ``cbReadWriteSynch`` (VPI) or ``vhpiCbRepLastKnownDeltaCycle`` (VHPI)
          In this mode we play back all the cached write updates.

    We can legally transition from Normal to Write by registering a :class:`~cocotb.triggers.ReadWrite`
    callback, however usually once a simulator has entered the ReadOnly phase
    of a given timestep then we must move to a new timestep before performing
    any writes.  The mechanism for moving to a new timestep may not be
    consistent across simulators and therefore we provide an abstraction to
    assist with compatibility.


    Unless a coroutine has explicitly requested to be scheduled in ReadOnly
    mode (for example wanting to sample the finally settled value after all
    delta delays) then it can reasonably be expected to be scheduled during
    "normal mode" i.e. where writes are permitted.
    """

    _MODE_NORMAL = 1  # noqa
    _MODE_READONLY = 2  # noqa
    _MODE_WRITE = 3  # noqa
    _MODE_TERM = 4  # noqa

    # Singleton events, recycled to avoid spurious object creation
    _next_time_step = NextTimeStep()
    _read_write = ReadWrite()
    _read_only = ReadOnly()
    _timer1 = Timer(1)

    def __init__(self):

        self.log = SimLog("cocotb.scheduler")
        if _debug:
            self.log.setLevel(logging.DEBUG)

        # Use OrderedDict here for deterministic behavior (gh-934)

        # A dictionary of pending coroutines for each trigger,
        # indexed by trigger
        self._trigger2coros = _py_compat.insertion_ordered_dict()

        # Our main state
        self._mode = Scheduler._MODE_NORMAL

        # A dictionary of pending (write_func, args), keyed by handle. Only the last scheduled write
        # in a timestep is performed, all the rest are discarded in python.
        self._write_calls = _py_compat.insertion_ordered_dict()

        self._pending_coros = []
        self._pending_triggers = []
        self._pending_threads = []
        self._pending_events = [
        ]  # Events we need to call set on once we've unwound

        self._terminate = False
        self._test = None
        self._main_thread = threading.current_thread()

        self._current_task = None

        self._is_reacting = False

        self._write_coro_inst = None
        self._writes_pending = Event()

    async def _do_writes(self):
        """ An internal coroutine that performs pending writes """
        while True:
            await self._writes_pending.wait()
            if self._mode != Scheduler._MODE_NORMAL:
                await self._next_time_step

            await self._read_write

            while self._write_calls:
                handle, (func, args) = self._write_calls.popitem()
                func(*args)
            self._writes_pending.clear()

    def _check_termination(self):
        """
        Handle a termination that causes us to move onto the next test.
        """
        if self._terminate:
            if _debug:
                self.log.debug("Test terminating, scheduling Timer")

            if self._write_coro_inst is not None:
                self._write_coro_inst.kill()
                self._write_coro_inst = None

            for t in self._trigger2coros:
                t.unprime()

            if self._timer1.primed:
                self._timer1.unprime()

            self._timer1.prime(self._test_completed)
            self._trigger2coros = _py_compat.insertion_ordered_dict()
            self._terminate = False
            self._write_calls = _py_compat.insertion_ordered_dict()
            self._writes_pending.clear()
            self._mode = Scheduler._MODE_TERM

    def _test_completed(self, trigger=None):
        """Called after a test and its cleanup have completed"""
        if _debug:
            self.log.debug("begin_test called with trigger: %s" %
                           (str(trigger)))
        if _profiling:
            ps = pstats.Stats(_profile).sort_stats('cumulative')
            ps.dump_stats("test_profile.pstat")
            ctx = profiling_context()
        else:
            ctx = _py_compat.nullcontext()

        with ctx:
            self._mode = Scheduler._MODE_NORMAL
            if trigger is not None:
                trigger.unprime()

            # extract the current test, and clear it
            test = self._test
            self._test = None
            if test is None:
                raise InternalError(
                    "_test_completed called with no active test")
            if test._outcome is None:
                raise InternalError(
                    "_test_completed called with an incomplete test")

            # Issue previous test result
            if _debug:
                self.log.debug("Issue test result to regression object")

            # this may schedule another test
            cocotb.regression_manager.handle_result(test)

            # if it did, make sure we handle the test completing
            self._check_termination()

    def react(self, trigger):
        """
        Called when a trigger fires.

        We ensure that we only start the event loop once, rather than
        letting it recurse.
        """
        if self._is_reacting:
            # queue up the trigger, the event loop will get to it
            self._pending_triggers.append(trigger)
            return

        if self._pending_triggers:
            raise InternalError(
                "Expected all triggers to be handled but found {}".format(
                    self._pending_triggers))

        # start the event loop
        self._is_reacting = True
        try:
            self._event_loop(trigger)
        finally:
            self._is_reacting = False

    def _event_loop(self, trigger):
        """
        Run an event loop triggered by the given trigger.

        The loop will keep running until no further triggers fire.

        This should be triggered by only:
        * The beginning of a test, when there is no trigger to react to
        * A GPI trigger
        """
        if _profiling:
            ctx = profiling_context()
        else:
            ctx = _py_compat.nullcontext()

        with ctx:
            # When a trigger fires it is unprimed internally
            if _debug:
                self.log.debug("Trigger fired: %s" % str(trigger))
            # trigger.unprime()

            if self._mode == Scheduler._MODE_TERM:
                if _debug:
                    self.log.debug(
                        "Ignoring trigger %s since we're terminating" %
                        str(trigger))
                return

            if trigger is self._read_only:
                self._mode = Scheduler._MODE_READONLY
            # Only GPI triggers affect the simulator scheduling mode
            elif isinstance(trigger, GPITrigger):
                self._mode = Scheduler._MODE_NORMAL

            # work through triggers one by one
            is_first = True
            self._pending_triggers.append(trigger)
            while self._pending_triggers:
                trigger = self._pending_triggers.pop(0)

                if not is_first and isinstance(trigger, GPITrigger):
                    self.log.warning(
                        "A GPI trigger occurred after entering react - this "
                        "should not happen.")
                    assert False

                # this only exists to enable the warning above
                is_first = False

                # Scheduled coroutines may append to our waiting list so the first
                # thing to do is pop all entries waiting on this trigger.
                try:
                    scheduling = self._trigger2coros.pop(trigger)
                except KeyError:
                    # GPI triggers should only be ever pending if there is an
                    # associated coroutine waiting on that trigger, otherwise it would
                    # have been unprimed already
                    if isinstance(trigger, GPITrigger):
                        self.log.critical(
                            "No coroutines waiting on trigger that fired: %s" %
                            str(trigger))

                        trigger.log.info("I'm the culprit")
                    # For Python triggers this isn't actually an error - we might do
                    # event.set() without knowing whether any coroutines are actually
                    # waiting on this event, for example
                    elif _debug:
                        self.log.debug(
                            "No coroutines waiting on trigger that fired: %s" %
                            str(trigger))

                    del trigger
                    continue

                if _debug:
                    debugstr = "\n\t".join(
                        [coro._coro.__qualname__ for coro in scheduling])
                    if len(scheduling) > 0:
                        debugstr = "\n\t" + debugstr
                    self.log.debug("%d pending coroutines for event %s%s" %
                                   (len(scheduling), str(trigger), debugstr))

                # This trigger isn't needed any more
                trigger.unprime()

                for coro in scheduling:
                    if coro._outcome is not None:
                        # coroutine was killed by another coroutine waiting on the same trigger
                        continue
                    if _debug:
                        self.log.debug("Scheduling coroutine %s" %
                                       (coro._coro.__qualname__))
                    self.schedule(coro, trigger=trigger)
                    if _debug:
                        self.log.debug("Scheduled coroutine %s" %
                                       (coro._coro.__qualname__))

                    # remove our reference to the objects at the end of each loop,
                    # to try and avoid them being destroyed at a weird time (as
                    # happened in gh-957)
                    del coro

                # Schedule may have queued up some events so we'll burn through those
                while self._pending_events:
                    if _debug:
                        self.log.debug("Scheduling pending event %s" %
                                       (str(self._pending_events[0])))
                    self._pending_events.pop(0).set()

                # remove our reference to the objects at the end of each loop,
                # to try and avoid them being destroyed at a weird time (as
                # happened in gh-957)
                del trigger
                del scheduling

            # no more pending triggers
            self._check_termination()
            if _debug:
                self.log.debug("All coroutines scheduled, handing control back"
                               " to simulator")

    def unschedule(self, coro):
        """Unschedule a coroutine.  Unprime any pending triggers"""

        # Unprime the trigger this coroutine is waiting on
        trigger = coro._trigger
        if trigger is not None:
            coro._trigger = None
            if coro in self._trigger2coros.setdefault(trigger, []):
                self._trigger2coros[trigger].remove(coro)
            if not self._trigger2coros[trigger]:
                trigger.unprime()
                del self._trigger2coros[trigger]

        assert self._test is not None

        if coro is self._test:
            if _debug:
                self.log.debug("Unscheduling test {}".format(coro))

            if not self._terminate:
                self._terminate = True
                self.cleanup()

        elif Join(coro) in self._trigger2coros:
            self.react(Join(coro))
        else:
            try:
                # throws an error if the background coroutine errored
                # and no one was monitoring it
                coro._outcome.get()
            except (TestComplete, AssertionError) as e:
                coro.log.info("Test stopped by this forked coroutine")
                e = remove_traceback_frames(e, ['unschedule', 'get'])
                self._test.abort(e)
            except Exception as e:
                coro.log.error("Exception raised by this forked coroutine")
                e = remove_traceback_frames(e, ['unschedule', 'get'])
                self._test.abort(e)

    def _schedule_write(self, handle, write_func, *args):
        """ Queue `write_func` to be called on the next ReadWrite trigger. """
        if self._mode == Scheduler._MODE_READONLY:
            raise Exception(
                "Write to object {0} was scheduled during a read-only sync phase."
                .format(handle._name))

        # TODO: we should be able to better keep track of when this needs to
        # be scheduled
        if self._write_coro_inst is None:
            self._write_coro_inst = self.add(self._do_writes())

        self._write_calls[handle] = (write_func, args)
        self._writes_pending.set()

    def _resume_coro_upon(self, coro, trigger):
        """Schedule `coro` to be resumed when `trigger` fires."""
        coro._trigger = trigger

        trigger_coros = self._trigger2coros.setdefault(trigger, [])
        if coro is self._write_coro_inst:
            # Our internal write coroutine always runs before any user coroutines.
            # This preserves the behavior prior to the refactoring of writes to
            # this coroutine.
            trigger_coros.insert(0, coro)
        else:
            # Everything else joins the back of the queue
            trigger_coros.append(coro)

        if not trigger.primed:

            if trigger_coros != [coro]:
                # should never happen
                raise InternalError(
                    "More than one coroutine waiting on an unprimed trigger")

            try:
                trigger.prime(self.react)
            except Exception as e:
                # discard the trigger we associated, it will never fire
                self._trigger2coros.pop(trigger)

                # replace it with a new trigger that throws back the exception
                self._resume_coro_upon(
                    coro,
                    NullTrigger(name="Trigger.prime() Error",
                                outcome=outcomes.Error(e)))

    def queue(self, coroutine):
        """Queue a coroutine for execution"""
        self._pending_coros.append(coroutine)

    def queue_function(self, coro):
        """Queue a coroutine for execution and move the containing thread
        so that it does not block execution of the main thread any longer.
        """
        # We should be able to find ourselves inside the _pending_threads list
        matching_threads = [
            t for t in self._pending_threads
            if t.thread == threading.current_thread()
        ]
        if len(matching_threads) == 0:
            raise RuntimeError(
                "queue_function called from unrecognized thread")

        # Raises if there is more than one match. This can never happen, since
        # each entry always has a unique thread.
        t, = matching_threads

        async def wrapper():
            # This function runs in the scheduler thread
            try:
                _outcome = outcomes.Value(await coro)
            except BaseException as e:
                _outcome = outcomes.Error(e)
            event.outcome = _outcome
            # Notify the current (scheduler) thread that we are about to wake
            # up the background (`@external`) thread, making sure to do so
            # before the background thread gets a chance to go back to sleep by
            # calling thread_suspend.
            # We need to do this here in the scheduler thread so that no more
            # coroutines run until the background thread goes back to sleep.
            t.thread_resume()
            event.set()

        event = threading.Event()
        self._pending_coros.append(cocotb.decorators.RunningTask(wrapper()))
        # The scheduler thread blocks in `thread_wait`, and is woken when we
        # call `thread_suspend` - so we need to make sure the coroutine is
        # queued before that.
        t.thread_suspend()
        # This blocks the calling `@external` thread until the coroutine finishes
        event.wait()
        return event.outcome.get()

    def run_in_executor(self, func, *args, **kwargs):
        """Run the coroutine in a separate execution thread
        and return an awaitable object for the caller.
        """

        # Create a thread
        # Create a trigger that is called as a result of the thread finishing
        # Create an Event object that the caller can await on
        # Event object set when the thread finishes execution, this blocks the
        #   calling coroutine (but not the thread) until the external completes

        def execute_external(func, _waiter):
            _waiter._outcome = outcomes.capture(func, *args, **kwargs)
            if _debug:
                self.log.debug("Execution of external routine done %s" %
                               threading.current_thread())
            _waiter.thread_done()

        async def wrapper():
            waiter = external_waiter()
            thread = threading.Thread(group=None,
                                      target=execute_external,
                                      name=func.__qualname__ + "_thread",
                                      args=([func, waiter]),
                                      kwargs={})

            waiter.thread = thread
            self._pending_threads.append(waiter)

            await waiter.event.wait()

            return waiter.result  # raises if there was an exception

        return wrapper()

    @staticmethod
    def create_task(coroutine: Any) -> RunningTask:
        """ Checks to see if the given object is a schedulable coroutine object and if so, returns it """

        if isinstance(coroutine, RunningTask):
            return coroutine
        if inspect.iscoroutine(coroutine):
            return RunningTask(coroutine)
        if inspect.iscoroutinefunction(coroutine):
            raise TypeError(
                "Coroutine function {} should be called prior to being "
                "scheduled.".format(coroutine))
        if isinstance(coroutine, cocotb.decorators.coroutine):
            raise TypeError(
                "Attempt to schedule a coroutine that hasn't started: {}.\n"
                "Did you forget to add parentheses to the @cocotb.test() "
                "decorator?".format(coroutine))
        if sys.version_info >= (3, 6) and inspect.isasyncgen(coroutine):
            raise TypeError(
                "{} is an async generator, not a coroutine. "
                "You likely used the yield keyword instead of await.".format(
                    coroutine.__qualname__))
        raise TypeError(
            "Attempt to add an object of type {} to the scheduler, which "
            "isn't a coroutine: {!r}\n"
            "Did you forget to use the @cocotb.coroutine decorator?".format(
                type(coroutine), coroutine))

    def add(self, coroutine: Union[RunningTask, Coroutine]) -> RunningTask:
        """Add a new coroutine.

        Just a wrapper around self.schedule which provides some debug and
        useful error messages in the event of common gotchas.
        """

        task = self.create_task(coroutine)

        if _debug:
            self.log.debug("Adding new coroutine %s" % task._coro.__qualname__)

        self.schedule(task)
        self._check_termination()
        return task

    def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask:
        """
        Schedule a coroutine to be run concurrently, starting after the current coroutine yields control.

        In contrast to :func:`~cocotb.fork` which starts the given coroutine immediately, this function
        starts the given coroutine only after the current coroutine yields control.
        This is useful when the coroutine to be forked has logic before the first
        :keyword:`await` that may not be safe to execute immediately.
        """

        task = self.create_task(coro)

        if _debug:
            self.log.debug("Queueing a new coroutine %s" %
                           task._coro.__qualname__)

        self.queue(task)
        return task

    def add_test(self, test_coro):
        """Called by the regression manager to queue the next test"""
        if self._test is not None:
            raise InternalError("Test was added while another was in progress")

        self._test = test_coro
        self._resume_coro_upon(
            test_coro,
            NullTrigger(name="Start {!s}".format(test_coro),
                        outcome=outcomes.Value(None)))

    # This collection of functions parses a trigger out of the object
    # that was yielded by a coroutine, converting `list` -> `Waitable`,
    # `Waitable` -> `RunningTask`, `RunningTask` -> `Trigger`.
    # Doing them as separate functions allows us to avoid repeating unencessary
    # `isinstance` checks.

    def _trigger_from_started_coro(
            self, result: cocotb.decorators.RunningTask) -> Trigger:
        if _debug:
            self.log.debug("Joining to already running coroutine: %s" %
                           result._coro.__qualname__)
        return result.join()

    def _trigger_from_unstarted_coro(
            self, result: cocotb.decorators.RunningTask) -> Trigger:
        self.queue(result)
        if _debug:
            self.log.debug("Scheduling nested coroutine: %s" %
                           result._coro.__qualname__)
        return result.join()

    def _trigger_from_waitable(self,
                               result: cocotb.triggers.Waitable) -> Trigger:
        return self._trigger_from_unstarted_coro(
            cocotb.decorators.RunningTask(result._wait()))

    def _trigger_from_list(self, result: list) -> Trigger:
        return self._trigger_from_waitable(cocotb.triggers.First(*result))

    def _trigger_from_any(self, result) -> Trigger:
        """Convert a yielded object into a Trigger instance"""
        # note: the order of these can significantly impact performance

        if isinstance(result, Trigger):
            return result

        if isinstance(result, cocotb.decorators.RunningTask):
            if not result.has_started():
                return self._trigger_from_unstarted_coro(result)
            else:
                return self._trigger_from_started_coro(result)

        if inspect.iscoroutine(result):
            return self._trigger_from_unstarted_coro(
                cocotb.decorators.RunningTask(result))

        if isinstance(result, list):
            return self._trigger_from_list(result)

        if isinstance(result, cocotb.triggers.Waitable):
            return self._trigger_from_waitable(result)

        if sys.version_info >= (3, 6) and inspect.isasyncgen(result):
            raise TypeError(
                "{} is an async generator, not a coroutine. "
                "You likely used the yield keyword instead of await.".format(
                    result.__qualname__))

        raise TypeError(
            "Coroutine yielded an object of type {}, which the scheduler can't "
            "handle: {!r}\n"
            "Did you forget to decorate with @cocotb.coroutine?".format(
                type(result), result))

    @contextmanager
    def _task_context(self, task):
        """Context manager for the currently running task."""
        old_task = self._current_task
        self._current_task = task
        try:
            yield
        finally:
            self._current_task = old_task

    def schedule(self, coroutine, trigger=None):
        """Schedule a coroutine by calling the send method.

        Args:
            coroutine (cocotb.decorators.coroutine): The coroutine to schedule.
            trigger (cocotb.triggers.Trigger): The trigger that caused this
                coroutine to be scheduled.
        """
        with self._task_context(coroutine):
            if trigger is None:
                send_outcome = outcomes.Value(None)
            else:
                send_outcome = trigger._outcome
            if _debug:
                self.log.debug("Scheduling with {}".format(send_outcome))

            coro_completed = False
            try:
                coroutine._trigger = None
                result = coroutine._advance(send_outcome)
                if _debug:
                    self.log.debug("Coroutine %s yielded %s (mode %d)" %
                                   (coroutine._coro.__qualname__, str(result),
                                    self._mode))

            except cocotb.decorators.CoroutineComplete:
                if _debug:
                    self.log.debug("Coroutine {} completed with {}".format(
                        coroutine, coroutine._outcome))
                coro_completed = True

            # this can't go in the else above, as that causes unwanted exception
            # chaining
            if coro_completed:
                self.unschedule(coroutine)

            # Don't handle the result if we're shutting down
            if self._terminate:
                return

            if not coro_completed:
                try:
                    result = self._trigger_from_any(result)
                except TypeError as exc:
                    # restart this coroutine with an exception object telling it that
                    # it wasn't allowed to yield that
                    result = NullTrigger(outcome=outcomes.Error(exc))

                self._resume_coro_upon(coroutine, result)

            # We do not return from here until pending threads have completed, but only
            # from the main thread, this seems like it could be problematic in cases
            # where a sim might change what this thread is.

            if self._main_thread is threading.current_thread():

                for ext in self._pending_threads:
                    ext.thread_start()
                    if _debug:
                        self.log.debug(
                            "Blocking from %s on %s" %
                            (threading.current_thread(), ext.thread))
                    state = ext.thread_wait()
                    if _debug:
                        self.log.debug(
                            "Back from wait on self %s with newstate %d" %
                            (threading.current_thread(), state))
                    if state == external_state.EXITED:
                        self._pending_threads.remove(ext)
                        self._pending_events.append(ext.event)

            # Handle any newly queued coroutines that need to be scheduled
            while self._pending_coros:
                self.add(self._pending_coros.pop(0))

    def finish_test(self, exc):
        self._test.abort(exc)
        self._check_termination()

    def finish_scheduler(self, exc):
        """Directly call into the regression manager and end test
           once we return the sim will close us so no cleanup is needed.
        """
        # If there is an error during cocotb initialization, self._test may not
        # have been set yet. Don't cause another Python exception here.

        if self._test:
            self.log.debug("Issue sim closedown result to regression object")
            self._test.abort(exc)
            cocotb.regression_manager.handle_result(self._test)

    def cleanup(self):
        """Clear up all our state.

        Unprime all pending triggers and kill off any coroutines, stop all externals.
        """
        # copy since we modify this in kill
        items = list(self._trigger2coros.items())

        # reversing seems to fix gh-928, although the order is still somewhat
        # arbitrary.
        for trigger, waiting in items[::-1]:
            for coro in waiting:
                if _debug:
                    self.log.debug("Killing %s" % str(coro))
                coro.kill()

        if self._main_thread is not threading.current_thread():
            raise Exception("Cleanup() called outside of the main thread")

        for ext in self._pending_threads:
            self.log.warning("Waiting for %s to exit", ext.thread)
Ejemplo n.º 29
0
def do_test_afterdelay_in_readonly(dut, delay):
    global exited
    yield RisingEdge(dut.clk)
    yield ReadOnly()
    yield Timer(delay)
    exited = True
Ejemplo n.º 30
0
def first_test(dut):
    """
    Description:
        Very Basic Functionality
            Startup Nysa

    Test ID: 0

    Expected Results:
        Write to all registers
    """
    WIDTH = 8
    HEIGHT = 4
    H_BLANK = 40
    V_BLANK = 200
    PIXEL_COUNT = WIDTH * HEIGHT

    NUM_FRAMES = 2

    video = []
    for f in range(NUM_FRAMES):
        frame = []
        for v in range(HEIGHT):
            line = []
            for h in range(WIDTH):
                value = h << 16
                value |= h << 8
                value |= h
                line.append(value)
            frame.append(line)
        video.append(frame)

    dut.rst <= 1
    axim = AXI4LiteMaster(dut, "AXIML", dut.clk)
    video_out = VideoOutBus(dut,
                            "VIDEO",
                            dut.clk,
                            width=WIDTH,
                            height=HEIGHT,
                            hblank=H_BLANK,
                            vblank=V_BLANK)

    setup_dut(dut)
    yield Timer(CLK_PERIOD * 10)
    dut.rst <= 0

    dut.log.info("Ready")
    yield Timer(CLK_PERIOD * 10)

    control = 0x00
    control |= 1 << BIT_CONTROL_CHIP_SELECT
    control |= 1 << BIT_CONTROL_RESET_DISPLAY
    control |= 1 << BIT_CONTROL_ENABLE
    control |= 1 << BIT_CONTROL_BACKLIGHT_ENABLE
    control |= 1 << BIT_CONTROL_WRITE_OVERRIDE

    #Reset the LCD
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control &= ~(1 << BIT_CONTROL_RESET_DISPLAY)
    control &= ~(1 << BIT_CONTROL_WRITE_OVERRIDE)

    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control &= ~(1 << BIT_CONTROL_CHIP_SELECT)

    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control |= 1 << BIT_CONTROL_CHIP_SELECT

    ##################################################
    #Write a 0xAA55 to address 0xB8

    #First set up the correct mode
    control |= 1 << BIT_CONTROL_COMMAND_MODE
    control &= ~(1 << BIT_CONTROL_COMMAND_PARAMETER)
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #Set The Address to write to
    WRITE_ADDR = 0xB8
    yield axim.write(REG_COMMAND_DATA, WRITE_ADDR)
    yield Timer(CLK_PERIOD * 10)

    #Write the command
    control |= 1 << BIT_CONTROL_COMMAND_WRITE
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #Write a parameter
    WRITE_PARAMETER_1 = 0xAA  # Arbitrary Address
    WRITE_PARAMETER_2 = 0x55

    # Write Parameter 1
    yield axim.write(REG_COMMAND_DATA, WRITE_PARAMETER_1)
    yield Timer(CLK_PERIOD * 10)

    control |= 1 << BIT_CONTROL_COMMAND_PARAMETER
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    # Write Parameter 2
    yield axim.write(REG_COMMAND_DATA, WRITE_PARAMETER_2)
    yield Timer(CLK_PERIOD * 10)

    control &= ~(1 << BIT_CONTROL_COMMAND_MODE)
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    ##################################################
    #Read two bytes from address 0xB8

    #   Set the address
    READ_ADDR = 0xB8

    control |= 1 << BIT_CONTROL_COMMAND_MODE
    control &= ~(1 << BIT_CONTROL_COMMAND_PARAMETER)
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #   Set address
    yield axim.write(REG_COMMAND_DATA, READ_ADDR)
    yield Timer(CLK_PERIOD * 10)

    control |= 1 << BIT_CONTROL_COMMAND_WRITE
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    control &= ~(1 << BIT_CONTROL_COMMAND_WRITE)
    control |= 1 << BIT_CONTROL_COMMAND_PARAMETER
    control |= 1 << BIT_CONTROL_COMMAND_READ
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    data = yield axim.read(REG_COMMAND_DATA)
    yield Timer(CLK_PERIOD * 10)

    print "First Byte: 0x%02X" % data

    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)
    data = yield axim.read(REG_COMMAND_DATA)
    yield Timer(CLK_PERIOD * 10)

    print "Second Byte: 0x%02X" % data

    #Set the pixel count
    yield axim.write(REG_PIXEL_COUNT, PIXEL_COUNT)
    yield Timer(CLK_PERIOD * 10)

    #Enable image write
    control = 0x00
    control |= 1 << BIT_CONTROL_ENABLE
    control |= 1 << BIT_CONTROL_BACKLIGHT_ENABLE
    yield axim.write(REG_CONTROL, control)
    yield Timer(CLK_PERIOD * 10)

    #Stream the RGB Video (32 pixels), 4 rows of 8)
    #Write Video to the memodry controller
    yield video_out.write(video)

    yield Timer(CLK_PERIOD * 100)
    dut.log.info("Done")