From 907af25a697e9867b0253488ea2e7a5e7f4e89f0 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 3 Jan 2018 17:32:18 +0100 Subject: [PATCH] gateware/serwb: add scrambling, reduce cdc fifo depth --- artiq/gateware/serwb/core.py | 54 +++++++++++------ artiq/gateware/serwb/kusphy.py | 21 ++++--- artiq/gateware/serwb/phy.py | 1 + artiq/gateware/serwb/s7phy.py | 21 ++++--- artiq/gateware/serwb/scrambler.py | 99 +++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 33 deletions(-) create mode 100644 artiq/gateware/serwb/scrambler.py diff --git a/artiq/gateware/serwb/core.py b/artiq/gateware/serwb/core.py index 61c76a884..4e190e03b 100644 --- a/artiq/gateware/serwb/core.py +++ b/artiq/gateware/serwb/core.py @@ -2,38 +2,54 @@ from migen import * from misoc.interconnect import stream -from artiq.gateware.serwb.packet import Depacketizer, Packetizer +from artiq.gateware.serwb.scrambler import Scrambler, Descrambler +from artiq.gateware.serwb.packet import Packetizer, Depacketizer from artiq.gateware.serwb.etherbone import Etherbone class SERWBCore(Module): - def __init__(self, phy, clk_freq, mode): + def __init__(self, phy, clk_freq, mode, with_scrambling=False): + # etherbone self.submodules.etherbone = etherbone = Etherbone(mode) + + # packetizer / depacketizer depacketizer = Depacketizer(clk_freq) packetizer = Packetizer() self.submodules += depacketizer, packetizer - tx_cdc = stream.AsyncFIFO([("data", 32)], 32) - tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serwb_serdes"})(tx_cdc) - self.submodules += tx_cdc - rx_cdc = stream.AsyncFIFO([("data", 32)], 32) - rx_cdc = ClockDomainsRenamer({"write": "serwb_serdes", "read": "sys"})(rx_cdc) - self.submodules += rx_cdc - self.comb += [ - # core <--> etherbone - depacketizer.source.connect(etherbone.sink), - etherbone.source.connect(packetizer.sink), - # core --> serdes + # clock domain crossing + tx_cdc = stream.AsyncFIFO([("data", 32)], 16) + tx_cdc = ClockDomainsRenamer({"write": "sys", "read": phy.cd})(tx_cdc) + rx_cdc = stream.AsyncFIFO([("data", 32)], 16) + rx_cdc = ClockDomainsRenamer({"write": phy.cd, "read": "sys"})(rx_cdc) + self.submodules += tx_cdc, rx_cdc + + # scrambling + scrambler = ClockDomainsRenamer(phy.cd)(Scrambler(enable=with_scrambling)) + descrambler = ClockDomainsRenamer(phy.cd)(Descrambler(enable=with_scrambling)) + self.submodules += scrambler, descrambler + + # modules connection + self.comb += [ + # core --> phy packetizer.source.connect(tx_cdc.sink), + tx_cdc.source.connect(scrambler.sink), If(phy.init.ready, - If(tx_cdc.source.stb, - phy.serdes.tx_data.eq(tx_cdc.source.data) + If(scrambler.source.stb, + phy.serdes.tx_k.eq(scrambler.source.k), + phy.serdes.tx_d.eq(scrambler.source.d) ), - tx_cdc.source.ack.eq(1) + scrambler.source.ack.eq(1) ), - # serdes --> core - rx_cdc.sink.stb.eq(phy.init.ready), - rx_cdc.sink.data.eq(phy.serdes.rx_data), + # phy --> core + descrambler.sink.stb.eq(phy.init.ready), + descrambler.sink.k.eq(phy.serdes.rx_k), + descrambler.sink.d.eq(phy.serdes.rx_d), + descrambler.source.connect(rx_cdc.sink), rx_cdc.source.connect(depacketizer.sink), + + # etherbone <--> core + depacketizer.source.connect(etherbone.sink), + etherbone.source.connect(packetizer.sink) ] diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py index f4fd6cd6c..51a08a6d8 100644 --- a/artiq/gateware/serwb/kusphy.py +++ b/artiq/gateware/serwb/kusphy.py @@ -8,8 +8,10 @@ from misoc.cores.code_8b10b import Encoder, Decoder class KUSSerdes(Module): def __init__(self, pll, pads, mode="master"): - self.tx_data = Signal(32) - self.rx_data = Signal(32) + self.tx_k = Signal(4) + self.tx_d = Signal(32) + self.rx_k = Signal(4) + self.rx_d = Signal(32) self.tx_idle = Signal() self.tx_comma = Signal() @@ -111,10 +113,14 @@ class KUSSerdes(Module): self.encoder.k[0].eq(1), self.encoder.d[0].eq(0xbc) ).Else( - self.encoder.d[0].eq(self.tx_data[0:8]), - self.encoder.d[1].eq(self.tx_data[8:16]), - self.encoder.d[2].eq(self.tx_data[16:24]), - self.encoder.d[3].eq(self.tx_data[24:32]) + self.encoder.k[0].eq(self.tx_k[0]), + self.encoder.k[1].eq(self.tx_k[1]), + self.encoder.k[2].eq(self.tx_k[2]), + self.encoder.k[3].eq(self.tx_k[3]), + self.encoder.d[0].eq(self.tx_d[0:8]), + self.encoder.d[1].eq(self.tx_d[8:16]), + self.encoder.d[2].eq(self.tx_d[16:24]), + self.encoder.d[3].eq(self.tx_d[24:32]) ) ] self.sync.serwb_serdes += \ @@ -217,7 +223,8 @@ class KUSSerdes(Module): self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), - self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), + self.rx_k.eq(Cat(*[self.decoders[i].k for i in range(4)])), + self.rx_d.eq(Cat(*[self.decoders[i].d for i in range(4)])), rx_idle.eq(self.rx_bitslip.o == 0), rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index d48084116..556b53709 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -376,6 +376,7 @@ class SERWBPLL(Module): class SERWBPHY(Module, AutoCSR): + cd = "serwb_serdes" def __init__(self, device, pll, pads, mode="master"): assert mode in ["master", "slave"] if device[:4] == "xcku": diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py index d64f5bbb5..f21d39a4b 100644 --- a/artiq/gateware/serwb/s7phy.py +++ b/artiq/gateware/serwb/s7phy.py @@ -8,8 +8,10 @@ from misoc.cores.code_8b10b import Encoder, Decoder class S7Serdes(Module): def __init__(self, pll, pads, mode="master"): - self.tx_data = Signal(32) - self.rx_data = Signal(32) + self.tx_k = Signal(4) + self.tx_d = Signal(32) + self.rx_k = Signal(4) + self.rx_d = Signal(32) self.tx_idle = Signal() self.tx_comma = Signal() @@ -99,10 +101,14 @@ class S7Serdes(Module): self.encoder.k[0].eq(1), self.encoder.d[0].eq(0xbc) ).Else( - self.encoder.d[0].eq(self.tx_data[0:8]), - self.encoder.d[1].eq(self.tx_data[8:16]), - self.encoder.d[2].eq(self.tx_data[16:24]), - self.encoder.d[3].eq(self.tx_data[24:32]) + self.encoder.k[0].eq(self.tx_k[0]), + self.encoder.k[1].eq(self.tx_k[1]), + self.encoder.k[2].eq(self.tx_k[2]), + self.encoder.k[3].eq(self.tx_k[3]), + self.encoder.d[0].eq(self.tx_d[0:8]), + self.encoder.d[1].eq(self.tx_d[8:16]), + self.encoder.d[2].eq(self.tx_d[16:24]), + self.encoder.d[3].eq(self.tx_d[24:32]) ) ] self.sync.serwb_serdes += \ @@ -213,7 +219,8 @@ class S7Serdes(Module): self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), - self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), + self.rx_k.eq(Cat(*[self.decoders[i].k for i in range(4)])), + self.rx_d.eq(Cat(*[self.decoders[i].d for i in range(4)])), rx_idle.eq(self.rx_bitslip.o == 0), rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & diff --git a/artiq/gateware/serwb/scrambler.py b/artiq/gateware/serwb/scrambler.py new file mode 100644 index 000000000..83f8b5a55 --- /dev/null +++ b/artiq/gateware/serwb/scrambler.py @@ -0,0 +1,99 @@ +from functools import reduce +from operator import xor + +from migen import * + +from misoc.interconnect import stream + + +def K(x, y): + return (y << 5) | x + + +@ResetInserter() +class _Scrambler(Module): + def __init__(self, n_io, n_state=23, taps=[17, 22]): + self.i = Signal(n_io) + self.o = Signal(n_io) + + # # # + + state = Signal(n_state, reset=1) + curval = [state[i] for i in range(n_state)] + for i in reversed(range(n_io)): + flip = reduce(xor, [curval[tap] for tap in taps]) + self.comb += self.o[i].eq(flip ^ self.i[i]) + curval.insert(0, flip) + curval.pop() + + self.sync += state.eq(Cat(*curval[:n_state])) + + +class Scrambler(Module): + def __init__(self, sync_interval=1024, enable=True): + self.sink = sink = stream.Endpoint([("data", 32)]) + self.source = source = stream.Endpoint([("d", 32), ("k", 4)]) + + # # # + + if enable: + # scrambler + scrambler = _Scrambler(32) + self.submodules += scrambler + self.comb += scrambler.i.eq(sink.data) + + # insert K.29.7 as sync character + # every sync_interval cycles + count = Signal(max=sync_interval) + self.sync += count.eq(count + 1) + self.comb += [ + If(count == 0, + scrambler.reset.eq(1), + source.stb.eq(1), + source.k[0].eq(1), + source.d[:8].eq(K(29, 7)) + ).Else( + sink.ack.eq(source.ack), + source.stb.eq(sink.stb), + source.d.eq(scrambler.o) + ) + ] + else: + self.comb += [ + sink.connect(source, omit={"data"}), + source.k.eq(0b0000), + source.d.eq(sink.data) + ] + + +class Descrambler(Module): + def __init__(self, enable=True): + self.sink = sink = stream.Endpoint([("d", 32), ("k", 4)]) + self.source = source = stream.Endpoint([("data", 32)]) + + # # # + + if enable: + # descrambler + descrambler = _Scrambler(32) + self.submodules += descrambler + self.comb += descrambler.i.eq(sink.d) + + # detect K29.7 and synchronize descrambler + self.comb += [ + descrambler.reset.eq(0), + If((sink.k[0] == 1) & + (sink.d[:8] == K(29,7)), + sink.ack.eq(1), + descrambler.reset.eq(1) + ).Else( + sink.ack.eq(source.ack), + source.stb.eq(sink.stb), + source.data.eq(descrambler.o) + ) + ] + else: + self.comb += [ + sink.connect(source, omit={"d", "k"}), + source.data.eq(sink.d) + ]