serdes-transceiver/sync_serdes.py

388 lines
12 KiB
Python

from migen import *
from migen.genlib.misc import WaitTimer
from util import PriorityEncoderMSB
class SingleLineTX(Module):
def __init__(self):
self.txdata = Signal(5)
self.ser_out = Signal()
self.t_out = Signal()
# TX SERDES
self.specials += Instance("OSERDESE2",
p_DATA_RATE_OQ="SDR", p_DATA_RATE_TQ="BUF",
p_DATA_WIDTH=5, p_TRISTATE_WIDTH=1,
p_INIT_OQ=0b00000,
o_OQ=self.ser_out, o_TQ=self.t_out,
i_RST=ResetSignal(),
i_CLK=ClockSignal("sys5x"),
i_CLKDIV=ClockSignal(),
i_D1=self.txdata[0],
i_D2=self.txdata[1],
i_D3=self.txdata[2],
i_D4=self.txdata[3],
i_D5=self.txdata[4],
i_TCE=1, i_OCE=1,
# TODO: Hardcode t_in? Output disable is always unnecessary?
i_T1=0)
class SingleLineRX(Module):
def __init__(self):
self.rxdata = Signal(10)
self.ser_in_no_dly = Signal()
self.ce = Signal()
self.cnt_out = Signal(5)
self.opt_delay = Signal(5)
ser_in = Signal()
shifts = Signal(2)
self.specials += [
# Master deserializer
Instance("ISERDESE2",
p_DATA_RATE="DDR",
p_DATA_WIDTH=10,
p_INTERFACE_TYPE="NETWORKING",
p_NUM_CE=1,
p_SERDES_MODE="MASTER",
p_IOBDELAY="IFD",
o_Q1=self.rxdata[9],
o_Q2=self.rxdata[8],
o_Q3=self.rxdata[7],
o_Q4=self.rxdata[6],
o_Q5=self.rxdata[5],
o_Q6=self.rxdata[4],
o_Q7=self.rxdata[3],
o_Q8=self.rxdata[2],
o_SHIFTOUT1=shifts[0],
o_SHIFTOUT2=shifts[1],
i_DDLY=ser_in,
i_BITSLIP=0,
i_CLK=ClockSignal("rx_sys5x"),
i_CLKB=~ClockSignal("rx_sys5x"),
i_CE1=1,
i_RST=ResetSignal("rx_sys"),
i_CLKDIV=ClockSignal("rx_sys")),
# Slave deserializer
Instance("ISERDESE2",
p_DATA_RATE="DDR",
p_DATA_WIDTH=10,
p_INTERFACE_TYPE="NETWORKING",
p_NUM_CE=1,
p_SERDES_MODE="SLAVE",
p_IOBDELAY="IFD",
o_Q3=self.rxdata[1],
o_Q4=self.rxdata[0],
# i_DDLY=ser_in,
i_BITSLIP=0,
i_CLK=ClockSignal("rx_sys5x"),
i_CLKB=~ClockSignal("rx_sys5x"),
i_CE1=1,
i_RST=ResetSignal("rx_sys"),
i_CLKDIV=ClockSignal("rx_sys"),
i_SHIFTIN1=shifts[0],
i_SHIFTIN2=shifts[1]),
# Tunable delay
Instance("IDELAYE2",
p_DELAY_SRC="IDATAIN",
p_SIGNAL_PATTERN="DATA",
p_CINVCTRL_SEL="FALSE",
p_HIGH_PERFORMANCE_MODE="TRUE",
# REFCLK refers to the clock source of IDELAYCTRL
p_REFCLK_FREQUENCY=200.0,
p_PIPE_SEL="FALSE",
p_IDELAY_TYPE="VARIABLE",
p_IDELAY_VALUE=0,
i_C=ClockSignal("rx_sys"),
# i_LD=self._dly_sel.storage[i//8] & self._rdly_dq_rst.re,
# i_CE=self._dly_sel.storage[i//8] & self._rdly_dq_inc.re,
i_LD=0,
i_CE=self.ce, # TODO: Port output
i_LDPIPEEN=0,
i_INC=1, # Always increment
# Allow aligner to check delay tap value
o_CNTVALUEOUT=self.cnt_out,
i_IDATAIN=self.ser_in_no_dly, o_DATAOUT=ser_in
),
# IDELAYCTRL is with the clocking
]
class BitSlipReader(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.bitslip = Signal()
self.data_result = Array(Signal(10) for _ in range(5))
self.slip_count = Signal(3)
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
# Keep result alive until reset
NextValue(self.data_result[self.slip_count], self.loopback_rxdata),
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.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.bitslip.eq(1),
If(self.slip_count == 5,
NextState("TERMINATE"),
).Else(
NextState("WAIT_TIMER"),
)
)
fsm.act("TERMINATE",
self.done.eq(1),
NextState("TERMINATE"),
)
class PhaseReader(Module):
def __init__(self):
# Drive IDELAYE2 CE pin to increment delay
# The signal should only last for 1 cycle
self.inc_en = Signal()
self.loopback_rxdata = Signal(10)
self.delay_tap = Signal(5)
# Pull up to start the phase reader
self.start = Signal()
self.data_result = Array(Signal(10) for _ in range(32))
self.done = Signal()
# Wait for stabilization after increment
self.submodules.stab_timer = WaitTimer(511)
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
# Keep result alive until reset
NextValue(self.data_result[self.delay_tap], self.loopback_rxdata),
NextState("HIGH_CE"),
)
fsm.act("HIGH_CE",
self.inc_en.eq(1),
NextState("LOW_CE"),
)
fsm.act("LOW_CE",
# TAP OUT is available 1 cycle after the pulse
# Explicit signal reset is unnecessary, as signal assigned by
# combinatorial logic in FSM is after leaving the setting block
NextState("READ_TAP"),
)
fsm.act("READ_TAP",
If(self.delay_tap != 0,
NextState("WAIT_TIMER"),
).Else(
NextState("PROBE_FIN"),
)
)
fsm.act("PROBE_FIN",
self.done.eq(1),
NextState("PROBE_FIN"),
)
class DelayOptimizer(Module):
def __init__(self):
# IN
# Signals from the channel
self.loopback_rxdata = Signal(10)
self.delay_tap = Signal(5)
# IN
# Signal to start the calculation
self.start = Signal()
# OUT
# Signal for controlling the channel delay tap
self.inc_en = Signal()
# OUT
# The optimal delay
self.opt_delay_tap = Signal(5)
# OUT
# Optimal delay is calculated
self.done = Signal()
# Priority encoder for finding the pulse location
self.submodules.pulse_encoder = PriorityEncoderMSB(10)
# Wait for stabilization after increment
self.submodules.stab_timer = WaitTimer(511)
# Intermediate signals
self.expected_pulse = Signal(max=9)
self.min_delay = Signal(5)
self.max_offset = Signal(5)
# Translate rxdata into array to allow indexing
self.rxdata_array = Array(Signal() for _ in range(10))
self.comb += [ self.rxdata_array[i].eq(self.loopback_rxdata[i]) for i in range(10) ]
fsm = FSM(reset_state="WAIT_START")
self.submodules += fsm
fsm.act("WAIT_START",
If(self.start,
NextState("WAIT_ZERO"),
).Else(
NextState("WAIT_START"),
)
)
fsm.act("WAIT_ZERO",
self.stab_timer.wait.eq(1),
If(self.stab_timer.done,
NextState("SAMPLE_ZERO"),
)
)
fsm.act("SAMPLE_ZERO",
# Oversampling should guarantee the detection
# However, priority encoder itself does not wraparound
# So, we need to avoid passing wrapped around pulse signal into
# the priority encoder.
If(self.loopback_rxdata[0] & self.loopback_rxdata[-1],
NextValue(self.expected_pulse, 1),
).Else(
self.pulse_encoder.i.eq(self.loopback_rxdata),
If(self.pulse_encoder.o == 9,
NextValue(self.expected_pulse, 0),
).Else(
NextValue(self.expected_pulse, self.pulse_encoder.o + 1),
)
),
# Goto the next delay tap and wait for the pulse.
NextState("INC_PULSE_DELAY_IN"),
)
fsm.act("WAIT_PULSE_IN",
self.stab_timer.wait.eq(1),
If(self.stab_timer.done,
NextState("SAMPLE_PULSE_IN"),
)
)
fsm.act("SAMPLE_PULSE_IN",
If(self.rxdata_array[self.expected_pulse],
NextValue(self.min_delay, self.delay_tap),
NextState("INC_PULSE_DELAY_OUT"),
).Else(
NextState("INC_PULSE_DELAY_IN"),
)
)
fsm.act("INC_PULSE_DELAY_IN",
# This signal is automatically deasserted after this state
self.inc_en.eq(1),
NextState("WAIT_PULSE_IN"),
)
fsm.act("WAIT_PULSE_OUT",
self.stab_timer.wait.eq(1),
If(self.stab_timer.done,
NextState("SAMPLE_PULSE_OUT"),
)
)
fsm.act("SAMPLE_PULSE_OUT",
If(~self.rxdata_array[self.expected_pulse],
NextValue(self.opt_delay_tap, self.min_delay + (self.max_offset >> 1)),
NextState("TERMINATE"),
).Else(
NextValue(self.max_offset, self.max_offset + 1),
NextState("INC_PULSE_DELAY_OUT"),
)
)
fsm.act("INC_PULSE_DELAY_OUT",
# This signal is automatically deasserted after this state
self.inc_en.eq(1),
NextState("WAIT_PULSE_OUT"),
)
fsm.act("TERMINATE",
self.done.eq(1),
NextState("TERMINATE"),
)