2016-09-26 14:14:14 +08:00
|
|
|
from functools import reduce
|
2016-09-27 11:23:29 +08:00
|
|
|
from operator import xor, or_
|
2016-09-26 14:14:14 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
|
2016-09-26 14:14:14 +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()
|
2016-09-26 14:14:14 +08:00
|
|
|
|
|
|
|
# # #
|
|
|
|
|
|
|
|
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 14:14:14 +08:00
|
|
|
|
|
|
|
|
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
|
|
|
)
|
2016-09-27 11:23:29 +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()
|
2016-09-27 11:23:29 +08:00
|
|
|
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-09-27 11:23:29 +08:00
|
|
|
|
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-09-27 11:23:29 +08:00
|
|
|
]
|
|
|
|
|
2016-11-17 22:32:39 +08:00
|
|
|
# scrambler output
|
2016-09-27 11:23:29 +08:00
|
|
|
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 11:23:29 +08:00
|
|
|
]
|
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
|
2016-10-29 17:05:30 +08:00
|
|
|
self.rx_aux_frame = Signal()
|
2016-09-27 21:41:57 +08:00
|
|
|
self.rx_aux_data = rx.aux_data
|
2016-10-29 17:05:30 +08:00
|
|
|
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)
|
|
|
|
|
2016-10-29 17:05:30 +08:00
|
|
|
ready_rx = Signal()
|
2016-11-18 17:45:33 +08:00
|
|
|
ready.attr.add("no_retiming")
|
|
|
|
self.specials += MultiReg(ready, ready_rx, "rtio_rx")
|
2016-10-29 17:05:30 +08:00
|
|
|
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-11-19 00:05:59 +08:00
|
|
|
)
|
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
|
|
|
)
|