Merge branch 'master' into new

This commit is contained in:
Sebastien Bourdeauducq 2018-11-19 11:54:50 +08:00
commit 53e79f553f
8 changed files with 119 additions and 24 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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