artiq/artiq/gateware/rtio/phy/fastino.py

129 lines
4.6 KiB
Python

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
from .fastlink import SerDes, SerInterface
class Fastino(Module):
def __init__(self, pins, pins_n, log2_width=0):
width = 1 << log2_width
self.rtlink = rtlink.Interface(
rtlink.OInterface(data_width=max(16*width, 32),
address_width=8,
enable_replace=False),
rtlink.IInterface(data_width=14))
self.submodules.serializer = SerDes(
n_data=8, t_clk=7, d_clk=0b1100011,
n_frame=14, n_crc=12, poly=0x80f)
self.submodules.intf = SerInterface(pins, pins_n)
self.comb += [
Cat(self.intf.data[:-1]).eq(Cat(self.serializer.data[:-1])),
self.serializer.data[-1].eq(self.intf.data[-1]),
]
# dac data words
dacs = [Signal(16) for i in range(32)]
header = Record([
("cfg", 4),
("leds", 8),
("typ", 1),
("reserved", 7),
("addr", 4),
("enable", len(dacs)),
])
assert len(Cat(header.raw_bits(), dacs)) == len(self.serializer.payload)
# # #
# Support staging DAC data (in `dacs`) by writing to the
# DAC RTIO addresses, if a channel is not "held" by its
# bit in `hold` the next frame will contain the update.
# For the DACs held, the update is triggered by setting the
# corresponding bit in `update`. Update is self-clearing.
# This enables atomic DAC updates synchronized to a frame edge.
#
# The `log2_width=0` RTIO layout uses one DAC channel per RTIO address
# and a dense RTIO address space. The RTIO words are narrow.
# (32 bit compared to 512) and few-channel updates are efficient.
# There is the least amount of DAC state tracking in kernels,
# at the cost of more DMA and RTIO data ((n*(32+32+64) vs
# 32+32*16+64))
#
# Other `log2_width` (up to `log2_width=5) settings pack multiple
# (in powers of two) DAC channels into one group and
# into one RTIO write.
# The RTIO data width increases accordingly. The `log2_width`
# LSBs of the RTIO address for a DAC channel write must be zero and the
# address space is sparse.
hold = Signal.like(header.enable)
continuous = Signal.like(header.enable)
cic_config = Signal(16)
read_regs = Array([Signal.like(self.serializer.readback)
for _ in range(1 << len(header.addr))])
cases = {
# update
0x20: [
header.enable.eq(self.rtlink.o.data),
header.typ.eq(0),
],
# hold
0x21: hold.eq(self.rtlink.o.data),
# cfg
0x22: header.cfg.eq(self.rtlink.o.data),
# leds
0x23: header.leds.eq(self.rtlink.o.data),
# reserved bits
0x24: header.reserved.eq(self.rtlink.o.data),
# force continuous DAC updates
0x25: continuous.eq(self.rtlink.o.data),
# interpolator configuration stage
0x26: cic_config.eq(self.rtlink.o.data),
# interpolator update flags
0x27: [
header.enable.eq(self.rtlink.o.data),
header.typ.eq(1),
],
}
for i in range(0, len(dacs), width):
cases[i] = [
Cat(dacs[i:i + width]).eq(self.rtlink.o.data),
[If(~hold[i + j] & (header.typ == 0),
header.enable[i + j].eq(1),
) for j in range(width)]
]
self.comb += [
If(header.typ == 0,
self.serializer.payload.eq(Cat(header.raw_bits(), dacs)),
).Else(
self.serializer.payload.eq(Cat(header.raw_bits(), Replicate(cic_config, len(dacs)))),
),
]
self.sync.rio_phy += [
If(self.serializer.stb,
header.typ.eq(0),
header.enable.eq(continuous),
read_regs[header.addr].eq(self.serializer.readback),
header.addr.eq(header.addr + 1),
),
If(self.rtlink.o.stb,
Case(self.rtlink.o.address, cases),
),
]
self.sync.rtio += [
self.rtlink.i.stb.eq(self.rtlink.o.stb &
self.rtlink.o.address[-1]),
self.rtlink.i.data.eq(
read_regs[self.rtlink.o.address[:-1]]),
]