mirror of https://github.com/m-labs/artiq.git
fastino: port to NAC3
This commit is contained in:
parent
64a0c4b29a
commit
a407007e0b
|
@ -3,13 +3,23 @@ streaming DAC.
|
||||||
"""
|
"""
|
||||||
from numpy import int32, int64
|
from numpy import int32, int64
|
||||||
|
|
||||||
from artiq.language.core import kernel, portable, delay, delay_mu
|
from artiq.language.core import nac3, kernel, portable, KernelInvariant
|
||||||
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
|
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
|
||||||
rtio_input_data)
|
rtio_input_data)
|
||||||
from artiq.language.units import ns
|
from artiq.language.units import ns
|
||||||
from artiq.language.types import TInt32, TList
|
from artiq.coredevice.core import Core
|
||||||
|
|
||||||
|
|
||||||
|
# NAC3TODO work around https://git.m-labs.hk/M-Labs/nac3/issues/189
|
||||||
|
@nac3
|
||||||
|
class ValueError(Exception):
|
||||||
|
pass
|
||||||
|
@nac3
|
||||||
|
class NotImplementedError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@nac3
|
||||||
class Fastino:
|
class Fastino:
|
||||||
"""Fastino 32-channel, 16-bit, 2.5 MS/s per channel streaming DAC
|
"""Fastino 32-channel, 16-bit, 2.5 MS/s per channel streaming DAC
|
||||||
|
|
||||||
|
@ -41,7 +51,11 @@ class Fastino:
|
||||||
:param log2_width: Width of DAC channel group (logarithm base 2).
|
:param log2_width: Width of DAC channel group (logarithm base 2).
|
||||||
Value must match the corresponding value in the RTIO PHY (gateware).
|
Value must match the corresponding value in the RTIO PHY (gateware).
|
||||||
"""
|
"""
|
||||||
kernel_invariants = {"core", "channel", "width", "t_frame"}
|
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
channel: KernelInvariant[int32]
|
||||||
|
width: KernelInvariant[int32]
|
||||||
|
t_frame: KernelInvariant[int64]
|
||||||
|
|
||||||
def __init__(self, dmgr, channel, core_device="core", log2_width=0):
|
def __init__(self, dmgr, channel, core_device="core", log2_width=0):
|
||||||
self.channel = channel << 8
|
self.channel = channel << 8
|
||||||
|
@ -69,15 +83,15 @@ class Fastino:
|
||||||
Note: On Fastino gateware before v0.2 this may lead to 0 voltage being emitted
|
Note: On Fastino gateware before v0.2 this may lead to 0 voltage being emitted
|
||||||
transiently.
|
transiently.
|
||||||
"""
|
"""
|
||||||
self.set_cfg(reset=0, afe_power_down=0, dac_clr=0, clr_err=1)
|
self.set_cfg(reset=False, afe_power_down=False, dac_clr=False, clr_err=True)
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
self.set_cfg(reset=0, afe_power_down=0, dac_clr=0, clr_err=0)
|
self.set_cfg(reset=False, afe_power_down=False, dac_clr=False, clr_err=False)
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
self.set_continuous(0)
|
self.set_continuous(0)
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
self.stage_cic(1)
|
self.stage_cic(1)
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
self.apply_cic(0xffffffff)
|
self.apply_cic(int32(int64(0xffffffff)))
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
self.set_leds(0)
|
self.set_leds(0)
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
|
@ -85,7 +99,7 @@ class Fastino:
|
||||||
delay_mu(self.t_frame)
|
delay_mu(self.t_frame)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write(self, addr, data):
|
def write(self, addr: int32, data: int32):
|
||||||
"""Write data to a Fastino register.
|
"""Write data to a Fastino register.
|
||||||
|
|
||||||
:param addr: Address to write to.
|
:param addr: Address to write to.
|
||||||
|
@ -94,7 +108,7 @@ class Fastino:
|
||||||
rtio_output(self.channel | addr, data)
|
rtio_output(self.channel | addr, data)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def read(self, addr):
|
def read(self, addr: int32):
|
||||||
"""Read from Fastino register.
|
"""Read from Fastino register.
|
||||||
|
|
||||||
TODO: untested
|
TODO: untested
|
||||||
|
@ -102,12 +116,12 @@ class Fastino:
|
||||||
:param addr: Address to read from.
|
:param addr: Address to read from.
|
||||||
:return: The data read.
|
:return: The data read.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError()
|
||||||
# rtio_output(self.channel | addr | 0x80)
|
# rtio_output(self.channel | addr | 0x80)
|
||||||
# return rtio_input_data(self.channel >> 8)
|
# return rtio_input_data(self.channel >> 8)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_dac_mu(self, dac, data):
|
def set_dac_mu(self, dac: int32, data: int32):
|
||||||
"""Write DAC data in machine units.
|
"""Write DAC data in machine units.
|
||||||
|
|
||||||
:param dac: DAC channel to write to (0-31).
|
:param dac: DAC channel to write to (0-31).
|
||||||
|
@ -117,7 +131,7 @@ class Fastino:
|
||||||
self.write(dac, data)
|
self.write(dac, data)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_group_mu(self, dac: TInt32, data: TList(TInt32)):
|
def set_group_mu(self, dac: int32, data: list[int32]):
|
||||||
"""Write a group of DAC channels in machine units.
|
"""Write a group of DAC channels in machine units.
|
||||||
|
|
||||||
:param dac: First channel in DAC channel group (0-31). The `log2_width`
|
:param dac: First channel in DAC channel group (0-31). The `log2_width`
|
||||||
|
@ -127,24 +141,24 @@ class Fastino:
|
||||||
If the list length is less than group size, the remaining
|
If the list length is less than group size, the remaining
|
||||||
DAC channels within the group are cleared to 0 (machine units).
|
DAC channels within the group are cleared to 0 (machine units).
|
||||||
"""
|
"""
|
||||||
if dac & (self.width - 1):
|
if dac & (self.width - 1) != 0:
|
||||||
raise ValueError("Group index LSBs must be zero")
|
raise ValueError("Group index LSBs must be zero")
|
||||||
rtio_output_wide(self.channel | dac, data)
|
rtio_output_wide(self.channel | dac, data)
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def voltage_to_mu(self, voltage):
|
def voltage_to_mu(self, voltage: float) -> int32:
|
||||||
"""Convert SI Volts to DAC machine units.
|
"""Convert SI Volts to DAC machine units.
|
||||||
|
|
||||||
:param voltage: Voltage in SI Volts.
|
:param voltage: Voltage in SI Volts.
|
||||||
:return: DAC data word in machine units, 16 bit integer.
|
:return: DAC data word in machine units, 16 bit integer.
|
||||||
"""
|
"""
|
||||||
data = int32(round((0x8000/10.)*voltage)) + int32(0x8000)
|
data = int32(round((float(0x8000)/10.)*voltage)) + int32(0x8000)
|
||||||
if data < 0 or data > 0xffff:
|
if data < 0 or data > 0xffff:
|
||||||
raise ValueError("DAC voltage out of bounds")
|
raise ValueError("DAC voltage out of bounds")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def voltage_group_to_mu(self, voltage, data):
|
def voltage_group_to_mu(self, voltage: list[float], data: list[int32]):
|
||||||
"""Convert SI Volts to packed DAC channel group machine units.
|
"""Convert SI Volts to packed DAC channel group machine units.
|
||||||
|
|
||||||
:param voltage: List of SI Volt voltages.
|
:param voltage: List of SI Volt voltages.
|
||||||
|
@ -153,12 +167,12 @@ class Fastino:
|
||||||
"""
|
"""
|
||||||
for i in range(len(voltage)):
|
for i in range(len(voltage)):
|
||||||
v = self.voltage_to_mu(voltage[i])
|
v = self.voltage_to_mu(voltage[i])
|
||||||
if i & 1:
|
if i & 1 != 0:
|
||||||
v = data[i // 2] | (v << 16)
|
v = data[i // 2] | (v << 16)
|
||||||
data[i // 2] = int32(v)
|
data[i // 2] = int32(v)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_dac(self, dac, voltage):
|
def set_dac(self, dac: int32, voltage: float):
|
||||||
"""Set DAC data to given voltage.
|
"""Set DAC data to given voltage.
|
||||||
|
|
||||||
:param dac: DAC channel (0-31).
|
:param dac: DAC channel (0-31).
|
||||||
|
@ -167,18 +181,18 @@ class Fastino:
|
||||||
self.write(dac, self.voltage_to_mu(voltage))
|
self.write(dac, self.voltage_to_mu(voltage))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_group(self, dac, voltage):
|
def set_group(self, dac: int32, voltage: list[float]):
|
||||||
"""Set DAC group data to given voltage.
|
"""Set DAC group data to given voltage.
|
||||||
|
|
||||||
:param dac: DAC channel (0-31).
|
:param dac: DAC channel (0-31).
|
||||||
:param voltage: Desired output voltage.
|
:param voltage: Desired output voltage.
|
||||||
"""
|
"""
|
||||||
data = [int32(0)] * (len(voltage) // 2)
|
data = [int32(0) for _ in range(len(voltage) // 2)]
|
||||||
self.voltage_group_to_mu(voltage, data)
|
self.voltage_group_to_mu(voltage, data)
|
||||||
self.set_group_mu(dac, data)
|
self.set_group_mu(dac, data)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def update(self, update):
|
def update(self, update: int32):
|
||||||
"""Schedule channels for update.
|
"""Schedule channels for update.
|
||||||
|
|
||||||
:param update: Bit mask of channels to update (32 bit).
|
:param update: Bit mask of channels to update (32 bit).
|
||||||
|
@ -186,7 +200,7 @@ class Fastino:
|
||||||
self.write(0x20, update)
|
self.write(0x20, update)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_hold(self, hold):
|
def set_hold(self, hold: int32):
|
||||||
"""Set channels to manual update.
|
"""Set channels to manual update.
|
||||||
|
|
||||||
:param hold: Bit mask of channels to hold (32 bit).
|
:param hold: Bit mask of channels to hold (32 bit).
|
||||||
|
@ -194,7 +208,7 @@ class Fastino:
|
||||||
self.write(0x21, hold)
|
self.write(0x21, hold)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_cfg(self, reset=0, afe_power_down=0, dac_clr=0, clr_err=0):
|
def set_cfg(self, reset: bool = False, afe_power_down: bool = False, dac_clr: bool = False, clr_err: bool = False):
|
||||||
"""Set configuration bits.
|
"""Set configuration bits.
|
||||||
|
|
||||||
:param reset: Reset SPI PLL and SPI clock domain.
|
:param reset: Reset SPI PLL and SPI clock domain.
|
||||||
|
@ -205,11 +219,11 @@ class Fastino:
|
||||||
This clears the sticky red error LED. Must be cleared to enable
|
This clears the sticky red error LED. Must be cleared to enable
|
||||||
error counting.
|
error counting.
|
||||||
"""
|
"""
|
||||||
self.write(0x22, (reset << 0) | (afe_power_down << 1) |
|
self.write(0x22, (int32(reset) << 0) | (int32(afe_power_down) << 1) |
|
||||||
(dac_clr << 2) | (clr_err << 3))
|
(int32(dac_clr) << 2) | (int32(clr_err) << 3))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_leds(self, leds):
|
def set_leds(self, leds: int32):
|
||||||
"""Set the green user-defined LEDs
|
"""Set the green user-defined LEDs
|
||||||
|
|
||||||
:param leds: LED status, 8 bit integer each bit corresponding to one
|
:param leds: LED status, 8 bit integer each bit corresponding to one
|
||||||
|
@ -218,14 +232,14 @@ class Fastino:
|
||||||
self.write(0x23, leds)
|
self.write(0x23, leds)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_continuous(self, channel_mask):
|
def set_continuous(self, channel_mask: int32):
|
||||||
"""Enable continuous DAC updates on channels regardless of new data
|
"""Enable continuous DAC updates on channels regardless of new data
|
||||||
being submitted.
|
being submitted.
|
||||||
"""
|
"""
|
||||||
self.write(0x25, channel_mask)
|
self.write(0x25, channel_mask)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def stage_cic_mu(self, rate_mantissa, rate_exponent, gain_exponent):
|
def stage_cic_mu(self, rate_mantissa: int32, rate_exponent: int32, gain_exponent: int32):
|
||||||
"""Stage machine unit CIC interpolator configuration.
|
"""Stage machine unit CIC interpolator configuration.
|
||||||
"""
|
"""
|
||||||
if rate_mantissa < 0 or rate_mantissa >= 1 << 6:
|
if rate_mantissa < 0 or rate_mantissa >= 1 << 6:
|
||||||
|
@ -238,7 +252,7 @@ class Fastino:
|
||||||
self.write(0x26, config)
|
self.write(0x26, config)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def stage_cic(self, rate) -> TInt32:
|
def stage_cic(self, rate: int32) -> int32:
|
||||||
"""Compute and stage interpolator configuration.
|
"""Compute and stage interpolator configuration.
|
||||||
|
|
||||||
This method approximates the desired interpolation rate using a 10 bit
|
This method approximates the desired interpolation rate using a 10 bit
|
||||||
|
@ -269,12 +283,12 @@ class Fastino:
|
||||||
while gain > 1 << gain_exponent:
|
while gain > 1 << gain_exponent:
|
||||||
gain_exponent += 1
|
gain_exponent += 1
|
||||||
gain_exponent += order*rate_exponent
|
gain_exponent += order*rate_exponent
|
||||||
assert gain_exponent <= order*16
|
# NAC3TODO assert gain_exponent <= order*16
|
||||||
self.stage_cic_mu(rate_mantissa - 1, rate_exponent, gain_exponent)
|
self.stage_cic_mu(rate_mantissa - 1, rate_exponent, gain_exponent)
|
||||||
return rate_mantissa << rate_exponent
|
return rate_mantissa << rate_exponent
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def apply_cic(self, channel_mask):
|
def apply_cic(self, channel_mask: int32):
|
||||||
"""Apply the staged interpolator configuration on the specified channels.
|
"""Apply the staged interpolator configuration on the specified channels.
|
||||||
|
|
||||||
Each Fastino channel starting with gateware v0.2 includes a fourth order
|
Each Fastino channel starting with gateware v0.2 includes a fourth order
|
||||||
|
|
|
@ -36,6 +36,10 @@
|
||||||
{
|
{
|
||||||
"type": "grabber",
|
"type": "grabber",
|
||||||
"ports": [6]
|
"ports": [6]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "fastino",
|
||||||
|
"ports": [7]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ from artiq.coredevice.ad9912 import AD9912
|
||||||
from artiq.coredevice.sampler import Sampler
|
from artiq.coredevice.sampler import Sampler
|
||||||
from artiq.coredevice.edge_counter import EdgeCounter
|
from artiq.coredevice.edge_counter import EdgeCounter
|
||||||
from artiq.coredevice.grabber import Grabber
|
from artiq.coredevice.grabber import Grabber
|
||||||
|
from artiq.coredevice.fastino import Fastino
|
||||||
|
|
||||||
|
|
||||||
@nac3
|
@nac3
|
||||||
|
@ -23,6 +24,7 @@ class NAC3Devices(EnvExperiment):
|
||||||
sampler0: KernelInvariant[Sampler]
|
sampler0: KernelInvariant[Sampler]
|
||||||
ttl0_counter: KernelInvariant[EdgeCounter]
|
ttl0_counter: KernelInvariant[EdgeCounter]
|
||||||
grabber0: KernelInvariant[Grabber]
|
grabber0: KernelInvariant[Grabber]
|
||||||
|
fastino0: KernelInvariant[Fastino]
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.setattr_device("core")
|
self.setattr_device("core")
|
||||||
|
@ -35,6 +37,7 @@ class NAC3Devices(EnvExperiment):
|
||||||
self.setattr_device("sampler0")
|
self.setattr_device("sampler0")
|
||||||
self.setattr_device("ttl0_counter")
|
self.setattr_device("ttl0_counter")
|
||||||
self.setattr_device("grabber0")
|
self.setattr_device("grabber0")
|
||||||
|
self.setattr_device("fastino0")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
Loading…
Reference in New Issue