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.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.units import us
|
||||||
|
from artiq.language.types import TInt32, TList, TFloat
|
||||||
|
|
||||||
|
|
||||||
class Fastino:
|
class Fastino:
|
||||||
|
@ -14,13 +15,19 @@ class Fastino:
|
||||||
|
|
||||||
:param channel: RTIO channel number
|
:param channel: RTIO channel number
|
||||||
:param core_device: Core device name (default: "core")
|
: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", log2_width=0):
|
||||||
|
|
||||||
def __init__(self, dmgr, channel, core_device="core"):
|
|
||||||
self.channel = channel << 8
|
self.channel = channel << 8
|
||||||
self.core = dmgr.get(core_device)
|
self.core = dmgr.get(core_device)
|
||||||
|
self.width = 1 << log2_width
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def init(self):
|
def init(self):
|
||||||
|
@ -65,6 +72,21 @@ class Fastino:
|
||||||
"""
|
"""
|
||||||
self.write(dac, data)
|
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
|
@portable
|
||||||
def voltage_to_mu(self, voltage):
|
def voltage_to_mu(self, voltage):
|
||||||
"""Convert SI Volts to DAC machine units.
|
"""Convert SI Volts to DAC machine units.
|
||||||
|
@ -74,6 +96,20 @@ class Fastino:
|
||||||
"""
|
"""
|
||||||
return int(round((0x8000/10.)*voltage)) + 0x8000
|
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
|
@kernel
|
||||||
def set_dac(self, dac, voltage):
|
def set_dac(self, dac, voltage):
|
||||||
"""Set DAC data to given voltage.
|
"""Set DAC data to given voltage.
|
||||||
|
@ -83,6 +119,17 @@ class Fastino:
|
||||||
"""
|
"""
|
||||||
self.write(dac, self.voltage_to_mu(voltage))
|
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
|
@kernel
|
||||||
def update(self, update):
|
def update(self, update):
|
||||||
"""Schedule channels for update.
|
"""Schedule channels for update.
|
||||||
|
|
|
@ -622,6 +622,7 @@ class Fastino(_EEM):
|
||||||
cls.add_extension(target, eem, iostandard=iostandard)
|
cls.add_extension(target, eem, iostandard=iostandard)
|
||||||
|
|
||||||
phy = fastino.Fastino(target.platform.request("fastino{}_ser_p".format(eem)),
|
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.submodules += phy
|
||||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
|
@ -133,26 +133,36 @@ class SerDes(Module):
|
||||||
|
|
||||||
|
|
||||||
class Fastino(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(
|
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),
|
enable_replace=False),
|
||||||
rtlink.IInterface(data_width=32))
|
rtlink.IInterface(data_width=32))
|
||||||
|
|
||||||
self.submodules.serializer = SerDes(pins, pins_n)
|
self.submodules.serializer = SerDes(pins, pins_n)
|
||||||
|
|
||||||
# Support staging DAC data (in `dacs`) by writing to the
|
# 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.
|
# bit in `hold` the next frame will contain the update.
|
||||||
# For the DACs held, the update is triggered by setting the
|
# For the DACs held, the update is triggered by setting the
|
||||||
# corresponding bit in `update`. Update is self-clearing.
|
# corresponding bit in `update`. Update is self-clearing.
|
||||||
# This enables atomic DAC updates synchronized to a frame edge.
|
# This enables atomic DAC updates synchronized to a frame edge.
|
||||||
#
|
#
|
||||||
# This RTIO layout enables narrow RTIO words (32 bit
|
# The `log2_width=0` RTIO layout uses one DAC channel per RTIO address
|
||||||
# compared to 512), efficient few-channel updates,
|
# and a dense RTIO address space. The RTIO words are narrow.
|
||||||
# least amount of DAC state tracking in kernels,
|
# (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
|
# at the cost of more DMA and RTIO data ((n*(32+32+64) vs
|
||||||
# 32+32*16+64))
|
# 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)
|
hold = Signal.like(self.serializer.enable)
|
||||||
|
|
||||||
|
@ -174,12 +184,12 @@ class Fastino(Module):
|
||||||
# reserved
|
# reserved
|
||||||
0x24: self.serializer.cfg[12:].eq(self.rtlink.o.data),
|
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] = [
|
cases[i] = [
|
||||||
self.serializer.dacs[i].eq(self.rtlink.o.data),
|
Cat(self.serializer.dacs[i:i + width]).eq(self.rtlink.o.data),
|
||||||
If(~hold[i],
|
[If(~hold[i + j],
|
||||||
self.serializer.enable[i].eq(1),
|
self.serializer.enable[i + j].eq(1),
|
||||||
)
|
) for j in range(width)]
|
||||||
]
|
]
|
||||||
|
|
||||||
self.sync.rio_phy += [
|
self.sync.rio_phy += [
|
||||||
|
|
Loading…
Reference in New Issue