def test_all_generators_step():
    """Test all the generator classes implemented in this overlay.

    In this test, the boolean generators, pattern generators, and 
    FSM generators are tested together by calling the `step()` method. By
    stepping for enough number of samples, the waveform should look identical
    to the results received by calling `run()` directly.

    """
    ol.download()
    logictools_controller = LogicToolsController(
        mb_info, 'PYNQZ1_LOGICTOOLS_SPECIFICATION')
    for generator_name in logictools_controller.status:
        assert logictools_controller.status[generator_name] == 'RESET'

    print("\nConnect {} to GND, and {} to VCC.".format(rst, direction))
    input("Hit enter after done ...")
    print('Connect randomly {} to VCC or GND.'.format(in_pins))
    input("Hit enter after done ...")

    num_samples = 64
    fsm_generator = FSMGenerator(mb_info)
    fsm_generator.trace(num_analyzer_samples=num_samples)
    fsm_generator.setup(fsm_spec, frequency_mhz=10)
    pattern_generator = PatternGenerator(mb_info)
    pattern_generator.trace(num_analyzer_samples=num_samples)
    pattern_generator.setup(loopback_64_samples,
                            stimulus_group_name='stimulus',
                            analysis_group_name='analysis',
                            frequency_mhz=10)
    boolean_generator = BooleanGenerator(mb_info)
    boolean_generator.trace(num_analyzer_samples=num_samples)
    boolean_generator.setup(expressions=test_expressions, frequency_mhz=10)

    for _ in range(num_samples):
        logictools_controller.step(
            [fsm_generator, pattern_generator, boolean_generator])
    for generator_name in logictools_controller.status:
        if generator_name != 'TraceAnalyzer':
            assert logictools_controller.status[generator_name] == 'RUNNING'

    check_boolean_data(boolean_generator)
    check_pattern_data(pattern_generator, num_samples)
    check_fsm_data(fsm_generator, num_samples)

    logictools_controller.stop(
        [fsm_generator, pattern_generator, boolean_generator])
    for generator_name in logictools_controller.status:
        assert logictools_controller.status[generator_name] == 'READY'

    logictools_controller.reset(
        [fsm_generator, pattern_generator, boolean_generator])
    for generator_name in logictools_controller.status:
        assert logictools_controller.status[generator_name] == 'RESET'
    del fsm_generator, pattern_generator, boolean_generator
def test_all_generators_state():
    """Test all the generator classes implemented in this overlay.

    In this test, the boolean generators, pattern generators, and 
    FSM generators are all instantiated. Their states are checked during 
    the test. A maximum number of pattern samples are tested.

    """
    ol.download()
    print("\nConnect {} to GND, and {} to VCC.".format(rst, direction))
    input("Hit enter after done ...")
    print('Connect randomly {} to VCC or GND.'.format(in_pins))
    input('Hit enter after done ...')

    fsm_generator = FSMGenerator(mb_info)
    assert fsm_generator.status == 'RESET'
    fsm_generator.trace(use_analyzer=False)
    fsm_generator.setup(fsm_spec, frequency_mhz=10)
    assert fsm_generator.status == 'READY'

    pattern_generator = PatternGenerator(mb_info)
    assert pattern_generator.status == 'RESET'
    pattern_generator.trace(use_analyzer=False)
    pattern_generator.setup(loopback_max_samples,
                            stimulus_group_name='stimulus',
                            analysis_group_name='analysis',
                            frequency_mhz=10)
    assert pattern_generator.status == 'READY'

    boolean_generator = BooleanGenerator(mb_info)
    assert boolean_generator.status == 'RESET'
    boolean_generator.trace(use_analyzer=False)
    boolean_generator.setup(expressions=test_expressions, frequency_mhz=10)
    assert boolean_generator.status == 'READY'

    for generator in [fsm_generator, pattern_generator, boolean_generator]:
        generator.step()
        assert generator.status == 'RUNNING'
        generator.stop()
        assert fsm_generator.status == 'READY'
        assert pattern_generator.status == 'READY'
        assert boolean_generator.status == 'READY'
        generator.run()
        assert generator.status == 'RUNNING'
        generator.stop()
        assert fsm_generator.status == 'READY'
        assert pattern_generator.status == 'READY'
        assert boolean_generator.status == 'READY'

    for generator in [fsm_generator, pattern_generator, boolean_generator]:
        generator.reset()
        assert generator.status == 'RESET'

    del fsm_generator, pattern_generator, boolean_generator
def test_all_generators_data():
    """Test all the generator classes implemented in this overlay.

    In this test, the boolean generators, pattern generators, and 
    FSM generators are tested together by a single call to the controller. 
    The input and output patterns are checked during the test.

    """
    ol.download()
    logictools_controller = LogicToolsController(
        mb_info, 'PYNQZ1_LOGICTOOLS_SPECIFICATION')
    for generator_name in logictools_controller.status:
        assert logictools_controller.status[generator_name] == 'RESET'

    print("\nConnect {} to GND, and {} to VCC.".format(rst, direction))
    input("Hit enter after done ...")
    print('Connect randomly {} to VCC or GND.'.format(in_pins))
    input("Hit enter after done ...")

    num_samples = MAX_NUM_PATTERN_SAMPLES
    fsm_generator = FSMGenerator(mb_info)
    fsm_generator.trace(num_analyzer_samples=num_samples)
    fsm_generator.setup(fsm_spec, frequency_mhz=100)
    pattern_generator = PatternGenerator(mb_info)
    pattern_generator.trace(num_analyzer_samples=num_samples)
    pattern_generator.setup(loopback_max_samples,
                            stimulus_group_name='stimulus',
                            analysis_group_name='analysis',
                            frequency_mhz=100)
    boolean_generator = BooleanGenerator(mb_info)
    boolean_generator.trace(num_analyzer_samples=num_samples)
    boolean_generator.setup(expressions=test_expressions, frequency_mhz=100)

    logictools_controller.run(
        [fsm_generator, pattern_generator, boolean_generator])
    for generator_name in logictools_controller.status:
        if generator_name != 'TraceAnalyzer':
            assert logictools_controller.status[generator_name] == 'RUNNING'

    check_boolean_data(boolean_generator)
    check_pattern_data(pattern_generator, num_samples)
    check_fsm_data(fsm_generator, num_samples)

    logictools_controller.stop(
        [fsm_generator, pattern_generator, boolean_generator])
    for generator_name in logictools_controller.status:
        assert logictools_controller.status[generator_name] == 'READY'

    logictools_controller.reset(
        [fsm_generator, pattern_generator, boolean_generator])
    for generator_name in logictools_controller.status:
        assert logictools_controller.status[generator_name] == 'RESET'
    del fsm_generator, pattern_generator, boolean_generator
def test_bool_no_trace():
    """Test for the BooleanGenerator class.

    This test will test whether users can show waveform when no trace analyzer
    is used. An exception should be raised.

    """
    ol.download()
    bool_generator = BooleanGenerator(mb_info)
    bool_generator.trace(use_analyzer=False)
    exception_raised = False
    try:
        bool_generator.show_waveform()
    except ValueError:
        exception_raised = True
    assert exception_raised, 'Should raise exception for show_waveform().'

    bool_generator.reset()
    del bool_generator
def test_bool_zero_outputs():
    """Test for the BooleanGenerator class.

    This test will test whether 0-output expressions are accepted.

    """
    ol.download()
    pin_dict = PYNQZ1_LOGICTOOLS_SPECIFICATION['traceable_outputs']
    first_1_pin = list(pin_dict.keys())[0]
    expr_no_rhs = first_1_pin

    bool_generator = BooleanGenerator(mb_info)
    exception_raised = False
    try:
        bool_generator.trace()
        bool_generator.setup(expr_no_rhs)
    except ValueError:
        exception_raised = True
    assert exception_raised, 'Should raise exception if function has no RHS.'

    bool_generator.reset()
    del bool_generator
def test_bool_six_inputs():
    """Test for the BooleanGenerator class.

    This test will test whether 6-input expressions are accepted.

    """
    ol.download()
    pin_dict = PYNQZ1_LOGICTOOLS_SPECIFICATION['traceable_outputs']
    first_1_pin = list(pin_dict.keys())[0]
    next_6_pins = [k for k in list(pin_dict.keys())[1:7]]
    expr_6_inputs = first_1_pin + '=' + ('&'.join(next_6_pins))

    bool_generator = BooleanGenerator(mb_info)
    exception_raised = False
    try:
        bool_generator.trace()
        bool_generator.setup(expressions=[expr_6_inputs])
    except ValueError:
        exception_raised = True
    assert exception_raised, 'Should raise exception if function has 6 inputs.'

    bool_generator.reset()
    del bool_generator
def test_bool_state():
    """Test for the BooleanGenerator class.

    This test will test configurations when all 5 pins of a LUT are 
    specified. Users need to manually check the output.

    """
    ol.download()
    input('\nDisconnect all the pins. Hit enter after done ...')
    pin_dict = PYNQZ1_LOGICTOOLS_SPECIFICATION['traceable_outputs']
    first_6_pins = [k for k in list(pin_dict.keys())[:6]]
    out_pin = first_6_pins[5]
    in_pins = first_6_pins[0:5]
    or_expr = out_pin + '=' + ('|'.join(in_pins))

    bool_generator = BooleanGenerator(mb_info)
    assert bool_generator.status == 'RESET'

    bool_generator.trace()
    bool_generator.setup({'test_bool_state': or_expr})
    assert bool_generator.status == 'READY'

    bool_generator.run()
    assert bool_generator.status == 'RUNNING'

    print('Connect all of {} to GND ...'.format(in_pins))
    assert user_answer_yes("{} outputs logic low?".format(out_pin)), \
        "Boolean configurator fails to show logic low."
    print('Connect any of {} to VCC ...'.format(in_pins))
    assert user_answer_yes("{} outputs logic high?".format(out_pin)), \
        "Boolean configurator fails to show logic high."

    bool_generator.stop()
    assert bool_generator.status == 'READY'

    bool_generator.step()
    assert bool_generator.status == 'RUNNING'

    bool_generator.stop()
    assert bool_generator.status == 'READY'

    bool_generator.reset()
    assert bool_generator.status == 'RESET'

    del bool_generator
def test_bool_max_num_expr():
    """Test for the BooleanGenerator class.

    This test will implement a maximum number of boolean generators, 
    each having 1 input. For example, PYNQ-Z1 has 20 pins for Arduino header, 
    so 19 boolean generators will be implemented, each having 1 output 
    assigned to 1 pin. All the generators share the same input pin.

    """
    ol.download()
    pin_dict = PYNQZ1_LOGICTOOLS_SPECIFICATION['traceable_outputs']
    interface_width = PYNQZ1_LOGICTOOLS_SPECIFICATION['interface_width']
    all_pins = [k for k in list(pin_dict.keys())[:interface_width]]
    num_expressions = interface_width - 1
    in_pin = all_pins[0]
    out_pins = all_pins[1:]
    test_expressions = list()
    for i in range(num_expressions):
        test_expressions.append(out_pins[i] + '=' + in_pin)

    print("")
    bool_generator = BooleanGenerator(mb_info)
    for voltage in ['VCC', 'GND']:
        print('Disconnect all the pins. Connect only {} to {}.'.format(
            in_pin, voltage))
        input('Press enter when done ...')

        bool_generator.trace()
        bool_generator.setup(expressions=test_expressions)
        bool_generator.run()

        for expr_label in bool_generator.expressions.keys():
            waveform = bool_generator.waveforms[expr_label]
            wavelanes_in = waveform.waveform_dict['signal'][0][1:]
            wavelanes_out = waveform.waveform_dict['signal'][-1][1:]
            expr = deepcopy(bool_generator.expressions[expr_label])

            wavelane = wavelanes_in[0]
            wavelane_bitstring = wave_to_bitstring(wavelane['wave'])
            str_replace = wavelane_bitstring[0]
            expr = re.sub(r"\b{}\b".format(wavelane['name']),
                          str_replace, expr)

            wavelane = wavelanes_out[0]
            wavelane_bitstring = wave_to_bitstring(wavelane['wave'])
            str_replace = wavelane_bitstring[0]
            expr = re.sub(r"\b{}\b".format(wavelane['name']),
                          str_replace, expr)

            expr = expr.replace('=', '==')
            assert eval(expr), "Boolean expression {} not evaluating " \
                               "correctly.".format(
                bool_generator.expressions[expr_label])

        bool_generator.stop()
        bool_generator.reset()

    del bool_generator
