add Almazny support (#1780)

This commit is contained in:
Spaqin 2022-01-11 09:55:39 +08:00 committed by GitHub
parent 4e3e0d129c
commit 095fb9e333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 198 additions and 3 deletions

View File

@ -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:

View File

@ -409,6 +409,10 @@
}
],
"default": 0
},
"almazny": {
"type": "boolean",
"default": false
}
},
"required": ["ports"]

View File

@ -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)

View File

@ -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)

View File

@ -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()))