2
0
mirror of https://github.com/m-labs/artiq.git synced 2024-12-29 05:03:34 +08:00

fastino: support wide RTIO interface and channel groups

This commit is contained in:
Robert Jördens 2020-03-05 16:13:40 +00:00
parent 8dbf30b23e
commit e803830b3b
3 changed files with 74 additions and 16 deletions

View File

@ -5,8 +5,9 @@ TODO: Example, describe update/hold
"""
from artiq.language.core import kernel, portable, delay
from artiq.coredevice.rtio import rtio_output, rtio_input_data
from artiq.coredevice.rtio import rtio_output, rtio_output_wide, rtio_input_data
from artiq.language.units import us
from artiq.language.types import TInt32, TList, TFloat
class Fastino:
@ -14,13 +15,19 @@ class Fastino:
:param channel: RTIO channel number
:param core_device: Core device name (default: "core")
:param log2_width: Width of DAC channel group (power of two,
see the RTIO PHY for details). If zero, the
:meth:`set_dac`/:meth:`set_dac_mu` interface must be used.
If non-zero, the :meth:`set_group`/:meth:`set_group_mu`
interface must be used. Value must match the corresponding value
in the RTIO PHY.
"""
kernel_invariants = {"core", "channel", "width"}
kernel_invariants = {"core", "channel"}
def __init__(self, dmgr, channel, core_device="core"):
def __init__(self, dmgr, channel, core_device="core", log2_width=0):
self.channel = channel << 8
self.core = dmgr.get(core_device)
self.width = 1 << log2_width
@kernel
def init(self):
@ -65,6 +72,21 @@ class Fastino:
"""
self.write(dac, data)
@kernel
def set_group_mu(self, dac: TInt32, data: TList(TInt32)):
"""Write a group of DAC channels in machine units.
:param dac: First channel in DAC channel group (0-31). The `log2_width`
LSBs must be zero.
:param data: List of DAC data pairs (2x16 bit unsigned) to write,
in machine units. Data exceeding group size is ignored.
If the list length is less than group size, the remaining
DAC channels within the group are cleared to 0 (machine units).
"""
if dac & (self.width - 1):
raise ValueError("Group index LSBs must be zero")
rtio_output_wide(self.channel | dac, data)
@portable
def voltage_to_mu(self, voltage):
"""Convert SI Volts to DAC machine units.
@ -74,6 +96,20 @@ class Fastino:
"""
return int(round((0x8000/10.)*voltage)) + 0x8000
@portable
def voltage_group_to_mu(self, voltage, data):
"""Convert SI Volts to packed DAC channel group machine units.
:param voltage: List of SI Volt voltages.
:param data: List of DAC channel data pairs to write to.
Half the length of `voltage`.
"""
for i in range(len(voltage)):
v = self.voltage_to_mu(voltage[i])
if i & 1:
v = data[i // 2] | (v << 16)
data[i // 2] = v
@kernel
def set_dac(self, dac, voltage):
"""Set DAC data to given voltage.
@ -83,6 +119,17 @@ class Fastino:
"""
self.write(dac, self.voltage_to_mu(voltage))
@kernel
def set_group(self, dac, voltage):
"""Set DAC group data to given voltage.
:param dac: DAC channel (0-31).
:param voltage: Desired output voltage.
"""
data = [int32(0)] * (len(voltage) // 2)
self.voltage_group_to_mu(voltage, data)
self.set_group_mu(dac, data)
@kernel
def update(self, update):
"""Schedule channels for update.

View File

@ -622,6 +622,7 @@ class Fastino(_EEM):
cls.add_extension(target, eem, iostandard=iostandard)
phy = fastino.Fastino(target.platform.request("fastino{}_ser_p".format(eem)),
target.platform.request("fastino{}_ser_n".format(eem)))
target.platform.request("fastino{}_ser_n".format(eem)),
log2_width=0)
target.submodules += phy
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

View File

@ -133,26 +133,36 @@ class SerDes(Module):
class Fastino(Module):
def __init__(self, pins, pins_n):
def __init__(self, pins, pins_n, log2_width=0):
width = 1 << log2_width
self.rtlink = rtlink.Interface(
rtlink.OInterface(data_width=32, address_width=8,
rtlink.OInterface(data_width=max(16*width, 32),
address_width=8,
enable_replace=False),
rtlink.IInterface(data_width=32))
self.submodules.serializer = SerDes(pins, pins_n)
# Support staging DAC data (in `dacs`) by writing to the
# 32 DAC RTIO addresses, if a channel is not "held" by its
# 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.
#
# This RTIO layout enables narrow RTIO words (32 bit
# compared to 512), efficient few-channel updates,
# least amount of DAC state tracking in kernels,
# 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(self.serializer.enable)
@ -174,12 +184,12 @@ class Fastino(Module):
# reserved
0x24: self.serializer.cfg[12:].eq(self.rtlink.o.data),
}
for i in range(len(self.serializer.dacs)):
for i in range(0, len(self.serializer.dacs), width):
cases[i] = [
self.serializer.dacs[i].eq(self.rtlink.o.data),
If(~hold[i],
self.serializer.enable[i].eq(1),
)
Cat(self.serializer.dacs[i:i + width]).eq(self.rtlink.o.data),
[If(~hold[i + j],
self.serializer.enable[i + j].eq(1),
) for j in range(width)]
]
self.sync.rio_phy += [