forked from M-Labs/artiq
phaser: initial
This commit is contained in:
parent
5f6aa02b61
commit
aa0154d8e2
|
@ -5,7 +5,7 @@ from migen.genlib.io import DifferentialOutput
|
|||
from artiq.gateware import rtio
|
||||
from artiq.gateware.rtio.phy import spi2, ad53xx_monitor, grabber
|
||||
from artiq.gateware.suservo import servo, pads as servo_pads
|
||||
from artiq.gateware.rtio.phy import servo as rtservo, fastino
|
||||
from artiq.gateware.rtio.phy import servo as rtservo, fastino, phaser
|
||||
|
||||
|
||||
def _eem_signal(i):
|
||||
|
@ -613,7 +613,8 @@ class Fastino(_EEM):
|
|||
Subsignal("clk", Pins(_eem_pin(eem, 0, pol))),
|
||||
Subsignal("mosi", Pins(*(_eem_pin(eem, i, pol)
|
||||
for i in range(1, 7)))),
|
||||
Subsignal("miso", Pins(_eem_pin(eem, 7, pol))),
|
||||
Subsignal("miso", Pins(_eem_pin(eem, 7, pol)),
|
||||
Misc("DIFF_TERM=TRUE")),
|
||||
IOStandard(iostandard),
|
||||
) for pol in "pn"]
|
||||
|
||||
|
@ -626,3 +627,29 @@ class Fastino(_EEM):
|
|||
log2_width=0)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
|
||||
class Phaser(_EEM):
|
||||
@staticmethod
|
||||
def io(eem, iostandard="LVDS_25"):
|
||||
return [
|
||||
("phaser{}_ser_{}".format(eem, pol), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 0, pol))),
|
||||
Subsignal("mosi", Pins(*(_eem_pin(eem, i, pol)
|
||||
for i in range(1, 7)))),
|
||||
Subsignal("miso", Pins(_eem_pin(eem, 7, pol)),
|
||||
Misc("DIFF_TERM=TRUE")),
|
||||
IOStandard(iostandard),
|
||||
) for pol in "pn"]
|
||||
|
||||
@classmethod
|
||||
def add_std(cls, target, eem, iostandard="LVDS_25"):
|
||||
cls.add_extension(target, eem, iostandard=iostandard)
|
||||
|
||||
phy = phaser.Phaser(target.platform.request("phaser{}_ser_p".format(eem)),
|
||||
target.platform.request("phaser{}_ser_n".format(eem)))
|
||||
target.submodules += phy
|
||||
target.rtio_channels.extend([
|
||||
rtio.Channel(phy.config, ififo_depth=4),
|
||||
rtio.Channel(phy.data),
|
||||
])
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
from migen import *
|
||||
from migen.genlib.cdc import MultiReg
|
||||
from migen.genlib.io import DifferentialOutput, DifferentialInput, DDROutput
|
||||
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
||||
|
||||
from artiq.gateware.rtio import rtlink
|
||||
|
||||
|
||||
class SerDes(Module):
|
||||
# crc-12 telco: 0x80f
|
||||
def __init__(self, pins, pins_n, t_clk=7, d_clk=0b1100011,
|
||||
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.
|
||||
|
||||
* `n_frame` words per frame
|
||||
* `t_clk` bits per clk cycle with pattern `d_clk`
|
||||
* `n_crc` CRC bits per frame
|
||||
"""
|
||||
n_lanes = len(pins.mosi) # number of data lanes
|
||||
n_word = n_lanes*t_clk
|
||||
n_body = n_word*n_frame - (n_frame//2 + 1) - n_crc
|
||||
|
||||
# frame data
|
||||
self.payload = Signal(n_body)
|
||||
# readback data
|
||||
self.readback = Signal(n_frame, reset_less=True)
|
||||
# data load synchronization event
|
||||
self.stb = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.crc = LiteEthMACCRCEngine(
|
||||
data_width=2*n_lanes, width=n_crc, polynom=poly)
|
||||
|
||||
words_ = []
|
||||
j = 0
|
||||
for i in range(n_frame): # iterate over words
|
||||
if i == 0: # data and checksum
|
||||
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_)
|
||||
assert len(words_) == n_frame*n_word - n_crc
|
||||
words = Signal(len(words_))
|
||||
self.comb += words.eq(words_)
|
||||
|
||||
clk = Signal(t_clk, reset=d_clk)
|
||||
clk_stb = Signal()
|
||||
i_frame = Signal(max=t_clk*n_frame//2) # DDR
|
||||
frame_stb = Signal()
|
||||
# big shift register for clk and mosi
|
||||
sr = [Signal(n_frame*t_clk - n_crc//n_lanes, reset_less=True)
|
||||
for i in range(n_lanes)]
|
||||
assert len(Cat(sr)) == len(words)
|
||||
# DDR bits for each register
|
||||
ddr_data = Cat([sri[-2] for sri in sr], [sri[-1] for sri in sr])
|
||||
self.comb += [
|
||||
# assert one cycle ahead
|
||||
clk_stb.eq(~clk[0] & clk[-1]),
|
||||
# double period because of DDR
|
||||
frame_stb.eq(i_frame == t_clk*n_frame//2 - 1),
|
||||
|
||||
# LiteETHMACCRCEngine takes data LSB first
|
||||
self.crc.data[::-1].eq(ddr_data),
|
||||
self.stb.eq(frame_stb & clk_stb),
|
||||
]
|
||||
miso = Signal()
|
||||
miso_sr = Signal(n_frame, reset_less=True)
|
||||
self.sync.rio_phy += [
|
||||
# shift clock pattern by two bits each DDR cycle
|
||||
clk.eq(Cat(clk[-2:], clk)),
|
||||
[sri[2:].eq(sri) for sri in sr],
|
||||
self.crc.last.eq(self.crc.next),
|
||||
If(clk[:2] == 0, # TODO: tweak MISO sampling
|
||||
miso_sr.eq(Cat(miso, miso_sr)),
|
||||
),
|
||||
If(~frame_stb,
|
||||
i_frame.eq(i_frame + 1),
|
||||
),
|
||||
If(frame_stb & clk_stb,
|
||||
i_frame.eq(0),
|
||||
self.crc.last.eq(0),
|
||||
# transpose, load
|
||||
Cat(sr).eq(Cat(words[mm::n_lanes] for mm in range(n_lanes))),
|
||||
self.readback.eq(miso_sr),
|
||||
),
|
||||
If(i_frame == t_clk*n_frame//2 - 2,
|
||||
# inject crc
|
||||
ddr_data.eq(self.crc.next),
|
||||
),
|
||||
]
|
||||
|
||||
clk_ddr = Signal()
|
||||
miso0 = Signal()
|
||||
self.specials += [
|
||||
DDROutput(clk[-1], clk[-2], clk_ddr, ClockSignal("rio_phy")),
|
||||
DifferentialOutput(clk_ddr, pins.clk, pins_n.clk),
|
||||
DifferentialInput(pins.miso, pins_n.miso, miso0),
|
||||
MultiReg(miso0, miso, "rio_phy"),
|
||||
]
|
||||
for sri, ddr, mp, mn in zip(
|
||||
sr, Signal(n_lanes), pins.mosi, pins_n.mosi):
|
||||
self.specials += [
|
||||
DDROutput(sri[-1], sri[-2], ddr, ClockSignal("rio_phy")),
|
||||
DifferentialOutput(ddr, mp, mn),
|
||||
]
|
|
@ -0,0 +1,51 @@
|
|||
from migen import *
|
||||
|
||||
from artiq.gateware.rtio import rtlink
|
||||
from .fastlink import SerDes
|
||||
|
||||
|
||||
class Phaser(Module):
|
||||
def __init__(self, pins, pins_n):
|
||||
self.config = rtlink.Interface(
|
||||
rtlink.OInterface(data_width=8, address_width=8,
|
||||
enable_replace=False),
|
||||
rtlink.IInterface(data_width=8))
|
||||
self.data = rtlink.Interface(
|
||||
rtlink.OInterface(data_width=32, address_width=8,
|
||||
enable_replace=True))
|
||||
|
||||
self.submodules.serializer = SerDes(
|
||||
pins, pins_n, t_clk=8, d_clk=0b00001111,
|
||||
n_frame=10, n_crc=6, poly=0x2f)
|
||||
|
||||
header = Record([
|
||||
("we", 1),
|
||||
("addr", 7),
|
||||
("data", 8),
|
||||
("type", 4)
|
||||
])
|
||||
n_channels = 2
|
||||
n_samples = 8
|
||||
body = [[(Signal(14), Signal(14)) for i in range(n_channels)]
|
||||
for j in range(n_samples)]
|
||||
assert len(Cat(header.raw_bits(), body)) == \
|
||||
len(self.serializer.payload)
|
||||
self.comb += self.serializer.payload.eq(Cat(header.raw_bits(), body))
|
||||
|
||||
self.sync.rio_phy += [
|
||||
If(self.serializer.stb,
|
||||
header.we.eq(0),
|
||||
),
|
||||
If(self.config.o.stb,
|
||||
header.we.eq(~self.config.o.address[-1]),
|
||||
header.addr.eq(self.config.o.address),
|
||||
header.data.eq(self.config.o.data),
|
||||
header.type.eq(0), # reserved
|
||||
),
|
||||
]
|
||||
|
||||
self.sync.rtio += [
|
||||
self.config.i.stb.eq(self.config.o.stb &
|
||||
self.config.o.address[-1]),
|
||||
self.config.i.data.eq(self.serializer.readback),
|
||||
]
|
|
@ -112,6 +112,12 @@ def peripheral_fastino(module, peripheral):
|
|||
eem.Fastino.add_std(module, peripheral["ports"][0])
|
||||
|
||||
|
||||
def peripheral_phaser(module, peripheral):
|
||||
if len(peripheral["ports"]) != 1:
|
||||
raise ValueError("wrong number of ports")
|
||||
eem.Phaser.add_std(module, peripheral["ports"][0])
|
||||
|
||||
|
||||
peripheral_processors = {
|
||||
"dio": peripheral_dio,
|
||||
"urukul": peripheral_urukul,
|
||||
|
@ -122,6 +128,7 @@ peripheral_processors = {
|
|||
"grabber": peripheral_grabber,
|
||||
"mirny": peripheral_mirny,
|
||||
"fastino": peripheral_fastino,
|
||||
"phaser": peripheral_phaser,
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue