Merge branch 'master' into new

pull/1212/head
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
assert 0 <= pll_cp <= 7
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.io_update_delay = io_update_delay
self.phase_mode = PHASE_MODE_CONTINUOUS
@ -430,6 +432,8 @@ class AD9910:
Defaults to 15 (half range).
: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
# FIXME https://github.com/sinara-hw/Urukul/issues/16
# 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
running at the specified frequency since the start of the
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
@ -190,6 +194,11 @@ class AD9914:
The "frequency update" pulse is sent to the DDS with a fixed latency
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 pow: adds an offset to the phase.
: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.
: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 length: SPI transfer length (see: :meth:`set_config_mu`)
"""

View File

@ -107,12 +107,22 @@ class TTLInOut:
:param channel: channel number
"""
kernel_invariants = {"core", "channel",
kernel_invariants = {"core", "channel", "gate_latency_mu",
"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.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_oe = (channel << 8) + 1
self.target_sens = (channel << 8) + 2
@ -329,7 +339,7 @@ class TTLInOut:
ttl_input.count(ttl_input.gate_rising(100 * us))
"""
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
return count
@ -352,7 +362,7 @@ class TTLInOut:
:return: The timestamp (in machine units) of the first event received;
-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
@kernel
@ -420,7 +430,7 @@ class TTLInOut:
rtio_output(self.target_sens, 0)
success = True
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
except RTIOOverflow:
success = False

View File

@ -309,15 +309,17 @@ class CPLD:
def get_att_mu(self):
"""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
"""
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)
self.bus.write(self.att_reg)
return self.bus.read()
self.bus.write(0) # shift in zeros, shift out current value
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
def set_sync_div(self, div):

View File

@ -1186,24 +1186,26 @@ class VLBAISatellite(_SatelliteBase):
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():
parser = argparse.ArgumentParser(
description="ARTIQ device binary builder for Kasli systems")
builder_args(parser)
soc_kasli_args(parser)
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",
help="variant: {} (default: %(default)s)".format(
"/".join(sorted(variants.keys()))))
"/".join(sorted(VARIANTS.keys()))))
args = parser.parse_args()
variant = args.variant.lower()
try:
cls = variants[variant]
cls = VARIANTS[variant]
except KeyError:
raise SystemExit("Invalid variant (-V/--variant)")

View File

@ -402,6 +402,9 @@ class SMA_SPI(_StandaloneBase):
self.csr_devices.append("rtio_analyzer")
VARIANTS = {cls.__name__.lower(): cls for cls in [NIST_CLOCK, NIST_QC2, SMA_SPI]}
def main():
parser = argparse.ArgumentParser(
description="KC705 gateware and firmware builder")
@ -415,13 +418,9 @@ def main():
args = parser.parse_args()
variant = args.variant.lower()
if variant == "nist_clock":
cls = NIST_CLOCK
elif variant == "nist_qc2":
cls = NIST_QC2
elif variant == "sma_spi":
cls = SMA_SPI
else:
try:
cls = VARIANTS[variant]
except KeyError:
raise SystemExit("Invalid variant (-V/--variant)")
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()))
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):
pass
@ -430,6 +490,9 @@ class CoredeviceTest(ExperimentCase):
count = self.dataset_mgr.get("count")
self.assertEqual(count, npulses)
def test_loopback_gate_timing(self):
self.execute(LoopbackGateTiming)
def test_level(self):
self.execute(Level)