def test_fsm_state_bits(): """Test for the Finite State Machine Generator class. This test is similar to the first test, but in this test, we will test the case when the state bits are also used as outputs. """ ol.download() rst, direction = list(pin_dict.keys())[1:3] print("\nConnect both {} and {} to GND.".format(rst, direction)) input("Hit enter after done ...") fsm_spec_4_state, output_pattern, \ state_bit0_pattern, state_bit1_pattern = build_fsm_spec_4_state(0) fsm_period = len(fsm_spec_4_state['states']) golden_test_array = np.array(output_pattern) golden_state_bit0_array = np.array(state_bit0_pattern) golden_state_bit1_array = np.array(state_bit1_pattern) for fsm_frequency_mhz in [10, 100]: fsm_generator = FSMGenerator(mb_info) fsm_generator.trace(use_analyzer=True, num_analyzer_samples=fsm_period) fsm_generator.setup(fsm_spec_4_state, use_state_bits=True, frequency_mhz=fsm_frequency_mhz) fsm_generator.run() test_string = state_bit0_string = state_bit1_string = '' for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: if wavelane['name'] == 'test': test_string = wavelane['wave'] if wavelane['name'] == 'state_bit0': state_bit0_string = wavelane['wave'] if wavelane['name'] == 'state_bit1': state_bit1_string = wavelane['wave'] test_array = np.array(bitstring_to_int(wave_to_bitstring(test_string))) state_bit0_array = np.array( bitstring_to_int(wave_to_bitstring(state_bit0_string))) state_bit1_array = np.array( bitstring_to_int(wave_to_bitstring(state_bit1_string))) assert np.array_equal(golden_test_array, test_array), \ 'Data pattern not correct when running at {}MHz.'.format( fsm_frequency_mhz) assert np.array_equal(golden_state_bit0_array, state_bit0_array), \ 'State bit0 not correct when running at {}MHz.'.format( fsm_frequency_mhz) assert np.array_equal(golden_state_bit1_array, state_bit1_array), \ 'State bit1 not correct when running at {}MHz.'.format( fsm_frequency_mhz) fsm_generator.stop() fsm_generator.reset() del fsm_generator
def test_fsm_num_samples(): """Test for the Finite State Machine Generator class. In this test, the pattern generated by the FSM will be compared with the one specified. We will test a minimum number of (FSM period + 1) samples, and a maximum number of samples. 10MHz and 100MHz clocks are tested for each case. """ ol.download() rst, direction = list(pin_dict.keys())[1:3] print("\nConnect {} to GND, and {} to VCC.".format(rst, direction)) input("Hit enter after done ...") fsm_spec_4_state, output_pattern, _, _ = build_fsm_spec_4_state(1) fsm_period = len(fsm_spec_4_state['states']) for num_samples in [fsm_period, MAX_NUM_TRACE_SAMPLES]: test_tile = np.array(output_pattern) golden_test_array = np.tile(test_tile, ceil(num_samples / 4)) for fsm_frequency_mhz in [10, 100]: fsm_generator = FSMGenerator(mb_info) assert fsm_generator.status == 'RESET' fsm_generator.trace(use_analyzer=True, num_analyzer_samples=num_samples) fsm_generator.setup(fsm_spec_4_state, frequency_mhz=fsm_frequency_mhz) assert fsm_generator.status == 'READY' assert 'bram_data_buf' not in \ fsm_generator.logictools_controller.buffers, \ 'bram_data_buf is not freed after use.' fsm_generator.run() assert fsm_generator.status == 'RUNNING' test_string = '' for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: if wavelane['name'] == 'test': test_string = wavelane['wave'] test_array = np.array( bitstring_to_int(wave_to_bitstring(test_string))) assert np.array_equal(test_array, golden_test_array[:num_samples]), \ 'Data pattern not correct when running at {}MHz.'.format( fsm_frequency_mhz) fsm_generator.stop() assert fsm_generator.status == 'READY' fsm_generator.reset() assert fsm_generator.status == 'RESET' del fsm_generator
def build_fsm_spec_max_in_out(): """Build an FSM spec using a maximum number of inputs and outputs. The returned FSM spec has a maximum number of inputs and outputs. At the same time, the largest available number of states will be implemented. For example, on PYNQ-Z1, if FSM_MAX_INPUT_BITS = 8, and FSM_MAX_STATE_INPUT_BITS = 13, we will implement 2**(13-8)-1 = 31 states. This is the largest number of states available for this setup, since there is always 1 dummy state that has to be reserved. Returns ------- dict The FSM spec that can be consumed by the FSM generator. list The output patterns associated with this FSM spec. """ input_pins = list(pin_dict.keys())[:FSM_MAX_INPUT_BITS] output_pins = list(pin_dict.keys())[FSM_MAX_INPUT_BITS:interface_width] fsm_spec_inout = { 'inputs': [], 'outputs': [], 'states': [], 'transitions': [['1' * len(input_pins), '*', 'S0', '']] } test_lanes = [[] for _ in range(len(output_pins))] num_states = 2**(FSM_MAX_STATE_INPUT_BITS - FSM_MAX_INPUT_BITS) - 1 for i in range(len(input_pins)): fsm_spec_inout['inputs'].append(('input{}'.format(i), input_pins[i])) for i in range(len(output_pins)): fsm_spec_inout['outputs'].append( ('output{}'.format(i), output_pins[i])) for i in range(num_states): current_state = 'S{}'.format(i) next_state = 'S{}'.format((i + 1) % num_states) fsm_spec_inout['states'].append(current_state) output_pattern = '' for test_lane in test_lanes: random_1bit = '{}'.format(randint(0, 1)) output_pattern += random_1bit test_lane += random_1bit transition = [ '0' * len(input_pins), current_state, next_state, output_pattern ] fsm_spec_inout['transitions'].append(transition) test_patterns = [] for i in range(len(output_pins)): temp_string = ''.join(test_lanes[i]) test_patterns.append( np.array(bitstring_to_int(wave_to_bitstring(temp_string)))) return fsm_spec_inout, test_patterns
def test_fsm_max_in_out(): """Test for the Finite State Machine Generator class. This test will test when maximum number of inputs and outputs are used. At the same time, the largest available number of states will be implemented. """ ol.download() input_pins = list(pin_dict.keys())[:FSM_MAX_INPUT_BITS] print("\nConnect {} to GND.".format(input_pins)) print("Disconnect all other pins.") input("Hit enter after done ...") fsm_spec_inout, test_patterns = build_fsm_spec_max_in_out() period = 2**(FSM_MAX_STATE_INPUT_BITS - FSM_MAX_INPUT_BITS) - 1 num_output_pins = interface_width - FSM_MAX_INPUT_BITS fsm_generator = FSMGenerator(mb_info) fsm_generator.trace(use_analyzer=True, num_analyzer_samples=MAX_NUM_TRACE_SAMPLES) fsm_generator.setup(fsm_spec_inout, frequency_mhz=100) fsm_generator.run() test_strings = ['' for _ in range(num_output_pins)] test_arrays = [[] for _ in range(num_output_pins)] for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: for j in range(num_output_pins): if wavelane['name'] == 'output{}'.format(j): test_strings[j] = wavelane['wave'] test_arrays[j] = np.array( bitstring_to_int(wave_to_bitstring( test_strings[j]))) break golden_arrays = [[] for _ in range(num_output_pins)] for i in range(num_output_pins): golden_arrays[i] = np.tile(test_patterns[i], ceil(MAX_NUM_TRACE_SAMPLES / period)) assert np.array_equal(test_arrays[i], golden_arrays[i][:MAX_NUM_TRACE_SAMPLES]), \ 'Output{} not matching the generated pattern.'.format(i) fsm_generator.stop() fsm_generator.reset() del fsm_generator
def build_fsm_spec_free_run(): """Build a spec that results in a free-running FSM. This will return an FSM spec with no given inputs. In this case, the FSM is a free running state machine. A maximum number of states are deployed. Returns ------- dict The FSM spec that can be consumed by the FSM generator. list The output patterns associated with this FSM spec. """ input_pin = list(pin_dict.keys())[0] output_pins = list(pin_dict.keys())[1:interface_width] fsm_spec_inout = { 'inputs': [], 'outputs': [], 'states': [], 'transitions': [] } test_lanes = [[] for _ in range(len(output_pins))] num_states = FSM_MAX_NUM_STATES fsm_spec_inout['inputs'].append(('input0', input_pin)) for i in range(len(output_pins)): fsm_spec_inout['outputs'].append( ('output{}'.format(i), output_pins[i])) for i in range(num_states): current_state = 'S{}'.format(i) next_state = 'S{}'.format((i + 1) % num_states) fsm_spec_inout['states'].append(current_state) output_pattern = '' for test_lane in test_lanes: random_1bit = '{}'.format(randint(0, 1)) output_pattern += random_1bit test_lane += random_1bit transition = ['-', current_state, next_state, output_pattern] fsm_spec_inout['transitions'].append(transition) test_patterns = [] for i in range(len(output_pins)): temp_string = ''.join(test_lanes[i]) test_patterns.append( np.array(bitstring_to_int(wave_to_bitstring(temp_string)))) return fsm_spec_inout, test_patterns
def test_fsm_free_run(): """Test for the Finite State Machine Generator class. This will examine a special scenario where no inputs are given. In this case, the FSM is a free running state machine. Since the FSM specification requires at least 1 input pin to be specified, 1 pin can be used as `don't care` input, while all the other pins are used as outputs. A maximum number of states are deployed. """ ol.download() print("\nDisconnect all the pins.") input("Hit enter after done ...") fsm_spec_inout, test_patterns = build_fsm_spec_free_run() period = FSM_MAX_NUM_STATES num_output_pins = interface_width - 1 fsm_generator = FSMGenerator(mb_info) fsm_generator.trace(use_analyzer=True, num_analyzer_samples=period) fsm_generator.setup(fsm_spec_inout, frequency_mhz=100) fsm_generator.run() test_strings = ['' for _ in range(num_output_pins)] test_arrays = [[] for _ in range(num_output_pins)] for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: for j in range(num_output_pins): if wavelane['name'] == 'output{}'.format(j): test_strings[j] = wavelane['wave'] test_arrays[j] = np.array( bitstring_to_int(wave_to_bitstring( test_strings[j]))) break golden_arrays = test_patterns for i in range(num_output_pins): assert np.array_equal(test_arrays[i], golden_arrays[i]), \ 'Output{} not matching the generated pattern.'.format(i) fsm_generator.stop() fsm_generator.reset() del fsm_generator
def test_fsm_num_states2(): """Test for the Finite State Machine Generator class. This test will check 2 and MAX_NUM_STATES states. These cases should be able to pass random tests. For these tests, we use the minimum number of input and output pins. """ ol.download() input_pin = list(pin_dict.keys())[0] print("\nConnect {} to GND, and disconnect other pins.".format(input_pin)) input("Hit enter after done ...") for num_states in [2, FSM_MAX_NUM_STATES]: fsm_spec, test_pattern = build_fsm_spec_random(num_states) fsm_generator = FSMGenerator(mb_info) fsm_generator.trace(use_analyzer=True, num_analyzer_samples=MAX_NUM_TRACE_SAMPLES) fsm_generator.setup(fsm_spec, frequency_mhz=100) fsm_generator.run() test_string = '' for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: if wavelane['name'] == 'test': test_string = wavelane['wave'] test_array = np.array(bitstring_to_int(wave_to_bitstring(test_string))) period = num_states test_tile = np.array(test_pattern) golden_test_array = np.tile(test_tile, ceil(MAX_NUM_TRACE_SAMPLES / period)) assert np.array_equal(test_array, golden_test_array[:MAX_NUM_TRACE_SAMPLES]), \ 'Analysis not matching the generated pattern.' fsm_generator.stop() fsm_generator.reset() del fsm_generator
def check_fsm_data(fsm_generator, num_samples): """Check whether the FSM generator returns correct data pattern. Parameters ---------- fsm_generator : FSMGenerator The FSM generator after a successful run. num_samples : int The number of samples to test. """ test_string = '' for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: if wavelane['name'] == 'test': test_string = wavelane['wave'] test_array = np.array(bitstring_to_int(wave_to_bitstring(test_string))) golden_test_array = np.tile(np.array(output_pattern), ceil(num_samples / 4)) assert np.array_equal(test_array, golden_test_array[:num_samples]), \ 'Analysis not matching the generated pattern in FSM.'
def test_fsm_step(): """Test for the Finite State Machine Generator class. This test is similar to the above test, but in this test, we will test the `step()` method, and ask users to change the input logic values in the middle of the test. """ ol.download() rst, direction = list(pin_dict.keys())[1:3] print("") fsm_spec_4_state, output_pattern_up, \ state_bit0_pattern_up, \ state_bit1_pattern_up = build_fsm_spec_4_state(0) _, output_pattern_down, \ state_bit0_pattern_down, \ state_bit1_pattern_down = build_fsm_spec_4_state(1) output_pattern_down.append(output_pattern_down.pop(0)) state_bit0_pattern_down.append(state_bit0_pattern_down.pop(0)) state_bit1_pattern_down.append(state_bit1_pattern_down.pop(0)) fsm_period = len(fsm_spec_4_state['states']) golden_test_array = np.array(output_pattern_up + output_pattern_down[1:]) golden_state_bit0_array = np.array(state_bit0_pattern_up + state_bit0_pattern_down[1:]) golden_state_bit1_array = np.array(state_bit1_pattern_up + state_bit1_pattern_down[1:]) for fsm_frequency_mhz in [10, 100]: fsm_generator = FSMGenerator(mb_info) fsm_generator.trace(use_analyzer=True, num_analyzer_samples=fsm_period) fsm_generator.setup(fsm_spec_4_state, use_state_bits=True, frequency_mhz=fsm_frequency_mhz) print("Connect both {} and {} to GND.".format(rst, direction)) input("Hit enter after done ...") for _ in range(len(output_pattern_up) - 1): fsm_generator.step() print("Connect {} to GND, and {} to VCC.".format(rst, direction)) input("Hit enter after done ...") for _ in range(len(output_pattern_down)): fsm_generator.step() test_string = state_bit0_string = state_bit1_string = '' for wavegroup in fsm_generator.waveform.waveform_dict['signal']: if wavegroup and wavegroup[0] == 'analysis': for wavelane in wavegroup[1:]: if wavelane['name'] == 'test': test_string = wavelane['wave'] if wavelane['name'] == 'state_bit0': state_bit0_string = wavelane['wave'] if wavelane['name'] == 'state_bit1': state_bit1_string = wavelane['wave'] test_array = np.array(bitstring_to_int(wave_to_bitstring(test_string))) state_bit0_array = np.array( bitstring_to_int(wave_to_bitstring(state_bit0_string))) state_bit1_array = np.array( bitstring_to_int(wave_to_bitstring(state_bit1_string))) assert np.array_equal(golden_test_array, test_array), \ 'Data pattern not correct when stepping at {}MHz.'.format( fsm_frequency_mhz) assert np.array_equal(golden_state_bit0_array, state_bit0_array), \ 'State bit0 not correct when stepping at {}MHz.'.format( fsm_frequency_mhz) assert np.array_equal(golden_state_bit1_array, state_bit1_array), \ 'State bit1 not correct when stepping at {}MHz.'.format( fsm_frequency_mhz) fsm_generator.stop() fsm_generator.reset() del fsm_generator