From 3873d0969219e717c3e5a5e658a8a4ac7e5d9d89 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 15 May 2018 23:52:41 +0200 Subject: [PATCH] serwb: rewrite high-speed phys by splitting clocking/tx/rx, scrambling is now always enabled. --- artiq/gateware/serwb/__init__.py | 2 +- artiq/gateware/serwb/kuserdes.py | 213 ++++++++++++++++++++++++++++ artiq/gateware/serwb/kusphy.py | 218 ----------------------------- artiq/gateware/serwb/phy.py | 102 +++++++------- artiq/gateware/serwb/s7phy.py | 230 ------------------------------- artiq/gateware/serwb/s7serdes.py | 225 ++++++++++++++++++++++++++++++ 6 files changed, 486 insertions(+), 504 deletions(-) create mode 100644 artiq/gateware/serwb/kuserdes.py delete mode 100644 artiq/gateware/serwb/kusphy.py delete mode 100644 artiq/gateware/serwb/s7phy.py create mode 100644 artiq/gateware/serwb/s7serdes.py diff --git a/artiq/gateware/serwb/__init__.py b/artiq/gateware/serwb/__init__.py index 7c437f6f3..46938e85f 100644 --- a/artiq/gateware/serwb/__init__.py +++ b/artiq/gateware/serwb/__init__.py @@ -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 diff --git a/artiq/gateware/serwb/kuserdes.py b/artiq/gateware/serwb/kuserdes.py new file mode 100644 index 000000000..ced959eff --- /dev/null +++ b/artiq/gateware/serwb/kuserdes.py @@ -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) diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py deleted file mode 100644 index dd7cf6760..000000000 --- a/artiq/gateware/serwb/kusphy.py +++ /dev/null @@ -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)))) diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index ecd6f527c..a7c201e65 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -1,13 +1,12 @@ from migen import * -from migen.genlib.cdc import MultiReg, PulseSynchronizer from migen.genlib.misc import WaitTimer from misoc.interconnect import stream from misoc.interconnect.csr import * from artiq.gateware.serwb.scrambler import Scrambler, Descrambler -from artiq.gateware.serwb.kusphy import KUSSerdes -from artiq.gateware.serwb.s7phy import S7Serdes +from artiq.gateware.serwb.kuserdes import KUSerdes +from artiq.gateware.serwb.s7serdes import S7Serdes # Master <--> Slave synchronization: @@ -21,7 +20,7 @@ from artiq.gateware.serwb.s7phy import S7Serdes @ResetInserter() class _SerdesMasterInit(Module): - def __init__(self, serdes, taps, timeout=2**15): + def __init__(self, serdes, taps, timeout): self.ready = Signal() self.error = Signal() @@ -43,10 +42,10 @@ class _SerdesMasterInit(Module): NextValue(delay_min_found, 0), NextValue(delay_max, 0), NextValue(delay_max_found, 0), - serdes.rx_delay_rst.eq(1), + serdes.rx.delay_rst.eq(1), NextValue(bitslip, 0), NextState("RESET_SLAVE"), - serdes.tx_idle.eq(1) + serdes.tx.idle.eq(1) ) fsm.act("RESET_SLAVE", timer.wait.eq(1), @@ -54,16 +53,16 @@ class _SerdesMasterInit(Module): timer.wait.eq(0), NextState("SEND_PATTERN") ), - serdes.tx_idle.eq(1) + serdes.tx.idle.eq(1) ) fsm.act("SEND_PATTERN", - If(~serdes.rx_idle, + If(~serdes.rx.idle, timer.wait.eq(1), If(timer.done, NextState("CHECK_PATTERN") ) ), - serdes.tx_comma.eq(1) + serdes.tx.comma.eq(1) ) fsm.act("WAIT_STABLE", timer.wait.eq(1), @@ -71,11 +70,11 @@ class _SerdesMasterInit(Module): timer.wait.eq(0), NextState("CHECK_PATTERN") ), - serdes.tx_comma.eq(1) + serdes.tx.comma.eq(1) ) fsm.act("CHECK_PATTERN", If(~delay_min_found, - If(serdes.rx_comma, + If(serdes.rx.comma, timer.wait.eq(1), If(timer.done, timer.wait.eq(0), @@ -86,7 +85,7 @@ class _SerdesMasterInit(Module): NextState("INC_DELAY_BITSLIP") ), ).Else( - If(~serdes.rx_comma, + If(~serdes.rx.comma, NextValue(delay_max, delay), NextValue(delay_max_found, 1), NextState("CHECK_SAMPLING_WINDOW") @@ -94,9 +93,9 @@ class _SerdesMasterInit(Module): 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", NextState("WAIT_STABLE"), If(delay == (taps - 1), @@ -110,12 +109,12 @@ class _SerdesMasterInit(Module): NextValue(bitslip, bitslip + 1) ), NextValue(delay, 0), - serdes.rx_delay_rst.eq(1) + serdes.rx.delay_rst.eq(1) ).Else( 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", If((delay_min == 0) | @@ -126,19 +125,19 @@ class _SerdesMasterInit(Module): NextState("WAIT_STABLE") ).Else( NextValue(delay, 0), - serdes.rx_delay_rst.eq(1), + serdes.rx.delay_rst.eq(1), NextState("CONFIGURE_SAMPLING_WINDOW") ), - serdes.tx_comma.eq(1) + serdes.tx.comma.eq(1) ) fsm.act("CONFIGURE_SAMPLING_WINDOW", If(delay == (delay_min + (delay_max - delay_min)[1:]), NextState("READY") ).Else( 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", self.ready.eq(1) @@ -150,7 +149,7 @@ class _SerdesMasterInit(Module): @ResetInserter() class _SerdesSlaveInit(Module, AutoCSR): - def __init__(self, serdes, taps, timeout=2**15): + def __init__(self, serdes, taps, timeout): self.ready = Signal() self.error = Signal() @@ -173,14 +172,14 @@ class _SerdesSlaveInit(Module, AutoCSR): NextValue(delay_min_found, 0), NextValue(delay_max, 0), NextValue(delay_max_found, 0), - serdes.rx_delay_rst.eq(1), + serdes.rx.delay_rst.eq(1), NextValue(bitslip, 0), timer.wait.eq(1), If(timer.done, timer.wait.eq(0), NextState("WAIT_STABLE"), ), - serdes.tx_idle.eq(1) + serdes.tx.idle.eq(1) ) fsm.act("WAIT_STABLE", timer.wait.eq(1), @@ -188,11 +187,11 @@ class _SerdesSlaveInit(Module, AutoCSR): timer.wait.eq(0), NextState("CHECK_PATTERN") ), - serdes.tx_idle.eq(1) + serdes.tx.idle.eq(1) ) fsm.act("CHECK_PATTERN", If(~delay_min_found, - If(serdes.rx_comma, + If(serdes.rx.comma, timer.wait.eq(1), If(timer.done, timer.wait.eq(0), @@ -203,7 +202,7 @@ class _SerdesSlaveInit(Module, AutoCSR): NextState("INC_DELAY_BITSLIP") ), ).Else( - If(~serdes.rx_comma, + If(~serdes.rx.comma, NextValue(delay_max, delay), NextValue(delay_max_found, 1), NextState("CHECK_SAMPLING_WINDOW") @@ -211,9 +210,9 @@ class _SerdesSlaveInit(Module, AutoCSR): 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", NextState("WAIT_STABLE"), If(delay == (taps - 1), @@ -227,12 +226,12 @@ class _SerdesSlaveInit(Module, AutoCSR): NextValue(bitslip, bitslip + 1) ), NextValue(delay, 0), - serdes.rx_delay_rst.eq(1) + serdes.rx.delay_rst.eq(1) ).Else( 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", If((delay_min == 0) | @@ -243,28 +242,28 @@ class _SerdesSlaveInit(Module, AutoCSR): NextState("WAIT_STABLE") ).Else( NextValue(delay, 0), - serdes.rx_delay_rst.eq(1), + serdes.rx.delay_rst.eq(1), NextState("CONFIGURE_SAMPLING_WINDOW") ), - serdes.tx_idle.eq(1) + serdes.tx.idle.eq(1) ) fsm.act("CONFIGURE_SAMPLING_WINDOW", If(delay == (delay_min + (delay_max - delay_min)[1:]), NextState("SEND_PATTERN") ).Else( 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", timer.wait.eq(1), If(timer.done, - If(~serdes.rx_comma, + If(~serdes.rx.comma, NextState("READY") ) ), - serdes.tx_comma.eq(1) + serdes.tx.comma.eq(1) ) fsm.act("READY", self.ready.eq(1) @@ -288,8 +287,6 @@ class _SerdesControl(Module, AutoCSR): self.delay_max = CSRStatus(9) self.bitslip = CSRStatus(6) - self.scrambling_enable = CSRStorage() - self.prbs_error = Signal() self.prbs_start = CSR() self.prbs_cycles = CSRStorage(32) @@ -307,8 +304,8 @@ class _SerdesControl(Module, AutoCSR): # Master reset the Slave by putting the link # in idle. self.sync += [ - init.reset.eq(serdes.rx_idle), - serdes.reset.eq(serdes.rx_idle) + init.reset.eq(serdes.rx.idle), + serdes.reset.eq(serdes.rx.idle) ] self.comb += [ self.ready.status.eq(init.ready), @@ -364,31 +361,26 @@ class SERWBPHY(Module, AutoCSR): self.submodules.control = _SerdesControl(self.serdes, self.init, mode) # scrambling - scrambler = Scrambler() - descrambler = Descrambler() - self.submodules += scrambler, descrambler - self.comb += [ - scrambler.enable.eq(self.control.scrambling_enable.storage), - descrambler.enable.eq(self.control.scrambling_enable.storage) - ] + self.submodules.scrambler = scrambler = Scrambler() + self.submodules.descrambler = descrambler = Descrambler() # tx dataflow self.comb += \ If(self.init.ready, sink.connect(scrambler.sink), - scrambler.source.ack.eq(self.serdes.tx_ce), + scrambler.source.ack.eq(self.serdes.tx.ce), If(scrambler.source.stb, - self.serdes.tx_d.eq(scrambler.source.d), - self.serdes.tx_k.eq(scrambler.source.k) + self.serdes.tx.d.eq(scrambler.source.d), + self.serdes.tx.k.eq(scrambler.source.k) ) ) # rx dataflow self.comb += [ If(self.init.ready, - descrambler.sink.stb.eq(self.serdes.rx_ce), - descrambler.sink.d.eq(self.serdes.rx_d), - descrambler.sink.k.eq(self.serdes.rx_k), + descrambler.sink.stb.eq(self.serdes.rx.ce), + descrambler.sink.d.eq(self.serdes.rx.d), + descrambler.sink.k.eq(self.serdes.rx.k), descrambler.source.connect(source) ), # For PRBS test we are using the scrambler/descrambler as PRBS, diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py deleted file mode 100644 index cc7cc4e30..000000000 --- a/artiq/gateware/serwb/s7phy.py +++ /dev/null @@ -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)))) diff --git a/artiq/gateware/serwb/s7serdes.py b/artiq/gateware/serwb/s7serdes.py new file mode 100644 index 000000000..12fabade0 --- /dev/null +++ b/artiq/gateware/serwb/s7serdes.py @@ -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)