def test_bool_step():
    """Test for the BooleanGenerator class.

    This test will test whether the `step()` method works correctly.
    Users will be asked to change input values during the test. The test
    scenario is also an extreme case where only 2 samples are captured.

    """
    ol.download()
    pin_dict = PYNQZ1_LOGICTOOLS_SPECIFICATION['traceable_outputs']
    first_10_pins = [k for k in list(pin_dict.keys())[:10]]
    in_pins = first_10_pins[0:5]
    out_pins = first_10_pins[5:10]
    test_expressions = list()
    operations = ['&', '|', '^']
    for i in range(5):
        operation = choice(operations)
        test_expressions.append(out_pins[i] + '=' +
                                (operation.join(sample(in_pins, i+1))))

    print('\nConnect randomly {} to VCC or GND.'.format(in_pins))
    input('Hit enter after done ...')

    bool_generator = BooleanGenerator(mb_info)
    bool_generator.trace(num_analyzer_samples=2)
    bool_generator.setup(expressions=test_expressions)

    for i in range(2):
        print('Change some of the connections from {}.'.format(in_pins))
        input('Hit enter after done ...')
        bool_generator.step()

        for expr_label in bool_generator.expressions.keys():
            waveform = bool_generator.waveforms[expr_label]
            wavelanes_in = waveform.waveform_dict['signal'][0][1:]
            wavelanes_out = waveform.waveform_dict['signal'][-1][1:]
            expr = deepcopy(bool_generator.expressions[expr_label])
            for wavelane in wavelanes_in:
                wavelane_bitstring = wave_to_bitstring(wavelane['wave'])
                str_replace = wavelane_bitstring[i]
                expr = re.sub(r"\b{}\b".format(wavelane['name']),
                              str_replace, expr)

            wavelane = wavelanes_out[0]
            wavelane_bitstring = wave_to_bitstring(wavelane['wave'])
            str_replace = wavelane_bitstring[i]
            expr = re.sub(r"\b{}\b".format(wavelane['name']),
                          str_replace, expr)
            expr = expr.replace('=', '==')
            assert eval(expr), "Boolean expression {} not evaluating " \
                               "correctly in step {}.".format(
                bool_generator.expressions[expr_label], i)

    bool_generator.stop()
    bool_generator.reset()
    del bool_generator
def test_bool_multiple():
    """Test for the BooleanGenerator class.

    This test will test the configurations when only part of the 
    LUT pins are used. Multiple instances will be tested.
    This is an automatic test so no user interaction is needed.

    """
    ol.download()
    pin_dict = PYNQZ1_LOGICTOOLS_SPECIFICATION['traceable_outputs']
    first_10_pins = [k for k in list(pin_dict.keys())[:10]]
    in_pins = first_10_pins[0:5]
    out_pins = first_10_pins[5:10]
    test_expressions = list()
    operations = ['&', '|', '^']
    for i in range(5):
        operation = choice(operations)
        test_expressions.append(out_pins[i] + '=' +
                                (operation.join(sample(in_pins, i+1))))

    print('\nConnect randomly {} to VCC or GND.'.format(in_pins))
    input('Hit enter after done ...')

    bool_generator = BooleanGenerator(mb_info)
    bool_generator.trace()
    bool_generator.setup(expressions=test_expressions)
    bool_generator.run()

    for expr_label in bool_generator.expressions.keys():
        waveform = bool_generator.waveforms[expr_label]
        wavelanes_in = waveform.waveform_dict['signal'][0][1:]
        wavelanes_out = waveform.waveform_dict['signal'][-1][1:]
        expr = deepcopy(bool_generator.expressions[expr_label])
        for wavelane in wavelanes_in:
            if 'h' == wavelane['wave'][0]:
                str_replace = '1'
            elif 'l' == wavelane['wave'][0]:
                str_replace = '0'
            else:
                raise ValueError("Unrecognizable pattern captured.")
            expr = re.sub(r"\b{}\b".format(wavelane['name']),
                          str_replace, expr)

        wavelane = wavelanes_out[0]
        if 'h' == wavelane['wave'][0]:
            str_replace = '1'
        elif 'l' == wavelane['wave'][0]:
            str_replace = '0'
        else:
            raise ValueError("Unrecognizable pattern captured.")
        expr = re.sub(r"\b{}\b".format(wavelane['name']),
                      str_replace, expr)
        expr = expr.replace('=', '==')
        assert eval(expr), "Boolean expression {} not evaluating " \
                           "correctly.".format(
            bool_generator.expressions[expr_label])

    bool_generator.stop()
    bool_generator.reset()
    del bool_generator