From e2a8433f83c6ec612494e081a380a914d133f283 Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 24 Apr 2023 03:55:01 +0800 Subject: [PATCH] align master/slave pair --- single_serdes_loopback.py | 106 ++++++++++++++++++++------- sync_serdes.py | 129 ++++++++++++++++++++++++++++++++- test_aligner.py | 146 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 348 insertions(+), 33 deletions(-) diff --git a/single_serdes_loopback.py b/single_serdes_loopback.py index f38bb2e..39e6a68 100644 --- a/single_serdes_loopback.py +++ b/single_serdes_loopback.py @@ -24,7 +24,9 @@ class SingleSerDesLoopBack(Module): self.submodules.tx = SingleLineTX() self.submodules.rx = SingleLineRX() - self.submodules.phase_reader = PhaseReader() + self.submodules.bitslip_reader = BitSlipReader() + self.submodules.slave_aligner = SlaveAligner() + self.submodules.post_align_reader = BitSlipReader() # self.submodules.delay_optimizer = DelayOptimizer() # The actual channel @@ -51,17 +53,21 @@ class SingleSerDesLoopBack(Module): # Route deserializer to phase_reader & the delay tap optimizer self.comb += [ # Start the reader initially - self.phase_reader.start.eq(1), + self.bitslip_reader.start.eq(1), # Delay tap optimizer will start after the reader is done # self.delay_optimizer.start.eq(0), + self.slave_aligner.start.eq(0), + self.post_align_reader.start.eq(0), # RXDATA for both reader and optimzer - self.phase_reader.loopback_rxdata.eq(self.rx.rxdata), + self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata), # TODO: Reconnet # self.delay_optimizer.loopback_rxdata.eq(self.rx.rxdata), + self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata), + self.post_align_reader.loopback_rxdata.eq(self.rx.rxdata), # Delay tap value - self.phase_reader.delay_tap.eq(self.rx.cnt_out), + # self.phase_reader.delay_tap.eq(self.rx.cnt_out), # TODO: Reconnet # self.delay_optimizer.delay_tap.eq(self.rx.cnt_out), @@ -73,54 +79,104 @@ class SingleSerDesLoopBack(Module): # ).Else( # self.rx.ce.eq(self.phase_reader.inc_en), # ) - self.rx.ce.eq(self.phase_reader.inc_en), + # self.rx.ce.eq(self.phase_reader.inc_en), + self.rx.master_bitslip.eq( + self.bitslip_reader.bitslip | + self.slave_aligner.master_bitslip | + self.post_align_reader.bitslip + ), + self.rx.slave_bitslip.eq( + self.bitslip_reader.bitslip | + self.slave_aligner.slave_bitslip | + self.post_align_reader.bitslip + ), ] # Show measured result on UART delay_tap = Signal(6) + bitslip_count = Signal(3) + post_align_bitslip_count = Signal(3) fsm = FSM(reset_state="WAIT_DONE") self.submodules += fsm fsm.act("WAIT_DONE", - If(self.phase_reader.done, + If(self.bitslip_reader.done, NextState("WRITE_UPPER"), ), ) fsm.act("WRITE_UPPER", # Exist state if all results are sent - If(delay_tap == 32, - NextState("TERMINATE"), + If(bitslip_count == 5, + NextState("FIND_OPT_DELAY"), ).Elif(self.tx_fifo.writable, self.tx_fifo.we.eq(1), - self.tx_fifo.din.eq(self.phase_reader.data_result[delay_tap][8:]), + self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][8:]), NextState("WRITE_LOWER"), ), ) fsm.act("WRITE_LOWER", self.tx_fifo.we.eq(1), - self.tx_fifo.din.eq(self.phase_reader.data_result[delay_tap][:8]), - NextValue(delay_tap, delay_tap + 1), + self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]), + NextValue(bitslip_count, bitslip_count + 1), NextState("WRITE_UPPER"), ) - # fsm.act("FIND_OPT_DELAY", - # self.delay_optimizer.start.eq(1), - # self.rx.ce.eq(self.delay_optimizer.inc_en), - # If(self.delay_optimizer.done, - # NextState("WRITE_OPT"), - # ).Else( - # NextState("FIND_OPT_DELAY") - # ) - # ) + fsm.act("FIND_OPT_DELAY", + self.slave_aligner.start.eq(1), + # self.rx.ce.eq(self.delay_optimizer.inc_en), + If(self.slave_aligner.done, + NextState("WRITE_DONE_UPPER"), + ).Else( + NextState("FIND_OPT_DELAY") + ) + ) - # fsm.act("WRITE_OPT", - # self.tx_fifo.we.eq(1), - # self.tx_fifo.din.eq(self.delay_optimizer.opt_delay_tap), - # NextState("TERMINATE") - # ) + fsm.act("WRITE_DONE_UPPER", + self.post_align_reader.start.eq(1), + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(0xFF), + NextState("WRITE_DONE_LOWER") + ) + ) + + fsm.act("WRITE_DONE_LOWER", + self.post_align_reader.start.eq(1), + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(0xFF), + NextState("REREAD_BITSLIP") + ) + ) + + fsm.act("REREAD_BITSLIP", + If(self.post_align_reader.done, + NextState("REWRITE_UPPER"), + ).Else( + NextState("REREAD_BITSLIP"), + ), + ) + + fsm.act("REWRITE_UPPER", + # Exist state if all results are sent + If(post_align_bitslip_count == 5, + NextState("TERMINATE"), + ).Elif(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(self.post_align_reader.data_result[post_align_bitslip_count][8:]), + NextState("REWRITE_LOWER"), + ), + ) + + fsm.act("REWRITE_LOWER", + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(self.post_align_reader.data_result[post_align_bitslip_count][:8]), + NextValue(post_align_bitslip_count, post_align_bitslip_count + 1), + NextState("REWRITE_UPPER"), + ) fsm.act("TERMINATE", NextState("TERMINATE"), diff --git a/sync_serdes.py b/sync_serdes.py index 7245026..2d347b2 100644 --- a/sync_serdes.py +++ b/sync_serdes.py @@ -35,6 +35,8 @@ class SingleLineRX(Module): self.ce = Signal() self.cnt_out = Signal(5) self.opt_delay = Signal(5) + self.master_bitslip = Signal() + self.slave_bitslip = Signal() ser_in = Signal() shifts = Signal(2) @@ -59,7 +61,7 @@ class SingleLineRX(Module): o_SHIFTOUT1=shifts[0], o_SHIFTOUT2=shifts[1], i_DDLY=ser_in, - i_BITSLIP=0, + i_BITSLIP=self.master_bitslip, i_CLK=ClockSignal("rx_sys5x"), i_CLKB=~ClockSignal("rx_sys5x"), i_CE1=1, @@ -77,7 +79,7 @@ class SingleLineRX(Module): o_Q3=self.rxdata[1], o_Q4=self.rxdata[0], # i_DDLY=ser_in, - i_BITSLIP=0, + i_BITSLIP=self.slave_bitslip, i_CLK=ClockSignal("rx_sys5x"), i_CLKB=~ClockSignal("rx_sys5x"), i_CE1=1, @@ -189,6 +191,129 @@ class BitSlipReader(Module): ) +class SlaveAligner(Module): + def __init__(self): + # IN + self.loopback_rxdata = Signal(10) + self.start = Signal() + + # Wait for stabilization after bitslip + self.submodules.stab_timer = WaitTimer(511) + + # OUT + self.done = Signal() + self.master_bitslip = Signal() + self.slave_bitslip = Signal() + # self.data_result = Array(Signal(10) for _ in range(5)) + + self.slip_count = Signal(3) + + check_odd = Signal() + check_even = Signal() + + fsm = FSM(reset_state="WAIT_START") + self.submodules += fsm + + fsm.act("WAIT_START", + If(self.start, + NextState("WAIT_TIMER"), + ).Else( + NextState("WAIT_START"), + ) + ) + + fsm.act("WAIT_TIMER", + self.stab_timer.wait.eq(1), + If(self.stab_timer.done, + NextState("SAMPLE"), + ) + ) + + fsm.act("SAMPLE", + # Wait is reset now + # Explicit assignment is unnecessary, as combinatorial statement + # falls back to he default value when not driven + + # Detect the last 2 bits + # If signal is received, detune the master bitslip if necessary + If(self.loopback_rxdata[0] | self.loopback_rxdata[1], + NextValue(check_odd, self.loopback_rxdata[1]), + NextValue(check_even, self.loopback_rxdata[0]), + NextState("CHECK_MASTER_BITSLIP"), + ).Else( + NextValue(self.slip_count, self.slip_count + 1), + NextState("HIGH_BITSLIP_FIRST"), + ) + ) + + # Pulsing BITSLIP alternate between 1 right shift and 3 left shifts + # We are trying to figure out which 2-bits are the slave copying from + # Hence, we only want shifts by 2. Pulsing twice does exactly that. + fsm.act("HIGH_BITSLIP_FIRST", + self.master_bitslip.eq(1), + self.slave_bitslip.eq(1), + NextState("LOW_BITSLIP"), + ) + + fsm.act("LOW_BITSLIP", + # bitslip signal is auto-reset + NextState("HIGH_BITSLIP_SECOND"), + ) + + fsm.act("HIGH_BITSLIP_SECOND", + self.master_bitslip.eq(1), + self.slave_bitslip.eq(1), + If(self.slip_count == 5, + NextState("TERMINATE"), + ).Else( + NextState("WAIT_TIMER"), + ) + ) + + odd_master_rxdata = self.loopback_rxdata[3::2] + even_master_rxdata = self.loopback_rxdata[2::2] + + # Alternatively, we align the master with the slave + fsm.act("CHECK_MASTER_BITSLIP", + # At any point if the odd and/or even bits from the master reads 0 + # It implies the detuning is completed + NextState("TERMINATE"), + If(check_odd & (odd_master_rxdata != 0), + NextState("MASTER_HIGH_BITSLIP_FIRST"), + ), + If(check_even & (even_master_rxdata != 0), + NextState("MASTER_HIGH_BITSLIP_FIRST"), + ), + ) + + fsm.act("MASTER_HIGH_BITSLIP_FIRST", + self.master_bitslip.eq(1), + NextState("MASTER_LOW_BITSLIP"), + ) + + fsm.act("MASTER_LOW_BITSLIP", + # bitslip signal is auto-reset + NextState("MASTER_HIGH_BITSLIP_SECOND"), + ) + + fsm.act("MASTER_HIGH_BITSLIP_SECOND", + self.master_bitslip.eq(1), + NextState("MASTER_WAIT_TIMER"), + ) + + fsm.act("MASTER_WAIT_TIMER", + self.stab_timer.wait.eq(1), + If(self.stab_timer.done, + NextState("CHECK_MASTER_BITSLIP"), + ) + ) + + fsm.act("TERMINATE", + self.done.eq(1), + NextState("TERMINATE"), + ) + + class PhaseReader(Module): def __init__(self): diff --git a/test_aligner.py b/test_aligner.py index 8393756..fad011b 100644 --- a/test_aligner.py +++ b/test_aligner.py @@ -1,5 +1,5 @@ from migen import * -from sync_serdes import PhaseReader, DelayOptimizer, BitSlipReader +from sync_serdes import PhaseReader, DelayOptimizer, BitSlipReader, SlaveAligner import random @@ -131,6 +131,73 @@ def bitslip_reader_tb(dut, rxdata_list): 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) ] @@ -191,8 +258,75 @@ def bitslip_reader_tb(dut, rxdata_list): # 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") +# # 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)