forked from M-Labs/artiq
1
0
Fork 0
artiq/artiq/gateware/rtio/phy/fastlink.py

134 lines
4.9 KiB
Python
Raw Normal View History

2020-08-21 19:31:26 +08:00
from migen import *
2020-08-22 19:46:41 +08:00
from migen.genlib.io import (DifferentialOutput, DifferentialInput,
DDROutput, DDRInput)
2020-08-21 19:31:26 +08:00
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
from artiq.gateware.rtio import rtlink
class SerDes(Module):
# crc-12 telco: 0x80f
2020-08-21 23:21:36 +08:00
def __init__(self, n_data=8, t_clk=7, d_clk=0b1100011,
2020-08-21 19:31:26 +08:00
n_frame=14, n_crc=12, poly=0x80f):
"""DDR fast link.
* One word clock lane with `t_clk` period.
* Multiple data lanes at DDR speed.
* One return data lane at slower speed.
* n_frame//2 - 1 marker bits are used to provide framing.
2020-08-22 19:46:41 +08:00
* `n_data` lanes
2020-08-21 19:31:26 +08:00
* `t_clk` bits per clk cycle with pattern `d_clk`
2020-08-22 19:46:41 +08:00
* `n_frame` words per frame
* `n_crc` CRC bits per frame for divisor poly `poly`
2020-08-21 19:31:26 +08:00
"""
2020-08-21 23:21:36 +08:00
# pins
2020-08-24 03:41:13 +08:00
self.data = [Signal(2, reset_less=True) for _ in range(n_data)]
2020-08-22 19:46:41 +08:00
n_mosi = n_data - 2 # mosi lanes
n_word = n_mosi*t_clk # bits per word
t_frame = t_clk*n_frame # frame duration
n_marker = n_frame//2 + 1
n_body = n_word*n_frame - n_marker - n_crc
2020-08-21 23:21:36 +08:00
t_miso = 0 # miso sampling latency TODO
2020-08-24 22:51:50 +08:00
assert n_crc % n_mosi == 0
2020-08-21 19:31:26 +08:00
# frame data
self.payload = Signal(n_body)
# readback data
self.readback = Signal(n_frame, reset_less=True)
# data load synchronization event
self.stb = Signal()
# # #
2020-08-24 22:51:50 +08:00
self.submodules.crca = LiteEthMACCRCEngine(
data_width=n_mosi, width=n_crc, polynom=poly)
self.submodules.crcb = LiteEthMACCRCEngine(
data_width=n_mosi, width=n_crc, polynom=poly)
2020-08-21 19:31:26 +08:00
words_ = []
j = 0
2020-08-22 19:46:41 +08:00
# build from LSB to MSB because MSB first
2020-08-21 19:31:26 +08:00
for i in range(n_frame): # iterate over words
if i == 0: # data and checksum
2020-08-24 03:41:13 +08:00
words_.append(C(0, n_crc))
2020-08-21 19:31:26 +08:00
k = n_word - n_crc
elif i == 1: # marker
words_.append(C(1))
k = n_word - 1
elif i < n_frame//2 + 2: # marker
words_.append(C(0))
k = n_word - 1
else: # full word
k = n_word
# append corresponding frame body bits
words_.append(self.payload[j:j + k])
j += k
words_ = Cat(words_)
2020-08-24 03:41:13 +08:00
assert len(words_) == n_frame*n_word
2020-08-21 19:31:26 +08:00
words = Signal(len(words_))
self.comb += words.eq(words_)
clk = Signal(t_clk, reset=d_clk)
2020-08-22 19:46:41 +08:00
i = Signal(max=t_frame//2)
2020-08-24 03:41:13 +08:00
# big shift register for mosi and
sr = [Signal(t_frame, reset_less=True) for i in range(n_mosi)]
2020-08-21 19:31:26 +08:00
assert len(Cat(sr)) == len(words)
2020-10-19 04:27:05 +08:00
crc_insert = Cat(([d[0] for d in self.data[1:-1]] +
[d[1] for d in self.data[1:-1]])[:n_crc])
2020-08-22 19:46:41 +08:00
miso_sr = Signal(t_frame, reset_less=True)
miso_sr_next = Signal.like(miso_sr)
2020-08-21 19:31:26 +08:00
self.comb += [
2020-08-22 19:46:41 +08:00
self.stb.eq(i == t_frame//2 - 1),
2020-08-21 19:31:26 +08:00
# LiteETHMACCRCEngine takes data LSB first
2020-08-24 22:51:50 +08:00
self.crca.data.eq(Cat([sri[-1] for sri in sr[::-1]])),
self.crcb.data.eq(Cat([sri[-2] for sri in sr[::-1]])),
self.crcb.last.eq(self.crca.next),
2020-08-22 19:46:41 +08:00
miso_sr_next.eq(Cat(self.data[-1], miso_sr)),
2020-08-24 23:46:31 +08:00
# unload miso
2020-09-12 19:02:37 +08:00
# TODO: align to marker
2020-08-24 23:46:31 +08:00
self.readback.eq(Cat([miso_sr_next[t_miso + i*t_clk]
for i in range(n_frame)])),
2020-08-21 19:31:26 +08:00
]
self.sync.rio_phy += [
2020-08-21 23:21:36 +08:00
# shift everything by two bits
2020-08-24 03:41:13 +08:00
[di.eq(sri[-2:]) for di, sri in zip(self.data, [clk] + sr)],
clk.eq(Cat(clk[-2:], clk)),
[sri.eq(Cat(C(0, 2), sri)) for sri in sr],
2020-08-22 19:46:41 +08:00
miso_sr.eq(miso_sr_next),
2020-08-24 22:51:50 +08:00
self.crca.last.eq(self.crcb.next),
2020-08-21 23:21:36 +08:00
i.eq(i + 1),
If(self.stb,
i.eq(0),
clk.eq(clk.reset),
2020-08-24 22:51:50 +08:00
self.crca.last.eq(0),
2020-08-21 19:31:26 +08:00
# transpose, load
2020-08-22 19:46:41 +08:00
[sri.eq(Cat(words[i::n_mosi])) for i, sri in enumerate(sr)],
2020-08-21 23:21:36 +08:00
# inject crc for the last cycle
2020-10-19 04:27:05 +08:00
crc_insert.eq(self.crca.next if n_crc // n_mosi <= 1
else self.crca.last),
2020-08-21 19:31:26 +08:00
),
]
2020-08-21 23:21:36 +08:00
class SerInterface(Module):
def __init__(self, pins, pins_n):
2020-08-22 19:46:41 +08:00
self.data = [Signal(2) for _ in range(2 + len(pins.mosi))]
for d, pp, pn in zip(self.data,
[pins.clk] + list(pins.mosi),
[pins_n.clk] + list(pins_n.mosi)):
2020-08-21 23:21:36 +08:00
ddr = Signal()
2020-08-21 19:31:26 +08:00
self.specials += [
2020-08-24 22:51:50 +08:00
# d1 closer to q
2020-08-24 22:49:36 +08:00
DDROutput(d[1], d[0], ddr, ClockSignal("rio_phy")),
2020-08-22 19:46:41 +08:00
DifferentialOutput(ddr, pp, pn),
2020-08-21 19:31:26 +08:00
]
2020-08-22 19:46:41 +08:00
ddr = Signal()
self.specials += [
DifferentialInput(pins.miso, pins_n.miso, ddr),
2020-08-24 22:51:50 +08:00
# q1 closer to d
DDRInput(ddr, self.data[-1][0], self.data[-1][1],
2020-08-22 19:46:41 +08:00
ClockSignal("rio_phy")),
]