forked from M-Labs/artiq
serwb: rewrite high-speed phys by splitting clocking/tx/rx, scrambling is now always enabled.
This commit is contained in:
parent
f8a9dd930b
commit
3873d09692
|
@ -1 +1 @@
|
||||||
from artiq.gateware.serwb import s7phy, kusphy, genphy, phy, core, packet, etherbone
|
from artiq.gateware.serwb import s7serdes, kuserdes, genphy, phy, core, packet, etherbone
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.io import *
|
||||||
|
from migen.genlib.misc import BitSlip, WaitTimer
|
||||||
|
|
||||||
|
from misoc.interconnect import stream
|
||||||
|
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||||
|
|
||||||
|
|
||||||
|
def K(x, y):
|
||||||
|
return (y << 5) | x
|
||||||
|
|
||||||
|
|
||||||
|
class _KUSerdesClocking(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
self.refclk = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# In Master mode, generate the linerate/10 clock. Slave will re-multiply it.
|
||||||
|
if mode == "master":
|
||||||
|
converter = stream.Converter(40, 8)
|
||||||
|
self.submodules += converter
|
||||||
|
self.comb += [
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.source.ack.eq(1),
|
||||||
|
converter.sink.data.eq(Replicate(Signal(10, reset=0b1111100000), 4)),
|
||||||
|
]
|
||||||
|
self.specials += [
|
||||||
|
Instance("OSERDESE3",
|
||||||
|
p_DATA_WIDTH=8, p_INIT=0,
|
||||||
|
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0,
|
||||||
|
p_IS_RST_INVERTED=0,
|
||||||
|
|
||||||
|
o_OQ=self.refclk,
|
||||||
|
i_RST=ResetSignal("sys"),
|
||||||
|
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
||||||
|
i_D=converter.source.data
|
||||||
|
),
|
||||||
|
DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
|
||||||
|
]
|
||||||
|
|
||||||
|
# In Slave mode, multiply the clock provided by Master with a PLL/MMCM
|
||||||
|
elif mode == "slave":
|
||||||
|
self.specials += DifferentialInput(pads.clk_p, pads.clk_n, self.refclk)
|
||||||
|
|
||||||
|
|
||||||
|
class _KUSerdesTX(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
# Control
|
||||||
|
self.idle = idle = Signal()
|
||||||
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
|
# Datapath
|
||||||
|
self.ce = ce = Signal()
|
||||||
|
self.k = k = Signal(4)
|
||||||
|
self.d = d = Signal(32)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# 8b10b encoder
|
||||||
|
self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
|
||||||
|
self.comb += encoder.ce.eq(ce)
|
||||||
|
|
||||||
|
# 40 --> 8 converter
|
||||||
|
converter = stream.Converter(40, 8)
|
||||||
|
self.submodules += converter
|
||||||
|
self.comb += [
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.source.ack.eq(1),
|
||||||
|
# Enable pipeline when converter accepts the 40 bits
|
||||||
|
ce.eq(converter.sink.ack),
|
||||||
|
# If not idle, connect encoder to converter
|
||||||
|
If(~idle,
|
||||||
|
converter.sink.data.eq(Cat(*[encoder.output[i] for i in range(4)]))
|
||||||
|
),
|
||||||
|
# If comma, send K28.5
|
||||||
|
If(comma,
|
||||||
|
encoder.k[0].eq(1),
|
||||||
|
encoder.d[0].eq(K(28,5)),
|
||||||
|
# Else connect TX to encoder
|
||||||
|
).Else(
|
||||||
|
encoder.k[0].eq(k[0]),
|
||||||
|
encoder.k[1].eq(k[1]),
|
||||||
|
encoder.k[2].eq(k[2]),
|
||||||
|
encoder.k[3].eq(k[3]),
|
||||||
|
encoder.d[0].eq(d[0:8]),
|
||||||
|
encoder.d[1].eq(d[8:16]),
|
||||||
|
encoder.d[2].eq(d[16:24]),
|
||||||
|
encoder.d[3].eq(d[24:32])
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Data output (DDR with sys4x)
|
||||||
|
data = Signal()
|
||||||
|
self.specials += [
|
||||||
|
Instance("OSERDESE3",
|
||||||
|
p_DATA_WIDTH=8, p_INIT=0,
|
||||||
|
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0,
|
||||||
|
|
||||||
|
o_OQ=data,
|
||||||
|
i_RST=ResetSignal("sys"),
|
||||||
|
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
||||||
|
i_D=converter.source.data
|
||||||
|
),
|
||||||
|
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _KUSerdesRX(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
# Control
|
||||||
|
self.delay_rst = Signal()
|
||||||
|
self.delay_inc = Signal()
|
||||||
|
self.bitslip_value = bitslip_value = Signal(6)
|
||||||
|
|
||||||
|
# Status
|
||||||
|
self.idle = idle = Signal()
|
||||||
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
|
# Datapath
|
||||||
|
self.ce = ce = Signal()
|
||||||
|
self.k = k = Signal(4)
|
||||||
|
self.d = d = Signal(32)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# Data input (DDR with sys4x)
|
||||||
|
data_nodelay = Signal()
|
||||||
|
data_delayed = Signal()
|
||||||
|
data_deserialized = Signal(8)
|
||||||
|
self.specials += [
|
||||||
|
DifferentialInput(pads.rx_p, pads.rx_n, data_nodelay),
|
||||||
|
Instance("IDELAYE3",
|
||||||
|
p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0,
|
||||||
|
p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0,
|
||||||
|
p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN",
|
||||||
|
p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0,
|
||||||
|
|
||||||
|
i_CLK=ClockSignal("sys"),
|
||||||
|
i_RST=self.delay_rst, i_LOAD=0,
|
||||||
|
i_INC=1, i_EN_VTC=0,
|
||||||
|
i_CE=self.delay_inc,
|
||||||
|
|
||||||
|
i_IDATAIN=data_nodelay, o_DATAOUT=data_delayed
|
||||||
|
),
|
||||||
|
Instance("ISERDESE3",
|
||||||
|
p_IS_CLK_INVERTED=0,
|
||||||
|
p_IS_CLK_B_INVERTED=1,
|
||||||
|
p_DATA_WIDTH=8,
|
||||||
|
|
||||||
|
i_D=data_delayed,
|
||||||
|
i_RST=ResetSignal("sys"),
|
||||||
|
i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0,
|
||||||
|
i_CLK=ClockSignal("sys4x"),
|
||||||
|
i_CLK_B=ClockSignal("sys4x"), # locally inverted
|
||||||
|
i_CLKDIV=ClockSignal("sys"),
|
||||||
|
o_Q=data_deserialized
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 8 --> 40 converter and bitslip
|
||||||
|
converter = stream.Converter(8, 40)
|
||||||
|
self.submodules += converter
|
||||||
|
bitslip = CEInserter()(BitSlip(40))
|
||||||
|
self.submodules += bitslip
|
||||||
|
self.comb += [
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.source.ack.eq(1),
|
||||||
|
# Enable pipeline when converter outputs the 40 bits
|
||||||
|
ce.eq(converter.source.stb),
|
||||||
|
# Connect input data to converter
|
||||||
|
converter.sink.data.eq(data_deserialized),
|
||||||
|
# Connect converter to bitslip
|
||||||
|
bitslip.ce.eq(ce),
|
||||||
|
bitslip.value.eq(bitslip_value),
|
||||||
|
bitslip.i.eq(converter.source.data)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 8b10b decoder
|
||||||
|
self.submodules.decoders = decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
|
||||||
|
self.comb += [decoders[i].ce.eq(ce) for i in range(4)]
|
||||||
|
self.comb += [
|
||||||
|
# Connect bitslip to decoder
|
||||||
|
decoders[0].input.eq(bitslip.o[0:10]),
|
||||||
|
decoders[1].input.eq(bitslip.o[10:20]),
|
||||||
|
decoders[2].input.eq(bitslip.o[20:30]),
|
||||||
|
decoders[3].input.eq(bitslip.o[30:40]),
|
||||||
|
# Connect decoder to output
|
||||||
|
self.k.eq(Cat(*[decoders[i].k for i in range(4)])),
|
||||||
|
self.d.eq(Cat(*[decoders[i].d for i in range(4)])),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Status
|
||||||
|
idle_timer = WaitTimer(256)
|
||||||
|
self.submodules += idle_timer
|
||||||
|
self.comb += [
|
||||||
|
idle_timer.wait.eq(1),
|
||||||
|
self.idle.eq(idle_timer.done &
|
||||||
|
((bitslip.o == 0) | (bitslip.o == (2**40-1)))),
|
||||||
|
self.comma.eq(
|
||||||
|
(decoders[0].k == 1) & (decoders[0].d == K(28,5)) &
|
||||||
|
(decoders[1].k == 0) & (decoders[1].d == 0) &
|
||||||
|
(decoders[2].k == 0) & (decoders[2].d == 0) &
|
||||||
|
(decoders[3].k == 0) & (decoders[3].d == 0))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ResetInserter()
|
||||||
|
class KUSerdes(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
self.submodules.clocking = _KUSerdesClocking(pads, mode)
|
||||||
|
self.submodules.tx = _KUSerdesTX(pads, mode)
|
||||||
|
self.submodules.rx = _KUSerdesRX(pads, mode)
|
|
@ -1,218 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.misc import BitSlip
|
|
||||||
from migen.genlib.misc import WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
|
||||||
|
|
||||||
|
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class KUSSerdes(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
if mode == "slave":
|
|
||||||
self.refclk = Signal()
|
|
||||||
|
|
||||||
self.tx_ce = Signal()
|
|
||||||
self.tx_k = Signal(4)
|
|
||||||
self.tx_d = Signal(32)
|
|
||||||
|
|
||||||
self.rx_ce = Signal()
|
|
||||||
self.rx_k = Signal(4)
|
|
||||||
self.rx_d = Signal(32)
|
|
||||||
|
|
||||||
self.tx_idle = Signal()
|
|
||||||
self.tx_comma = Signal()
|
|
||||||
self.rx_idle = Signal()
|
|
||||||
self.rx_comma = Signal()
|
|
||||||
|
|
||||||
self.rx_bitslip_value = Signal(6)
|
|
||||||
self.rx_delay_rst = Signal()
|
|
||||||
self.rx_delay_inc = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
|
|
||||||
self.comb += encoder.ce.eq(self.tx_ce)
|
|
||||||
self.submodules.decoders = decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
|
|
||||||
self.comb += [decoders[i].ce.eq(self.rx_ce) for i in range(4)]
|
|
||||||
|
|
||||||
# clocking:
|
|
||||||
|
|
||||||
# In master mode:
|
|
||||||
# - linerate/10 refclk generated on clk_pads
|
|
||||||
# In Slave mode:
|
|
||||||
# - linerate/10 refclk provided by clk_pads
|
|
||||||
|
|
||||||
# tx clock (linerate/10)
|
|
||||||
if mode == "master":
|
|
||||||
clk_converter = stream.Converter(40, 8)
|
|
||||||
self.submodules += clk_converter
|
|
||||||
self.comb += [
|
|
||||||
clk_converter.sink.stb.eq(1),
|
|
||||||
clk_converter.sink.data.eq(Replicate(Signal(10, reset=0b1111100000), 4)),
|
|
||||||
clk_converter.source.ack.eq(1)
|
|
||||||
]
|
|
||||||
clk_o = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE3",
|
|
||||||
p_DATA_WIDTH=8, p_INIT=0,
|
|
||||||
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0,
|
|
||||||
p_IS_RST_INVERTED=0,
|
|
||||||
|
|
||||||
o_OQ=clk_o,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D=clk_converter.source.data
|
|
||||||
),
|
|
||||||
Instance("OBUFDS",
|
|
||||||
i_I=clk_o,
|
|
||||||
o_O=pads.clk_p,
|
|
||||||
o_OB=pads.clk_n
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# tx datapath
|
|
||||||
# tx_data -> encoders -> converter -> serdes
|
|
||||||
self.submodules.tx_converter = tx_converter = stream.Converter(40, 8)
|
|
||||||
self.comb += [
|
|
||||||
tx_converter.sink.stb.eq(1),
|
|
||||||
self.tx_ce.eq(tx_converter.sink.ack),
|
|
||||||
tx_converter.source.ack.eq(1),
|
|
||||||
If(self.tx_idle,
|
|
||||||
tx_converter.sink.data.eq(0)
|
|
||||||
).Else(
|
|
||||||
tx_converter.sink.data.eq(
|
|
||||||
Cat(*[encoder.output[i] for i in range(4)]))
|
|
||||||
),
|
|
||||||
If(self.tx_comma,
|
|
||||||
encoder.k[0].eq(1),
|
|
||||||
encoder.d[0].eq(K(28,5)),
|
|
||||||
).Else(
|
|
||||||
encoder.k[0].eq(self.tx_k[0]),
|
|
||||||
encoder.k[1].eq(self.tx_k[1]),
|
|
||||||
encoder.k[2].eq(self.tx_k[2]),
|
|
||||||
encoder.k[3].eq(self.tx_k[3]),
|
|
||||||
encoder.d[0].eq(self.tx_d[0:8]),
|
|
||||||
encoder.d[1].eq(self.tx_d[8:16]),
|
|
||||||
encoder.d[2].eq(self.tx_d[16:24]),
|
|
||||||
encoder.d[3].eq(self.tx_d[24:32])
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
serdes_o = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE3",
|
|
||||||
p_DATA_WIDTH=8, p_INIT=0,
|
|
||||||
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0,
|
|
||||||
|
|
||||||
o_OQ=serdes_o,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D=tx_converter.source.data
|
|
||||||
),
|
|
||||||
Instance("OBUFDS",
|
|
||||||
i_I=serdes_o,
|
|
||||||
o_O=pads.tx_p,
|
|
||||||
o_OB=pads.tx_n
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# rx clock
|
|
||||||
use_bufr = True
|
|
||||||
if mode == "slave":
|
|
||||||
clk_i = Signal()
|
|
||||||
clk_i_bufg = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFDS",
|
|
||||||
i_I=pads.clk_p,
|
|
||||||
i_IB=pads.clk_n,
|
|
||||||
o_O=clk_i
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if use_bufr:
|
|
||||||
clk_i_bufr = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
|
|
||||||
Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
|
|
||||||
self.comb += self.refclk.eq(clk_i_bufg)
|
|
||||||
|
|
||||||
# rx datapath
|
|
||||||
# serdes -> converter -> bitslip -> decoders -> rx_data
|
|
||||||
self.submodules.rx_converter = rx_converter = stream.Converter(8, 40)
|
|
||||||
self.comb += [
|
|
||||||
self.rx_ce.eq(rx_converter.source.stb),
|
|
||||||
rx_converter.source.ack.eq(1)
|
|
||||||
]
|
|
||||||
self.submodules.rx_bitslip = rx_bitslip = CEInserter()(BitSlip(40))
|
|
||||||
self.comb += rx_bitslip.ce.eq(self.rx_ce)
|
|
||||||
|
|
||||||
serdes_i_nodelay = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFDS_DIFF_OUT",
|
|
||||||
i_I=pads.rx_p,
|
|
||||||
i_IB=pads.rx_n,
|
|
||||||
o_O=serdes_i_nodelay
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
serdes_i_delayed = Signal()
|
|
||||||
serdes_q = Signal(8)
|
|
||||||
self.specials += [
|
|
||||||
Instance("IDELAYE3",
|
|
||||||
p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0,
|
|
||||||
p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0,
|
|
||||||
p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN",
|
|
||||||
p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0,
|
|
||||||
|
|
||||||
i_CLK=ClockSignal("sys"),
|
|
||||||
i_RST=self.rx_delay_rst, i_LOAD=0,
|
|
||||||
i_INC=1, i_EN_VTC=0,
|
|
||||||
i_CE=self.rx_delay_inc,
|
|
||||||
|
|
||||||
i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed
|
|
||||||
),
|
|
||||||
Instance("ISERDESE3",
|
|
||||||
p_IS_CLK_INVERTED=0,
|
|
||||||
p_IS_CLK_B_INVERTED=1,
|
|
||||||
p_DATA_WIDTH=8,
|
|
||||||
|
|
||||||
i_D=serdes_i_delayed,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0,
|
|
||||||
i_CLK=ClockSignal("sys4x"),
|
|
||||||
i_CLK_B=ClockSignal("sys4x"), # locally inverted
|
|
||||||
i_CLKDIV=ClockSignal("sys"),
|
|
||||||
o_Q=serdes_q
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
rx_converter.sink.stb.eq(1),
|
|
||||||
rx_converter.sink.data.eq(serdes_q),
|
|
||||||
rx_bitslip.value.eq(self.rx_bitslip_value),
|
|
||||||
rx_bitslip.i.eq(rx_converter.source.data),
|
|
||||||
decoders[0].input.eq(rx_bitslip.o[0:10]),
|
|
||||||
decoders[1].input.eq(rx_bitslip.o[10:20]),
|
|
||||||
decoders[2].input.eq(rx_bitslip.o[20:30]),
|
|
||||||
decoders[3].input.eq(rx_bitslip.o[30:40]),
|
|
||||||
self.rx_k.eq(Cat(*[decoders[i].k for i in range(4)])),
|
|
||||||
self.rx_d.eq(Cat(*[decoders[i].d for i in range(4)])),
|
|
||||||
self.rx_comma.eq(
|
|
||||||
(decoders[0].k == 1) & (decoders[0].d == K(28,5)) &
|
|
||||||
(decoders[1].k == 0) & (decoders[1].d == 0) &
|
|
||||||
(decoders[2].k == 0) & (decoders[2].d == 0) &
|
|
||||||
(decoders[3].k == 0) & (decoders[3].d == 0))
|
|
||||||
]
|
|
||||||
|
|
||||||
idle_timer = WaitTimer(32)
|
|
||||||
self.submodules += idle_timer
|
|
||||||
self.comb += idle_timer.wait.eq(1)
|
|
||||||
self.sync += self.rx_idle.eq(idle_timer.done &
|
|
||||||
((rx_bitslip.o == 0) | (rx_bitslip.o == (2**40-1))))
|
|
|
@ -1,13 +1,12 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
|
||||||
from migen.genlib.misc import WaitTimer
|
from migen.genlib.misc import WaitTimer
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
from misoc.interconnect import stream
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
|
|
||||||
from artiq.gateware.serwb.scrambler import Scrambler, Descrambler
|
from artiq.gateware.serwb.scrambler import Scrambler, Descrambler
|
||||||
from artiq.gateware.serwb.kusphy import KUSSerdes
|
from artiq.gateware.serwb.kuserdes import KUSerdes
|
||||||
from artiq.gateware.serwb.s7phy import S7Serdes
|
from artiq.gateware.serwb.s7serdes import S7Serdes
|
||||||
|
|
||||||
|
|
||||||
# Master <--> Slave synchronization:
|
# Master <--> Slave synchronization:
|
||||||
|
@ -21,7 +20,7 @@ from artiq.gateware.serwb.s7phy import S7Serdes
|
||||||
|
|
||||||
@ResetInserter()
|
@ResetInserter()
|
||||||
class _SerdesMasterInit(Module):
|
class _SerdesMasterInit(Module):
|
||||||
def __init__(self, serdes, taps, timeout=2**15):
|
def __init__(self, serdes, taps, timeout):
|
||||||
self.ready = Signal()
|
self.ready = Signal()
|
||||||
self.error = Signal()
|
self.error = Signal()
|
||||||
|
|
||||||
|
@ -43,10 +42,10 @@ class _SerdesMasterInit(Module):
|
||||||
NextValue(delay_min_found, 0),
|
NextValue(delay_min_found, 0),
|
||||||
NextValue(delay_max, 0),
|
NextValue(delay_max, 0),
|
||||||
NextValue(delay_max_found, 0),
|
NextValue(delay_max_found, 0),
|
||||||
serdes.rx_delay_rst.eq(1),
|
serdes.rx.delay_rst.eq(1),
|
||||||
NextValue(bitslip, 0),
|
NextValue(bitslip, 0),
|
||||||
NextState("RESET_SLAVE"),
|
NextState("RESET_SLAVE"),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("RESET_SLAVE",
|
fsm.act("RESET_SLAVE",
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
|
@ -54,16 +53,16 @@ class _SerdesMasterInit(Module):
|
||||||
timer.wait.eq(0),
|
timer.wait.eq(0),
|
||||||
NextState("SEND_PATTERN")
|
NextState("SEND_PATTERN")
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("SEND_PATTERN",
|
fsm.act("SEND_PATTERN",
|
||||||
If(~serdes.rx_idle,
|
If(~serdes.rx.idle,
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
If(timer.done,
|
If(timer.done,
|
||||||
NextState("CHECK_PATTERN")
|
NextState("CHECK_PATTERN")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_STABLE",
|
fsm.act("WAIT_STABLE",
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
|
@ -71,11 +70,11 @@ class _SerdesMasterInit(Module):
|
||||||
timer.wait.eq(0),
|
timer.wait.eq(0),
|
||||||
NextState("CHECK_PATTERN")
|
NextState("CHECK_PATTERN")
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("CHECK_PATTERN",
|
fsm.act("CHECK_PATTERN",
|
||||||
If(~delay_min_found,
|
If(~delay_min_found,
|
||||||
If(serdes.rx_comma,
|
If(serdes.rx.comma,
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
If(timer.done,
|
If(timer.done,
|
||||||
timer.wait.eq(0),
|
timer.wait.eq(0),
|
||||||
|
@ -86,7 +85,7 @@ class _SerdesMasterInit(Module):
|
||||||
NextState("INC_DELAY_BITSLIP")
|
NextState("INC_DELAY_BITSLIP")
|
||||||
),
|
),
|
||||||
).Else(
|
).Else(
|
||||||
If(~serdes.rx_comma,
|
If(~serdes.rx.comma,
|
||||||
NextValue(delay_max, delay),
|
NextValue(delay_max, delay),
|
||||||
NextValue(delay_max_found, 1),
|
NextValue(delay_max_found, 1),
|
||||||
NextState("CHECK_SAMPLING_WINDOW")
|
NextState("CHECK_SAMPLING_WINDOW")
|
||||||
|
@ -94,9 +93,9 @@ class _SerdesMasterInit(Module):
|
||||||
NextState("INC_DELAY_BITSLIP")
|
NextState("INC_DELAY_BITSLIP")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
self.comb += serdes.rx_bitslip_value.eq(bitslip)
|
self.comb += serdes.rx.bitslip_value.eq(bitslip)
|
||||||
fsm.act("INC_DELAY_BITSLIP",
|
fsm.act("INC_DELAY_BITSLIP",
|
||||||
NextState("WAIT_STABLE"),
|
NextState("WAIT_STABLE"),
|
||||||
If(delay == (taps - 1),
|
If(delay == (taps - 1),
|
||||||
|
@ -110,12 +109,12 @@ class _SerdesMasterInit(Module):
|
||||||
NextValue(bitslip, bitslip + 1)
|
NextValue(bitslip, bitslip + 1)
|
||||||
),
|
),
|
||||||
NextValue(delay, 0),
|
NextValue(delay, 0),
|
||||||
serdes.rx_delay_rst.eq(1)
|
serdes.rx.delay_rst.eq(1)
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(delay, delay + 1),
|
NextValue(delay, delay + 1),
|
||||||
serdes.rx_delay_inc.eq(1)
|
serdes.rx.delay_inc.eq(1)
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("CHECK_SAMPLING_WINDOW",
|
fsm.act("CHECK_SAMPLING_WINDOW",
|
||||||
If((delay_min == 0) |
|
If((delay_min == 0) |
|
||||||
|
@ -126,19 +125,19 @@ class _SerdesMasterInit(Module):
|
||||||
NextState("WAIT_STABLE")
|
NextState("WAIT_STABLE")
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(delay, 0),
|
NextValue(delay, 0),
|
||||||
serdes.rx_delay_rst.eq(1),
|
serdes.rx.delay_rst.eq(1),
|
||||||
NextState("CONFIGURE_SAMPLING_WINDOW")
|
NextState("CONFIGURE_SAMPLING_WINDOW")
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("CONFIGURE_SAMPLING_WINDOW",
|
fsm.act("CONFIGURE_SAMPLING_WINDOW",
|
||||||
If(delay == (delay_min + (delay_max - delay_min)[1:]),
|
If(delay == (delay_min + (delay_max - delay_min)[1:]),
|
||||||
NextState("READY")
|
NextState("READY")
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(delay, delay + 1),
|
NextValue(delay, delay + 1),
|
||||||
serdes.rx_delay_inc.eq(1)
|
serdes.rx.delay_inc.eq(1)
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("READY",
|
fsm.act("READY",
|
||||||
self.ready.eq(1)
|
self.ready.eq(1)
|
||||||
|
@ -150,7 +149,7 @@ class _SerdesMasterInit(Module):
|
||||||
|
|
||||||
@ResetInserter()
|
@ResetInserter()
|
||||||
class _SerdesSlaveInit(Module, AutoCSR):
|
class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
def __init__(self, serdes, taps, timeout=2**15):
|
def __init__(self, serdes, taps, timeout):
|
||||||
self.ready = Signal()
|
self.ready = Signal()
|
||||||
self.error = Signal()
|
self.error = Signal()
|
||||||
|
|
||||||
|
@ -173,14 +172,14 @@ class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
NextValue(delay_min_found, 0),
|
NextValue(delay_min_found, 0),
|
||||||
NextValue(delay_max, 0),
|
NextValue(delay_max, 0),
|
||||||
NextValue(delay_max_found, 0),
|
NextValue(delay_max_found, 0),
|
||||||
serdes.rx_delay_rst.eq(1),
|
serdes.rx.delay_rst.eq(1),
|
||||||
NextValue(bitslip, 0),
|
NextValue(bitslip, 0),
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
If(timer.done,
|
If(timer.done,
|
||||||
timer.wait.eq(0),
|
timer.wait.eq(0),
|
||||||
NextState("WAIT_STABLE"),
|
NextState("WAIT_STABLE"),
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_STABLE",
|
fsm.act("WAIT_STABLE",
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
|
@ -188,11 +187,11 @@ class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
timer.wait.eq(0),
|
timer.wait.eq(0),
|
||||||
NextState("CHECK_PATTERN")
|
NextState("CHECK_PATTERN")
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("CHECK_PATTERN",
|
fsm.act("CHECK_PATTERN",
|
||||||
If(~delay_min_found,
|
If(~delay_min_found,
|
||||||
If(serdes.rx_comma,
|
If(serdes.rx.comma,
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
If(timer.done,
|
If(timer.done,
|
||||||
timer.wait.eq(0),
|
timer.wait.eq(0),
|
||||||
|
@ -203,7 +202,7 @@ class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
NextState("INC_DELAY_BITSLIP")
|
NextState("INC_DELAY_BITSLIP")
|
||||||
),
|
),
|
||||||
).Else(
|
).Else(
|
||||||
If(~serdes.rx_comma,
|
If(~serdes.rx.comma,
|
||||||
NextValue(delay_max, delay),
|
NextValue(delay_max, delay),
|
||||||
NextValue(delay_max_found, 1),
|
NextValue(delay_max_found, 1),
|
||||||
NextState("CHECK_SAMPLING_WINDOW")
|
NextState("CHECK_SAMPLING_WINDOW")
|
||||||
|
@ -211,9 +210,9 @@ class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
NextState("INC_DELAY_BITSLIP")
|
NextState("INC_DELAY_BITSLIP")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
self.comb += serdes.rx_bitslip_value.eq(bitslip)
|
self.comb += serdes.rx.bitslip_value.eq(bitslip)
|
||||||
fsm.act("INC_DELAY_BITSLIP",
|
fsm.act("INC_DELAY_BITSLIP",
|
||||||
NextState("WAIT_STABLE"),
|
NextState("WAIT_STABLE"),
|
||||||
If(delay == (taps - 1),
|
If(delay == (taps - 1),
|
||||||
|
@ -227,12 +226,12 @@ class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
NextValue(bitslip, bitslip + 1)
|
NextValue(bitslip, bitslip + 1)
|
||||||
),
|
),
|
||||||
NextValue(delay, 0),
|
NextValue(delay, 0),
|
||||||
serdes.rx_delay_rst.eq(1)
|
serdes.rx.delay_rst.eq(1)
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(delay, delay + 1),
|
NextValue(delay, delay + 1),
|
||||||
serdes.rx_delay_inc.eq(1)
|
serdes.rx.delay_inc.eq(1)
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("CHECK_SAMPLING_WINDOW",
|
fsm.act("CHECK_SAMPLING_WINDOW",
|
||||||
If((delay_min == 0) |
|
If((delay_min == 0) |
|
||||||
|
@ -243,28 +242,28 @@ class _SerdesSlaveInit(Module, AutoCSR):
|
||||||
NextState("WAIT_STABLE")
|
NextState("WAIT_STABLE")
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(delay, 0),
|
NextValue(delay, 0),
|
||||||
serdes.rx_delay_rst.eq(1),
|
serdes.rx.delay_rst.eq(1),
|
||||||
NextState("CONFIGURE_SAMPLING_WINDOW")
|
NextState("CONFIGURE_SAMPLING_WINDOW")
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("CONFIGURE_SAMPLING_WINDOW",
|
fsm.act("CONFIGURE_SAMPLING_WINDOW",
|
||||||
If(delay == (delay_min + (delay_max - delay_min)[1:]),
|
If(delay == (delay_min + (delay_max - delay_min)[1:]),
|
||||||
NextState("SEND_PATTERN")
|
NextState("SEND_PATTERN")
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(delay, delay + 1),
|
NextValue(delay, delay + 1),
|
||||||
serdes.rx_delay_inc.eq(1),
|
serdes.rx.delay_inc.eq(1),
|
||||||
),
|
),
|
||||||
serdes.tx_idle.eq(1)
|
serdes.tx.idle.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("SEND_PATTERN",
|
fsm.act("SEND_PATTERN",
|
||||||
timer.wait.eq(1),
|
timer.wait.eq(1),
|
||||||
If(timer.done,
|
If(timer.done,
|
||||||
If(~serdes.rx_comma,
|
If(~serdes.rx.comma,
|
||||||
NextState("READY")
|
NextState("READY")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
serdes.tx_comma.eq(1)
|
serdes.tx.comma.eq(1)
|
||||||
)
|
)
|
||||||
fsm.act("READY",
|
fsm.act("READY",
|
||||||
self.ready.eq(1)
|
self.ready.eq(1)
|
||||||
|
@ -288,8 +287,6 @@ class _SerdesControl(Module, AutoCSR):
|
||||||
self.delay_max = CSRStatus(9)
|
self.delay_max = CSRStatus(9)
|
||||||
self.bitslip = CSRStatus(6)
|
self.bitslip = CSRStatus(6)
|
||||||
|
|
||||||
self.scrambling_enable = CSRStorage()
|
|
||||||
|
|
||||||
self.prbs_error = Signal()
|
self.prbs_error = Signal()
|
||||||
self.prbs_start = CSR()
|
self.prbs_start = CSR()
|
||||||
self.prbs_cycles = CSRStorage(32)
|
self.prbs_cycles = CSRStorage(32)
|
||||||
|
@ -307,8 +304,8 @@ class _SerdesControl(Module, AutoCSR):
|
||||||
# Master reset the Slave by putting the link
|
# Master reset the Slave by putting the link
|
||||||
# in idle.
|
# in idle.
|
||||||
self.sync += [
|
self.sync += [
|
||||||
init.reset.eq(serdes.rx_idle),
|
init.reset.eq(serdes.rx.idle),
|
||||||
serdes.reset.eq(serdes.rx_idle)
|
serdes.reset.eq(serdes.rx.idle)
|
||||||
]
|
]
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.ready.status.eq(init.ready),
|
self.ready.status.eq(init.ready),
|
||||||
|
@ -364,31 +361,26 @@ class SERWBPHY(Module, AutoCSR):
|
||||||
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
||||||
|
|
||||||
# scrambling
|
# scrambling
|
||||||
scrambler = Scrambler()
|
self.submodules.scrambler = scrambler = Scrambler()
|
||||||
descrambler = Descrambler()
|
self.submodules.descrambler = descrambler = Descrambler()
|
||||||
self.submodules += scrambler, descrambler
|
|
||||||
self.comb += [
|
|
||||||
scrambler.enable.eq(self.control.scrambling_enable.storage),
|
|
||||||
descrambler.enable.eq(self.control.scrambling_enable.storage)
|
|
||||||
]
|
|
||||||
|
|
||||||
# tx dataflow
|
# tx dataflow
|
||||||
self.comb += \
|
self.comb += \
|
||||||
If(self.init.ready,
|
If(self.init.ready,
|
||||||
sink.connect(scrambler.sink),
|
sink.connect(scrambler.sink),
|
||||||
scrambler.source.ack.eq(self.serdes.tx_ce),
|
scrambler.source.ack.eq(self.serdes.tx.ce),
|
||||||
If(scrambler.source.stb,
|
If(scrambler.source.stb,
|
||||||
self.serdes.tx_d.eq(scrambler.source.d),
|
self.serdes.tx.d.eq(scrambler.source.d),
|
||||||
self.serdes.tx_k.eq(scrambler.source.k)
|
self.serdes.tx.k.eq(scrambler.source.k)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# rx dataflow
|
# rx dataflow
|
||||||
self.comb += [
|
self.comb += [
|
||||||
If(self.init.ready,
|
If(self.init.ready,
|
||||||
descrambler.sink.stb.eq(self.serdes.rx_ce),
|
descrambler.sink.stb.eq(self.serdes.rx.ce),
|
||||||
descrambler.sink.d.eq(self.serdes.rx_d),
|
descrambler.sink.d.eq(self.serdes.rx.d),
|
||||||
descrambler.sink.k.eq(self.serdes.rx_k),
|
descrambler.sink.k.eq(self.serdes.rx.k),
|
||||||
descrambler.source.connect(source)
|
descrambler.source.connect(source)
|
||||||
),
|
),
|
||||||
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
||||||
|
|
|
@ -1,230 +0,0 @@
|
||||||
from migen import *
|
|
||||||
from migen.genlib.misc import BitSlip
|
|
||||||
from migen.genlib.misc import WaitTimer
|
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
|
||||||
|
|
||||||
|
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
@ResetInserter()
|
|
||||||
class S7Serdes(Module):
|
|
||||||
def __init__(self, pads, mode="master"):
|
|
||||||
if mode == "slave":
|
|
||||||
self.refclk = Signal()
|
|
||||||
|
|
||||||
self.tx_ce = Signal()
|
|
||||||
self.tx_k = Signal(4)
|
|
||||||
self.tx_d = Signal(32)
|
|
||||||
|
|
||||||
self.rx_ce = Signal()
|
|
||||||
self.rx_k = Signal(4)
|
|
||||||
self.rx_d = Signal(32)
|
|
||||||
|
|
||||||
self.tx_idle = Signal()
|
|
||||||
self.tx_comma = Signal()
|
|
||||||
self.rx_idle = Signal()
|
|
||||||
self.rx_comma = Signal()
|
|
||||||
|
|
||||||
self.rx_bitslip_value = Signal(6)
|
|
||||||
self.rx_delay_rst = Signal()
|
|
||||||
self.rx_delay_inc = Signal()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
|
|
||||||
self.comb += encoder.ce.eq(self.tx_ce)
|
|
||||||
self.submodules.decoders = decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
|
|
||||||
self.comb += [decoders[i].ce.eq(self.rx_ce) for i in range(4)]
|
|
||||||
|
|
||||||
# clocking:
|
|
||||||
|
|
||||||
# In Master mode:
|
|
||||||
# - linerate/10 refclk is generated on clk_pads
|
|
||||||
# In Slave mode:
|
|
||||||
# - linerate/10 refclk is provided by clk_pads
|
|
||||||
|
|
||||||
# tx clock (linerate/10)
|
|
||||||
if mode == "master":
|
|
||||||
clk_converter = stream.Converter(40, 8)
|
|
||||||
self.submodules += clk_converter
|
|
||||||
self.comb += [
|
|
||||||
clk_converter.sink.stb.eq(1),
|
|
||||||
clk_converter.sink.data.eq(Replicate(Signal(10, reset=0b1111100000), 4)),
|
|
||||||
clk_converter.source.ack.eq(1)
|
|
||||||
]
|
|
||||||
clk_o = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE2",
|
|
||||||
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
|
||||||
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
|
||||||
p_SERDES_MODE="MASTER",
|
|
||||||
|
|
||||||
o_OQ=clk_o,
|
|
||||||
i_OCE=1,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D1=clk_converter.source.data[0], i_D2=clk_converter.source.data[1],
|
|
||||||
i_D3=clk_converter.source.data[2], i_D4=clk_converter.source.data[3],
|
|
||||||
i_D5=clk_converter.source.data[4], i_D6=clk_converter.source.data[5],
|
|
||||||
i_D7=clk_converter.source.data[6], i_D8=clk_converter.source.data[7]
|
|
||||||
),
|
|
||||||
Instance("OBUFDS",
|
|
||||||
i_I=clk_o,
|
|
||||||
o_O=pads.clk_p,
|
|
||||||
o_OB=pads.clk_n
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# tx datapath
|
|
||||||
# tx_data -> encoders -> converter -> serdes
|
|
||||||
self.submodules.tx_converter = tx_converter = stream.Converter(40, 8)
|
|
||||||
self.comb += [
|
|
||||||
tx_converter.sink.stb.eq(1),
|
|
||||||
self.tx_ce.eq(tx_converter.sink.ack),
|
|
||||||
tx_converter.source.ack.eq(1),
|
|
||||||
If(self.tx_idle,
|
|
||||||
tx_converter.sink.data.eq(0)
|
|
||||||
).Else(
|
|
||||||
tx_converter.sink.data.eq(
|
|
||||||
Cat(*[encoder.output[i] for i in range(4)]))
|
|
||||||
),
|
|
||||||
If(self.tx_comma,
|
|
||||||
encoder.k[0].eq(1),
|
|
||||||
encoder.d[0].eq(K(28,5)),
|
|
||||||
).Else(
|
|
||||||
encoder.k[0].eq(self.tx_k[0]),
|
|
||||||
encoder.k[1].eq(self.tx_k[1]),
|
|
||||||
encoder.k[2].eq(self.tx_k[2]),
|
|
||||||
encoder.k[3].eq(self.tx_k[3]),
|
|
||||||
encoder.d[0].eq(self.tx_d[0:8]),
|
|
||||||
encoder.d[1].eq(self.tx_d[8:16]),
|
|
||||||
encoder.d[2].eq(self.tx_d[16:24]),
|
|
||||||
encoder.d[3].eq(self.tx_d[24:32])
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
serdes_o = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("OSERDESE2",
|
|
||||||
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
|
||||||
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
|
||||||
p_SERDES_MODE="MASTER",
|
|
||||||
|
|
||||||
o_OQ=serdes_o,
|
|
||||||
i_OCE=1,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_D1=tx_converter.source.data[0], i_D2=tx_converter.source.data[1],
|
|
||||||
i_D3=tx_converter.source.data[2], i_D4=tx_converter.source.data[3],
|
|
||||||
i_D5=tx_converter.source.data[4], i_D6=tx_converter.source.data[5],
|
|
||||||
i_D7=tx_converter.source.data[6], i_D8=tx_converter.source.data[7]
|
|
||||||
),
|
|
||||||
Instance("OBUFDS",
|
|
||||||
i_I=serdes_o,
|
|
||||||
o_O=pads.tx_p,
|
|
||||||
o_OB=pads.tx_n
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# rx clock
|
|
||||||
use_bufr = True
|
|
||||||
if mode == "slave":
|
|
||||||
clk_i = Signal()
|
|
||||||
clk_i_bufg = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFDS",
|
|
||||||
i_I=pads.clk_p,
|
|
||||||
i_IB=pads.clk_n,
|
|
||||||
o_O=clk_i
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if use_bufr:
|
|
||||||
clk_i_bufr = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
|
|
||||||
Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
|
|
||||||
self.comb += self.refclk.eq(clk_i_bufg)
|
|
||||||
|
|
||||||
# rx datapath
|
|
||||||
# serdes -> converter -> bitslip -> decoders -> rx_data
|
|
||||||
self.submodules.rx_converter = rx_converter = stream.Converter(8, 40)
|
|
||||||
self.comb += [
|
|
||||||
self.rx_ce.eq(rx_converter.source.stb),
|
|
||||||
rx_converter.source.ack.eq(1)
|
|
||||||
]
|
|
||||||
self.submodules.rx_bitslip = rx_bitslip = CEInserter()(BitSlip(40))
|
|
||||||
self.comb += rx_bitslip.ce.eq(self.rx_ce)
|
|
||||||
|
|
||||||
serdes_i_nodelay = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFDS_DIFF_OUT",
|
|
||||||
i_I=pads.rx_p,
|
|
||||||
i_IB=pads.rx_n,
|
|
||||||
o_O=serdes_i_nodelay
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
serdes_i_delayed = Signal()
|
|
||||||
serdes_q = Signal(8)
|
|
||||||
self.specials += [
|
|
||||||
Instance("IDELAYE2",
|
|
||||||
p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA",
|
|
||||||
p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE",
|
|
||||||
p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE",
|
|
||||||
p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=0,
|
|
||||||
|
|
||||||
i_C=ClockSignal(),
|
|
||||||
i_LD=self.rx_delay_rst,
|
|
||||||
i_CE=self.rx_delay_inc,
|
|
||||||
i_LDPIPEEN=0, i_INC=1,
|
|
||||||
|
|
||||||
i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed
|
|
||||||
),
|
|
||||||
Instance("ISERDESE2",
|
|
||||||
p_DATA_WIDTH=8, p_DATA_RATE="DDR",
|
|
||||||
p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING",
|
|
||||||
p_NUM_CE=1, p_IOBDELAY="IFD",
|
|
||||||
|
|
||||||
i_DDLY=serdes_i_delayed,
|
|
||||||
i_CE1=1,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"),
|
|
||||||
i_CLKDIV=ClockSignal("sys"),
|
|
||||||
i_BITSLIP=0,
|
|
||||||
o_Q8=serdes_q[0], o_Q7=serdes_q[1],
|
|
||||||
o_Q6=serdes_q[2], o_Q5=serdes_q[3],
|
|
||||||
o_Q4=serdes_q[4], o_Q3=serdes_q[5],
|
|
||||||
o_Q2=serdes_q[6], o_Q1=serdes_q[7]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
rx_converter.sink.stb.eq(1),
|
|
||||||
rx_converter.sink.data.eq(serdes_q),
|
|
||||||
rx_bitslip.value.eq(self.rx_bitslip_value),
|
|
||||||
rx_bitslip.i.eq(rx_converter.source.data),
|
|
||||||
decoders[0].input.eq(rx_bitslip.o[0:10]),
|
|
||||||
decoders[1].input.eq(rx_bitslip.o[10:20]),
|
|
||||||
decoders[2].input.eq(rx_bitslip.o[20:30]),
|
|
||||||
decoders[3].input.eq(rx_bitslip.o[30:40]),
|
|
||||||
self.rx_k.eq(Cat(*[decoders[i].k for i in range(4)])),
|
|
||||||
self.rx_d.eq(Cat(*[decoders[i].d for i in range(4)])),
|
|
||||||
self.rx_comma.eq(
|
|
||||||
(decoders[0].k == 1) & (decoders[0].d == K(28,5)) &
|
|
||||||
(decoders[1].k == 0) & (decoders[1].d == 0) &
|
|
||||||
(decoders[2].k == 0) & (decoders[2].d == 0) &
|
|
||||||
(decoders[3].k == 0) & (decoders[3].d == 0))
|
|
||||||
]
|
|
||||||
|
|
||||||
idle_timer = WaitTimer(32)
|
|
||||||
self.submodules += idle_timer
|
|
||||||
self.comb += idle_timer.wait.eq(1)
|
|
||||||
self.sync += self.rx_idle.eq(idle_timer.done &
|
|
||||||
((rx_bitslip.o == 0) | (rx_bitslip.o == (2**40-1))))
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.io import *
|
||||||
|
from migen.genlib.misc import BitSlip, WaitTimer
|
||||||
|
|
||||||
|
from misoc.interconnect import stream
|
||||||
|
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||||
|
|
||||||
|
|
||||||
|
def K(x, y):
|
||||||
|
return (y << 5) | x
|
||||||
|
|
||||||
|
|
||||||
|
class _S7SerdesClocking(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
self.refclk = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# In Master mode, generate the linerate/10 clock. Slave will re-multiply it.
|
||||||
|
if mode == "master":
|
||||||
|
converter = stream.Converter(40, 8)
|
||||||
|
self.submodules += converter
|
||||||
|
self.comb += [
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.source.ack.eq(1),
|
||||||
|
converter.sink.data.eq(Replicate(Signal(10, reset=0b1111100000), 4)),
|
||||||
|
]
|
||||||
|
self.specials += [
|
||||||
|
Instance("OSERDESE2",
|
||||||
|
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
||||||
|
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
||||||
|
p_SERDES_MODE="MASTER",
|
||||||
|
|
||||||
|
o_OQ=self.refclk,
|
||||||
|
i_OCE=1,
|
||||||
|
i_RST=ResetSignal("sys"),
|
||||||
|
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
||||||
|
i_D1=converter.source.data[0], i_D2=converter.source.data[1],
|
||||||
|
i_D3=converter.source.data[2], i_D4=converter.source.data[3],
|
||||||
|
i_D5=converter.source.data[4], i_D6=converter.source.data[5],
|
||||||
|
i_D7=converter.source.data[6], i_D8=converter.source.data[7]
|
||||||
|
),
|
||||||
|
DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
|
||||||
|
]
|
||||||
|
|
||||||
|
# In Slave mode, multiply the clock provided by Master with a PLL/MMCM
|
||||||
|
elif mode == "slave":
|
||||||
|
self.specials += DifferentialInput(pads.clk_p, pads.clk_n, self.refclk)
|
||||||
|
|
||||||
|
|
||||||
|
class _S7SerdesTX(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
# Control
|
||||||
|
self.idle = idle = Signal()
|
||||||
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
|
# Datapath
|
||||||
|
self.ce = ce = Signal()
|
||||||
|
self.k = k = Signal(4)
|
||||||
|
self.d = d = Signal(32)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# 8b10b encoder
|
||||||
|
self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
|
||||||
|
self.comb += encoder.ce.eq(ce)
|
||||||
|
|
||||||
|
# 40 --> 8 converter
|
||||||
|
converter = stream.Converter(40, 8)
|
||||||
|
self.submodules += converter
|
||||||
|
self.comb += [
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.source.ack.eq(1),
|
||||||
|
# Enable pipeline when converter accepts the 40 bits
|
||||||
|
ce.eq(converter.sink.ack),
|
||||||
|
# If not idle, connect encoder to converter
|
||||||
|
If(~idle,
|
||||||
|
converter.sink.data.eq(Cat(*[encoder.output[i] for i in range(4)]))
|
||||||
|
),
|
||||||
|
# If comma, send K28.5
|
||||||
|
If(comma,
|
||||||
|
encoder.k[0].eq(1),
|
||||||
|
encoder.d[0].eq(K(28,5)),
|
||||||
|
# Else connect TX to encoder
|
||||||
|
).Else(
|
||||||
|
encoder.k[0].eq(k[0]),
|
||||||
|
encoder.k[1].eq(k[1]),
|
||||||
|
encoder.k[2].eq(k[2]),
|
||||||
|
encoder.k[3].eq(k[3]),
|
||||||
|
encoder.d[0].eq(d[0:8]),
|
||||||
|
encoder.d[1].eq(d[8:16]),
|
||||||
|
encoder.d[2].eq(d[16:24]),
|
||||||
|
encoder.d[3].eq(d[24:32])
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Data output (DDR with sys4x)
|
||||||
|
data = Signal()
|
||||||
|
self.specials += [
|
||||||
|
Instance("OSERDESE2",
|
||||||
|
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
||||||
|
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
||||||
|
p_SERDES_MODE="MASTER",
|
||||||
|
|
||||||
|
o_OQ=data,
|
||||||
|
i_OCE=1,
|
||||||
|
i_RST=ResetSignal("sys"),
|
||||||
|
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
||||||
|
i_D1=converter.source.data[0], i_D2=converter.source.data[1],
|
||||||
|
i_D3=converter.source.data[2], i_D4=converter.source.data[3],
|
||||||
|
i_D5=converter.source.data[4], i_D6=converter.source.data[5],
|
||||||
|
i_D7=converter.source.data[6], i_D8=converter.source.data[7]
|
||||||
|
),
|
||||||
|
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _S7SerdesRX(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
# Control
|
||||||
|
self.delay_rst = Signal()
|
||||||
|
self.delay_inc = Signal()
|
||||||
|
self.bitslip_value = bitslip_value = Signal(6)
|
||||||
|
|
||||||
|
# Status
|
||||||
|
self.idle = idle = Signal()
|
||||||
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
|
# Datapath
|
||||||
|
self.ce = ce = Signal()
|
||||||
|
self.k = k = Signal(4)
|
||||||
|
self.d = d = Signal(32)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# Data input (DDR with sys4x)
|
||||||
|
data_nodelay = Signal()
|
||||||
|
data_delayed = Signal()
|
||||||
|
data_deserialized = Signal(8)
|
||||||
|
self.specials += [
|
||||||
|
DifferentialInput(pads.rx_p, pads.rx_n, data_nodelay),
|
||||||
|
Instance("IDELAYE2",
|
||||||
|
p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA",
|
||||||
|
p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE",
|
||||||
|
p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE",
|
||||||
|
p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=0,
|
||||||
|
|
||||||
|
i_C=ClockSignal(),
|
||||||
|
i_LD=self.delay_rst,
|
||||||
|
i_CE=self.delay_inc,
|
||||||
|
i_LDPIPEEN=0, i_INC=1,
|
||||||
|
|
||||||
|
i_IDATAIN=data_nodelay, o_DATAOUT=data_delayed
|
||||||
|
),
|
||||||
|
Instance("ISERDESE2",
|
||||||
|
p_DATA_WIDTH=8, p_DATA_RATE="DDR",
|
||||||
|
p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING",
|
||||||
|
p_NUM_CE=1, p_IOBDELAY="IFD",
|
||||||
|
|
||||||
|
i_DDLY=data_delayed,
|
||||||
|
i_CE1=1,
|
||||||
|
i_RST=ResetSignal("sys"),
|
||||||
|
i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"),
|
||||||
|
i_CLKDIV=ClockSignal("sys"),
|
||||||
|
i_BITSLIP=0,
|
||||||
|
o_Q8=data_deserialized[0], o_Q7=data_deserialized[1],
|
||||||
|
o_Q6=data_deserialized[2], o_Q5=data_deserialized[3],
|
||||||
|
o_Q4=data_deserialized[4], o_Q3=data_deserialized[5],
|
||||||
|
o_Q2=data_deserialized[6], o_Q1=data_deserialized[7]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 8 --> 40 converter and bitslip
|
||||||
|
converter = stream.Converter(8, 40)
|
||||||
|
self.submodules += converter
|
||||||
|
bitslip = CEInserter()(BitSlip(40))
|
||||||
|
self.submodules += bitslip
|
||||||
|
self.comb += [
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.source.ack.eq(1),
|
||||||
|
# Enable pipeline when converter outputs the 40 bits
|
||||||
|
ce.eq(converter.source.stb),
|
||||||
|
# Connect input data to converter
|
||||||
|
converter.sink.data.eq(data_deserialized),
|
||||||
|
# Connect converter to bitslip
|
||||||
|
bitslip.ce.eq(ce),
|
||||||
|
bitslip.value.eq(bitslip_value),
|
||||||
|
bitslip.i.eq(converter.source.data)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 8b10b decoder
|
||||||
|
self.submodules.decoders = decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
|
||||||
|
self.comb += [decoders[i].ce.eq(ce) for i in range(4)]
|
||||||
|
self.comb += [
|
||||||
|
# Connect bitslip to decoder
|
||||||
|
decoders[0].input.eq(bitslip.o[0:10]),
|
||||||
|
decoders[1].input.eq(bitslip.o[10:20]),
|
||||||
|
decoders[2].input.eq(bitslip.o[20:30]),
|
||||||
|
decoders[3].input.eq(bitslip.o[30:40]),
|
||||||
|
# Connect decoder to output
|
||||||
|
self.k.eq(Cat(*[decoders[i].k for i in range(4)])),
|
||||||
|
self.d.eq(Cat(*[decoders[i].d for i in range(4)])),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Status
|
||||||
|
idle_timer = WaitTimer(256)
|
||||||
|
self.submodules += idle_timer
|
||||||
|
self.comb += [
|
||||||
|
idle_timer.wait.eq(1),
|
||||||
|
self.idle.eq(idle_timer.done &
|
||||||
|
((bitslip.o == 0) | (bitslip.o == (2**40-1)))),
|
||||||
|
self.comma.eq(
|
||||||
|
(decoders[0].k == 1) & (decoders[0].d == K(28,5)) &
|
||||||
|
(decoders[1].k == 0) & (decoders[1].d == 0) &
|
||||||
|
(decoders[2].k == 0) & (decoders[2].d == 0) &
|
||||||
|
(decoders[3].k == 0) & (decoders[3].d == 0))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ResetInserter()
|
||||||
|
class S7Serdes(Module):
|
||||||
|
def __init__(self, pads, mode="master"):
|
||||||
|
self.submodules.clocking = _S7SerdesClocking(pads, mode)
|
||||||
|
self.submodules.tx = _S7SerdesTX(pads, mode)
|
||||||
|
self.submodules.rx = _S7SerdesRX(pads, mode)
|
Loading…
Reference in New Issue