2
0
mirror of https://github.com/m-labs/artiq.git synced 2025-01-24 01:18:12 +08:00
artiq/artiq/gateware/drtio/link_layer.py

296 lines
8.7 KiB
Python
Raw Normal View History

from functools import reduce
from operator import xor, or_
2016-09-26 12:53:10 +08:00
from migen import *
2016-09-27 19:02:54 +08:00
from migen.genlib.fsm import *
2016-11-18 17:45:33 +08:00
from migen.genlib.cdc import MultiReg, PulseSynchronizer
2016-11-09 22:03:47 +08:00
from migen.genlib.misc import WaitTimer
2016-11-02 13:09:13 +08:00
from misoc.interconnect.csr import *
2016-09-26 12:53:10 +08:00
class Scrambler(Module):
2016-11-17 22:32:39 +08:00
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)
2016-11-17 22:32:39 +08:00
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)
2016-09-26 12:53:10 +08:00
def K(x, y):
return (y << 5) | x
2016-11-17 22:32:39 +08:00
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),
]
2016-09-26 12:53:10 +08:00
class LinkLayerTX(Module):
def __init__(self, encoder):
nwords = len(encoder.k)
# nwords must be a power of 2
assert nwords & (nwords - 1) == 0
self.aux_frame = Signal()
self.aux_data = Signal(2*nwords)
self.aux_ack = Signal()
self.rt_frame = Signal()
self.rt_data = Signal(8*nwords)
# # #
2016-11-17 22:32:39 +08:00
# Idle and auxiliary traffic use special characters defined in the
# aux_coding_* tables.
# The first (or only) character uses aux_coding_comma which guarantees
# 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.
#
2016-09-26 12:53:10 +08:00
# 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
# the following meanings:
# 100 idle/auxiliary framing
# 0AB 2 bits of auxiliary data
2016-11-17 22:32:39 +08:00
#
# 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
2016-09-26 12:53:10 +08:00
aux_data_ctl = []
for i in range(nwords):
2016-09-27 12:46:01 +08:00
aux_data_ctl.append(self.aux_data[i*2:i*2+2])
2016-09-26 12:53:10 +08:00
aux_data_ctl.append(0)
self.comb += [
If(self.aux_frame,
2016-11-17 22:32:39 +08:00
scrambler.i1.eq(Cat(*aux_data_ctl))
2016-09-26 12:53:10 +08:00
).Else(
2016-11-17 22:32:39 +08:00
scrambler.i1.eq(Replicate(0b100, nwords))
2016-09-26 12:53:10 +08:00
),
2016-11-17 22:32:39 +08:00
scrambler.i2.eq(self.rt_data),
scrambler.sel.eq(self.rt_frame),
2016-09-26 12:53:10 +08:00
self.aux_ack.eq(~self.rt_frame)
]
2016-11-17 22:32:39 +08:00
# compensate for scrambler latency
rt_frame_r = Signal()
self.sync += rt_frame_r.eq(self.rt_frame)
# scrambler output
2016-09-26 12:53:10 +08:00
for i in range(nwords):
2016-11-17 22:32:39 +08:00
scrambled_ctl = scrambler.o1[i*3:i*3+3]
if i:
aux_coding = aux_coding_nocomma
else:
aux_coding = aux_coding_comma
2016-09-26 12:53:10 +08:00
self.sync += [
encoder.k[i].eq(1),
2016-11-17 22:32:39 +08:00
encoder.d[i].eq(Array(aux_coding)[scrambled_ctl])
2016-09-26 12:53:10 +08:00
]
2016-11-17 22:32:39 +08:00
self.sync += \
2016-09-26 12:53:10 +08:00
If(rt_frame_r,
[k.eq(0) for k in encoder.k],
2016-11-17 22:32:39 +08:00
[d.eq(scrambler.o2[i*8:i*8+8]) for i, d in enumerate(encoder.d)]
2016-09-26 12:53:10 +08:00
)
class LinkLayerRX(Module):
def __init__(self, decoders):
nwords = len(decoders)
# nwords must be a power of 2
assert nwords & (nwords - 1) == 0
2016-09-27 12:46:01 +08:00
self.aux_stb = Signal()
self.aux_frame = Signal()
self.aux_data = Signal(2*nwords)
self.rt_frame = Signal()
self.rt_data = Signal(8*nwords)
# # #
2016-11-17 22:32:39 +08:00
descrambler = Descrambler(3*nwords, 8*nwords)
self.submodules += descrambler
2016-11-17 22:32:39 +08:00
# 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 += [
descrambler.i1.eq(Cat(*all_decoded_aux)),
descrambler.i2.eq(Cat(*[d.d for d in decoders])),
descrambler.sel.eq(~decoders[0].k)
]
2016-11-17 22:32:39 +08:00
# scrambler output
self.comb += [
2016-11-17 22:32:39 +08:00
self.aux_frame.eq(~descrambler.o1[2]),
self.aux_data.eq(
Cat(*[descrambler.o1[3*i:3*i+2] for i in range(nwords)])),
self.rt_data.eq(descrambler.o2)
]
2016-09-27 19:02:54 +08:00
self.sync += [
2016-11-17 22:32:39 +08:00
self.aux_stb.eq(decoders[0].k),
self.rt_frame.eq(~decoders[0].k)
2016-09-27 19:02:54 +08:00
]
2016-09-27 21:41:57 +08:00
2016-11-17 22:32:39 +08:00
2016-11-02 13:09:13 +08:00
class LinkLayer(Module, AutoCSR):
2016-11-17 22:32:39 +08:00
def __init__(self, encoder, decoders):
self.link_status = CSRStatus()
2016-11-18 17:45:33 +08:00
self.link_reset = CSR()
2016-11-02 13:09:13 +08:00
2016-09-27 21:41:57 +08:00
# pulsed to reset receiver, rx_ready must immediately go low
self.rx_reset = Signal()
# receiver locked including comma alignment
self.rx_ready = Signal()
2016-10-14 00:34:59 +08:00
tx = ClockDomainsRenamer("rtio")(LinkLayerTX(encoder))
rx = ClockDomainsRenamer("rtio_rx")(LinkLayerRX(decoders))
2016-09-27 21:41:57 +08:00
self.submodules += tx, rx
2016-10-14 00:34:59 +08:00
# in rtio clock domain
2016-09-27 21:41:57 +08:00
self.tx_aux_frame = tx.aux_frame
self.tx_aux_data = tx.aux_data
self.tx_aux_ack = tx.aux_ack
self.tx_rt_frame = tx.rt_frame
self.tx_rt_data = tx.rt_data
2016-10-14 00:34:59 +08:00
# in rtio_rx clock domain
2016-09-27 21:41:57 +08:00
self.rx_aux_stb = rx.aux_stb
self.rx_aux_frame = Signal()
2016-09-27 21:41:57 +08:00
self.rx_aux_data = rx.aux_data
self.rx_rt_frame = Signal()
2016-09-27 21:41:57 +08:00
self.rx_rt_data = rx.rt_data
2016-11-17 22:32:39 +08:00
# # #
2016-11-18 17:45:33 +08:00
ready = Signal()
reset_ps = PulseSynchronizer("sys", "rtio")
done_ps = PulseSynchronizer("rtio", "sys")
self.submodules += reset_ps, done_ps
self.comb += reset_ps.i.eq(self.link_reset.re)
self.sync += [
If(done_ps.o, ready.eq(1)),
If(reset_ps.i, ready.eq(0)),
]
self.comb += self.link_status.status.eq(ready)
ready_rx = Signal()
2016-11-18 17:45:33 +08:00
ready.attr.add("no_retiming")
self.specials += MultiReg(ready, ready_rx, "rtio_rx")
self.comb += [
self.rx_aux_frame.eq(rx.aux_frame & ready_rx),
self.rx_rt_frame.eq(rx.rt_frame & ready_rx),
]
2016-11-18 17:45:33 +08:00
wait_scrambler = ClockDomainsRenamer("rtio")(WaitTimer(15))
2016-11-17 22:32:39 +08:00
self.submodules += wait_scrambler
2016-09-27 21:41:57 +08:00
2016-11-18 17:45:33 +08:00
fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="RESET_RX"))
2016-09-27 21:41:57 +08:00
self.submodules += fsm
fsm.act("RESET_RX",
self.rx_reset.eq(1),
2016-11-17 22:32:39 +08:00
NextState("WAIT_RX_READY")
2016-09-27 21:41:57 +08:00
)
2016-11-17 22:32:39 +08:00
fsm.act("WAIT_RX_READY",
2016-11-18 17:45:33 +08:00
If(self.rx_ready, NextState("WAIT_SCRAMBLER_SYNC")),
If(reset_ps.o, NextState("RESET_RX"))
2016-11-09 22:03:47 +08:00
)
2016-11-17 22:32:39 +08:00
fsm.act("WAIT_SCRAMBLER_SYNC",
wait_scrambler.wait.eq(1),
2016-11-18 17:45:33 +08:00
If(wait_scrambler.done,
done_ps.i.eq(1),
NextState("READY")
)
2016-09-30 11:25:06 +08:00
)
2016-09-27 21:41:57 +08:00
fsm.act("READY",
2016-11-18 17:45:33 +08:00
If(reset_ps.o, NextState("RESET_RX"))
2016-09-27 21:41:57 +08:00
)