mirror of https://github.com/m-labs/artiq.git
serwb: move common datapath code to datapath.py, simplify flow control
This commit is contained in:
parent
89797d08ed
commit
7296a76f18
|
@ -0,0 +1,198 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
from artiq.gateware.serwb.scrambler import Scrambler, Descrambler
|
||||||
|
|
||||||
|
|
||||||
|
def K(x, y):
|
||||||
|
return (y << 5) | x
|
||||||
|
|
||||||
|
|
||||||
|
class _8b10bEncoder(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.sink = sink = stream.Endpoint([("d", 32), ("k", 4)])
|
||||||
|
self.source = source = stream.Endpoint([("data", 40)])
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
encoder = CEInserter()(Encoder(4, True))
|
||||||
|
self.submodules += encoder
|
||||||
|
|
||||||
|
# control
|
||||||
|
self.comb += [
|
||||||
|
source.stb.eq(sink.stb),
|
||||||
|
sink.ack.eq(source.ack),
|
||||||
|
encoder.ce.eq(source.stb & source.ack)
|
||||||
|
]
|
||||||
|
|
||||||
|
# datapath
|
||||||
|
for i in range(4):
|
||||||
|
self.comb += [
|
||||||
|
encoder.k[i].eq(sink.k[i]),
|
||||||
|
encoder.d[i].eq(sink.d[8*i:8*(i+1)]),
|
||||||
|
source.data[10*i:10*(i+1)].eq(encoder.output[i])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _8b10bDecoder(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.sink = sink = stream.Endpoint([("data", 40)])
|
||||||
|
self.source = source = stream.Endpoint([("d", 32), ("k", 4)])
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
|
||||||
|
self.submodules += decoders
|
||||||
|
|
||||||
|
# control
|
||||||
|
self.comb += [
|
||||||
|
source.stb.eq(sink.stb),
|
||||||
|
sink.ack.eq(source.ack)
|
||||||
|
]
|
||||||
|
self.comb += [decoders[i].ce.eq(source.stb & source.ack) for i in range(4)]
|
||||||
|
|
||||||
|
# datapath
|
||||||
|
for i in range(4):
|
||||||
|
self.comb += [
|
||||||
|
decoders[i].input.eq(sink.data[10*i:10*(i+1)]),
|
||||||
|
source.k[i].eq(decoders[i].k),
|
||||||
|
source.d[8*i:8*(i+1)].eq(decoders[i].d)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _Bitslip(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.value = value = Signal(6)
|
||||||
|
self.sink = sink = stream.Endpoint([("data", 40)])
|
||||||
|
self.source = source = stream.Endpoint([("data", 40)])
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
bitslip = CEInserter()(BitSlip(40))
|
||||||
|
self.submodules += bitslip
|
||||||
|
|
||||||
|
# control
|
||||||
|
self.comb += [
|
||||||
|
source.stb.eq(sink.stb),
|
||||||
|
sink.ack.eq(source.ack),
|
||||||
|
bitslip.value.eq(value),
|
||||||
|
bitslip.ce.eq(source.stb & source.ack)
|
||||||
|
]
|
||||||
|
|
||||||
|
# datapath
|
||||||
|
self.comb += [
|
||||||
|
bitslip.i.eq(sink.data),
|
||||||
|
source.data.eq(bitslip.o)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TXDatapath(Module):
|
||||||
|
def __init__(self, phy_dw, with_scrambling=True):
|
||||||
|
self.idle = idle = Signal()
|
||||||
|
self.comma = comma = Signal()
|
||||||
|
self.sink = sink = stream.Endpoint([("data", 32)])
|
||||||
|
self.source = source = stream.Endpoint([("data", phy_dw)])
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# scrambler
|
||||||
|
if with_scrambling:
|
||||||
|
self.submodules.scrambler = scrambler = Scrambler()
|
||||||
|
|
||||||
|
# line coding
|
||||||
|
self.submodules.encoder = encoder = _8b10bEncoder()
|
||||||
|
|
||||||
|
# converter
|
||||||
|
self.submodules.converter = converter = stream.Converter(40, phy_dw)
|
||||||
|
|
||||||
|
# dataflow
|
||||||
|
if with_scrambling:
|
||||||
|
self.comb += [
|
||||||
|
sink.connect(scrambler.sink),
|
||||||
|
If(comma,
|
||||||
|
encoder.sink.stb.eq(1),
|
||||||
|
encoder.sink.k.eq(1),
|
||||||
|
encoder.sink.d.eq(K(28,5))
|
||||||
|
).Else(
|
||||||
|
scrambler.source.connect(encoder.sink)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
self.comb += [
|
||||||
|
If(comma,
|
||||||
|
encoder.sink.stb.eq(1),
|
||||||
|
encoder.sink.k.eq(1),
|
||||||
|
encoder.sink.d.eq(K(28,5))
|
||||||
|
).Else(
|
||||||
|
sink.connect(encoder.sink, omit={"data"}),
|
||||||
|
encoder.sink.d.eq(sink.data)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.comb += [
|
||||||
|
If(idle,
|
||||||
|
converter.sink.stb.eq(1),
|
||||||
|
converter.sink.data.eq(0)
|
||||||
|
).Else(
|
||||||
|
encoder.source.connect(converter.sink),
|
||||||
|
),
|
||||||
|
converter.source.connect(source)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RXDatapath(Module):
|
||||||
|
def __init__(self, phy_dw, with_scrambling=True):
|
||||||
|
self.bitslip_value = bitslip_value = Signal(6)
|
||||||
|
self.sink = sink = stream.Endpoint([("data", phy_dw)])
|
||||||
|
self.source = source = stream.Endpoint([("data", 32)])
|
||||||
|
self.idle = idle = Signal()
|
||||||
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# converter
|
||||||
|
self.submodules.converter = converter = stream.Converter(phy_dw, 40)
|
||||||
|
|
||||||
|
# bitslip
|
||||||
|
self.submodules.bitslip = bitslip = _Bitslip()
|
||||||
|
self.comb += bitslip.value.eq(bitslip_value)
|
||||||
|
|
||||||
|
# line coding
|
||||||
|
self.submodules.decoder = decoder = _8b10bDecoder()
|
||||||
|
|
||||||
|
# descrambler
|
||||||
|
if with_scrambling:
|
||||||
|
self.submodules.descrambler = descrambler = Descrambler()
|
||||||
|
|
||||||
|
# dataflow
|
||||||
|
self.comb += [
|
||||||
|
sink.connect(converter.sink),
|
||||||
|
converter.source.connect(bitslip.sink),
|
||||||
|
bitslip.source.connect(decoder.sink)
|
||||||
|
]
|
||||||
|
if with_scrambling:
|
||||||
|
self.comb += [
|
||||||
|
decoder.source.connect(descrambler.sink),
|
||||||
|
descrambler.source.connect(source)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
self.comb += [
|
||||||
|
decoder.source.connect(source, omit={"d", "k"}),
|
||||||
|
source.data.eq(decoder.source.d)
|
||||||
|
]
|
||||||
|
|
||||||
|
# idle decoding
|
||||||
|
idle_timer = WaitTimer(256)
|
||||||
|
self.submodules += idle_timer
|
||||||
|
self.comb += [
|
||||||
|
idle_timer.wait.eq(1),
|
||||||
|
idle.eq(idle_timer.done & ((converter.source.data == 0) | (converter.source.data == (2**40-1))))
|
||||||
|
]
|
||||||
|
# comma decoding
|
||||||
|
self.sync += \
|
||||||
|
If(decoder.source.stb,
|
||||||
|
comma.eq((decoder.source.k == 1) & (decoder.source.d == K(28, 5)))
|
||||||
|
)
|
|
@ -4,13 +4,8 @@ from migen.genlib.misc import BitSlip, WaitTimer
|
||||||
|
|
||||||
from misoc.interconnect import stream
|
from misoc.interconnect import stream
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
|
||||||
|
|
||||||
from artiq.gateware.serwb.scrambler import Scrambler, Descrambler
|
from artiq.gateware.serwb.datapath import TXDatapath, RXDatapath
|
||||||
|
|
||||||
|
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
class _SerdesClocking(Module):
|
class _SerdesClocking(Module):
|
||||||
|
@ -30,59 +25,33 @@ class _SerdesClocking(Module):
|
||||||
|
|
||||||
|
|
||||||
class _SerdesTX(Module):
|
class _SerdesTX(Module):
|
||||||
def __init__(self, pads, mode="master"):
|
def __init__(self, pads):
|
||||||
# Control
|
# Control
|
||||||
self.idle = idle = Signal()
|
self.idle = idle = Signal()
|
||||||
self.comma = comma = Signal()
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
# Datapath
|
# Datapath
|
||||||
self.ce = ce = Signal()
|
self.sink = sink = stream.Endpoint([("data", 32)])
|
||||||
self.k = k = Signal(4)
|
|
||||||
self.d = d = Signal(32)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# 8b10b encoder
|
# Datapath
|
||||||
self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
|
self.submodules.datapath = datapath = TXDatapath(1)
|
||||||
self.comb += encoder.ce.eq(ce)
|
|
||||||
|
|
||||||
# 40 --> 1 converter
|
|
||||||
converter = stream.Converter(40, 1)
|
|
||||||
self.submodules += converter
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
converter.sink.stb.eq(1),
|
sink.connect(datapath.sink),
|
||||||
converter.source.ack.eq(1),
|
datapath.source.ack.eq(1),
|
||||||
# Enable pipeline when converter accepts the 40 bits
|
datapath.idle.eq(idle),
|
||||||
ce.eq(converter.sink.ack),
|
datapath.comma.eq(comma)
|
||||||
# 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 (on rising edge of sys_clk)
|
# Output data (on rising edge of sys_clk)
|
||||||
data = Signal()
|
data = Signal()
|
||||||
self.sync += data.eq(converter.source.data)
|
self.sync += data.eq(datapath.source.data)
|
||||||
self.specials += DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
self.specials += DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
||||||
|
|
||||||
|
|
||||||
class _SerdesRX(Module):
|
class _SerdesRX(Module):
|
||||||
def __init__(self, pads, mode="master"):
|
def __init__(self, pads):
|
||||||
# Control
|
# Control
|
||||||
self.bitslip_value = bitslip_value = Signal(6)
|
self.bitslip_value = bitslip_value = Signal(6)
|
||||||
|
|
||||||
|
@ -91,9 +60,7 @@ class _SerdesRX(Module):
|
||||||
self.comma = comma = Signal()
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
# Datapath
|
# Datapath
|
||||||
self.ce = ce = Signal()
|
self.source = source = stream.Endpoint([("data", 32)])
|
||||||
self.k = k = Signal(4)
|
|
||||||
self.d = d = Signal(32)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
@ -103,50 +70,15 @@ class _SerdesRX(Module):
|
||||||
self.specials += DifferentialInput(pads.rx_p, pads.rx_n, data)
|
self.specials += DifferentialInput(pads.rx_p, pads.rx_n, data)
|
||||||
self.sync += data_d.eq(data)
|
self.sync += data_d.eq(data)
|
||||||
|
|
||||||
# 1 --> 40 converter and bitslip
|
# Datapath
|
||||||
converter = stream.Converter(1, 40)
|
self.submodules.datapath = datapath = RXDatapath(1)
|
||||||
self.submodules += converter
|
|
||||||
bitslip = CEInserter()(BitSlip(40))
|
|
||||||
self.submodules += bitslip
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
converter.sink.stb.eq(1),
|
datapath.sink.stb.eq(1),
|
||||||
converter.source.ack.eq(1),
|
datapath.sink.data.eq(data_d),
|
||||||
# Enable pipeline when converter outputs the 40 bits
|
datapath.bitslip_value.eq(bitslip_value),
|
||||||
ce.eq(converter.source.stb),
|
datapath.source.connect(source),
|
||||||
# Connect input data to converter
|
idle.eq(datapath.idle),
|
||||||
converter.sink.data.eq(data),
|
comma.eq(datapath.comma)
|
||||||
# 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))
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,8 +86,8 @@ class _SerdesRX(Module):
|
||||||
class _Serdes(Module):
|
class _Serdes(Module):
|
||||||
def __init__(self, pads, mode="master"):
|
def __init__(self, pads, mode="master"):
|
||||||
self.submodules.clocking = _SerdesClocking(pads, mode)
|
self.submodules.clocking = _SerdesClocking(pads, mode)
|
||||||
self.submodules.tx = _SerdesTX(pads, mode)
|
self.submodules.tx = _SerdesTX(pads)
|
||||||
self.submodules.rx = _SerdesRX(pads, mode)
|
self.submodules.rx = _SerdesRX(pads)
|
||||||
|
|
||||||
|
|
||||||
# SERWB Master <--> Slave physical synchronization process:
|
# SERWB Master <--> Slave physical synchronization process:
|
||||||
|
@ -378,34 +310,21 @@ class SERWBPHY(Module, AutoCSR):
|
||||||
self.submodules.init = _SerdesSlaveInit(self.serdes, init_timeout)
|
self.submodules.init = _SerdesSlaveInit(self.serdes, init_timeout)
|
||||||
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
||||||
|
|
||||||
# scrambling
|
# tx/rx dataflow
|
||||||
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),
|
|
||||||
If(scrambler.source.stb,
|
|
||||||
self.serdes.tx.d.eq(scrambler.source.d),
|
|
||||||
self.serdes.tx.k.eq(scrambler.source.k)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# rx dataflow
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
If(self.init.ready,
|
If(self.init.ready,
|
||||||
descrambler.sink.stb.eq(self.serdes.rx.ce),
|
sink.connect(self.serdes.tx.sink),
|
||||||
descrambler.sink.d.eq(self.serdes.rx.d),
|
self.serdes.rx.source.connect(source)
|
||||||
descrambler.sink.k.eq(self.serdes.rx.k),
|
).Else(
|
||||||
descrambler.source.connect(source)
|
self.serdes.rx.source.ack.eq(1)
|
||||||
),
|
),
|
||||||
|
self.serdes.tx.sink.stb.eq(1) # always transmitting
|
||||||
|
]
|
||||||
|
|
||||||
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
||||||
# sending 0 to the scrambler and checking that descrambler
|
# sending 0 to the scrambler and checking that descrambler
|
||||||
# output is always 0.
|
# output is always 0.
|
||||||
self.control.prbs_error.eq(
|
self.comb += self.control.prbs_error.eq(
|
||||||
descrambler.source.stb &
|
source.stb &
|
||||||
descrambler.source.ack &
|
source.ack &
|
||||||
(descrambler.source.data != 0))
|
(source.data != 0))
|
||||||
]
|
|
||||||
|
|
|
@ -5,9 +5,7 @@ from migen.genlib.misc import BitSlip, WaitTimer
|
||||||
from misoc.interconnect import stream
|
from misoc.interconnect import stream
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||||
|
|
||||||
|
from artiq.gateware.serwb.datapath import TXDatapath, RXDatapath
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
class _KUSerdesClocking(Module):
|
class _KUSerdesClocking(Module):
|
||||||
|
@ -45,52 +43,27 @@ class _KUSerdesClocking(Module):
|
||||||
|
|
||||||
|
|
||||||
class _KUSerdesTX(Module):
|
class _KUSerdesTX(Module):
|
||||||
def __init__(self, pads, mode="master"):
|
def __init__(self, pads):
|
||||||
# Control
|
# Control
|
||||||
self.idle = idle = Signal()
|
self.idle = idle = Signal()
|
||||||
self.comma = comma = Signal()
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
# Datapath
|
# Datapath
|
||||||
self.ce = ce = Signal()
|
self.sink = sink = stream.Endpoint([("data", 32)])
|
||||||
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
|
# Datapath
|
||||||
converter = stream.Converter(40, 8)
|
self.submodules.datapath = datapath = TXDatapath(8)
|
||||||
self.submodules += converter
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
converter.sink.stb.eq(1),
|
sink.connect(datapath.sink),
|
||||||
converter.source.ack.eq(1),
|
datapath.source.ack.eq(1),
|
||||||
# Enable pipeline when converter accepts the 40 bits
|
datapath.idle.eq(idle),
|
||||||
ce.eq(converter.sink.ack),
|
datapath.comma.eq(comma)
|
||||||
# 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)
|
# Output Data(DDR with sys4x)
|
||||||
data = Signal()
|
data = Signal()
|
||||||
self.specials += [
|
self.specials += [
|
||||||
Instance("OSERDESE3",
|
Instance("OSERDESE3",
|
||||||
|
@ -100,14 +73,14 @@ class _KUSerdesTX(Module):
|
||||||
o_OQ=data,
|
o_OQ=data,
|
||||||
i_RST=ResetSignal("sys"),
|
i_RST=ResetSignal("sys"),
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
||||||
i_D=converter.source.data
|
i_D=datapath.source.data
|
||||||
),
|
),
|
||||||
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class _KUSerdesRX(Module):
|
class _KUSerdesRX(Module):
|
||||||
def __init__(self, pads, mode="master"):
|
def __init__(self, pads):
|
||||||
# Control
|
# Control
|
||||||
self.delay_rst = Signal()
|
self.delay_rst = Signal()
|
||||||
self.delay_inc = Signal()
|
self.delay_inc = Signal()
|
||||||
|
@ -118,9 +91,7 @@ class _KUSerdesRX(Module):
|
||||||
self.comma = comma = Signal()
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
# Datapath
|
# Datapath
|
||||||
self.ce = ce = Signal()
|
self.source = source = stream.Endpoint([("data", 32)])
|
||||||
self.k = k = Signal(4)
|
|
||||||
self.d = d = Signal(32)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
@ -158,50 +129,15 @@ class _KUSerdesRX(Module):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# 8 --> 40 converter and bitslip
|
# Datapath
|
||||||
converter = stream.Converter(8, 40)
|
self.submodules.datapath = datapath = RXDatapath(8)
|
||||||
self.submodules += converter
|
|
||||||
bitslip = CEInserter()(BitSlip(40))
|
|
||||||
self.submodules += bitslip
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
converter.sink.stb.eq(1),
|
datapath.sink.stb.eq(1),
|
||||||
converter.source.ack.eq(1),
|
datapath.sink.data.eq(data_deserialized),
|
||||||
# Enable pipeline when converter outputs the 40 bits
|
datapath.bitslip_value.eq(bitslip_value),
|
||||||
ce.eq(converter.source.stb),
|
datapath.source.connect(source),
|
||||||
# Connect input data to converter
|
idle.eq(datapath.idle),
|
||||||
converter.sink.data.eq(data_deserialized),
|
comma.eq(datapath.comma)
|
||||||
# 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))
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,5 +145,5 @@ class _KUSerdesRX(Module):
|
||||||
class KUSerdes(Module):
|
class KUSerdes(Module):
|
||||||
def __init__(self, pads, mode="master"):
|
def __init__(self, pads, mode="master"):
|
||||||
self.submodules.clocking = _KUSerdesClocking(pads, mode)
|
self.submodules.clocking = _KUSerdesClocking(pads, mode)
|
||||||
self.submodules.tx = _KUSerdesTX(pads, mode)
|
self.submodules.tx = _KUSerdesTX(pads)
|
||||||
self.submodules.rx = _KUSerdesRX(pads, mode)
|
self.submodules.rx = _KUSerdesRX(pads)
|
||||||
|
|
|
@ -4,14 +4,13 @@ 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.kuserdes import KUSerdes
|
from artiq.gateware.serwb.kuserdes import KUSerdes
|
||||||
from artiq.gateware.serwb.s7serdes import S7Serdes
|
from artiq.gateware.serwb.s7serdes import S7Serdes
|
||||||
|
|
||||||
|
|
||||||
# Master <--> Slave synchronization:
|
# SERWB Master <--> Slave physical synchronization process:
|
||||||
# 1) Master sends idle pattern (zeroes) to reset Slave.
|
# 1) Master sends idle patterns (zeroes) to Slave to reset it.
|
||||||
# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle pattern.
|
# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle patterns.
|
||||||
# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas.
|
# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas.
|
||||||
# 4) Master stops sending K28.5 commas.
|
# 4) Master stops sending K28.5 commas.
|
||||||
# 5) Slave stops sending K28.5 commas.
|
# 5) Slave stops sending K28.5 commas.
|
||||||
|
@ -360,34 +359,21 @@ class SERWBPHY(Module, AutoCSR):
|
||||||
self.submodules.init = _SerdesSlaveInit(self.serdes, taps, init_timeout)
|
self.submodules.init = _SerdesSlaveInit(self.serdes, taps, init_timeout)
|
||||||
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
self.submodules.control = _SerdesControl(self.serdes, self.init, mode)
|
||||||
|
|
||||||
# scrambling
|
# tx/rx dataflow
|
||||||
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),
|
|
||||||
If(scrambler.source.stb,
|
|
||||||
self.serdes.tx.d.eq(scrambler.source.d),
|
|
||||||
self.serdes.tx.k.eq(scrambler.source.k)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# rx dataflow
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
If(self.init.ready,
|
If(self.init.ready,
|
||||||
descrambler.sink.stb.eq(self.serdes.rx.ce),
|
sink.connect(self.serdes.tx.sink),
|
||||||
descrambler.sink.d.eq(self.serdes.rx.d),
|
self.serdes.rx.source.connect(source)
|
||||||
descrambler.sink.k.eq(self.serdes.rx.k),
|
).Else(
|
||||||
descrambler.source.connect(source)
|
self.serdes.rx.source.ack.eq(1)
|
||||||
),
|
),
|
||||||
|
self.serdes.tx.sink.stb.eq(1) # always transmitting
|
||||||
|
]
|
||||||
|
|
||||||
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
# For PRBS test we are using the scrambler/descrambler as PRBS,
|
||||||
# sending 0 to the scrambler and checking that descrambler
|
# sending 0 to the scrambler and checking that descrambler
|
||||||
# output is always 0.
|
# output is always 0.
|
||||||
self.control.prbs_error.eq(
|
self.comb += self.control.prbs_error.eq(
|
||||||
descrambler.source.stb &
|
source.stb &
|
||||||
descrambler.source.ack &
|
source.ack &
|
||||||
(descrambler.source.data != 0))
|
(source.data != 0))
|
||||||
]
|
|
||||||
|
|
|
@ -5,9 +5,7 @@ from migen.genlib.misc import BitSlip, WaitTimer
|
||||||
from misoc.interconnect import stream
|
from misoc.interconnect import stream
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||||
|
|
||||||
|
from artiq.gateware.serwb.datapath import TXDatapath, RXDatapath
|
||||||
def K(x, y):
|
|
||||||
return (y << 5) | x
|
|
||||||
|
|
||||||
|
|
||||||
class _S7SerdesClocking(Module):
|
class _S7SerdesClocking(Module):
|
||||||
|
@ -55,46 +53,20 @@ class _S7SerdesTX(Module):
|
||||||
self.comma = comma = Signal()
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
# Datapath
|
# Datapath
|
||||||
self.ce = ce = Signal()
|
self.sink = sink = stream.Endpoint([("data", 32)])
|
||||||
self.k = k = Signal(4)
|
|
||||||
self.d = d = Signal(32)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# 8b10b encoder
|
# Datapath
|
||||||
self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
|
self.submodules.datapath = datapath = TXDatapath(8)
|
||||||
self.comb += encoder.ce.eq(ce)
|
|
||||||
|
|
||||||
# 40 --> 8 converter
|
|
||||||
converter = stream.Converter(40, 8)
|
|
||||||
self.submodules += converter
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
converter.sink.stb.eq(1),
|
sink.connect(datapath.sink),
|
||||||
converter.source.ack.eq(1),
|
datapath.source.ack.eq(1),
|
||||||
# Enable pipeline when converter accepts the 40 bits
|
datapath.idle.eq(idle),
|
||||||
ce.eq(converter.sink.ack),
|
datapath.comma.eq(comma)
|
||||||
# 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)
|
# Output Data(DDR with sys4x)
|
||||||
data = Signal()
|
data = Signal()
|
||||||
self.specials += [
|
self.specials += [
|
||||||
Instance("OSERDESE2",
|
Instance("OSERDESE2",
|
||||||
|
@ -106,10 +78,10 @@ class _S7SerdesTX(Module):
|
||||||
i_OCE=1,
|
i_OCE=1,
|
||||||
i_RST=ResetSignal("sys"),
|
i_RST=ResetSignal("sys"),
|
||||||
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
|
||||||
i_D1=converter.source.data[0], i_D2=converter.source.data[1],
|
i_D1=datapath.source.data[0], i_D2=datapath.source.data[1],
|
||||||
i_D3=converter.source.data[2], i_D4=converter.source.data[3],
|
i_D3=datapath.source.data[2], i_D4=datapath.source.data[3],
|
||||||
i_D5=converter.source.data[4], i_D6=converter.source.data[5],
|
i_D5=datapath.source.data[4], i_D6=datapath.source.data[5],
|
||||||
i_D7=converter.source.data[6], i_D8=converter.source.data[7]
|
i_D7=datapath.source.data[6], i_D8=datapath.source.data[7]
|
||||||
),
|
),
|
||||||
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
DifferentialOutput(data, pads.tx_p, pads.tx_n)
|
||||||
]
|
]
|
||||||
|
@ -127,9 +99,7 @@ class _S7SerdesRX(Module):
|
||||||
self.comma = comma = Signal()
|
self.comma = comma = Signal()
|
||||||
|
|
||||||
# Datapath
|
# Datapath
|
||||||
self.ce = ce = Signal()
|
self.source = source = stream.Endpoint([("data", 32)])
|
||||||
self.k = k = Signal(4)
|
|
||||||
self.d = d = Signal(32)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
@ -170,50 +140,15 @@ class _S7SerdesRX(Module):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# 8 --> 40 converter and bitslip
|
# Datapath
|
||||||
converter = stream.Converter(8, 40)
|
self.submodules.datapath = datapath = RXDatapath(8)
|
||||||
self.submodules += converter
|
|
||||||
bitslip = CEInserter()(BitSlip(40))
|
|
||||||
self.submodules += bitslip
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
converter.sink.stb.eq(1),
|
datapath.sink.stb.eq(1),
|
||||||
converter.source.ack.eq(1),
|
datapath.sink.data.eq(data_deserialized),
|
||||||
# Enable pipeline when converter outputs the 40 bits
|
datapath.bitslip_value.eq(bitslip_value),
|
||||||
ce.eq(converter.source.stb),
|
datapath.source.connect(source),
|
||||||
# Connect input data to converter
|
idle.eq(datapath.idle),
|
||||||
converter.sink.data.eq(data_deserialized),
|
comma.eq(datapath.comma)
|
||||||
# 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))
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue