align master/slave pair

This commit is contained in:
occheung 2023-04-24 03:55:01 +08:00
parent 7f23de0ce8
commit e2a8433f83
3 changed files with 348 additions and 33 deletions

View File

@ -24,7 +24,9 @@ class SingleSerDesLoopBack(Module):
self.submodules.tx = SingleLineTX() self.submodules.tx = SingleLineTX()
self.submodules.rx = SingleLineRX() 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() # self.submodules.delay_optimizer = DelayOptimizer()
# The actual channel # The actual channel
@ -51,17 +53,21 @@ class SingleSerDesLoopBack(Module):
# Route deserializer to phase_reader & the delay tap optimizer # Route deserializer to phase_reader & the delay tap optimizer
self.comb += [ self.comb += [
# Start the reader initially # 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 # Delay tap optimizer will start after the reader is done
# self.delay_optimizer.start.eq(0), # 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 # 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 # TODO: Reconnet
# self.delay_optimizer.loopback_rxdata.eq(self.rx.rxdata), # 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 # 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 # TODO: Reconnet
# self.delay_optimizer.delay_tap.eq(self.rx.cnt_out), # self.delay_optimizer.delay_tap.eq(self.rx.cnt_out),
@ -73,54 +79,104 @@ class SingleSerDesLoopBack(Module):
# ).Else( # ).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.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 # Show measured result on UART
delay_tap = Signal(6) delay_tap = Signal(6)
bitslip_count = Signal(3)
post_align_bitslip_count = Signal(3)
fsm = FSM(reset_state="WAIT_DONE") fsm = FSM(reset_state="WAIT_DONE")
self.submodules += fsm self.submodules += fsm
fsm.act("WAIT_DONE", fsm.act("WAIT_DONE",
If(self.phase_reader.done, If(self.bitslip_reader.done,
NextState("WRITE_UPPER"), NextState("WRITE_UPPER"),
), ),
) )
fsm.act("WRITE_UPPER", fsm.act("WRITE_UPPER",
# Exist state if all results are sent # Exist state if all results are sent
If(delay_tap == 32, If(bitslip_count == 5,
NextState("TERMINATE"), NextState("FIND_OPT_DELAY"),
).Elif(self.tx_fifo.writable, ).Elif(self.tx_fifo.writable,
self.tx_fifo.we.eq(1), 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"), NextState("WRITE_LOWER"),
), ),
) )
fsm.act("WRITE_LOWER", fsm.act("WRITE_LOWER",
self.tx_fifo.we.eq(1), 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]),
NextValue(delay_tap, delay_tap + 1), NextValue(bitslip_count, bitslip_count + 1),
NextState("WRITE_UPPER"), NextState("WRITE_UPPER"),
) )
# fsm.act("FIND_OPT_DELAY", fsm.act("FIND_OPT_DELAY",
# self.delay_optimizer.start.eq(1), self.slave_aligner.start.eq(1),
# self.rx.ce.eq(self.delay_optimizer.inc_en), # self.rx.ce.eq(self.delay_optimizer.inc_en),
# If(self.delay_optimizer.done, If(self.slave_aligner.done,
# NextState("WRITE_OPT"), NextState("WRITE_DONE_UPPER"),
# ).Else( ).Else(
# NextState("FIND_OPT_DELAY") NextState("FIND_OPT_DELAY")
# ) )
# ) )
# fsm.act("WRITE_OPT", fsm.act("WRITE_DONE_UPPER",
# self.tx_fifo.we.eq(1), self.post_align_reader.start.eq(1),
# self.tx_fifo.din.eq(self.delay_optimizer.opt_delay_tap), If(self.tx_fifo.writable,
# NextState("TERMINATE") 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", fsm.act("TERMINATE",
NextState("TERMINATE"), NextState("TERMINATE"),

View File

@ -35,6 +35,8 @@ class SingleLineRX(Module):
self.ce = Signal() self.ce = Signal()
self.cnt_out = Signal(5) self.cnt_out = Signal(5)
self.opt_delay = Signal(5) self.opt_delay = Signal(5)
self.master_bitslip = Signal()
self.slave_bitslip = Signal()
ser_in = Signal() ser_in = Signal()
shifts = Signal(2) shifts = Signal(2)
@ -59,7 +61,7 @@ class SingleLineRX(Module):
o_SHIFTOUT1=shifts[0], o_SHIFTOUT1=shifts[0],
o_SHIFTOUT2=shifts[1], o_SHIFTOUT2=shifts[1],
i_DDLY=ser_in, i_DDLY=ser_in,
i_BITSLIP=0, i_BITSLIP=self.master_bitslip,
i_CLK=ClockSignal("rx_sys5x"), i_CLK=ClockSignal("rx_sys5x"),
i_CLKB=~ClockSignal("rx_sys5x"), i_CLKB=~ClockSignal("rx_sys5x"),
i_CE1=1, i_CE1=1,
@ -77,7 +79,7 @@ class SingleLineRX(Module):
o_Q3=self.rxdata[1], o_Q3=self.rxdata[1],
o_Q4=self.rxdata[0], o_Q4=self.rxdata[0],
# i_DDLY=ser_in, # i_DDLY=ser_in,
i_BITSLIP=0, i_BITSLIP=self.slave_bitslip,
i_CLK=ClockSignal("rx_sys5x"), i_CLK=ClockSignal("rx_sys5x"),
i_CLKB=~ClockSignal("rx_sys5x"), i_CLKB=~ClockSignal("rx_sys5x"),
i_CE1=1, 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): class PhaseReader(Module):
def __init__(self): def __init__(self):

View File

@ -1,5 +1,5 @@
from migen import * from migen import *
from sync_serdes import PhaseReader, DelayOptimizer, BitSlipReader from sync_serdes import PhaseReader, DelayOptimizer, BitSlipReader, SlaveAligner
import random import random
@ -131,6 +131,73 @@ def bitslip_reader_tb(dut, rxdata_list):
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 # # Random testing for delay reader
# for _ in range(32): # for _ in range(32):
# rxdata_list = [ random.getrandbits(10) 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" # vcd_name="delay_opt.vcd"
# ) # )
# Random test for bitslip reader # # Random test for bitslip reader
for _ in range(32): # for _ in range(32):
rxdata_list = [ random.getrandbits(10) for _ in range(5) ] # rxdata_list = [ random.getrandbits(10) for _ in range(5) ]
dut = BitSlipReader() # dut = BitSlipReader()
run_simulation(dut, bitslip_reader_tb(dut, rxdata_list), vcd_name="bitslip_reader.vcd") # 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)