mirror of
https://github.com/m-labs/artiq.git
synced 2024-12-24 10:54:02 +08:00
add Almazny support (#1780)
This commit is contained in:
parent
4e3e0d129c
commit
095fb9e333
@ -11,6 +11,7 @@ Highlights:
|
||||
* New hardware support:
|
||||
- Kasli-SoC, a new EEM carrier based on a Zynq SoC, enabling much faster kernel execution.
|
||||
- HVAMP_8CH 8 channel HV amplifier for Fastino / Zotinos
|
||||
- Almazny mezzanine board for Mirny
|
||||
* Softcore targets now use the RISC-V architecture (VexRiscv) instead of OR1K (mor1kx).
|
||||
* Faster compilation for large arrays/lists.
|
||||
* Phaser:
|
||||
|
@ -409,6 +409,10 @@
|
||||
}
|
||||
],
|
||||
"default": 0
|
||||
},
|
||||
"almazny": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["ports"]
|
||||
|
@ -31,6 +31,16 @@ WE = 1 << 24
|
||||
# supported CPLD code version
|
||||
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
|
||||
|
||||
|
||||
class Mirny:
|
||||
"""
|
||||
@ -132,11 +142,114 @@ class Mirny:
|
||||
self.bus.write(((channel | 8) << 25) | (att << 16))
|
||||
|
||||
@kernel
|
||||
def write_ext(self, addr, length, data):
|
||||
def write_ext(self, addr, length, data, ext_div=SPIT_WR):
|
||||
"""Perform SPI write to a prefixed address"""
|
||||
self.bus.set_config_mu(SPI_CONFIG, 8, SPIT_WR, SPI_CS)
|
||||
self.bus.write(addr << 25)
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, length, SPIT_WR, SPI_CS)
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, length, ext_div, SPI_CS)
|
||||
if length < 32:
|
||||
data <<= 32 - length
|
||||
self.bus.write(data)
|
||||
|
||||
|
||||
class Almazny:
|
||||
"""
|
||||
Almazny (High frequency mezzanine board for Mirny)
|
||||
|
||||
:param host_mirny - Mirny device Almazny is connected to
|
||||
"""
|
||||
|
||||
def __init__(self, dmgr, host_mirny):
|
||||
self.mirny_cpld = dmgr.get(host_mirny)
|
||||
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):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Convert a digital attenuator setting to dB.
|
||||
|
||||
:param att_mu: attenuator setting in machine units
|
||||
:return: attenuator setting in dB
|
||||
"""
|
||||
return att_mu / 2
|
||||
|
||||
@kernel
|
||||
def set_att(self, channel, att, rf_switch=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, att_mu, rf_switch=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):
|
||||
"""
|
||||
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
|
||||
delay(100 * us)
|
||||
new_reg = (en << ALMAZNY_OE_SHIFT) | (cfg_reg & 0x3FF)
|
||||
self.mirny_cpld.write_reg(1, new_reg)
|
||||
delay(100 * us)
|
||||
|
||||
@kernel
|
||||
def _flip_mu_bits(self, mu):
|
||||
# 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):
|
||||
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
|
||||
)
|
||||
delay(100 * us)
|
||||
|
||||
@kernel
|
||||
def _update_all_registers(self):
|
||||
for i in range(4):
|
||||
self._update_register(i)
|
@ -294,6 +294,18 @@ class PeripheralManager:
|
||||
name=mirny_name,
|
||||
refclk=peripheral["refclk"],
|
||||
clk_sel=clk_sel)
|
||||
almazny = peripheral.get("almazny", False)
|
||||
if almazny:
|
||||
self.gen("""
|
||||
device_db["{name}_almazny"] = {{
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.mirny",
|
||||
"class": "Almazny",
|
||||
"arguments": {{
|
||||
"host_mirny": "{name}_cpld",
|
||||
}},
|
||||
}}""",
|
||||
name=mirny_name)
|
||||
|
||||
return next(channel)
|
||||
|
||||
|
@ -59,6 +59,7 @@ class SinaraTester(EnvExperiment):
|
||||
self.mirnies = dict()
|
||||
self.suservos = dict()
|
||||
self.suschannels = dict()
|
||||
self.almaznys = dict()
|
||||
|
||||
ddb = self.get_device_db()
|
||||
for name, desc in ddb.items():
|
||||
@ -96,6 +97,8 @@ class SinaraTester(EnvExperiment):
|
||||
self.suservos[name] = self.get_device(name)
|
||||
elif (module, cls) == ("artiq.coredevice.suservo", "Channel"):
|
||||
self.suschannels[name] = self.get_device(name)
|
||||
elif (module, cls) == ("artiq.coredevice.mirny", "Almazny"):
|
||||
self.almaznys[name] = self.get_device(name)
|
||||
|
||||
# Remove Urukul, Sampler, Zotino and Mirny control signals
|
||||
# from TTL outs (tested separately) and remove Urukuls covered by
|
||||
@ -351,6 +354,68 @@ class SinaraTester(EnvExperiment):
|
||||
for channel in channels:
|
||||
channel.pulse(100*ms)
|
||||
delay(100*ms)
|
||||
@kernel
|
||||
def init_almazny(self, almazny):
|
||||
self.core.break_realtime()
|
||||
almazny.init()
|
||||
almazny.output_toggle(True)
|
||||
|
||||
@kernel
|
||||
def almazny_set_attenuators_mu(self, almazny, ch, atts):
|
||||
self.core.break_realtime()
|
||||
almazny.set_att_mu(ch, atts)
|
||||
|
||||
@kernel
|
||||
def almazny_set_attenuators(self, almazny, ch, atts):
|
||||
self.core.break_realtime()
|
||||
almazny.set_att(ch, atts)
|
||||
|
||||
@kernel
|
||||
def almazny_toggle_output(self, almazny, rf_on):
|
||||
self.core.break_realtime()
|
||||
almazny.output_toggle(rf_on)
|
||||
|
||||
def test_almaznys(self):
|
||||
print("*** Testing Almaznys.")
|
||||
for name, almazny in sorted(self.almaznys.items(), key=lambda x: x[0]):
|
||||
print(name + "...")
|
||||
print("Initializing Mirny CPLDs...")
|
||||
for name, cpld in sorted(self.mirny_cplds.items(), key=lambda x: x[0]):
|
||||
print(name + "...")
|
||||
self.init_mirny(cpld)
|
||||
print("...done")
|
||||
|
||||
print("Testing attenuators. Frequencies:")
|
||||
for card_n, channels in enumerate(chunker(self.mirnies, 4)):
|
||||
for channel_n, (channel_name, channel_dev) in enumerate(channels):
|
||||
frequency = 2000 + card_n * 250 + channel_n * 50
|
||||
print("{}\t{}MHz".format(channel_name, frequency*2))
|
||||
self.setup_mirny(channel_dev, frequency)
|
||||
print("{} info: {}".format(channel_name, channel_dev.info()))
|
||||
self.init_almazny(almazny)
|
||||
print("RF ON, all attenuators ON. Press ENTER when done.")
|
||||
for i in range(4):
|
||||
self.almazny_set_attenuators_mu(almazny, i, 63)
|
||||
input()
|
||||
print("RF ON, half power attenuators ON. Press ENTER when done.")
|
||||
for i in range(4):
|
||||
self.almazny_set_attenuators(almazny, i, 15.5)
|
||||
input()
|
||||
print("RF ON, all attenuators OFF. Press ENTER when done.")
|
||||
for i in range(4):
|
||||
self.almazny_set_attenuators(almazny, i, 0)
|
||||
input()
|
||||
print("SR outputs are OFF. Press ENTER when done.")
|
||||
self.almazny_toggle_output(almazny, False)
|
||||
input()
|
||||
print("RF ON, all attenuators are ON. Press ENTER when done.")
|
||||
for i in range(4):
|
||||
self.almazny_set_attenuators(almazny, i, 31.5)
|
||||
self.almazny_toggle_output(almazny, True)
|
||||
input()
|
||||
print("RF OFF. Press ENTER when done.")
|
||||
self.almazny_toggle_output(almazny, False)
|
||||
input()
|
||||
|
||||
def test_mirnies(self):
|
||||
print("*** Testing Mirny PLLs.")
|
||||
@ -365,7 +430,7 @@ class SinaraTester(EnvExperiment):
|
||||
print("Frequencies:")
|
||||
for card_n, channels in enumerate(chunker(self.mirnies, 4)):
|
||||
for channel_n, (channel_name, channel_dev) in enumerate(channels):
|
||||
frequency = 1000*(card_n + 1) + channel_n * 100 + 8 # Extra 8 Hz for easier observation
|
||||
frequency = 1000*(card_n + 1) + channel_n * 100
|
||||
print("{}\t{}MHz".format(channel_name, frequency))
|
||||
self.setup_mirny(channel_dev, frequency)
|
||||
print("{} info: {}".format(channel_name, channel_dev.info()))
|
||||
|
Loading…
Reference in New Issue
Block a user