align master/slave pair
This commit is contained in:
parent
7f23de0ce8
commit
e2a8433f83
@ -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),
|
||||
fsm.act("FIND_OPT_DELAY",
|
||||
self.slave_aligner.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")
|
||||
# )
|
||||
# )
|
||||
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"),
|
||||
|
129
sync_serdes.py
129
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):
|
||||
|
146
test_aligner.py
146
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)
|
||||
|
Loading…
Reference in New Issue
Block a user