from migen import * from sync_serdes import PhaseReader, DelayOptimizer, BitSlipReader, SlaveAligner import random def reader_testbench(dut, rxdata_list): yield dut.delay_tap.eq(0) yield dut.start.eq(1) assert (yield dut.stab_timer.wait) == 0 for i in range(32): yield dut.loopback_rxdata.eq(rxdata_list[i]) yield yield assert (yield dut.stab_timer.wait) == 1 # Keep yielding until the DUT gives CE signal while (yield dut.inc_en) == 0: yield # Check that inc_en is deassrted after 1 clock cycle yield assert (yield dut.inc_en) == 0 # Load a new tap value yield dut.delay_tap.eq(i + 1) yield # Nothing to check in the READ_TAP state yield assert(yield dut.done) == 1 for i in range(32): signal = yield dut.data_result[i] expected = rxdata_list[i] assert signal == expected for i in range(200): assert (yield dut.inc_en) == 0 yield # Untouched delay: Record should be invariant for i in range(32): signal = yield dut.data_result[i] expected = rxdata_list[i] assert signal == expected def optimal_delay_testbench(dut, pulse_list, cycles, pulse_index, min_delay, max_offset, opt_delay_tap): # Start the module yield dut.delay_tap.eq(0) yield dut.start.eq(1) assert (yield dut.stab_timer.wait) == 0 for i in range(cycles): # Pass in a new rxdata for sampling # The stab_timer should start waiting after yield dut.loopback_rxdata.eq(pulse_list[i]) yield yield assert (yield dut.stab_timer.wait) == 1 # Eventually, the wait will end # Either it triggers an increment or a finished signal # And we will get the expected pulse location # inc_en is pulsed after this is found if i == (cycles - 1): while (yield dut.done) == 0: yield break else: while (yield dut.inc_en) == 0: yield # Then we increment the rxdata index yield dut.delay_tap.eq(i + 1) yield # Fast-forward to the result # while (yield dut.done) == 0: # yield assert (yield dut.done) == 1 assert (yield dut.expected_pulse) == pulse_index assert (yield dut.min_delay) == min_delay assert (yield dut.max_offset) == max_offset assert (yield dut.opt_delay_tap) == opt_delay_tap for _ in range(100): yield # Invariant test: Everything is frozen after done assert (yield dut.done) == 1 assert (yield dut.expected_pulse) == pulse_index assert (yield dut.min_delay) == min_delay assert (yield dut.max_offset) == max_offset assert (yield dut.opt_delay_tap) == opt_delay_tap def bitslip_reader_tb(dut, rxdata_list): # Start the module yield dut.start.eq(1) assert (yield dut.stab_timer.wait) == 0 for i in range(5): yield dut.loopback_rxdata.eq(rxdata_list[i]) yield yield assert (yield dut.stab_timer.wait) == 1 # Keep yielding until the DUT gives BITSLIP signal while (yield dut.bitslip) == 0: yield # There will be 2 BITSLIP pulses # Both BITSLIP pulses should last for 1 cycle assert (yield dut.bitslip) == 1 yield assert (yield dut.bitslip) == 0 yield assert (yield dut.bitslip) == 1 yield assert (yield dut.bitslip) == 0 assert (yield dut.done) == 1 # The result in the module should contain all rxdata for i, rxdata in enumerate(rxdata_list): assert (yield dut.data_result[i]) == rxdata yield yield def bitslip_aligner_tb(dut, rxdata_list, adjust_list): # Start the module yield dut.start.eq(1) assert (yield dut.stab_timer.wait) == 0 for i in range(len(rxdata_list)): yield dut.loopback_rxdata.eq(rxdata_list[i]) yield yield assert (yield dut.stab_timer.wait) == 1 # Wait until the timer runs out while (yield dut.stab_timer.done) == 0: yield # There will not be unnecessary pulses if i == (len(rxdata_list) - 1): break # Keep yielding until the DUT gives BITSLIP signal while (((yield dut.master_bitslip) == 0) and ((yield dut.slave_bitslip) == 0)): yield # There will be 2 BITSLIP pulses # Both BITSLIP pulses should last for 1 cycle assert (yield dut.master_bitslip) == 1 assert (yield dut.slave_bitslip) == 1 yield assert (yield dut.master_bitslip) == 0 assert (yield dut.slave_bitslip) == 0 yield assert (yield dut.master_bitslip) == 1 assert (yield dut.slave_bitslip) == 1 yield assert (yield dut.master_bitslip) == 0 assert (yield dut.slave_bitslip) == 0 # Skip ahead 2 cycles for state transitions yield yield for i in range(len(adjust_list)): # Eventually the master bitslip signal will be pulled up while (yield dut.master_bitslip) == 0: yield assert (yield dut.master_bitslip) == 1 assert (yield dut.slave_bitslip) == 0 yield assert (yield dut.master_bitslip) == 0 assert (yield dut.slave_bitslip) == 0 yield assert (yield dut.master_bitslip) == 1 assert (yield dut.slave_bitslip) == 0 yield assert (yield dut.master_bitslip) == 0 assert (yield dut.slave_bitslip) == 0 # Give the new rxdata yield dut.loopback_rxdata.eq(adjust_list[i]) while (yield dut.stab_timer.done) == 0: yield yield yield assert (yield dut.done) == 1 # # Random testing for delay reader # for _ in range(32): # rxdata_list = [ random.getrandbits(10) for _ in range(32) ] # dut = PhaseReader() # run_simulation(dut, reader_testbench(dut, rxdata_list), vcd_name="phase_reader.vcd") # # Random testing for optimal delay calculation # # Generate a delay list # start = random.randint(0, 9) # start_length = random.randint(1, 10) # offset = random.randint(4, 5) # current_index = start # remaining_length = start_length # single_pulse_list = [] # expected_index = (current_index + 1) % 10 # expected_length = 10 # for tap in range(32 + offset): # single_pulse_list.append(1 << current_index) # remaining_length -= 1 # if remaining_length == 0: # current_index = (current_index + 1) % 10 # remaining_length = 10 # pulse_list = list(single_pulse_list) # for i in range(offset, 32): # pulse_list[i] |= single_pulse_list[i - offset] # found_start_edge = False # max_offset = 0 # # Calculate min_delay # for i, pulse in enumerate(pulse_list): # if (pulse & (1 << expected_index)) != 0: # if not found_start_edge: # min_delay = i # found_start_edge = True # else: # max_offset += 1 # if (pulse & (1 << expected_index)) == 0 and found_start_edge: # cycles = i + 1 # break # print(min_delay) # print(max_offset) # print(cycles) # opt_delay = int(min_delay + (max_offset / 2)) # print(opt_delay) # # Simulate # dut = DelayOptimizer() # run_simulation(dut, optimal_delay_testbench( # dut, pulse_list, cycles, expected_index, # min_delay, max_offset, opt_delay), # vcd_name="delay_opt.vcd" # ) # # Random test for bitslip reader # for _ in range(32): # rxdata_list = [ random.getrandbits(10) for _ in range(5) ] # dut = BitSlipReader() # run_simulation(dut, bitslip_reader_tb(dut, rxdata_list), vcd_name="bitslip_reader.vcd") # Test for bitslip alignment def generate_aligner_tb_list(mocked_pair, start_pair): print("Mocked pair", str(mocked_pair)) print("Start pair", str(start_pair)) double_bit_pattern = bool(random.randint(0, 1)) pulsed_indices = [ random.randint(0, 9) ] print("Pulsed indices", str(pulsed_indices)) if double_bit_pattern: pulsed_indices.append((pulsed_indices[0] + 1) % 10) def generate_rxdata(pair_shift): mocked_data = 0 rxdata = 0 for index in pulsed_indices: rxdata |= (1 << ((index + 2*pair_shift) % 10)) mocking_mask = (1 << (2*mocked_pair)) | (1 << ((2*mocked_pair) + 1)) mocked_data = ((rxdata & mocking_mask) >> (2*mocked_pair)) # Wipe out the 2 lsb rxdata &= 0x3FC # Copy the mocked data to the 2 lsb rxdata |= mocked_data return mocked_data, rxdata rxdata_list = [] for i in range(5): mocked_data, rxdata = generate_rxdata((start_pair + i) % 5) rxdata_list.append(rxdata) if mocked_data: break print("Mocked data", str(mocked_data)) def is_mocking(original): if original == 0: return False if (original & 0b11) == mocked_data: return True return is_mocking(original >> 2) adjust_list = [] master_rxdata = rxdata_list[-1] & 0x3FC if mocked_data and is_mocking(master_rxdata): # Keep adding shifted variant to the adjust_list # Until the 2 lsb is not mocking other pairs while True: master_rxdata <<= 2 master_rxdata &= 0x3FF adjust_list.append(master_rxdata | mocked_data) if not is_mocking(master_rxdata): break return rxdata_list, adjust_list for mocked_pair in range(5): for start_pair in range(5): for _ in range(12): rxdata_list, adjust_list = generate_aligner_tb_list(mocked_pair, start_pair) print(rxdata_list) print(adjust_list) dut = SlaveAligner() run_simulation(dut, bitslip_aligner_tb(dut, rxdata_list, adjust_list), vcd_name="bitslip_aligner.vcd") print(mocked_pair, start_pair)