forked from M-Labs/artiq
Merge branch 'master' into new
This commit is contained in:
commit
53e79f553f
|
@ -101,6 +101,8 @@ class AD9910:
|
||||||
self.pll_vco = pll_vco
|
self.pll_vco = pll_vco
|
||||||
assert 0 <= pll_cp <= 7
|
assert 0 <= pll_cp <= 7
|
||||||
self.pll_cp = pll_cp
|
self.pll_cp = pll_cp
|
||||||
|
if sync_delay_seed >= 0 and not self.cpld.sync_div:
|
||||||
|
raise ValueError("parent cpld does not drive SYNC")
|
||||||
self.sync_delay_seed = sync_delay_seed
|
self.sync_delay_seed = sync_delay_seed
|
||||||
self.io_update_delay = io_update_delay
|
self.io_update_delay = io_update_delay
|
||||||
self.phase_mode = PHASE_MODE_CONTINUOUS
|
self.phase_mode = PHASE_MODE_CONTINUOUS
|
||||||
|
@ -430,6 +432,8 @@ class AD9910:
|
||||||
Defaults to 15 (half range).
|
Defaults to 15 (half range).
|
||||||
:return: Tuple of optimal delay and window size.
|
:return: Tuple of optimal delay and window size.
|
||||||
"""
|
"""
|
||||||
|
if not self.cpld.sync_div:
|
||||||
|
raise ValueError("parent cpld does not drive SYNC")
|
||||||
search_span = 31
|
search_span = 31
|
||||||
# FIXME https://github.com/sinara-hw/Urukul/issues/16
|
# FIXME https://github.com/sinara-hw/Urukul/issues/16
|
||||||
# should both be 2-4 once kasli sync_in jitter is identified
|
# should both be 2-4 once kasli sync_in jitter is identified
|
||||||
|
|
|
@ -175,6 +175,10 @@ class AD9914:
|
||||||
accumulator is set to the value it would have if the DDS had been
|
accumulator is set to the value it would have if the DDS had been
|
||||||
running at the specified frequency since the start of the
|
running at the specified frequency since the start of the
|
||||||
experiment.
|
experiment.
|
||||||
|
|
||||||
|
.. warning:: This setting may become inconsistent when used as part of
|
||||||
|
a DMA recording. When using DMA, it is recommended to specify the
|
||||||
|
phase mode explicitly when calling :meth:`set` or :meth:`set_mu`.
|
||||||
"""
|
"""
|
||||||
self.phase_mode = phase_mode
|
self.phase_mode = phase_mode
|
||||||
|
|
||||||
|
@ -190,6 +194,11 @@ class AD9914:
|
||||||
The "frequency update" pulse is sent to the DDS with a fixed latency
|
The "frequency update" pulse is sent to the DDS with a fixed latency
|
||||||
with respect to the current position of the time cursor.
|
with respect to the current position of the time cursor.
|
||||||
|
|
||||||
|
When switching from other phase modes to the continuous phase mode,
|
||||||
|
there is no jump in the DDS phase. This is however not true when
|
||||||
|
using the continuous phase mode after playing back a DMA sequence
|
||||||
|
that contained the other phase modes.
|
||||||
|
|
||||||
:param ftw: frequency to generate.
|
:param ftw: frequency to generate.
|
||||||
:param pow: adds an offset to the phase.
|
:param pow: adds an offset to the phase.
|
||||||
:param phase_mode: if specified, overrides the default phase mode set
|
:param phase_mode: if specified, overrides the default phase mode set
|
||||||
|
|
|
@ -186,6 +186,12 @@ class SPIMaster:
|
||||||
This method is portable and can also be called from e.g.
|
This method is portable and can also be called from e.g.
|
||||||
:meth:`__init__`.
|
:meth:`__init__`.
|
||||||
|
|
||||||
|
.. warning:: If this method is called while recording a DMA
|
||||||
|
sequence, the playback of the sequence will not update the
|
||||||
|
driver state.
|
||||||
|
When required, update the driver state manually (by calling
|
||||||
|
this method) after playing back a DMA sequence.
|
||||||
|
|
||||||
:param div: SPI clock divider (see: :meth:`set_config_mu`)
|
:param div: SPI clock divider (see: :meth:`set_config_mu`)
|
||||||
:param length: SPI transfer length (see: :meth:`set_config_mu`)
|
:param length: SPI transfer length (see: :meth:`set_config_mu`)
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -107,12 +107,22 @@ class TTLInOut:
|
||||||
|
|
||||||
:param channel: channel number
|
:param channel: channel number
|
||||||
"""
|
"""
|
||||||
kernel_invariants = {"core", "channel",
|
kernel_invariants = {"core", "channel", "gate_latency_mu",
|
||||||
"target_o", "target_oe", "target_sens", "target_sample"}
|
"target_o", "target_oe", "target_sens", "target_sample"}
|
||||||
|
|
||||||
def __init__(self, dmgr, channel, core_device="core"):
|
def __init__(self, dmgr, channel, gate_latency_mu=None,
|
||||||
|
core_device="core"):
|
||||||
self.core = dmgr.get(core_device)
|
self.core = dmgr.get(core_device)
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
|
||||||
|
# With TTLs inputs, the gate control is connected to a high-latency
|
||||||
|
# path through SED. When looking at the RTIO counter to determine if
|
||||||
|
# the gate has closed, we need to take this latency into account.
|
||||||
|
# See: https://github.com/m-labs/artiq/issues/1137
|
||||||
|
if gate_latency_mu is None:
|
||||||
|
gate_latency_mu = 13*self.core.ref_multiplier
|
||||||
|
self.gate_latency_mu = gate_latency_mu
|
||||||
|
|
||||||
self.target_o = (channel << 8) + 0
|
self.target_o = (channel << 8) + 0
|
||||||
self.target_oe = (channel << 8) + 1
|
self.target_oe = (channel << 8) + 1
|
||||||
self.target_sens = (channel << 8) + 2
|
self.target_sens = (channel << 8) + 2
|
||||||
|
@ -329,7 +339,7 @@ class TTLInOut:
|
||||||
ttl_input.count(ttl_input.gate_rising(100 * us))
|
ttl_input.count(ttl_input.gate_rising(100 * us))
|
||||||
"""
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
while rtio_input_timestamp(up_to_timestamp_mu, self.channel) >= 0:
|
while rtio_input_timestamp(up_to_timestamp_mu + self.gate_latency_mu, self.channel) >= 0:
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
@ -352,7 +362,7 @@ class TTLInOut:
|
||||||
:return: The timestamp (in machine units) of the first event received;
|
:return: The timestamp (in machine units) of the first event received;
|
||||||
-1 on timeout.
|
-1 on timeout.
|
||||||
"""
|
"""
|
||||||
return rtio_input_timestamp(up_to_timestamp_mu, self.channel)
|
return rtio_input_timestamp(up_to_timestamp_mu + self.gate_latency_mu, self.channel)
|
||||||
|
|
||||||
# Input API: sampling
|
# Input API: sampling
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -420,7 +430,7 @@ class TTLInOut:
|
||||||
rtio_output(self.target_sens, 0)
|
rtio_output(self.target_sens, 0)
|
||||||
success = True
|
success = True
|
||||||
try:
|
try:
|
||||||
while rtio_input_timestamp(now_mu(), self.channel) != -1:
|
while rtio_input_timestamp(now_mu() + self.gate_latency_mu, self.channel) != -1:
|
||||||
success = False
|
success = False
|
||||||
except RTIOOverflow:
|
except RTIOOverflow:
|
||||||
success = False
|
success = False
|
||||||
|
|
|
@ -309,15 +309,17 @@ class CPLD:
|
||||||
def get_att_mu(self):
|
def get_att_mu(self):
|
||||||
"""Return the digital step attenuator settings in machine units.
|
"""Return the digital step attenuator settings in machine units.
|
||||||
|
|
||||||
This method will also (as a side effect) write the attenuator
|
|
||||||
settings of all four channels.
|
|
||||||
|
|
||||||
:return: 32 bit attenuator settings
|
:return: 32 bit attenuator settings
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT, 32,
|
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT, 32,
|
||||||
SPIT_ATT_RD, CS_ATT)
|
SPIT_ATT_RD, CS_ATT)
|
||||||
self.bus.write(self.att_reg)
|
self.bus.write(0) # shift in zeros, shift out current value
|
||||||
return self.bus.read()
|
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 32,
|
||||||
|
SPIT_ATT_WR, CS_ATT)
|
||||||
|
delay(10*us)
|
||||||
|
self.att_reg = self.bus.read()
|
||||||
|
self.bus.write(self.att_reg) # shift in current value again and latch
|
||||||
|
return self.att_reg
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_sync_div(self, div):
|
def set_sync_div(self, div):
|
||||||
|
|
|
@ -1186,24 +1186,26 @@ class VLBAISatellite(_SatelliteBase):
|
||||||
self.add_rtio(self.rtio_channels)
|
self.add_rtio(self.rtio_channels)
|
||||||
|
|
||||||
|
|
||||||
|
VARIANTS = {cls.__name__.lower(): cls for cls in [
|
||||||
|
Opticlock, SUServo, PTB, PTB2, HUB, LUH,
|
||||||
|
SYSU, MITLL, MITLL2, USTC, Tsinghua, Tsinghua2, WIPM, NUDT,
|
||||||
|
VLBAIMaster, VLBAISatellite, Tester, Master, Satellite]}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="ARTIQ device binary builder for Kasli systems")
|
description="ARTIQ device binary builder for Kasli systems")
|
||||||
builder_args(parser)
|
builder_args(parser)
|
||||||
soc_kasli_args(parser)
|
soc_kasli_args(parser)
|
||||||
parser.set_defaults(output_dir="artiq_kasli")
|
parser.set_defaults(output_dir="artiq_kasli")
|
||||||
variants = {cls.__name__.lower(): cls for cls in [
|
|
||||||
Opticlock, SUServo, PTB, PTB2, HUB, LUH,
|
|
||||||
SYSU, MITLL, MITLL2, USTC, Tsinghua, Tsinghua2, WIPM, NUDT,
|
|
||||||
VLBAIMaster, VLBAISatellite, Tester, Master, Satellite]}
|
|
||||||
parser.add_argument("-V", "--variant", default="opticlock",
|
parser.add_argument("-V", "--variant", default="opticlock",
|
||||||
help="variant: {} (default: %(default)s)".format(
|
help="variant: {} (default: %(default)s)".format(
|
||||||
"/".join(sorted(variants.keys()))))
|
"/".join(sorted(VARIANTS.keys()))))
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
variant = args.variant.lower()
|
variant = args.variant.lower()
|
||||||
try:
|
try:
|
||||||
cls = variants[variant]
|
cls = VARIANTS[variant]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise SystemExit("Invalid variant (-V/--variant)")
|
raise SystemExit("Invalid variant (-V/--variant)")
|
||||||
|
|
||||||
|
|
|
@ -402,6 +402,9 @@ class SMA_SPI(_StandaloneBase):
|
||||||
self.csr_devices.append("rtio_analyzer")
|
self.csr_devices.append("rtio_analyzer")
|
||||||
|
|
||||||
|
|
||||||
|
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_QC2, SMA_SPI]}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="KC705 gateware and firmware builder")
|
description="KC705 gateware and firmware builder")
|
||||||
|
@ -415,13 +418,9 @@ def main():
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
variant = args.variant.lower()
|
variant = args.variant.lower()
|
||||||
if variant == "nist_clock":
|
try:
|
||||||
cls = NIST_CLOCK
|
cls = VARIANTS[variant]
|
||||||
elif variant == "nist_qc2":
|
except KeyError:
|
||||||
cls = NIST_QC2
|
|
||||||
elif variant == "sma_spi":
|
|
||||||
cls = SMA_SPI
|
|
||||||
else:
|
|
||||||
raise SystemExit("Invalid variant (-V/--variant)")
|
raise SystemExit("Invalid variant (-V/--variant)")
|
||||||
|
|
||||||
soc = cls(**soc_kc705_argdict(args))
|
soc = cls(**soc_kc705_argdict(args))
|
||||||
|
|
|
@ -206,6 +206,66 @@ class LoopbackCount(EnvExperiment):
|
||||||
self.set_dataset("count", self.loop_in.count(now_mu()))
|
self.set_dataset("count", self.loop_in.count(now_mu()))
|
||||||
|
|
||||||
|
|
||||||
|
class IncorrectPulseTiming(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LoopbackGateTiming(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.setattr_device("loop_in")
|
||||||
|
self.setattr_device("loop_out")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
# Make sure there are no leftover events.
|
||||||
|
self.core.reset()
|
||||||
|
self.loop_in.input()
|
||||||
|
self.loop_out.output()
|
||||||
|
delay_mu(500)
|
||||||
|
self.loop_out.off()
|
||||||
|
delay_mu(5000)
|
||||||
|
|
||||||
|
# Determine loop delay.
|
||||||
|
with parallel:
|
||||||
|
self.loop_in.gate_rising_mu(10000)
|
||||||
|
with sequential:
|
||||||
|
delay_mu(5000)
|
||||||
|
out_mu = now_mu()
|
||||||
|
self.loop_out.pulse_mu(1000)
|
||||||
|
in_mu = self.loop_in.timestamp_mu(now_mu())
|
||||||
|
if in_mu < 0:
|
||||||
|
raise PulseNotReceived("Cannot determine loop delay")
|
||||||
|
loop_delay_mu = in_mu - out_mu
|
||||||
|
|
||||||
|
# With the exact delay known, make sure tight gate timings work.
|
||||||
|
# In the most common configuration, 24 mu == 24 ns == 3 coarse periods,
|
||||||
|
# which should be plenty of slack.
|
||||||
|
delay_mu(10000)
|
||||||
|
|
||||||
|
gate_start_mu = now_mu()
|
||||||
|
self.loop_in.gate_both_mu(24)
|
||||||
|
gate_end_mu = now_mu()
|
||||||
|
|
||||||
|
# gateware latency offset between gate and input
|
||||||
|
lat_offset = 12*8
|
||||||
|
out_mu = gate_start_mu - loop_delay_mu + lat_offset
|
||||||
|
at_mu(out_mu)
|
||||||
|
self.loop_out.pulse_mu(24)
|
||||||
|
|
||||||
|
in_mu = self.loop_in.timestamp_mu(gate_end_mu)
|
||||||
|
if in_mu < 0:
|
||||||
|
raise PulseNotReceived()
|
||||||
|
if not (gate_start_mu <= (in_mu - lat_offset) <= gate_end_mu):
|
||||||
|
raise IncorrectPulseTiming("Input event should occur during gate")
|
||||||
|
if not (-2 < (in_mu - out_mu - loop_delay_mu) < 2):
|
||||||
|
raise IncorrectPulseTiming("Loop delay should not change")
|
||||||
|
|
||||||
|
in_mu = self.loop_in.timestamp_mu(gate_end_mu)
|
||||||
|
if in_mu > 0:
|
||||||
|
raise IncorrectPulseTiming("Only one pulse should be received")
|
||||||
|
|
||||||
|
|
||||||
class IncorrectLevel(Exception):
|
class IncorrectLevel(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -430,6 +490,9 @@ class CoredeviceTest(ExperimentCase):
|
||||||
count = self.dataset_mgr.get("count")
|
count = self.dataset_mgr.get("count")
|
||||||
self.assertEqual(count, npulses)
|
self.assertEqual(count, npulses)
|
||||||
|
|
||||||
|
def test_loopback_gate_timing(self):
|
||||||
|
self.execute(LoopbackGateTiming)
|
||||||
|
|
||||||
def test_level(self):
|
def test_level(self):
|
||||||
self.execute(Level)
|
self.execute(Level)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue