forked from M-Labs/artiq
fastino: support wide RTIO interface and channel groups
This commit is contained in:
parent
8dbf30b23e
commit
e803830b3b
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 += [
|
||||
|
|
Loading…
Reference in New Issue