forked from M-Labs/artiq
Merge branch 'master' into nac3
This commit is contained in:
commit
60aa64b79e
|
@ -15,6 +15,8 @@ Highlights:
|
||||||
- Sampler: fixed ADC MU to Volt conversion factor for Sampler v2.2+.
|
- Sampler: fixed ADC MU to Volt conversion factor for Sampler v2.2+.
|
||||||
For earlier hardware versions, specify the hardware version in the device
|
For earlier hardware versions, specify the hardware version in the device
|
||||||
database file (e.g. ``"hw_rev": "v2.1"``) to use the correct conversion factor.
|
database file (e.g. ``"hw_rev": "v2.1"``) to use the correct conversion factor.
|
||||||
|
- Almazny v1.2. It is incompatible with the legacy versions and is the default. To use legacy
|
||||||
|
versions, specify ``almazny_hw_rev`` in the JSON description.
|
||||||
- Metlino and Sayma support has been dropped due to complications with synchronous RTIO clocking.
|
- Metlino and Sayma support has been dropped due to complications with synchronous RTIO clocking.
|
||||||
* CPU (on softcore platforms) and AXI bus (on Zynq) are now clocked synchronously with the RTIO
|
* CPU (on softcore platforms) and AXI bus (on Zynq) are now clocked synchronously with the RTIO
|
||||||
clock, to facilitate implementation of local processing on DRTIO satellites, and to slightly
|
clock, to facilitate implementation of local processing on DRTIO satellites, and to slightly
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
from numpy import int32
|
||||||
|
|
||||||
|
from artiq.language.core import nac3, Kernel, KernelInvariant, kernel, portable
|
||||||
|
from artiq.language.units import us
|
||||||
|
|
||||||
|
from artiq.coredevice.core import Core
|
||||||
|
from artiq.coredevice.mirny import Mirny
|
||||||
|
from artiq.coredevice.spi2 import *
|
||||||
|
|
||||||
|
|
||||||
|
# almazny-specific data
|
||||||
|
ALMAZNY_LEGACY_REG_BASE = 0x0C
|
||||||
|
ALMAZNY_LEGACY_OE_SHIFT = 12
|
||||||
|
|
||||||
|
# higher SPI write divider to match almazny shift register timing
|
||||||
|
# min SER time before SRCLK rise = 125ns
|
||||||
|
# -> div=32 gives 125ns for data before clock rise
|
||||||
|
# works at faster dividers too but could be less reliable
|
||||||
|
ALMAZNY_LEGACY_SPIT_WR = 32
|
||||||
|
|
||||||
|
|
||||||
|
@nac3
|
||||||
|
class AlmaznyLegacy:
|
||||||
|
"""
|
||||||
|
Almazny (High frequency mezzanine board for Mirny)
|
||||||
|
|
||||||
|
This applies to Almazny hardware v1.1 and earlier.
|
||||||
|
Use :class:`artiq.coredevice.almazny.AlmaznyChannel` for Almazny v1.2 and later.
|
||||||
|
|
||||||
|
:param host_mirny - Mirny device Almazny is connected to
|
||||||
|
"""
|
||||||
|
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
mirny_cpld: KernelInvariant[Mirny]
|
||||||
|
att_mu: Kernel[list[int32]]
|
||||||
|
channel_sw: Kernel[list[int32]]
|
||||||
|
output_enable: Kernel[bool]
|
||||||
|
|
||||||
|
def __init__(self, dmgr, host_mirny):
|
||||||
|
self.mirny_cpld = dmgr.get(host_mirny)
|
||||||
|
self.core = self.mirny_cpld.core
|
||||||
|
self.att_mu = [0x3f] * 4
|
||||||
|
self.channel_sw = [0] * 4
|
||||||
|
self.output_enable = False
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def init(self):
|
||||||
|
self.output_toggle(self.output_enable)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def att_to_mu(self, att: float) -> int32:
|
||||||
|
"""
|
||||||
|
Convert an attenuator setting in dB to machine units.
|
||||||
|
|
||||||
|
:param att: attenuator setting in dB [0-31.5]
|
||||||
|
:return: attenuator setting in machine units
|
||||||
|
"""
|
||||||
|
mu = round(att * 2.0)
|
||||||
|
if mu > 63 or mu < 0:
|
||||||
|
raise ValueError("Invalid Almazny attenuator settings!")
|
||||||
|
return mu
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def mu_to_att(self, att_mu: int32) -> float:
|
||||||
|
"""
|
||||||
|
Convert a digital attenuator setting to dB.
|
||||||
|
|
||||||
|
:param att_mu: attenuator setting in machine units
|
||||||
|
:return: attenuator setting in dB
|
||||||
|
"""
|
||||||
|
return float(att_mu) / 2.
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_att(self, channel: int32, att: float, rf_switch: bool = True):
|
||||||
|
"""
|
||||||
|
Sets attenuators on chosen shift register (channel).
|
||||||
|
:param channel - index of the register [0-3]
|
||||||
|
:param att_mu - attenuation setting in dBm [0-31.5]
|
||||||
|
:param rf_switch - rf switch (bool)
|
||||||
|
"""
|
||||||
|
self.set_att_mu(channel, self.att_to_mu(att), rf_switch)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_att_mu(self, channel: int32, att_mu: int32, rf_switch: bool = True):
|
||||||
|
"""
|
||||||
|
Sets attenuators on chosen shift register (channel).
|
||||||
|
:param channel - index of the register [0-3]
|
||||||
|
:param att_mu - attenuation setting in machine units [0-63]
|
||||||
|
:param rf_switch - rf switch (bool)
|
||||||
|
"""
|
||||||
|
self.channel_sw[channel] = 1 if rf_switch else 0
|
||||||
|
self.att_mu[channel] = att_mu
|
||||||
|
self._update_register(channel)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def output_toggle(self, oe: bool):
|
||||||
|
"""
|
||||||
|
Toggles output on all shift registers on or off.
|
||||||
|
:param oe - toggle output enable (bool)
|
||||||
|
"""
|
||||||
|
self.output_enable = oe
|
||||||
|
cfg_reg = self.mirny_cpld.read_reg(1)
|
||||||
|
en = 1 if self.output_enable else 0
|
||||||
|
self.core.delay(100. * us)
|
||||||
|
new_reg = (en << ALMAZNY_LEGACY_OE_SHIFT) | (cfg_reg & 0x3FF)
|
||||||
|
self.mirny_cpld.write_reg(1, new_reg)
|
||||||
|
self.core.delay(100. * us)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def _flip_mu_bits(self, mu: int32) -> int32:
|
||||||
|
# in this form MSB is actually 0.5dB attenuator
|
||||||
|
# unnatural for users, so we flip the six bits
|
||||||
|
return (((mu & 0x01) << 5)
|
||||||
|
| ((mu & 0x02) << 3)
|
||||||
|
| ((mu & 0x04) << 1)
|
||||||
|
| ((mu & 0x08) >> 1)
|
||||||
|
| ((mu & 0x10) >> 3)
|
||||||
|
| ((mu & 0x20) >> 5))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def _update_register(self, ch: int32):
|
||||||
|
self.mirny_cpld.write_ext(
|
||||||
|
ALMAZNY_LEGACY_REG_BASE + ch,
|
||||||
|
8,
|
||||||
|
self._flip_mu_bits(self.att_mu[ch]) | (self.channel_sw[ch] << 6),
|
||||||
|
ALMAZNY_LEGACY_SPIT_WR
|
||||||
|
)
|
||||||
|
self.core.delay(100. * us)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def _update_all_registers(self):
|
||||||
|
for i in range(4):
|
||||||
|
self._update_register(i)
|
||||||
|
|
||||||
|
|
||||||
|
@nac3
|
||||||
|
class AlmaznyChannel:
|
||||||
|
"""
|
||||||
|
One Almazny channel
|
||||||
|
Almazny is a mezzanine for the Quad PLL RF source Mirny that exposes and
|
||||||
|
controls the frequency-doubled outputs.
|
||||||
|
This driver requires Almazny hardware revision v1.2 or later
|
||||||
|
and Mirny CPLD gateware v0.3 or later.
|
||||||
|
Use :class:`artiq.coredevice.almazny.AlmaznyLegacy` for Almazny hardware v1.1 and earlier.
|
||||||
|
:param host_mirny: Mirny CPLD device name
|
||||||
|
:param channel: channel index (0-3)
|
||||||
|
"""
|
||||||
|
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
mirny_cpld: KernelInvariant[Mirny]
|
||||||
|
channel: KernelInvariant[int32]
|
||||||
|
|
||||||
|
def __init__(self, dmgr, host_mirny, channel):
|
||||||
|
self.mirny_cpld = dmgr.get(host_mirny)
|
||||||
|
self.core = self.mirny_cpld.core
|
||||||
|
self.channel = channel
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def to_mu(self, att: float, enable: bool, led: bool) -> int32:
|
||||||
|
"""
|
||||||
|
Convert an attenuation in dB, RF switch state and LED state to machine
|
||||||
|
units.
|
||||||
|
:param att: attenuator setting in dB (0-31.5)
|
||||||
|
:param enable: RF switch state (bool)
|
||||||
|
:param led: LED state (bool)
|
||||||
|
:return: channel setting in machine units
|
||||||
|
"""
|
||||||
|
mu = round(att * 2.)
|
||||||
|
if mu >= 64 or mu < 0:
|
||||||
|
raise ValueError("Attenuation out of range")
|
||||||
|
# unfortunate hardware design: bit reverse
|
||||||
|
mu = ((mu & 0x15) << 1) | ((mu >> 1) & 0x15)
|
||||||
|
mu = ((mu & 0x03) << 4) | (mu & 0x0c) | ((mu >> 4) & 0x03)
|
||||||
|
if enable:
|
||||||
|
mu |= 1 << 6
|
||||||
|
if led:
|
||||||
|
mu |= 1 << 7
|
||||||
|
return mu
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_mu(self, mu: int32):
|
||||||
|
"""
|
||||||
|
Set channel state (machine units).
|
||||||
|
:param mu: channel state in machine units.
|
||||||
|
"""
|
||||||
|
self.mirny_cpld.write_ext(
|
||||||
|
addr=0xc + self.channel, length=8, data=mu, ext_div=32)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set(self, att: float, enable: bool, led: bool = False):
|
||||||
|
"""
|
||||||
|
Set attenuation, RF switch, and LED state (SI units).
|
||||||
|
:param att: attenuator setting in dB (0-31.5)
|
||||||
|
:param enable: RF switch state (bool)
|
||||||
|
:param led: LED state (bool)
|
||||||
|
"""
|
||||||
|
self.set_mu(self.to_mu(att, enable, led))
|
|
@ -464,6 +464,11 @@
|
||||||
"almazny": {
|
"almazny": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"almazny_hw_rev": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^v[0-9]+\\.[0-9]+",
|
||||||
|
"default": "v1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["ports"]
|
"required": ["ports"]
|
||||||
|
|
|
@ -32,16 +32,6 @@ WE = 1 << 24
|
||||||
# supported CPLD code version
|
# supported CPLD code version
|
||||||
PROTO_REV_MATCH = 0x0
|
PROTO_REV_MATCH = 0x0
|
||||||
|
|
||||||
# almazny-specific data
|
|
||||||
ALMAZNY_REG_BASE = 0x0C
|
|
||||||
ALMAZNY_OE_SHIFT = 12
|
|
||||||
|
|
||||||
# higher SPI write divider to match almazny shift register timing
|
|
||||||
# min SER time before SRCLK rise = 125ns
|
|
||||||
# -> div=32 gives 125ns for data before clock rise
|
|
||||||
# works at faster dividers too but could be less reliable
|
|
||||||
ALMAZNY_SPIT_WR = 32
|
|
||||||
|
|
||||||
|
|
||||||
@nac3
|
@nac3
|
||||||
class Mirny:
|
class Mirny:
|
||||||
|
@ -183,114 +173,3 @@ class Mirny:
|
||||||
if length < 32:
|
if length < 32:
|
||||||
data <<= 32 - length
|
data <<= 32 - length
|
||||||
self.bus.write(data)
|
self.bus.write(data)
|
||||||
|
|
||||||
|
|
||||||
@nac3
|
|
||||||
class Almazny:
|
|
||||||
"""
|
|
||||||
Almazny (High frequency mezzanine board for Mirny)
|
|
||||||
|
|
||||||
:param host_mirny - Mirny device Almazny is connected to
|
|
||||||
"""
|
|
||||||
|
|
||||||
core: KernelInvariant[Core]
|
|
||||||
mirny_cpld: KernelInvariant[Mirny]
|
|
||||||
att_mu: Kernel[list[int32]]
|
|
||||||
channel_sw: Kernel[list[int32]]
|
|
||||||
output_enable: Kernel[bool]
|
|
||||||
|
|
||||||
def __init__(self, dmgr, host_mirny):
|
|
||||||
self.mirny_cpld = dmgr.get(host_mirny)
|
|
||||||
self.core = self.mirny_cpld.core
|
|
||||||
self.att_mu = [0x3f] * 4
|
|
||||||
self.channel_sw = [0] * 4
|
|
||||||
self.output_enable = False
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def init(self):
|
|
||||||
self.output_toggle(self.output_enable)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def att_to_mu(self, att: float) -> int32:
|
|
||||||
"""
|
|
||||||
Convert an attenuator setting in dB to machine units.
|
|
||||||
|
|
||||||
:param att: attenuator setting in dB [0-31.5]
|
|
||||||
:return: attenuator setting in machine units
|
|
||||||
"""
|
|
||||||
mu = round(att * 2.0)
|
|
||||||
if mu > 63 or mu < 0:
|
|
||||||
raise ValueError("Invalid Almazny attenuator settings!")
|
|
||||||
return mu
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def mu_to_att(self, att_mu: int32) -> float:
|
|
||||||
"""
|
|
||||||
Convert a digital attenuator setting to dB.
|
|
||||||
|
|
||||||
:param att_mu: attenuator setting in machine units
|
|
||||||
:return: attenuator setting in dB
|
|
||||||
"""
|
|
||||||
return float(att_mu) / 2.
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_att(self, channel: int32, att: float, rf_switch: bool = True):
|
|
||||||
"""
|
|
||||||
Sets attenuators on chosen shift register (channel).
|
|
||||||
:param channel - index of the register [0-3]
|
|
||||||
:param att_mu - attenuation setting in dBm [0-31.5]
|
|
||||||
:param rf_switch - rf switch (bool)
|
|
||||||
"""
|
|
||||||
self.set_att_mu(channel, self.att_to_mu(att), rf_switch)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_att_mu(self, channel: int32, att_mu: int32, rf_switch: bool = True):
|
|
||||||
"""
|
|
||||||
Sets attenuators on chosen shift register (channel).
|
|
||||||
:param channel - index of the register [0-3]
|
|
||||||
:param att_mu - attenuation setting in machine units [0-63]
|
|
||||||
:param rf_switch - rf switch (bool)
|
|
||||||
"""
|
|
||||||
self.channel_sw[channel] = 1 if rf_switch else 0
|
|
||||||
self.att_mu[channel] = att_mu
|
|
||||||
self._update_register(channel)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def output_toggle(self, oe: bool):
|
|
||||||
"""
|
|
||||||
Toggles output on all shift registers on or off.
|
|
||||||
:param oe - toggle output enable (bool)
|
|
||||||
"""
|
|
||||||
self.output_enable = oe
|
|
||||||
cfg_reg = self.mirny_cpld.read_reg(1)
|
|
||||||
en = 1 if self.output_enable else 0
|
|
||||||
self.core.delay(100. * us)
|
|
||||||
new_reg = (en << ALMAZNY_OE_SHIFT) | (cfg_reg & 0x3FF)
|
|
||||||
self.mirny_cpld.write_reg(1, new_reg)
|
|
||||||
self.core.delay(100. * us)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def _flip_mu_bits(self, mu: int32) -> int32:
|
|
||||||
# in this form MSB is actually 0.5dB attenuator
|
|
||||||
# unnatural for users, so we flip the six bits
|
|
||||||
return (((mu & 0x01) << 5)
|
|
||||||
| ((mu & 0x02) << 3)
|
|
||||||
| ((mu & 0x04) << 1)
|
|
||||||
| ((mu & 0x08) >> 1)
|
|
||||||
| ((mu & 0x10) >> 3)
|
|
||||||
| ((mu & 0x20) >> 5))
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def _update_register(self, ch: int32):
|
|
||||||
self.mirny_cpld.write_ext(
|
|
||||||
ALMAZNY_REG_BASE + ch,
|
|
||||||
8,
|
|
||||||
self._flip_mu_bits(self.att_mu[ch]) | (self.channel_sw[ch] << 6),
|
|
||||||
ALMAZNY_SPIT_WR
|
|
||||||
)
|
|
||||||
self.core.delay(100. * us)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def _update_all_registers(self):
|
|
||||||
for i in range(4):
|
|
||||||
self._update_register(i)
|
|
||||||
|
|
|
@ -287,6 +287,7 @@ class PeripheralManager:
|
||||||
return next(channel)
|
return next(channel)
|
||||||
|
|
||||||
def process_mirny(self, rtio_offset, peripheral):
|
def process_mirny(self, rtio_offset, peripheral):
|
||||||
|
legacy_almazny = ("v1.0", "v1.1")
|
||||||
mirny_name = self.get_name("mirny")
|
mirny_name = self.get_name("mirny")
|
||||||
channel = count(0)
|
channel = count(0)
|
||||||
self.gen("""
|
self.gen("""
|
||||||
|
@ -326,6 +327,20 @@ class PeripheralManager:
|
||||||
name=mirny_name,
|
name=mirny_name,
|
||||||
mchn=i)
|
mchn=i)
|
||||||
|
|
||||||
|
if peripheral["almazny"] and peripheral["almazny_hw_rev"] not in legacy_almazny:
|
||||||
|
self.gen("""
|
||||||
|
device_db["{name}_almazny{i}"] = {{
|
||||||
|
"type": "local",
|
||||||
|
"module": "artiq.coredevice.almazny",
|
||||||
|
"class": "AlmaznyChannel",
|
||||||
|
"arguments": {{
|
||||||
|
"cpld_device": "{name}_cpld",
|
||||||
|
"channel": {i},
|
||||||
|
}},
|
||||||
|
}}""",
|
||||||
|
name=mirny_name,
|
||||||
|
i=i)
|
||||||
|
|
||||||
clk_sel = peripheral["clk_sel"]
|
clk_sel = peripheral["clk_sel"]
|
||||||
if isinstance(peripheral["clk_sel"], str):
|
if isinstance(peripheral["clk_sel"], str):
|
||||||
clk_sel = '"' + peripheral["clk_sel"] + '"'
|
clk_sel = '"' + peripheral["clk_sel"] + '"'
|
||||||
|
@ -343,13 +358,12 @@ class PeripheralManager:
|
||||||
name=mirny_name,
|
name=mirny_name,
|
||||||
refclk=peripheral["refclk"],
|
refclk=peripheral["refclk"],
|
||||||
clk_sel=clk_sel)
|
clk_sel=clk_sel)
|
||||||
almazny = peripheral.get("almazny", False)
|
if peripheral["almazny"] and peripheral["almazny_hw_rev"] in legacy_almazny:
|
||||||
if almazny:
|
|
||||||
self.gen("""
|
self.gen("""
|
||||||
device_db["{name}_almazny"] = {{
|
device_db["{name}_almazny"] = {{
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.mirny",
|
"module": "artiq.coredevice.almazny",
|
||||||
"class": "Almazny",
|
"class": "AlmaznyLegacy",
|
||||||
"arguments": {{
|
"arguments": {{
|
||||||
"host_mirny": "{name}_cpld",
|
"host_mirny": "{name}_cpld",
|
||||||
}},
|
}},
|
||||||
|
|
|
@ -115,7 +115,7 @@ class SinaraTester(EnvExperiment):
|
||||||
self.suservos[name] = self.get_device(name)
|
self.suservos[name] = self.get_device(name)
|
||||||
elif (module, cls) == ("artiq.coredevice.suservo", "Channel"):
|
elif (module, cls) == ("artiq.coredevice.suservo", "Channel"):
|
||||||
self.suschannels[name] = self.get_device(name)
|
self.suschannels[name] = self.get_device(name)
|
||||||
elif (module, cls) == ("artiq.coredevice.mirny", "Almazny"):
|
elif (module, cls) == ("artiq.coredevice.almazny", "AlmaznyLegacy"):
|
||||||
self.almaznys[name] = self.get_device(name)
|
self.almaznys[name] = self.get_device(name)
|
||||||
|
|
||||||
# Remove Urukul, Sampler, Zotino and Mirny control signals
|
# Remove Urukul, Sampler, Zotino and Mirny control signals
|
||||||
|
|
|
@ -92,6 +92,12 @@ RF generation drivers
|
||||||
.. automodule:: artiq.coredevice.mirny
|
.. automodule:: artiq.coredevice.mirny
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
:mod:`artiq.coredevice.almazny` module
|
||||||
|
++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. automodule:: artiq.coredevice.almazny
|
||||||
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.adf5356` module
|
:mod:`artiq.coredevice.adf5356` module
|
||||||
+++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue