mirror of https://github.com/m-labs/artiq.git
drtio: simpler link layer
This commit is contained in:
parent
09363e1da8
commit
bb047aabe9
|
@ -6,10 +6,9 @@ from artiq.gateware.drtio import link_layer, rt_packets, iot, rt_controller, aux
|
||||||
|
|
||||||
|
|
||||||
class DRTIOSatellite(Module):
|
class DRTIOSatellite(Module):
|
||||||
def __init__(self, transceiver, rx_synchronizer, channels, fine_ts_width=3, full_ts_width=63,
|
def __init__(self, transceiver, rx_synchronizer, channels, fine_ts_width=3, full_ts_width=63):
|
||||||
ll_rx_ready_confirm=1000):
|
|
||||||
self.submodules.link_layer = link_layer.LinkLayer(
|
self.submodules.link_layer = link_layer.LinkLayer(
|
||||||
transceiver.encoder, transceiver.decoders, ll_rx_ready_confirm)
|
transceiver.encoder, transceiver.decoders)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
transceiver.rx_reset.eq(self.link_layer.rx_reset),
|
transceiver.rx_reset.eq(self.link_layer.rx_reset),
|
||||||
self.link_layer.rx_ready.eq(transceiver.rx_ready)
|
self.link_layer.rx_ready.eq(transceiver.rx_ready)
|
||||||
|
@ -52,9 +51,9 @@ class DRTIOSatellite(Module):
|
||||||
|
|
||||||
|
|
||||||
class DRTIOMaster(Module):
|
class DRTIOMaster(Module):
|
||||||
def __init__(self, transceiver, channel_count=1024, fine_ts_width=3, ll_rx_ready_confirm=1000):
|
def __init__(self, transceiver, channel_count=1024, fine_ts_width=3):
|
||||||
self.submodules.link_layer = link_layer.LinkLayer(
|
self.submodules.link_layer = link_layer.LinkLayer(
|
||||||
transceiver.encoder, transceiver.decoders, ll_rx_ready_confirm)
|
transceiver.encoder, transceiver.decoders)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
transceiver.rx_reset.eq(self.link_layer.rx_reset),
|
transceiver.rx_reset.eq(self.link_layer.rx_reset),
|
||||||
self.link_layer.rx_ready.eq(transceiver.rx_ready)
|
self.link_layer.rx_ready.eq(transceiver.rx_ready)
|
||||||
|
|
|
@ -3,43 +3,102 @@ from operator import xor, or_
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.fsm import *
|
from migen.genlib.fsm import *
|
||||||
from migen.genlib.cdc import MultiReg, BusSynchronizer
|
from migen.genlib.cdc import MultiReg
|
||||||
from migen.genlib.misc import WaitTimer
|
from migen.genlib.misc import WaitTimer
|
||||||
|
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
|
|
||||||
|
|
||||||
class Scrambler(Module):
|
class Scrambler(Module):
|
||||||
def __init__(self, n_io, n_state=23, taps=[17, 22]):
|
def __init__(self, n_io1, n_io2, n_state=23, taps=[17, 22]):
|
||||||
self.i = Signal(n_io)
|
self.i1 = Signal(n_io1)
|
||||||
self.o = Signal(n_io)
|
self.o1 = Signal(n_io1)
|
||||||
|
self.i2 = Signal(n_io2)
|
||||||
|
self.o2 = Signal(n_io2)
|
||||||
|
self.sel = Signal()
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
state = Signal(n_state, reset=1)
|
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.sync += self.o[i].eq(flip ^ self.i[i])
|
|
||||||
curval.insert(0, flip)
|
|
||||||
curval.pop()
|
|
||||||
|
|
||||||
self.sync += state.eq(Cat(*curval[:n_state]))
|
stmts1 = []
|
||||||
|
stmts2 = []
|
||||||
|
for stmts, si, so in ((stmts1, self.i1, self.o1),
|
||||||
|
(stmts2, self.i2, self.o2)):
|
||||||
|
curval = [state[i] for i in range(n_state)]
|
||||||
|
for i in reversed(range(len(si))):
|
||||||
|
out = si[i] ^ reduce(xor, [curval[tap] for tap in taps])
|
||||||
|
stmts += [so[i].eq(out)]
|
||||||
|
curval.insert(0, out)
|
||||||
|
curval.pop()
|
||||||
|
|
||||||
|
stmts += [state.eq(Cat(*curval[:n_state]))]
|
||||||
|
|
||||||
|
self.sync += If(self.sel, stmts2).Else(stmts1)
|
||||||
|
|
||||||
|
|
||||||
|
class Descrambler(Module):
|
||||||
|
def __init__(self, n_io1, n_io2, n_state=23, taps=[17, 22]):
|
||||||
|
self.i1 = Signal(n_io1)
|
||||||
|
self.o1 = Signal(n_io1)
|
||||||
|
self.i2 = Signal(n_io2)
|
||||||
|
self.o2 = Signal(n_io2)
|
||||||
|
self.sel = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
state = Signal(n_state, reset=1)
|
||||||
|
|
||||||
|
stmts1 = []
|
||||||
|
stmts2 = []
|
||||||
|
for stmts, si, so in ((stmts1, self.i1, self.o1),
|
||||||
|
(stmts2, self.i2, self.o2)):
|
||||||
|
curval = [state[i] for i in range(n_state)]
|
||||||
|
for i in reversed(range(len(si))):
|
||||||
|
flip = reduce(xor, [curval[tap] for tap in taps])
|
||||||
|
stmts += [so[i].eq(si[i] ^ flip)]
|
||||||
|
curval.insert(0, si[i])
|
||||||
|
curval.pop()
|
||||||
|
|
||||||
|
stmts += [state.eq(Cat(*curval[:n_state]))]
|
||||||
|
|
||||||
|
self.sync += If(self.sel, stmts2).Else(stmts1)
|
||||||
|
|
||||||
|
|
||||||
def K(x, y):
|
def K(x, y):
|
||||||
return (y << 5) | x
|
return (y << 5) | x
|
||||||
|
|
||||||
|
|
||||||
|
aux_coding_comma = [
|
||||||
|
K(28, 5),
|
||||||
|
K(28, 0),
|
||||||
|
K(28, 1),
|
||||||
|
K(28, 2),
|
||||||
|
K(23, 7),
|
||||||
|
K(27, 7),
|
||||||
|
K(29, 7),
|
||||||
|
K(30, 7),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
aux_coding_nocomma = [
|
||||||
|
K(28, 0),
|
||||||
|
K(28, 2),
|
||||||
|
K(28, 3),
|
||||||
|
K(28, 4),
|
||||||
|
K(23, 7),
|
||||||
|
K(27, 7),
|
||||||
|
K(29, 7),
|
||||||
|
K(30, 7),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class LinkLayerTX(Module):
|
class LinkLayerTX(Module):
|
||||||
def __init__(self, encoder):
|
def __init__(self, encoder):
|
||||||
nwords = len(encoder.k)
|
nwords = len(encoder.k)
|
||||||
# nwords must be a power of 2
|
# nwords must be a power of 2
|
||||||
assert nwords & (nwords - 1) == 0
|
assert nwords & (nwords - 1) == 0
|
||||||
|
|
||||||
self.link_init = Signal()
|
|
||||||
self.signal_rx_ready = Signal()
|
|
||||||
|
|
||||||
self.aux_frame = Signal()
|
self.aux_frame = Signal()
|
||||||
self.aux_data = Signal(2*nwords)
|
self.aux_data = Signal(2*nwords)
|
||||||
self.aux_ack = Signal()
|
self.aux_ack = Signal()
|
||||||
|
@ -49,93 +108,64 @@ class LinkLayerTX(Module):
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# Idle and auxiliary traffic use special characters excluding K.28.7,
|
# Idle and auxiliary traffic use special characters defined in the
|
||||||
# K.29.7 and K.30.7 in order to easily separate the link initialization
|
# aux_coding_* tables.
|
||||||
# phase (K.28.7 is additionally excluded as we cannot guarantee its
|
# The first (or only) character uses aux_coding_comma which guarantees
|
||||||
# non-repetition here).
|
# that commas appear regularly in the absence of traffic.
|
||||||
|
# The subsequent characters, if any (depending on the transceiver
|
||||||
|
# serialization ratio) use aux_coding_nocomma which does not contain
|
||||||
|
# commas. This permits aligning the comma to the first character at
|
||||||
|
# the receiver.
|
||||||
|
#
|
||||||
# A set of 8 special characters is chosen using a 3-bit control word.
|
# A set of 8 special characters is chosen using a 3-bit control word.
|
||||||
# This control word is scrambled to reduce EMI. The control words have
|
# This control word is scrambled to reduce EMI. The control words have
|
||||||
# the following meanings:
|
# the following meanings:
|
||||||
# 100 idle/auxiliary framing
|
# 100 idle/auxiliary framing
|
||||||
# 0AB 2 bits of auxiliary data
|
# 0AB 2 bits of auxiliary data
|
||||||
aux_scrambler = ResetInserter()(CEInserter()(Scrambler(3*nwords)))
|
#
|
||||||
self.submodules += aux_scrambler
|
# RT traffic uses D characters and is also scrambled. The aux and RT
|
||||||
|
# scramblers are multiplicative and share the same state so that idle
|
||||||
|
# or aux traffic can synchronize the RT descrambler.
|
||||||
|
|
||||||
|
scrambler = Scrambler(3*nwords, 8*nwords)
|
||||||
|
self.submodules += scrambler
|
||||||
|
|
||||||
|
# scrambler input
|
||||||
aux_data_ctl = []
|
aux_data_ctl = []
|
||||||
for i in range(nwords):
|
for i in range(nwords):
|
||||||
aux_data_ctl.append(self.aux_data[i*2:i*2+2])
|
aux_data_ctl.append(self.aux_data[i*2:i*2+2])
|
||||||
aux_data_ctl.append(0)
|
aux_data_ctl.append(0)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
If(self.aux_frame,
|
If(self.aux_frame,
|
||||||
aux_scrambler.i.eq(Cat(*aux_data_ctl))
|
scrambler.i1.eq(Cat(*aux_data_ctl))
|
||||||
).Else(
|
).Else(
|
||||||
aux_scrambler.i.eq(Replicate(0b100, nwords))
|
scrambler.i1.eq(Replicate(0b100, nwords))
|
||||||
),
|
),
|
||||||
aux_scrambler.reset.eq(self.link_init),
|
scrambler.i2.eq(self.rt_data),
|
||||||
aux_scrambler.ce.eq(~self.rt_frame),
|
scrambler.sel.eq(self.rt_frame),
|
||||||
self.aux_ack.eq(~self.rt_frame)
|
self.aux_ack.eq(~self.rt_frame)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# compensate for scrambler latency
|
||||||
|
rt_frame_r = Signal()
|
||||||
|
self.sync += rt_frame_r.eq(self.rt_frame)
|
||||||
|
|
||||||
|
# scrambler output
|
||||||
for i in range(nwords):
|
for i in range(nwords):
|
||||||
scrambled_ctl = aux_scrambler.o[i*3:i*3+3]
|
scrambled_ctl = scrambler.o1[i*3:i*3+3]
|
||||||
|
if i:
|
||||||
|
aux_coding = aux_coding_nocomma
|
||||||
|
else:
|
||||||
|
aux_coding = aux_coding_comma
|
||||||
self.sync += [
|
self.sync += [
|
||||||
encoder.k[i].eq(1),
|
encoder.k[i].eq(1),
|
||||||
If(scrambled_ctl == 7,
|
encoder.d[i].eq(Array(aux_coding)[scrambled_ctl])
|
||||||
encoder.d[i].eq(K(23, 7))
|
|
||||||
).Else(
|
|
||||||
encoder.d[i].eq(K(28, scrambled_ctl))
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
self.sync += \
|
||||||
# Real-time traffic uses data characters and is framed by the special
|
|
||||||
# characters of auxiliary traffic. RT traffic is also scrambled.
|
|
||||||
rt_scrambler = ResetInserter()(CEInserter()(Scrambler(8*nwords)))
|
|
||||||
self.submodules += rt_scrambler
|
|
||||||
self.comb += [
|
|
||||||
rt_scrambler.i.eq(self.rt_data),
|
|
||||||
rt_scrambler.reset.eq(self.link_init),
|
|
||||||
rt_scrambler.ce.eq(self.rt_frame)
|
|
||||||
]
|
|
||||||
rt_frame_r = Signal()
|
|
||||||
self.sync += [
|
|
||||||
rt_frame_r.eq(self.rt_frame),
|
|
||||||
If(rt_frame_r,
|
If(rt_frame_r,
|
||||||
[k.eq(0) for k in encoder.k],
|
[k.eq(0) for k in encoder.k],
|
||||||
[d.eq(rt_scrambler.o[i*8:i*8+8]) for i, d in enumerate(encoder.d)]
|
[d.eq(scrambler.o2[i*8:i*8+8]) for i, d in enumerate(encoder.d)]
|
||||||
)
|
)
|
||||||
]
|
|
||||||
|
|
||||||
# During link init, send a series of 1*K.28.7 (comma) + 31*K.29.7/K.30.7
|
|
||||||
# The receiving end configures its transceiver to also place the comma
|
|
||||||
# on its LSB, achieving fixed (or known) latency and alignment of
|
|
||||||
# packet starts.
|
|
||||||
# K.29.7 and K.30.7 are chosen to avoid comma alignment issues arising
|
|
||||||
# from K.28.7.
|
|
||||||
# K.30.7 is sent instead of K.29.7 to signal the alignment of the local
|
|
||||||
# receiver, thus the remote can end its link initialization pattern.
|
|
||||||
link_init_r = Signal()
|
|
||||||
link_init_counter = Signal(max=32//nwords)
|
|
||||||
self.sync += [
|
|
||||||
link_init_r.eq(self.link_init),
|
|
||||||
If(link_init_r,
|
|
||||||
link_init_counter.eq(link_init_counter + 1),
|
|
||||||
[k.eq(1) for k in encoder.k],
|
|
||||||
If(self.signal_rx_ready,
|
|
||||||
[d.eq(K(30, 7)) for d in encoder.d[1:]]
|
|
||||||
).Else(
|
|
||||||
[d.eq(K(29, 7)) for d in encoder.d[1:]]
|
|
||||||
),
|
|
||||||
If(link_init_counter == 0,
|
|
||||||
encoder.d[0].eq(K(28, 7)),
|
|
||||||
).Else(
|
|
||||||
If(self.signal_rx_ready,
|
|
||||||
encoder.d[0].eq(K(30, 7))
|
|
||||||
).Else(
|
|
||||||
encoder.d[0].eq(K(29, 7))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
link_init_counter.eq(0)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class LinkLayerRX(Module):
|
class LinkLayerRX(Module):
|
||||||
|
@ -144,9 +174,6 @@ class LinkLayerRX(Module):
|
||||||
# nwords must be a power of 2
|
# nwords must be a power of 2
|
||||||
assert nwords & (nwords - 1) == 0
|
assert nwords & (nwords - 1) == 0
|
||||||
|
|
||||||
self.link_init = Signal()
|
|
||||||
self.remote_rx_ready = Signal()
|
|
||||||
|
|
||||||
self.aux_stb = Signal()
|
self.aux_stb = Signal()
|
||||||
self.aux_frame = Signal()
|
self.aux_frame = Signal()
|
||||||
self.aux_data = Signal(2*nwords)
|
self.aux_data = Signal(2*nwords)
|
||||||
|
@ -156,65 +183,46 @@ class LinkLayerRX(Module):
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
aux_descrambler = ResetInserter()(CEInserter()(Scrambler(3*nwords)))
|
descrambler = Descrambler(3*nwords, 8*nwords)
|
||||||
rt_descrambler = ResetInserter()(CEInserter()(Scrambler(8*nwords)))
|
self.submodules += descrambler
|
||||||
self.submodules += aux_descrambler, rt_descrambler
|
|
||||||
|
# scrambler input
|
||||||
|
all_decoded_aux = []
|
||||||
|
for i, d in enumerate(decoders):
|
||||||
|
decoded_aux = Signal(3)
|
||||||
|
all_decoded_aux.append(decoded_aux)
|
||||||
|
|
||||||
|
if i:
|
||||||
|
aux_coding = aux_coding_nocomma
|
||||||
|
else:
|
||||||
|
aux_coding = aux_coding_comma
|
||||||
|
|
||||||
|
cases = {code: decoded_aux.eq(i) for i, code in enumerate(aux_coding)}
|
||||||
|
self.comb += Case(d.d, cases).makedefault()
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.aux_frame.eq(~aux_descrambler.o[2]),
|
descrambler.i1.eq(Cat(*all_decoded_aux)),
|
||||||
|
descrambler.i2.eq(Cat(*[d.d for d in decoders])),
|
||||||
|
descrambler.sel.eq(~decoders[0].k)
|
||||||
|
]
|
||||||
|
|
||||||
|
# scrambler output
|
||||||
|
self.comb += [
|
||||||
|
self.aux_frame.eq(~descrambler.o1[2]),
|
||||||
self.aux_data.eq(
|
self.aux_data.eq(
|
||||||
Cat(*[aux_descrambler.o[3*i:3*i+2] for i in range(nwords)])),
|
Cat(*[descrambler.o1[3*i:3*i+2] for i in range(nwords)])),
|
||||||
self.rt_data.eq(rt_descrambler.o),
|
self.rt_data.eq(descrambler.o2)
|
||||||
]
|
|
||||||
|
|
||||||
aux_stb_d = Signal()
|
|
||||||
rt_frame_d = Signal()
|
|
||||||
self.sync += [
|
|
||||||
self.aux_stb.eq(aux_stb_d),
|
|
||||||
self.rt_frame.eq(rt_frame_d)
|
|
||||||
]
|
|
||||||
|
|
||||||
link_init_char = Signal()
|
|
||||||
self.comb += [
|
|
||||||
link_init_char.eq(
|
|
||||||
(decoders[0].d == K(28, 7)) |
|
|
||||||
(decoders[0].d == K(29, 7)) |
|
|
||||||
(decoders[0].d == K(30, 7))),
|
|
||||||
If(decoders[0].k,
|
|
||||||
If(link_init_char,
|
|
||||||
aux_descrambler.reset.eq(1),
|
|
||||||
rt_descrambler.reset.eq(1)
|
|
||||||
).Else(
|
|
||||||
aux_stb_d.eq(1)
|
|
||||||
),
|
|
||||||
aux_descrambler.ce.eq(1)
|
|
||||||
).Else(
|
|
||||||
rt_frame_d.eq(1),
|
|
||||||
rt_descrambler.ce.eq(1)
|
|
||||||
),
|
|
||||||
aux_descrambler.i.eq(Cat(*[d.d[5:] for d in decoders])),
|
|
||||||
rt_descrambler.i.eq(Cat(*[d.d for d in decoders]))
|
|
||||||
]
|
]
|
||||||
self.sync += [
|
self.sync += [
|
||||||
self.link_init.eq(0),
|
self.aux_stb.eq(decoders[0].k),
|
||||||
If(decoders[0].k,
|
self.rt_frame.eq(~decoders[0].k)
|
||||||
If(link_init_char, self.link_init.eq(1)),
|
|
||||||
If(decoders[0].d == K(30, 7),
|
|
||||||
self.remote_rx_ready.eq(1)
|
|
||||||
).Elif(decoders[0].d != K(28, 7),
|
|
||||||
self.remote_rx_ready.eq(0)
|
|
||||||
),
|
|
||||||
If(decoders[1].d == K(30, 7),
|
|
||||||
self.remote_rx_ready.eq(1)
|
|
||||||
) if len(decoders) > 1 else None
|
|
||||||
).Else(
|
|
||||||
self.remote_rx_ready.eq(0)
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LinkLayer(Module, AutoCSR):
|
class LinkLayer(Module, AutoCSR):
|
||||||
def __init__(self, encoder, decoders, rx_ready_confirm_cycles):
|
def __init__(self, encoder, decoders):
|
||||||
self.link_status = CSRStatus(3)
|
self.link_status = CSRStatus()
|
||||||
|
|
||||||
# control signals, in rtio clock domain
|
# control signals, in rtio clock domain
|
||||||
self.reset = Signal()
|
self.reset = Signal()
|
||||||
|
@ -242,6 +250,8 @@ class LinkLayer(Module, AutoCSR):
|
||||||
self.rx_rt_frame = Signal()
|
self.rx_rt_frame = Signal()
|
||||||
self.rx_rt_data = rx.rt_data
|
self.rx_rt_data = rx.rt_data
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
ready_r = Signal()
|
ready_r = Signal()
|
||||||
ready_rx = Signal()
|
ready_rx = Signal()
|
||||||
self.sync.rtio += ready_r.eq(self.ready)
|
self.sync.rtio += ready_r.eq(self.ready)
|
||||||
|
@ -251,8 +261,10 @@ class LinkLayer(Module, AutoCSR):
|
||||||
self.rx_aux_frame.eq(rx.aux_frame & ready_rx),
|
self.rx_aux_frame.eq(rx.aux_frame & ready_rx),
|
||||||
self.rx_rt_frame.eq(rx.rt_frame & ready_rx),
|
self.rx_rt_frame.eq(rx.rt_frame & ready_rx),
|
||||||
]
|
]
|
||||||
|
self.specials += MultiReg(ready_r, self.link_status.status)
|
||||||
|
|
||||||
# # #
|
wait_scrambler = WaitTimer(15)
|
||||||
|
self.submodules += wait_scrambler
|
||||||
|
|
||||||
fsm = ClockDomainsRenamer("rtio")(
|
fsm = ClockDomainsRenamer("rtio")(
|
||||||
ResetInserter()(FSM(reset_state="RESET_RX")))
|
ResetInserter()(FSM(reset_state="RESET_RX")))
|
||||||
|
@ -260,68 +272,17 @@ class LinkLayer(Module, AutoCSR):
|
||||||
|
|
||||||
self.comb += fsm.reset.eq(self.reset)
|
self.comb += fsm.reset.eq(self.reset)
|
||||||
|
|
||||||
rx_remote_rx_ready = Signal()
|
|
||||||
rx_link_init = Signal()
|
|
||||||
rx.remote_rx_ready.attr.add("no_retiming")
|
|
||||||
rx.link_init.attr.add("no_retiming")
|
|
||||||
self.specials += [
|
|
||||||
MultiReg(rx.remote_rx_ready, rx_remote_rx_ready, "rtio"),
|
|
||||||
MultiReg(rx.link_init, rx_link_init, "rtio")
|
|
||||||
]
|
|
||||||
|
|
||||||
link_status = BusSynchronizer(3, "rtio", "sys")
|
|
||||||
self.submodules += link_status
|
|
||||||
self.comb += self.link_status.status.eq(link_status.o)
|
|
||||||
|
|
||||||
wait_confirm = ClockDomainsRenamer("rtio")(
|
|
||||||
WaitTimer(rx_ready_confirm_cycles))
|
|
||||||
self.submodules += wait_confirm
|
|
||||||
signal_rx_ready_margin = ClockDomainsRenamer("rtio")(WaitTimer(15))
|
|
||||||
self.submodules += signal_rx_ready_margin
|
|
||||||
|
|
||||||
fsm.act("RESET_RX",
|
fsm.act("RESET_RX",
|
||||||
link_status.i.eq(0),
|
|
||||||
tx.link_init.eq(1),
|
|
||||||
self.rx_reset.eq(1),
|
self.rx_reset.eq(1),
|
||||||
NextState("WAIT_LOCAL_RX_READY")
|
NextState("WAIT_RX_READY")
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_LOCAL_RX_READY",
|
fsm.act("WAIT_RX_READY",
|
||||||
link_status.i.eq(1),
|
If(self.rx_ready, NextState("WAIT_SCRAMBLER_SYNC"))
|
||||||
tx.link_init.eq(1),
|
|
||||||
If(self.rx_ready, NextState("CONFIRM_LOCAL_RX_READY"))
|
|
||||||
)
|
)
|
||||||
fsm.act("CONFIRM_LOCAL_RX_READY",
|
fsm.act("WAIT_SCRAMBLER_SYNC",
|
||||||
link_status.i.eq(2),
|
wait_scrambler.wait.eq(1),
|
||||||
tx.link_init.eq(1),
|
If(wait_scrambler.done, NextState("READY")),
|
||||||
wait_confirm.wait.eq(1),
|
|
||||||
If(wait_confirm.done, NextState("WAIT_REMOTE_RX_READY")),
|
|
||||||
If(~rx_link_init, NextState("RESET_RX"))
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_REMOTE_RX_READY",
|
|
||||||
link_status.i.eq(3),
|
|
||||||
tx.link_init.eq(1),
|
|
||||||
tx.signal_rx_ready.eq(1),
|
|
||||||
If(rx_remote_rx_ready, NextState("ENSURE_SIGNAL_RX_READY"))
|
|
||||||
)
|
|
||||||
# If the transceiver transmits one character per RTIO cycle,
|
|
||||||
# we may be unlucky and signal_rx_ready will transmit a comma
|
|
||||||
# on the first cycle instead of a "RX ready" character.
|
|
||||||
# Further, we need to ensure the rx.remote_rx_ready
|
|
||||||
# gets through MultiReg to rx_remote_rx_ready at the receiver.
|
|
||||||
# So transmit the "RX ready" pattern for several cycles.
|
|
||||||
fsm.act("ENSURE_SIGNAL_RX_READY",
|
|
||||||
link_status.i.eq(3),
|
|
||||||
tx.link_init.eq(1),
|
|
||||||
tx.signal_rx_ready.eq(1),
|
|
||||||
signal_rx_ready_margin.wait.eq(1),
|
|
||||||
If(signal_rx_ready_margin.done, NextState("WAIT_REMOTE_LINK_UP"))
|
|
||||||
)
|
|
||||||
fsm.act("WAIT_REMOTE_LINK_UP",
|
|
||||||
link_status.i.eq(4),
|
|
||||||
If(~rx_link_init, NextState("READY"))
|
|
||||||
)
|
)
|
||||||
fsm.act("READY",
|
fsm.act("READY",
|
||||||
link_status.i.eq(5),
|
|
||||||
If(rx_link_init, NextState("RESET_RX")), # TODO: remove this, link deinit should be detected at upper layer
|
|
||||||
self.ready.eq(1)
|
self.ready.eq(1)
|
||||||
)
|
)
|
||||||
|
|
|
@ -185,7 +185,7 @@ class GTX_1000BASE_BX10(Module):
|
||||||
self.decoders[1].input.eq(rxdata[10:])
|
self.decoders[1].input.eq(rxdata[10:])
|
||||||
]
|
]
|
||||||
|
|
||||||
clock_aligner = BruteforceClockAligner(0b0001111100, self.rtio_clk_freq)
|
clock_aligner = BruteforceClockAligner(0b0101111100, self.rtio_clk_freq)
|
||||||
self.submodules += clock_aligner
|
self.submodules += clock_aligner
|
||||||
self.comb += [
|
self.comb += [
|
||||||
clock_aligner.rxdata.eq(rxdata),
|
clock_aligner.rxdata.eq(rxdata),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use sched::{Waiter, Spawner};
|
||||||
|
|
||||||
fn drtio_link_is_up() -> bool {
|
fn drtio_link_is_up() -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::drtio::link_status_read() == 5
|
csr::drtio::link_status_read() == 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,10 @@ fn drtio_init_channel(channel: u16) {
|
||||||
pub fn link_thread(waiter: Waiter, _spawner: Spawner) {
|
pub fn link_thread(waiter: Waiter, _spawner: Spawner) {
|
||||||
loop {
|
loop {
|
||||||
waiter.until(drtio_link_is_up).unwrap();
|
waiter.until(drtio_link_is_up).unwrap();
|
||||||
info!("link is up");
|
info!("link RX is up");
|
||||||
|
|
||||||
|
waiter.sleep(300);
|
||||||
|
info!("wait for remote side done");
|
||||||
|
|
||||||
drtio_sync_tsc();
|
drtio_sync_tsc();
|
||||||
info!("TSC synced");
|
info!("TSC synced");
|
||||||
|
|
|
@ -44,13 +44,7 @@ class TestAuxController(unittest.TestCase):
|
||||||
dut = TB(4)
|
dut = TB(4)
|
||||||
|
|
||||||
def link_init():
|
def link_init():
|
||||||
yield dut.link_layer.tx.link_init.eq(1)
|
for i in range(8):
|
||||||
yield
|
|
||||||
yield
|
|
||||||
yield dut.link_layer.tx.link_init.eq(0)
|
|
||||||
while not (yield dut.link_layer.rx.link_init):
|
|
||||||
yield
|
|
||||||
while (yield dut.link_layer.rx.link_init):
|
|
||||||
yield
|
yield
|
||||||
yield dut.link_layer.ready.eq(1)
|
yield dut.link_layer.ready.eq(1)
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,7 @@ class DUT(Module):
|
||||||
self.ttl1 = Signal()
|
self.ttl1 = Signal()
|
||||||
self.transceivers = DummyTransceiverPair(nwords)
|
self.transceivers = DummyTransceiverPair(nwords)
|
||||||
|
|
||||||
self.submodules.master = DRTIOMaster(self.transceivers.alice,
|
self.submodules.master = DRTIOMaster(self.transceivers.alice)
|
||||||
ll_rx_ready_confirm=15)
|
|
||||||
|
|
||||||
rx_synchronizer = DummyRXSynchronizer()
|
rx_synchronizer = DummyRXSynchronizer()
|
||||||
self.submodules.phy0 = ttl_simple.Output(self.ttl0)
|
self.submodules.phy0 = ttl_simple.Output(self.ttl0)
|
||||||
|
@ -52,8 +51,7 @@ class DUT(Module):
|
||||||
rtio.Channel.from_phy(self.phy1, ofifo_depth=4)
|
rtio.Channel.from_phy(self.phy1, ofifo_depth=4)
|
||||||
]
|
]
|
||||||
self.submodules.satellite = DRTIOSatellite(
|
self.submodules.satellite = DRTIOSatellite(
|
||||||
self.transceivers.bob, rx_synchronizer, rtio_channels,
|
self.transceivers.bob, rx_synchronizer, rtio_channels)
|
||||||
ll_rx_ready_confirm=15)
|
|
||||||
|
|
||||||
|
|
||||||
class TestFullStack(unittest.TestCase):
|
class TestFullStack(unittest.TestCase):
|
||||||
|
|
|
@ -22,15 +22,6 @@ def process(seq):
|
||||||
return rseq
|
return rseq
|
||||||
|
|
||||||
|
|
||||||
class TestScrambler(unittest.TestCase):
|
|
||||||
def test_roundtrip(self):
|
|
||||||
seq = list(range(256))*3
|
|
||||||
scrambled_seq = process(seq)
|
|
||||||
descrambled_seq = process(scrambled_seq)
|
|
||||||
self.assertNotEqual(seq, scrambled_seq)
|
|
||||||
self.assertEqual(seq, descrambled_seq)
|
|
||||||
|
|
||||||
|
|
||||||
class Loopback(Module):
|
class Loopback(Module):
|
||||||
def __init__(self, nwords):
|
def __init__(self, nwords):
|
||||||
ks = [Signal() for k in range(nwords)]
|
ks = [Signal() for k in range(nwords)]
|
||||||
|
@ -45,12 +36,9 @@ class TestLinkLayer(unittest.TestCase):
|
||||||
def test_packets(self):
|
def test_packets(self):
|
||||||
dut = Loopback(4)
|
dut = Loopback(4)
|
||||||
|
|
||||||
def link_init():
|
def scrambler_sync():
|
||||||
yield dut.tx.link_init.eq(1)
|
for i in range(8):
|
||||||
yield
|
yield
|
||||||
yield
|
|
||||||
yield dut.tx.link_init.eq(0)
|
|
||||||
yield
|
|
||||||
|
|
||||||
rt_packets = [
|
rt_packets = [
|
||||||
[0x12459970, 0x9938cdef, 0x12340000],
|
[0x12459970, 0x9938cdef, 0x12340000],
|
||||||
|
@ -59,10 +47,7 @@ class TestLinkLayer(unittest.TestCase):
|
||||||
[0x88277475, 0x19883332, 0x19837662, 0x81726668, 0x81876261]
|
[0x88277475, 0x19883332, 0x19837662, 0x81726668, 0x81876261]
|
||||||
]
|
]
|
||||||
def transmit_rt_packets():
|
def transmit_rt_packets():
|
||||||
while not (yield dut.tx.link_init):
|
yield from scrambler_sync()
|
||||||
yield
|
|
||||||
while (yield dut.tx.link_init):
|
|
||||||
yield
|
|
||||||
|
|
||||||
for packet in rt_packets:
|
for packet in rt_packets:
|
||||||
yield dut.tx.rt_frame.eq(1)
|
yield dut.tx.rt_frame.eq(1)
|
||||||
|
@ -78,10 +63,7 @@ class TestLinkLayer(unittest.TestCase):
|
||||||
rx_rt_packets = []
|
rx_rt_packets = []
|
||||||
@passive
|
@passive
|
||||||
def receive_rt_packets():
|
def receive_rt_packets():
|
||||||
while not (yield dut.rx.link_init):
|
yield from scrambler_sync()
|
||||||
yield
|
|
||||||
while (yield dut.rx.link_init):
|
|
||||||
yield
|
|
||||||
|
|
||||||
previous_frame = 0
|
previous_frame = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -100,10 +82,7 @@ class TestLinkLayer(unittest.TestCase):
|
||||||
[0xbb, 0xaa, 0xdd, 0xcc, 0x00, 0xff, 0xee]
|
[0xbb, 0xaa, 0xdd, 0xcc, 0x00, 0xff, 0xee]
|
||||||
]
|
]
|
||||||
def transmit_aux_packets():
|
def transmit_aux_packets():
|
||||||
while not (yield dut.tx.link_init):
|
yield from scrambler_sync()
|
||||||
yield
|
|
||||||
while (yield dut.tx.link_init):
|
|
||||||
yield
|
|
||||||
|
|
||||||
for packet in aux_packets:
|
for packet in aux_packets:
|
||||||
yield dut.tx.aux_frame.eq(1)
|
yield dut.tx.aux_frame.eq(1)
|
||||||
|
@ -123,10 +102,7 @@ class TestLinkLayer(unittest.TestCase):
|
||||||
rx_aux_packets = []
|
rx_aux_packets = []
|
||||||
@passive
|
@passive
|
||||||
def receive_aux_packets():
|
def receive_aux_packets():
|
||||||
while not (yield dut.rx.link_init):
|
yield from scrambler_sync()
|
||||||
yield
|
|
||||||
while (yield dut.rx.link_init):
|
|
||||||
yield
|
|
||||||
|
|
||||||
previous_frame = 0
|
previous_frame = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -140,8 +116,7 @@ class TestLinkLayer(unittest.TestCase):
|
||||||
packet.append((yield dut.rx.aux_data))
|
packet.append((yield dut.rx.aux_data))
|
||||||
yield
|
yield
|
||||||
|
|
||||||
run_simulation(dut, [link_init(),
|
run_simulation(dut, [transmit_rt_packets(), receive_rt_packets(),
|
||||||
transmit_rt_packets(), receive_rt_packets(),
|
|
||||||
transmit_aux_packets(), receive_aux_packets()])
|
transmit_aux_packets(), receive_aux_packets()])
|
||||||
|
|
||||||
# print("RT:")
|
# print("RT:")
|
||||||
|
|
Loading…
Reference in New Issue