forked from M-Labs/artiq
1
0
Fork 0

Merge branch 'phaser' into phaser2

* phaser: (23 commits)
  RELEASE_NOTES: update
  pipistrello: add some inputs
  Remove last vestiges of nist_qc1.
  Fully drop AD9858 and kc705-nist_qc1 support (closes #576).
  coredevice.dds: reimplement fully in ARTIQ Python.
  compiler: unbreak casts to int32/int64.
  analyses.constness: fix false positive on x[...].
  inferencer: significantly improve the op-assignment diagnostic.
  Fix tests.
  Move mu_to_seconds, seconds_to_mu to Core.
  artiq_devtool: don't crash on invalid utf-8.
  artiq_devtool: detect a race condition during connect.
  llvm_ir_generator: handle no-op coercions.
  conda: use development version of migen/misoc
  Revert accidentally committed code.
  Revert "gateware: increase RTIO FIFO sizes for NIST_CLOCK. Closes #623"
  analyses.invariant_detection: implement (#622).
  Fix whitespace.
  coredevice.dds: work around the round(numpy.float64()) snafu.
  coredevice.dds: update from obsolete int(width=) syntax (fixes #621).
  ...
This commit is contained in:
Robert Jördens 2016-11-21 17:29:46 +01:00
commit 4160490e0a
57 changed files with 566 additions and 950 deletions

View File

@ -16,6 +16,12 @@ Release notes
* Datasets requested by experiments are by default archived into their HDF5
output. If this behavior is undesirable, turn it off by passing
``archive=False`` to ``get_dataset``.
* ``seconds_to_mu`` and ``mu_to_seconds`` have become methods of the core
device driver (use e.g. ``self.core.seconds_to_mu()``).
* AD9858 DDSes and NIST QC1 hardware are no longer supported.
* The Pipistrello port now has exclusively TTLs.
* The DDS class names and setup options have changed, this requires an update of
the device database.
2.0

View File

@ -1,3 +1,4 @@
from .constness import Constness
from .domination import DominatorTree
from .devirtualization import Devirtualization
from .invariant_detection import InvariantDetection

View File

@ -17,6 +17,12 @@ class Constness(algorithm.Visitor):
self.visit(node.targets)
self.in_assign = False
def visit_SubscriptT(self, node):
old_in_assign, self.in_assign = self.in_assign, False
self.visit(node.value)
self.visit(node.slice)
self.in_assign = old_in_assign
def visit_AttributeT(self, node):
self.generic_visit(node)
if self.in_assign:

View File

@ -0,0 +1,49 @@
"""
:class:`InvariantDetection` determines which attributes can be safely
marked kernel invariant.
"""
from pythonparser import diagnostic
from .. import ir, types
class InvariantDetection:
def __init__(self, engine):
self.engine = engine
def process(self, functions):
self.attr_locs = dict()
self.attr_written = set()
for func in functions:
self.process_function(func)
for key in self.attr_locs:
if key not in self.attr_written:
typ, attr = key
if attr in typ.constant_attributes:
continue
diag = diagnostic.Diagnostic("note",
"attribute '{attr}' of type '{type}' is never written to; " +
"it could be marked as kernel invariant to potentially increase performance",
{"attr": attr,
"type": typ.name},
self.attr_locs[key])
self.engine.process(diag)
def process_function(self, func):
for block in func.basic_blocks:
for insn in block.instructions:
if not isinstance(insn, (ir.GetAttr, ir.SetAttr)):
continue
if not types.is_instance(insn.object().type):
continue
key = (insn.object().type, insn.attr)
if isinstance(insn, ir.GetAttr):
if types.is_method(insn.type):
continue
if key not in self.attr_locs and insn.loc is not None:
self.attr_locs[key] = insn.loc
elif isinstance(insn, ir.SetAttr):
self.attr_written.add(key)

View File

@ -126,10 +126,10 @@ def fn_int():
return types.TConstructor(TInt())
def fn_int32():
return types.TConstructor(TInt32())
return types.TBuiltinFunction("int32")
def fn_int64():
return types.TConstructor(TInt64())
return types.TBuiltinFunction("int64")
def fn_float():
return types.TConstructor(TFloat())
@ -203,12 +203,6 @@ def fn_delay_mu():
def fn_at_mu():
return types.TBuiltinFunction("at_mu")
def fn_mu_to_seconds():
return types.TBuiltinFunction("mu_to_seconds")
def fn_seconds_to_mu():
return types.TBuiltinFunction("seconds_to_mu")
def fn_rtio_log():
return types.TBuiltinFunction("rtio_log")

View File

@ -40,7 +40,7 @@ class Source:
return cls(source.Buffer(f.read(), filename, 1), engine=engine)
class Module:
def __init__(self, src, ref_period=1e-6, attribute_writeback=True):
def __init__(self, src, ref_period=1e-6, attribute_writeback=True, remarks=True):
self.attribute_writeback = attribute_writeback
self.engine = src.engine
self.embedding_map = src.embedding_map
@ -61,6 +61,7 @@ class Module:
local_access_validator = validators.LocalAccessValidator(engine=self.engine)
devirtualization = analyses.Devirtualization()
interleaver = transforms.Interleaver(engine=self.engine)
invariant_detection = analyses.InvariantDetection(engine=self.engine)
int_monomorphizer.visit(src.typedtree)
inferencer.visit(src.typedtree)
@ -74,6 +75,8 @@ class Module:
dead_code_eliminator.process(self.artiq_ir)
interleaver.process(self.artiq_ir)
local_access_validator.process(self.artiq_ir)
if remarks:
invariant_detection.process(self.artiq_ir)
def build_llvm_ir(self, target):
"""Compile the module to LLVM IR for the specified target."""

View File

@ -14,6 +14,8 @@ def globals():
"list": builtins.fn_list(),
"array": builtins.fn_array(),
"range": builtins.fn_range(),
"int32": builtins.fn_int32(),
"int64": builtins.fn_int64(),
# Exception constructors
"Exception": builtins.fn_Exception(),
@ -44,8 +46,6 @@ def globals():
"now_mu": builtins.fn_now_mu(),
"delay_mu": builtins.fn_delay_mu(),
"at_mu": builtins.fn_at_mu(),
"mu_to_seconds": builtins.fn_mu_to_seconds(),
"seconds_to_mu": builtins.fn_seconds_to_mu(),
# ARTIQ utility functions
"rtio_log": builtins.fn_rtio_log(),

View File

@ -1599,7 +1599,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
return self.coerce_to_bool(arg)
else:
assert False
elif types.is_builtin(typ, "int"):
elif types.is_builtin(typ, "int") or \
types.is_builtin(typ, "int32") or types.is_builtin(typ, "int64"):
if len(node.args) == 0 and len(node.keywords) == 0:
return ir.Constant(0, node.type)
elif len(node.args) == 1 and \
@ -1731,20 +1732,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
or types.is_builtin(typ, "at_mu"):
return self.append(ir.Builtin(typ.name,
[self.visit(arg) for arg in node.args], node.type))
elif types.is_builtin(typ, "mu_to_seconds"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_float = self.append(ir.Coerce(arg, builtins.TFloat()))
return self.append(ir.Arith(ast.Mult(loc=None), arg_float, self.ref_period))
else:
assert False
elif types.is_builtin(typ, "seconds_to_mu"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_mu = self.append(ir.Arith(ast.Div(loc=None), arg, self.ref_period))
return self.append(ir.Coerce(arg_mu, builtins.TInt64()))
else:
assert False
elif types.is_exn_constructor(typ):
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
elif types.is_constructor(typ):

View File

@ -622,14 +622,28 @@ class Inferencer(algorithm.Visitor):
self._unify(node.type, builtins.TBool(),
node.loc, None)
elif types.is_builtin(typ, "int"):
elif types.is_builtin(typ, "int") or \
types.is_builtin(typ, "int32") or types.is_builtin(typ, "int64"):
if types.is_builtin(typ, "int"):
valid_forms = lambda: [
valid_form("int() -> numpy.int?"),
valid_form("int(x:'a) -> numpy.int?"),
valid_form("int(x:'a, width=?) -> numpy.int?")
valid_form("int(x:'a) -> numpy.int? where 'a is numeric")
]
result_typ = builtins.TInt()
elif types.is_builtin(typ, "int32"):
valid_forms = lambda: [
valid_form("numpy.int32() -> numpy.int32"),
valid_form("numpy.int32(x:'a) -> numpy.int32 where 'a is numeric")
]
result_typ = builtins.TInt32()
elif types.is_builtin(typ, "int64"):
valid_forms = lambda: [
valid_form("numpy.int64() -> numpy.int64"),
valid_form("numpy.int64(x:'a) -> numpy.int64 where 'a is numeric")
]
result_typ = builtins.TInt64()
self._unify(node.type, builtins.TInt(),
self._unify(node.type, result_typ,
node.loc, None)
if len(node.args) == 0 and len(node.keywords) == 0:
@ -639,20 +653,7 @@ class Inferencer(algorithm.Visitor):
pass # undetermined yet
elif len(node.args) == 1 and len(node.keywords) == 0 and \
builtins.is_numeric(node.args[0].type):
self._unify(node.type, builtins.TInt(),
node.loc, None)
elif len(node.args) == 1 and len(node.keywords) == 1 and \
builtins.is_numeric(node.args[0].type) and \
node.keywords[0].arg == 'width':
width = node.keywords[0].value
if not (isinstance(width, asttyped.NumT) and isinstance(width.n, int)):
diag = diagnostic.Diagnostic("error",
"the width argument of int() must be an integer literal", {},
node.keywords[0].loc)
self.engine.process(diag)
return
self._unify(node.type, builtins.TInt(types.TValue(width.n)),
self._unify(node.type, result_typ,
node.loc, None)
else:
diagnose(valid_forms())
@ -899,12 +900,6 @@ class Inferencer(algorithm.Visitor):
elif types.is_builtin(typ, "at_mu"):
simple_form("at_mu(time_mu:numpy.int64) -> None",
[builtins.TInt64()])
elif types.is_builtin(typ, "mu_to_seconds"):
simple_form("mu_to_seconds(time_mu:numpy.int64) -> float",
[builtins.TInt64()], builtins.TFloat())
elif types.is_builtin(typ, "seconds_to_mu"):
simple_form("seconds_to_mu(time:float) -> numpy.int64",
[builtins.TFloat()], builtins.TInt64())
elif types.is_builtin(typ, "watchdog"):
simple_form("watchdog(time:float) -> [builtin context manager]",
[builtins.TFloat()], builtins.TNone())
@ -1049,22 +1044,10 @@ class Inferencer(algorithm.Visitor):
if coerced:
return_type, target_type, value_type = coerced
try:
node.target.type.unify(target_type)
except types.UnificationError as e:
printer = types.TypePrinter()
note = diagnostic.Diagnostic("note",
"expression of type {typec}",
{"typec": printer.name(node.value.type)},
node.value.loc)
diag = diagnostic.Diagnostic("error",
"expression of type {typea} has to be coerced to {typeb}, "
"which makes assignment invalid",
{"typea": printer.name(node.target.type),
"typeb": printer.name(target_type)},
node.op.loc, [node.target.loc], [note])
self.engine.process(diag)
return
if isinstance(node.value, asttyped.CoerceT):
orig_value_type = node.value.value.type
else:
orig_value_type = node.value.type
try:
node.target.type.unify(return_type)
@ -1072,17 +1055,34 @@ class Inferencer(algorithm.Visitor):
printer = types.TypePrinter()
note = diagnostic.Diagnostic("note",
"expression of type {typec}",
{"typec": printer.name(node.value.type)},
{"typec": printer.name(orig_value_type)},
node.value.loc)
diag = diagnostic.Diagnostic("error",
"the result of this operation has type {typeb}, "
"which makes assignment to a slot of type {typea} invalid",
"which cannot be assigned to a left-hand side of type {typea}",
{"typea": printer.name(node.target.type),
"typeb": printer.name(return_type)},
node.op.loc, [node.target.loc], [note])
self.engine.process(diag)
return
try:
node.target.type.unify(target_type)
except types.UnificationError as e:
printer = types.TypePrinter()
note = diagnostic.Diagnostic("note",
"expression of type {typec}",
{"typec": printer.name(orig_value_type)},
node.value.loc)
diag = diagnostic.Diagnostic("error",
"this operation requires the left-hand side of type {typea} "
"to be coerced to {typeb}, which cannot be done",
{"typea": printer.name(node.target.type),
"typeb": printer.name(target_type)},
node.op.loc, [node.target.loc], [note])
self.engine.process(diag)
return
node.value = self._coerce_one(value_type, node.value, other_node=node.target)
def visit_ForT(self, node):

View File

@ -848,6 +848,8 @@ class LLVMIRGenerator:
def process_Coerce(self, insn):
typ, value_typ = insn.type, insn.value().type
if typ == value_typ:
return self.map(insn.value())
if builtins.is_int(typ) and builtins.is_float(value_typ):
return self.llbuilder.fptosi(self.map(insn.value()), self.llty_of_type(typ),
name=insn.name)

View File

@ -1,5 +1,4 @@
from artiq.language.core import (kernel, portable, delay_mu, delay,
seconds_to_mu)
from artiq.language.core import (kernel, portable, delay_mu, delay)
from artiq.language.units import ns, us
from artiq.coredevice import spi
@ -166,10 +165,10 @@ class AD5360:
self.bus.write_period_mu +
self.bus.ref_period_mu) -
3*self.bus.ref_period_mu -
seconds_to_mu(1.5*us))
self.core.seconds_to_mu(1.5*us))
for i in range(len(values)):
self.write_channel(i, values[i], op)
delay_mu(3*self.bus.ref_period_mu + # latency alignment ttl to spi
seconds_to_mu(1.5*us)) # t10 max busy low for one channel
self.core.seconds_to_mu(1.5*us)) # t10 max busy low for one channel
self.load()
delay_mu(-2*self.bus.ref_period_mu) # load(), t13

View File

@ -182,10 +182,7 @@ class DDSHandler:
self.vcd_manager.get_channel("dds/" + name + "/frequency", 64)
dds_channel["vcd_phase"] = \
self.vcd_manager.get_channel("dds/" + name + "/phase", 64)
if self.dds_type == "AD9858":
dds_channel["ftw"] = [None, None, None, None]
dds_channel["pow"] = [None, None]
elif self.dds_type == "AD9914":
if self.dds_type == "DDSChannelAD9914":
dds_channel["ftw"] = [None, None]
dds_channel["pow"] = None
self.dds_channels[dds_channel_nr] = dds_channel
@ -205,26 +202,6 @@ class DDSHandler:
else:
return {gpio}
def _decode_ad9858_write(self, message):
if message.address == 0x41:
self.selected_dds_channels = self._gpio_to_channels(message.data)
for dds_channel_nr in self.selected_dds_channels:
dds_channel = self.dds_channels[dds_channel_nr]
if message.address in range(0x0a, 0x0e):
dds_channel["ftw"][message.address - 0x0a] = message.data
elif message.address in range(0x0e, 0x10):
dds_channel["pow"][message.address - 0x0e] = message.data
elif message.address == 0x40: # FUD
if None not in dds_channel["ftw"]:
ftw = sum(x << i*8
for i, x in enumerate(dds_channel["ftw"]))
frequency = ftw*self.sysclk/2**32
dds_channel["vcd_frequency"].set_value_double(frequency)
if None not in dds_channel["pow"]:
pow = dds_channel["pow"][0] | (dds_channel["pow"][1] & 0x3f) << 8
phase = pow/2**14
dds_channel["vcd_phase"].set_value_double(phase)
def _decode_ad9914_write(self, message):
if message.address == 0x81:
self.selected_dds_channels = self._gpio_to_channels(message.data)
@ -251,9 +228,7 @@ class DDSHandler:
logger.debug("DDS write @%d 0x%04x to 0x%02x, selected channels: %s",
message.timestamp, message.data, message.address,
self.selected_dds_channels)
if self.dds_type == "AD9858":
self._decode_ad9858_write(message)
elif self.dds_type == "AD9914":
if self.dds_type == "DDSChannelAD9914":
self._decode_ad9914_write(message)
@ -312,7 +287,7 @@ def get_single_device_argument(devices, module, cls, argument):
for desc in devices.values():
if isinstance(desc, dict) and desc["type"] == "local":
if (desc["module"] == module
and desc["class"] == cls):
and desc["class"] in cls):
if ref_period is None:
ref_period = desc["arguments"][argument]
else:
@ -322,12 +297,12 @@ def get_single_device_argument(devices, module, cls, argument):
def get_ref_period(devices):
return get_single_device_argument(devices, "artiq.coredevice.core",
"Core", "ref_period")
("Core",), "ref_period")
def get_dds_sysclk(devices):
return get_single_device_argument(devices, "artiq.coredevice.dds",
"CoreDDS", "sysclk")
("DDSGroupAD9914",), "sysclk")
def create_channel_handlers(vcd_manager, devices, ref_period,
@ -344,7 +319,7 @@ def create_channel_handlers(vcd_manager, devices, ref_period,
channel = desc["arguments"]["channel"]
channel_handlers[channel] = TTLClockGenHandler(vcd_manager, name, ref_period)
if (desc["module"] == "artiq.coredevice.dds"
and desc["class"] in {"AD9858", "AD9914"}):
and desc["class"] in {"DDSChannelAD9914"}):
dds_bus_channel = desc["arguments"]["bus_channel"]
dds_channel = desc["arguments"]["channel"]
if dds_bus_channel in channel_handlers:

View File

@ -1,4 +1,5 @@
import os, sys
import numpy
from pythonparser import diagnostic
@ -124,6 +125,23 @@ class Core:
return result
@portable
def seconds_to_mu(self, seconds):
"""Converts seconds to the corresponding number of machine units
(RTIO cycles).
:param seconds: time (in seconds) to convert.
"""
return numpy.int64(seconds//self.ref_period)
@portable
def mu_to_seconds(self, mu):
"""Converts machine units (RTIO cycles) to seconds.
:param mu: cycle count to convert.
"""
return mu*self.ref_period
@kernel
def get_rtio_counter_mu(self):
return rtio_get_counter()

View File

@ -1,55 +1,84 @@
from artiq.language.core import *
from artiq.language.types import *
from artiq.language.units import *
from artiq.coredevice.rtio import rtio_output
from artiq.coredevice.exceptions import DDSError
from numpy import int32, int64
_PHASE_MODE_DEFAULT = -1
# keep in sync with dds.h
PHASE_MODE_CONTINUOUS = 0
PHASE_MODE_ABSOLUTE = 1
PHASE_MODE_TRACKING = 2
@syscall(flags={"nowrite"})
def dds_init(time_mu: TInt64, bus_channel: TInt32, channel: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def dds_init_sync(time_mu: TInt64, bus_channel: TInt32,
channel: TInt32, sync_delay: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def dds_set(time_mu: TInt64, bus_channel: TInt32, channel: TInt32, ftw: TInt32,
pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def dds_batch_enter(time_mu: TInt64) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def dds_batch_exit() -> TNone:
raise NotImplementedError("syscall not simulated")
class DDSParams:
def __init__(self):
self.bus_channel = 0
self.channel = 0
self.ftw = 0
self.pow = 0
self.phase_mode = 0
self.amplitude = 0
class _BatchContextManager:
kernel_invariants = {"core", "core_dds"}
class BatchContextManager:
kernel_invariants = {"core", "core_dds", "params"}
def __init__(self, core_dds):
self.core_dds = core_dds
self.core = self.core_dds.core
self.active = False
self.params = [DDSParams() for _ in range(16)]
self.count = 0
self.ref_time = int64(0)
@kernel
def __enter__(self):
self.core_dds.dds_batch_enter()
"""Starts a DDS command batch. All DDS commands are buffered
after this call, until ``batch_exit`` is called.
The time of execution of the DDS commands is the time cursor position
when the batch is entered."""
if self.active:
raise DDSError("DDS batch entered twice")
self.active = True
self.count = 0
self.ref_time = now_mu()
@kernel
def append(self, bus_channel, channel, ftw, pow, phase_mode, amplitude):
if self.count == len(self.params):
raise DDSError("Too many commands in DDS batch")
params = self.params[self.count]
params.bus_channel = bus_channel
params.channel = channel
params.ftw = ftw
params.pow = pow
params.phase_mode = phase_mode
params.amplitude = amplitude
self.count += 1
@kernel
def __exit__(self, type, value, traceback):
self.core_dds.dds_batch_exit()
"""Ends a DDS command batch. All buffered DDS commands are issued
on the bus."""
if not self.active:
raise DDSError("DDS batch exited twice")
self.active = False
at_mu(self.ref_time - self.core_dds.batch_duration_mu())
for i in range(self.count):
param = self.params[i]
self.core_dds.program(self.ref_time,
param.bus_channel, param.channel, param.ftw,
param.pow, param.phase_mode, param.amplitude)
class CoreDDS:
class DDSGroup:
"""Core device Direct Digital Synthesis (DDS) driver.
Gives access to the DDS functionality of the core device.
@ -63,31 +92,75 @@ class CoreDDS:
def __init__(self, dmgr, sysclk, core_device="core"):
self.core = dmgr.get(core_device)
self.sysclk = sysclk
self.batch = _BatchContextManager(self)
self.batch = BatchContextManager(self)
@kernel
def dds_batch_enter(self):
"""Starts a DDS command batch. All DDS commands are buffered
after this call, until ``batch_exit`` is called.
The time of execution of the DDS commands is the time cursor position
when the batch is entered."""
dds_batch_enter(now_mu())
def batch_duration_mu(self):
raise NotImplementedError
@kernel
def dds_batch_exit(self):
"""Ends a DDS command batch. All buffered DDS commands are issued
on the bus."""
dds_batch_exit()
def init(self, bus_channel, channel):
raise NotImplementedError
@kernel
def program(self, ref_time, bus_channel, channel, ftw, pow, phase_mode, amplitude):
raise NotImplementedError
@kernel
def set(self, bus_channel, channel, ftw, pow, phase_mode, amplitude):
if self.batch.active:
self.batch.append(bus_channel, channel, ftw, pow, phase_mode, amplitude)
else:
ref_time = now_mu()
at_mu(ref_time - self.program_duration_mu)
self.program(ref_time,
bus_channel, channel, ftw, pow, phase_mode, amplitude)
@portable(flags={"fast-math"})
def frequency_to_ftw(self, frequency):
"""Returns the frequency tuning word corresponding to the given
frequency.
"""
return round(float(int64(2)**32*frequency/self.sysclk))
@portable(flags={"fast-math"})
def ftw_to_frequency(self, ftw):
"""Returns the frequency corresponding to the given frequency tuning
word.
"""
return ftw*self.sysclk/int64(2)**32
@portable(flags={"fast-math"})
def turns_to_pow(self, turns):
"""Returns the phase offset word corresponding to the given phase
in turns."""
return round(float(turns*2**self.pow_width))
@portable(flags={"fast-math"})
def pow_to_turns(self, pow):
"""Returns the phase in turns corresponding to the given phase offset
word."""
return pow/2**self.pow_width
@portable(flags={"fast-math"})
def amplitude_to_asf(self, amplitude):
"""Returns amplitude scale factor corresponding to given amplitude."""
return round(float(amplitude*0x0fff))
@portable(flags={"fast-math"})
def asf_to_amplitude(self, asf):
"""Returns the amplitude corresponding to the given amplitude scale
factor."""
return asf/0x0fff
class _DDSGeneric:
class DDSChannel:
"""Core device Direct Digital Synthesis (DDS) channel driver.
Controls one DDS channel managed directly by the core device's runtime.
This class should not be used directly, instead, use the chip-specific
drivers such as ``AD9858`` and ``AD9914``.
drivers such as ``DDSChannelAD9914``.
The time cursor is not modified by any function in this class.
@ -96,7 +169,7 @@ class _DDSGeneric:
"""
kernel_invariants = {
"core", "core_dds", "bus_channel", "channel", "pow_width"
"core", "core_dds", "bus_channel", "channel",
}
def __init__(self, dmgr, bus_channel, channel, core_dds_device="core_dds"):
@ -106,43 +179,6 @@ class _DDSGeneric:
self.channel = channel
self.phase_mode = PHASE_MODE_CONTINUOUS
@portable(flags=["fast-math"])
def frequency_to_ftw(self, frequency):
"""Returns the frequency tuning word corresponding to the given
frequency.
"""
return round(int(2, width=64)**32*frequency/self.core_dds.sysclk)
@portable(flags=["fast-math"])
def ftw_to_frequency(self, ftw):
"""Returns the frequency corresponding to the given frequency tuning
word.
"""
return ftw*self.core_dds.sysclk/int(2, width=64)**32
@portable(flags=["fast-math"])
def turns_to_pow(self, turns):
"""Returns the phase offset word corresponding to the given phase
in turns."""
return round(turns*2**self.pow_width)
@portable(flags=["fast-math"])
def pow_to_turns(self, pow):
"""Returns the phase in turns corresponding to the given phase offset
word."""
return pow/2**self.pow_width
@portable(flags=["fast-math"])
def amplitude_to_asf(self, amplitude):
"""Returns amplitude scale factor corresponding to given amplitude."""
return round(amplitude*0x0fff)
@portable(flags=["fast-math"])
def asf_to_amplitude(self, asf):
"""Returns the amplitude corresponding to the given amplitude scale
factor."""
return asf/0x0fff
@kernel
def init(self):
"""Resets and initializes the DDS channel.
@ -154,7 +190,7 @@ class _DDSGeneric:
initializing multiple DDS channels is to call this function
sequentially with a delay between the calls. 2ms provides a good
timing margin."""
dds_init(now_mu(), self.bus_channel, self.channel)
self.core_dds.init(self.bus_channel, self.channel)
@kernel
def set_phase_mode(self, phase_mode):
@ -196,29 +232,147 @@ class _DDSGeneric:
"""
if phase_mode == _PHASE_MODE_DEFAULT:
phase_mode = self.phase_mode
dds_set(now_mu(), self.bus_channel, self.channel,
frequency, phase, phase_mode, amplitude)
self.core_dds.set(self.bus_channel, self.channel, frequency, phase, phase_mode, amplitude)
@kernel
def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT,
amplitude=1.0):
"""Like ``set_mu``, but uses Hz and turns."""
self.set_mu(self.frequency_to_ftw(frequency),
self.turns_to_pow(phase), phase_mode,
self.amplitude_to_asf(amplitude))
self.set_mu(self.core_dds.frequency_to_ftw(frequency),
self.core_dds.turns_to_pow(phase), phase_mode,
self.core_dds.amplitude_to_asf(amplitude))
class AD9858(_DDSGeneric):
"""Driver for AD9858 DDS chips. See ``_DDSGeneric`` for a description
AD9914_REG_CFR1L = 0x01
AD9914_REG_CFR1H = 0x03
AD9914_REG_CFR2L = 0x05
AD9914_REG_CFR2H = 0x07
AD9914_REG_CFR3L = 0x09
AD9914_REG_CFR3H = 0x0b
AD9914_REG_CFR4L = 0x0d
AD9914_REG_CFR4H = 0x0f
AD9914_REG_FTWL = 0x2d
AD9914_REG_FTWH = 0x2f
AD9914_REG_POW = 0x31
AD9914_REG_ASF = 0x33
AD9914_REG_USR0 = 0x6d
AD9914_FUD = 0x80
AD9914_GPIO = 0x81
class DDSGroupAD9914(DDSGroup):
"""Driver for AD9914 DDS chips. See ``DDSGroup`` for a description
of the functionality."""
pow_width = 14
kernel_invariants = DDSGroup.kernel_invariants.union({
"pow_width", "rtio_period_mu", "sysclk_per_mu", "write_duration_mu", "dac_cal_duration_mu",
"init_duration_mu", "init_sync_duration_mu", "program_duration_mu",
"first_dds_bus_channel", "dds_channel_count", "continuous_phase_comp"
})
class AD9914(_DDSGeneric):
"""Driver for AD9914 DDS chips. See ``_DDSGeneric`` for a description
of the functionality."""
pow_width = 16
def __init__(self, *args, first_dds_bus_channel, dds_bus_count, dds_channel_count, **kwargs):
super().__init__(*args, **kwargs)
self.first_dds_bus_channel = first_dds_bus_channel
self.dds_bus_count = dds_bus_count
self.dds_channel_count = dds_channel_count
self.rtio_period_mu = int64(8)
self.sysclk_per_mu = int32(self.sysclk * self.core.ref_period)
self.write_duration_mu = 5 * self.rtio_period_mu
self.dac_cal_duration_mu = 147000 * self.rtio_period_mu
self.init_duration_mu = 8 * self.write_duration_mu + self.dac_cal_duration_mu
self.init_sync_duration_mu = 16 * self.write_duration_mu + 2 * self.dac_cal_duration_mu
self.program_duration_mu = 6 * self.write_duration_mu
self.continuous_phase_comp = [0] * (self.dds_bus_count * self.dds_channel_count)
@kernel
def batch_duration_mu(self):
return self.batch.count * (self.program_duration_mu +
self.write_duration_mu) # + FUD time
@kernel
def write(self, bus_channel, addr, data):
rtio_output(now_mu(), bus_channel, addr, data)
delay_mu(self.write_duration_mu)
@kernel
def init(self, bus_channel, channel):
delay_mu(-self.init_duration_mu)
self.write(bus_channel, AD9914_GPIO, (1 << channel) << 1);
self.write(bus_channel, AD9914_REG_CFR1H, 0x0000) # Enable cosine output
self.write(bus_channel, AD9914_REG_CFR2L, 0x8900) # Enable matched latency
self.write(bus_channel, AD9914_REG_CFR2H, 0x0080) # Enable profile mode
self.write(bus_channel, AD9914_REG_ASF, 0x0fff) # Set amplitude to maximum
self.write(bus_channel, AD9914_REG_CFR4H, 0x0105) # Enable DAC calibration
self.write(bus_channel, AD9914_FUD, 0)
delay_mu(self.dac_cal_duration_mu)
self.write(bus_channel, AD9914_REG_CFR4H, 0x0005) # Disable DAC calibration
self.write(bus_channel, AD9914_FUD, 0)
@kernel
def init_sync(self, bus_channel, channel, sync_delay):
delay_mu(-self.init_sync_duration_mu)
self.write(bus_channel, AD9914_GPIO, (1 << channel) << 1)
self.write(bus_channel, AD9914_REG_CFR4H, 0x0105) # Enable DAC calibration
self.write(bus_channel, AD9914_FUD, 0)
delay_mu(self.dac_cal_duration_mu)
self.write(bus_channel, AD9914_REG_CFR4H, 0x0005) # Disable DAC calibration
self.write(bus_channel, AD9914_FUD, 0)
self.write(bus_channel, AD9914_REG_CFR2L, 0x8b00) # Enable matched latency and sync_out
self.write(bus_channel, AD9914_FUD, 0)
# Set cal with sync and set sync_out and sync_in delay
self.write(bus_channel, AD9914_REG_USR0, 0x0840 | (sync_delay & 0x3f))
self.write(bus_channel, AD9914_FUD, 0)
self.write(bus_channel, AD9914_REG_CFR4H, 0x0105) # Enable DAC calibration
self.write(bus_channel, AD9914_FUD, 0)
delay_mu(self.dac_cal_duration_mu)
self.write(bus_channel, AD9914_REG_CFR4H, 0x0005) # Disable DAC calibration
self.write(bus_channel, AD9914_FUD, 0)
self.write(bus_channel, AD9914_REG_CFR1H, 0x0000) # Enable cosine output
self.write(bus_channel, AD9914_REG_CFR2H, 0x0080) # Enable profile mode
self.write(bus_channel, AD9914_REG_ASF, 0x0fff) # Set amplitude to maximum
self.write(bus_channel, AD9914_FUD, 0)
@kernel
def program(self, ref_time, bus_channel, channel, ftw, pow, phase_mode, amplitude):
self.write(bus_channel, AD9914_GPIO, (1 << channel) << 1)
self.write(bus_channel, AD9914_REG_FTWL, ftw & 0xffff)
self.write(bus_channel, AD9914_REG_FTWH, (ftw >> 16) & 0xffff)
# We need the RTIO fine timestamp clock to be phase-locked
# to DDS SYSCLK, and divided by an integer self.sysclk_per_mu.
dds_bus_index = bus_channel - self.first_dds_bus_channel
phase_comp_index = dds_bus_index * self.dds_channel_count + channel
if phase_mode == PHASE_MODE_CONTINUOUS:
# Do not clear phase accumulator on FUD
# Disable autoclear phase accumulator and enables OSK.
self.write(bus_channel, AD9914_REG_CFR1L, 0x0108)
pow += self.continuous_phase_comp[phase_comp_index]
else:
# Clear phase accumulator on FUD
# Enable autoclear phase accumulator and enables OSK.
self.write(bus_channel, AD9914_REG_CFR1L, 0x2108)
fud_time = now_mu() + 2 * self.write_duration_mu
pow -= int32((ref_time - fud_time) * self.sysclk_per_mu * ftw >> (32 - self.pow_width))
if phase_mode == PHASE_MODE_TRACKING:
pow += int32(ref_time * self.sysclk_per_mu * ftw >> (32 - self.pow_width))
self.continuous_phase_comp[phase_comp_index] = pow
self.write(bus_channel, AD9914_REG_POW, pow)
self.write(bus_channel, AD9914_REG_ASF, amplitude)
self.write(bus_channel, AD9914_FUD, 0)
class DDSChannelAD9914(DDSChannel):
"""Driver for AD9914 DDS chips. See ``DDSChannel`` for a description
of the functionality."""
@kernel
def init_sync(self, sync_delay=0):
"""Resets and initializes the DDS channel as well as configures
@ -236,4 +390,4 @@ class AD9914(_DDSGeneric):
:param sync_delay: integer from 0 to 0x3f that sets the value of
SYNC_OUT (bits 3-5) and SYNC_IN (bits 0-2) delay ADJ bits.
"""
dds_init_sync(now_mu(), self.bus_channel, self.channel, sync_delay)
self.core_dds.init_sync(self.bus_channel, self.channel, sync_delay)

View File

@ -125,7 +125,6 @@ class DDSError(Exception):
when too many commands are batched, and when DDS channel settings are
incorrect.
"""
artiq_builtin = True
class I2CError(Exception):
"""Raised with a I2C transaction fails."""

View File

@ -1,7 +1,6 @@
import numpy
from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu,
delay_mu, mu_to_seconds)
from artiq.language.core import (kernel, portable, now_mu, delay_mu)
from artiq.language.units import MHz
from artiq.coredevice.rtio import rtio_output, rtio_input_data
@ -59,8 +58,7 @@ class SPIMaster:
"""
def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device)
self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period,
self.core)
self.ref_period_mu = self.core.seconds_to_mu(self.core.coarse_ref_period)
self.channel = channel
self.write_period_mu = numpy.int64(0)
self.read_period_mu = numpy.int64(0)
@ -68,7 +66,7 @@ class SPIMaster:
@portable
def frequency_to_div(self, f):
return int(1/(f*mu_to_seconds(self.ref_period_mu))) + 1
return int(1/(f*self.core.mu_to_seconds(self.ref_period_mu))) + 1
@kernel
def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz):

View File

@ -219,10 +219,10 @@ class _DeviceManager:
self.ttl_widgets[k] = widget
self.ttl_cb()
if (v["module"] == "artiq.coredevice.dds"
and v["class"] == "CoreDDS"):
and v["class"] == "DDSGroupAD9914"):
self.dds_sysclk = v["arguments"]["sysclk"]
if (v["module"] == "artiq.coredevice.dds"
and v["class"] in {"AD9858", "AD9914"}):
and v["class"] in {"DDSChannelAD9914"}):
bus_channel = v["arguments"]["bus_channel"]
channel = v["arguments"]["channel"]
widget = _DDSWidget(

View File

@ -94,7 +94,7 @@ class _Frame:
def _arm(self):
self.segment_delays = [
seconds_to_mu(s.duration*delay_margin_factor, self.core)
self.core.seconds_to_mu(s.duration*delay_margin_factor)
for s in self.segments]
def _invalidate(self):
@ -125,7 +125,7 @@ class _Frame:
raise ArmError()
call_t = now_mu()
trigger_start_t = call_t - seconds_to_mu(trigger_duration/2)
trigger_start_t = call_t - self.core.seconds_to_mu(trigger_duration/2)
if self.pdq.current_frame >= 0:
# PDQ is in the middle of a frame. Check it is us.
@ -136,7 +136,7 @@ class _Frame:
# to play our first segment.
self.pdq.current_frame = self.frame_number
self.pdq.next_segment = 0
at_mu(trigger_start_t - seconds_to_mu(frame_setup))
at_mu(trigger_start_t - self.core.seconds_to_mu(frame_setup))
self.pdq.frame0.set_o(bool(self.frame_number & 1))
self.pdq.frame1.set_o(bool((self.frame_number & 2) >> 1))
self.pdq.frame2.set_o(bool((self.frame_number & 4) >> 2))

View File

@ -23,8 +23,13 @@
"core_dds": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "CoreDDS",
"arguments": {"sysclk": 3e9}
"class": "DDSGroupAD9914",
"arguments": {
"sysclk": 3e9,
"first_dds_bus_channel": 26,
"dds_bus_count": 2,
"dds_channel_count": 3
}
},
"i2c_switch": {
@ -136,20 +141,20 @@
"dds0": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "AD9914",
"class": "DDSChannelAD9914",
"arguments": {"bus_channel": 26, "channel": 0},
"comment": "Comments work in DDS panel as well"
},
"dds1": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "AD9914",
"class": "DDSChannelAD9914",
"arguments": {"bus_channel": 26, "channel": 1}
},
"dds2": {
"type": "local",
"module": "artiq.coredevice.dds",
"class": "AD9914",
"class": "DDSChannelAD9914",
"arguments": {"bus_channel": 26, "channel": 2}
},

View File

@ -8,7 +8,7 @@ class IdleKernel(EnvExperiment):
@kernel
def run(self):
start_time = now_mu() + seconds_to_mu(500*ms)
start_time = now_mu() + self.core.seconds_to_mu(500*ms)
while self.core.get_rtio_counter_mu() < start_time:
pass
self.core.reset()

View File

@ -42,7 +42,7 @@ class TDR(EnvExperiment):
pulse = 1e-6 # pulse length, larger than rtt
self.t = [0 for i in range(2)]
try:
self.many(n, seconds_to_mu(pulse, self.core))
self.many(n, self.core.seconds_to_mu(pulse))
except PulseNotReceivedError:
print("to few edges: cable too long or wiring bad")
else:

View File

@ -15,7 +15,7 @@ class DDSSetter(EnvExperiment):
if (isinstance(v, dict)
and v["type"] == "local"
and v["module"] == "artiq.coredevice.dds"
and v["class"] in {"AD9858", "AD9914"}):
and v["class"] in {"DDSChannelAD9914"}):
self.dds[k] = {
"driver": self.get_device(k),
"frequency": self.get_argument(

View File

@ -76,7 +76,7 @@ class DACSetup(EnvExperiment):
@kernel
def busywait_us(self, t):
t = self.core.get_rtio_counter_mu() + seconds_to_mu(t*us)
t = self.core.get_rtio_counter_mu() + self.core.seconds_to_mu(t*us)
while self.core.get_rtio_counter_mu() < t:
pass

View File

@ -15,7 +15,7 @@ class Test(EnvExperiment):
@kernel
def busywait_us(self, t):
t = self.core.get_rtio_counter_mu() + seconds_to_mu(t*us)
t = self.core.get_rtio_counter_mu() + self.core.seconds_to_mu(t*us)
while self.core.get_rtio_counter_mu() < t:
pass

View File

@ -78,7 +78,7 @@ def main():
char = chan.read(1)
if char == b"":
break
sys.stderr.write(char.decode("utf-8"))
sys.stderr.write(char.decode("utf-8", errors='replace'))
for action in args.actions:
if action == "build":
@ -116,6 +116,10 @@ def main():
local_stream, peer_addr = listener.accept()
logger.info("Accepting %s:%s and opening SSH channel to %s:%s",
*peer_addr, args.ip, port)
if get_ssh().get_transport() is None:
logger.error("Trying to open a channel before the transport is ready!")
continue
remote_stream = get_ssh().get_transport() \
.open_channel('direct-tcpip', (args.ip, port), peer_addr)
while True:

View File

@ -24,13 +24,6 @@ class AD9xxx(Module):
Design:
All IO pads are registered.
With QC1 adapter:
LVDS driver/receiver propagation delays are 3.6+4.5 ns max
LVDS state transition delays are 20, 15 ns max
Schmitt trigger delays are 6.4ns max
Round-trip addr A setup (> RX, RD, D to Z), RD prop, D valid (< D
valid), D prop is ~15 + 10 + 20 + 10 = 55ns
"""
def __init__(self, pads,
read_wait_cycles=10, hiz_wait_cycles=3,

View File

@ -1,94 +0,0 @@
from migen.build.generic_platform import *
papilio_adapter_io = [
("ext_led", 0, Pins("B:7"), IOStandard("LVTTL")),
# to feed the 125 MHz clock (preferrably from DDS SYNC_CLK)
# to the FPGA, use the xtrig pair.
#
# on papiliopro-adapter, xtrig (C:12) is connected to a GCLK
#
# on pipistrello, C:15 is the only GCLK in proximity, used as a button
# input, BTN2/PMT2 in papiliopro-adapter
# either improve the DDS box to feed 125MHz into the PMT2 pair, or:
#
# * disconnect C:15 from its periphery on the adapter board
# * bridge C:15 to the xtrig output of the transciever
# * optionally, disconnect C:12 from its periphery
("xtrig", 0, Pins("C:12"), IOStandard("LVTTL")),
("pmt", 0, Pins("C:13"), IOStandard("LVTTL")),
("pmt", 1, Pins("C:14"), IOStandard("LVTTL")),
("pmt", 2, Pins("C:15"), IOStandard("LVTTL")), # rarely equipped
("ttl", 0, Pins("C:11"), IOStandard("LVTTL")),
("ttl", 1, Pins("C:10"), IOStandard("LVTTL")),
("ttl", 2, Pins("C:9"), IOStandard("LVTTL")),
("ttl", 3, Pins("C:8"), IOStandard("LVTTL")),
("ttl", 4, Pins("C:7"), IOStandard("LVTTL")),
("ttl", 5, Pins("C:6"), IOStandard("LVTTL")),
("ttl", 6, Pins("C:5"), IOStandard("LVTTL")),
("ttl", 7, Pins("C:4"), IOStandard("LVTTL")),
("ttl_l_tx_en", 0, Pins("A:9"), IOStandard("LVTTL")),
("ttl", 8, Pins("C:3"), IOStandard("LVTTL")),
("ttl", 9, Pins("C:2"), IOStandard("LVTTL")),
("ttl", 10, Pins("C:1"), IOStandard("LVTTL")),
("ttl", 11, Pins("C:0"), IOStandard("LVTTL")),
("ttl", 12, Pins("B:4"), IOStandard("LVTTL")),
("ttl", 13, Pins("A:11"), IOStandard("LVTTL")),
("ttl", 14, Pins("B:5"), IOStandard("LVTTL")),
("ttl", 15, Pins("A:10"), IOStandard("LVTTL")),
("ttl_h_tx_en", 0, Pins("B:6"), IOStandard("LVTTL")),
("dds", 0,
Subsignal("a", Pins("A:5 B:10 A:6 B:9 A:7 B:8")),
Subsignal("d", Pins("A:12 B:3 A:13 B:2 A:14 B:1 A:15 B:0")),
Subsignal("sel", Pins("A:2 B:14 A:1 B:15 A:0")),
Subsignal("p", Pins("A:8 B:12")),
Subsignal("fud_n", Pins("B:11")),
Subsignal("wr_n", Pins("A:4")),
Subsignal("rd_n", Pins("B:13")),
Subsignal("rst_n", Pins("A:3")),
IOStandard("LVTTL")),
]
fmc_adapter_io = [
("pmt", 0, Pins("LPC:LA20_N"), IOStandard("LVTTL")),
("pmt", 1, Pins("LPC:LA24_P"), IOStandard("LVTTL")),
("ttl", 0, Pins("LPC:LA21_P"), IOStandard("LVTTL")),
("ttl", 1, Pins("LPC:LA25_P"), IOStandard("LVTTL")),
("ttl", 2, Pins("LPC:LA21_N"), IOStandard("LVTTL")),
("ttl", 3, Pins("LPC:LA25_N"), IOStandard("LVTTL")),
("ttl", 4, Pins("LPC:LA22_P"), IOStandard("LVTTL")),
("ttl", 5, Pins("LPC:LA26_P"), IOStandard("LVTTL")),
("ttl", 6, Pins("LPC:LA22_N"), IOStandard("LVTTL")),
("ttl", 7, Pins("LPC:LA26_N"), IOStandard("LVTTL")),
("ttl", 8, Pins("LPC:LA23_P"), IOStandard("LVTTL")),
("ttl", 9, Pins("LPC:LA27_P"), IOStandard("LVTTL")),
("ttl", 10, Pins("LPC:LA23_N"), IOStandard("LVTTL")),
("ttl", 11, Pins("LPC:LA27_N"), IOStandard("LVTTL")),
("ttl", 12, Pins("LPC:LA00_CC_P"), IOStandard("LVTTL")),
("ttl", 13, Pins("LPC:LA10_P"), IOStandard("LVTTL")),
("ttl", 14, Pins("LPC:LA00_CC_N"), IOStandard("LVTTL")),
("ttl", 15, Pins("LPC:LA10_N"), IOStandard("LVTTL")),
("ttl_l_tx_en", 0, Pins("LPC:LA11_P"), IOStandard("LVTTL")),
("ttl_h_tx_en", 0, Pins("LPC:LA01_CC_P"), IOStandard("LVTTL")),
("dds", 0,
Subsignal("a", Pins("LPC:LA04_N LPC:LA14_N LPC:LA05_P LPC:LA15_P "
"LPC:LA05_N LPC:LA15_N")),
Subsignal("d", Pins("LPC:LA06_P LPC:LA16_P LPC:LA06_N LPC:LA16_N "
"LPC:LA07_P LPC:LA17_CC_P LPC:LA07_N "
"LPC:LA17_CC_N")),
Subsignal("sel", Pins("LPC:LA12_N LPC:LA03_P LPC:LA13_P LPC:LA03_N "
"LPC:LA13_N")),
Subsignal("p", Pins("LPC:LA11_N LPC:LA02_P")),
Subsignal("fud_n", Pins("LPC:LA14_P")),
Subsignal("wr_n", Pins("LPC:LA04_P")),
Subsignal("rd_n", Pins("LPC:LA02_N")),
Subsignal("rst_n", Pins("LPC:LA12_P")),
IOStandard("LVTTL")),
]

View File

@ -56,11 +56,6 @@ class _AD9xxx(Module):
for c, (probe, ftw) in enumerate(zip(self.probes, ftws))])
class AD9858(_AD9xxx):
def __init__(self, *args, **kwargs):
_AD9xxx.__init__(self, 0x0a, *args, **kwargs)
class AD9914(_AD9xxx):
def __init__(self, *args, **kwargs):
_AD9xxx.__init__(self, 0x2d, *args, **kwargs)

View File

@ -27,7 +27,7 @@ from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict
from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.soc import AMPSoC, build_artiq_soc
from artiq.gateware import rtio, nist_qc1, nist_clock, nist_qc2, phaser
from artiq.gateware import rtio, nist_clock, nist_qc2, phaser
from artiq.gateware.rtio.phy import (ttl_simple, ttl_serdes_7series,
dds, spi, sawg)
from artiq import __version__ as artiq_version
@ -155,7 +155,6 @@ class _NIST_Ions(MiniSoC, AMPSoC):
self.csr_devices.append("rtio_crg")
self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio")
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
@ -170,62 +169,6 @@ class _NIST_Ions(MiniSoC, AMPSoC):
self.csr_devices.append("rtio_analyzer")
class NIST_QC1(_NIST_Ions):
"""
NIST QC1 hardware, as used in the Penning lab, with FMC to SCSI cables
adapter.
"""
def __init__(self, cpu_type="or1k", **kwargs):
_NIST_Ions.__init__(self, cpu_type, **kwargs)
platform = self.platform
platform.add_extension(nist_qc1.fmc_adapter_io)
self.comb += [
platform.request("ttl_l_tx_en").eq(1),
platform.request("ttl_h_tx_en").eq(1)
]
rtio_channels = []
for i in range(2):
phy = ttl_serdes_7series.Inout_8X(platform.request("pmt", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
for i in range(15):
phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = ttl_serdes_7series.Inout_8X(platform.request("user_sma_gpio_n_33"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)
phy = ttl_simple.ClockGen(platform.request("ttl", 15))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 8
self.config["DDS_AD9858"] = None
phy = dds.AD9858(platform.request("dds"), 8)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,
ififo_depth=4))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
assert self.rtio.fine_ts_width <= 3
self.config["DDS_RTIO_CLK_RATIO"] = 8 >> self.rtio.fine_ts_width
class NIST_CLOCK(_NIST_Ions):
"""
NIST clock hardware, with old backplane and 11 DDS channels
@ -606,19 +549,17 @@ class Phaser(MiniSoC, AMPSoC):
def main():
parser = argparse.ArgumentParser(
description="ARTIQ core device builder / KC705 "
"+ NIST Ions QC1/CLOCK/QC2 hardware adapters")
"+ NIST Ions CLOCK/QC2 hardware adapters")
builder_args(parser)
soc_kc705_args(parser)
parser.add_argument("-H", "--hw-adapter", default="nist_clock",
help="hardware adapter type: "
"nist_qc1/nist_clock/nist_qc2/phaser "
"nist_clock/nist_qc2/phaser "
"(default: %(default)s)")
args = parser.parse_args()
hw_adapter = args.hw_adapter.lower()
if hw_adapter == "nist_qc1":
cls = NIST_QC1
elif hw_adapter == "nist_clock":
if hw_adapter == "nist_clock":
cls = NIST_CLOCK
elif hw_adapter == "nist_qc2":
cls = NIST_QC2

View File

@ -18,7 +18,7 @@ from misoc.targets.pipistrello import (BaseSoC, soc_pipistrello_args,
from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.soc import AMPSoC, build_artiq_soc
from artiq.gateware import rtio, nist_qc1
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds, spi
from artiq import __version__ as artiq_version
@ -61,14 +61,14 @@ class _RTIOCRG(Module, AutoCSR):
f = Fraction(rtio_f, clk_freq)
rtio_internal_clk = Signal()
rtio_external_clk = Signal()
pmt2 = platform.request("pmt", 2)
ext_clk = platform.request("ext_clk")
dcm_locked = Signal()
rtio_clk = Signal()
pll_locked = Signal()
pll = Signal(3)
pll_fb = Signal()
self.specials += [
Instance("IBUFG", i_I=pmt2, o_O=rtio_external_clk),
Instance("IBUFG", i_I=ext_clk, o_O=rtio_external_clk),
Instance("DCM_CLKGEN", p_CLKFXDV_DIVIDE=2,
p_CLKFX_DIVIDE=f.denominator, p_CLKFX_MD_MAX=float(f),
p_CLKFX_MULTIPLY=f.numerator, p_CLKIN_PERIOD=1e9/clk_freq,
@ -124,7 +124,30 @@ TIMESPEC "TSfix_ise4" = FROM "GRPsys_clk" TO "GRPrtio_clk" TIG;
rtio_clk=self.cd_rtio.clk)
class NIST_QC1(BaseSoC, AMPSoC):
_ttl_io = [
("ext_clk", 0, Pins("C:15"), IOStandard("LVTTL")),
("ttl", 0, Pins("B:0"), IOStandard("LVTTL")),
("ttl", 1, Pins("B:1"), IOStandard("LVTTL")),
("ttl", 2, Pins("B:2"), IOStandard("LVTTL")),
("ttl", 3, Pins("B:3"), IOStandard("LVTTL")),
("ttl", 4, Pins("B:4"), IOStandard("LVTTL")),
("ttl", 5, Pins("B:5"), IOStandard("LVTTL")),
("ttl", 6, Pins("B:6"), IOStandard("LVTTL")),
("ttl", 7, Pins("B:7"), IOStandard("LVTTL")),
("ttl", 8, Pins("B:8"), IOStandard("LVTTL")),
("ttl", 9, Pins("B:9"), IOStandard("LVTTL")),
("ttl", 10, Pins("B:10"), IOStandard("LVTTL")),
("ttl", 11, Pins("B:11"), IOStandard("LVTTL")),
("ttl", 12, Pins("B:12"), IOStandard("LVTTL")),
("ttl", 13, Pins("B:13"), IOStandard("LVTTL")),
("ttl", 14, Pins("B:14"), IOStandard("LVTTL")),
("ttl", 15, Pins("B:15"), IOStandard("LVTTL")),
]
class Demo(BaseSoC, AMPSoC):
mem_map = {
"timer_kernel": 0x10000000, # (shadow @0x90000000)
"rtio": 0x20000000, # (shadow @0xa0000000)
@ -148,35 +171,22 @@ class NIST_QC1(BaseSoC, AMPSoC):
platform.toolchain.ise_commands += """
trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd {build_name}.pcf
"""
platform.add_extension(nist_qc1.papilio_adapter_io)
platform.add_extension(_ttl_io)
platform.add_extension(_pmod_spi)
self.submodules.leds = gpio.GPIOOut(platform.request("user_led", 4))
self.comb += [
platform.request("ttl_l_tx_en").eq(1),
platform.request("ttl_h_tx_en").eq(1)
]
self.submodules.rtio_crg = _RTIOCRG(platform, self.clk_freq)
self.csr_devices.append("rtio_crg")
# RTIO channels
rtio_channels = []
# pmt1 can run on a 8x serdes if pmt0 is not used
for i in range(2):
phy = ttl_serdes_spartan6.Inout_4X(platform.request("pmt", i),
self.rtio_crg.rtiox4_stb)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128,
ofifo_depth=4))
# the last TTL is used for ClockGen
for i in range(15):
if i in (0, 1):
phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i),
phy = ttl_serdes_spartan6.Inout_4X(platform.request("ttl", i),
self.rtio_crg.rtiox4_stb)
elif i in (2,): # ttl2 can run on a 8x serdes if xtrig is not used
elif i in (2,):
phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i),
self.rtio_crg.rtiox8_stb)
else:
@ -185,10 +195,6 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=128))
phy = ttl_simple.Output(platform.request("ext_led", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=4))
for led_number in range(4):
phy = ttl_simple.Output(platform.request("user_led", led_number))
self.submodules += phy
@ -206,27 +212,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=64, ififo_depth=64))
self.config["HAS_DDS"] = None
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 8
self.config["DDS_AD9858"] = None
dds_pins = platform.request("dds")
self.comb += dds_pins.p.eq(0)
phy = dds.AD9858(dds_pins, 8)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=128,
ififo_depth=4))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
# RTIO logic
self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio")
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
self.config["DDS_RTIO_CLK_RATIO"] = 8 >> self.rtio.fine_ts_width
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = rtio.Analyzer(
@ -236,13 +227,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
def main():
parser = argparse.ArgumentParser(
description="ARTIQ core device builder / Pipistrello "
"+ NIST Ions QC1 hardware adapter")
description="ARTIQ core device builder / Pipistrello demo")
builder_args(parser)
soc_pipistrello_args(parser)
args = parser.parse_args()
soc = NIST_QC1(**soc_pipistrello_argdict(args))
soc = Demo(**soc_pipistrello_argdict(args))
build_artiq_soc(soc, builder_argdict(args))

View File

@ -15,7 +15,6 @@ __all__ = ["kernel", "portable", "rpc", "syscall", "host_only",
kernel_globals = (
"sequential", "parallel", "interleave",
"delay_mu", "now_mu", "at_mu", "delay",
"seconds_to_mu", "mu_to_seconds",
"watchdog"
)
__all__.extend(kernel_globals)
@ -213,31 +212,6 @@ def delay(duration):
_time_manager.take_time(duration)
def seconds_to_mu(seconds, core=None):
"""Converts seconds to the corresponding number of machine units
(RTIO cycles).
:param seconds: time (in seconds) to convert.
:param core: core device for which to perform the conversion. Specify only
when running in the interpreter (not in kernel).
"""
if core is None:
raise ValueError("Core device must be specified for time conversion")
return numpy.int64(seconds//core.ref_period)
def mu_to_seconds(mu, core=None):
"""Converts machine units (RTIO cycles) to seconds.
:param mu: cycle count to convert.
:param core: core device for which to perform the conversion. Specify only
when running in the interpreter (not in kernel).
"""
if core is None:
raise ValueError("Core device must be specified for time conversion")
return mu*core.ref_period
class _DummyWatchdog:
def __init__(self, timeout):
pass

View File

@ -13,8 +13,10 @@ fn main() {
let dest_path = Path::new(&out_dir).join("git_info.rs");
let mut f = File::create(&dest_path).unwrap();
writeln!(f, "const GIT_COMMIT: &'static str = {:?};",
git_describe().unwrap()).unwrap();
let id = git_describe().unwrap();
let id = id.split("-").collect::<Vec<_>>();
let id = format!("{}+{}.{}", id[0], id[1], id[2]);
writeln!(f, "const GIT_COMMIT: &'static str = {:?};", id).unwrap();
println!("cargo:rerun-if-changed=../../.git/HEAD");
for entry in WalkDir::new("../../.git/refs") {

View File

@ -105,17 +105,6 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(rtio_input_timestamp),
api!(rtio_input_data),
#[cfg(has_dds)]
api!(dds_init),
#[cfg(has_dds)]
api!(dds_init_sync),
#[cfg(has_dds)]
api!(dds_batch_enter),
#[cfg(has_dds)]
api!(dds_batch_exit),
#[cfg(has_dds)]
api!(dds_set),
api!(i2c_init),
api!(i2c_start),
api!(i2c_stop),

View File

@ -4,7 +4,7 @@ include $(MISOC_DIRECTORY)/software/common.mak
PYTHON ?= python3.5
OBJECTS := flash_storage.o main.o
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o ad9154.o
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o i2c.o ad9154.o
RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b

View File

@ -1,263 +0,0 @@
#include <generated/csr.h>
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
#include <stdio.h>
#include "artiq_personality.h"
#include "rtio.h"
#include "dds.h"
#define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH)
#if defined CONFIG_DDS_AD9858
/* Assume 8-bit bus */
#define DURATION_INIT (7*DURATION_WRITE) /* not counting FUD */
#define DURATION_PROGRAM (8*DURATION_WRITE) /* not counting FUD */
#elif defined CONFIG_DDS_AD9914
/* Assume 16-bit bus */
/* DAC calibration takes max. 1ms as per datasheet */
#define DURATION_DAC_CAL (147000 << CONFIG_RTIO_FINE_TS_WIDTH)
/* not counting final FUD */
#define DURATION_INIT (8*DURATION_WRITE + DURATION_DAC_CAL)
#define DURATION_INIT_SYNC (16*DURATION_WRITE + 2*DURATION_DAC_CAL)
#define DURATION_PROGRAM (6*DURATION_WRITE) /* not counting FUD */
#else
#error Unknown DDS configuration
#endif
#define DDS_WRITE(addr, data) do { \
rtio_output(now, bus_channel, addr, data); \
now += DURATION_WRITE; \
} while(0)
void dds_init(long long int timestamp, int bus_channel, int channel)
{
long long int now;
now = timestamp - DURATION_INIT;
#ifdef CONFIG_DDS_ONEHOT_SEL
channel = 1 << channel;
#endif
channel <<= 1;
DDS_WRITE(DDS_GPIO, channel);
#ifndef CONFIG_DDS_AD9914
/*
* Resetting a AD9914 intermittently crashes it. It does not produce any
* output until power-cycled.
* Increasing the reset pulse length and the delay until the first write
* to 300ns do not solve the problem.
* The chips seem fine without a reset.
*/
DDS_WRITE(DDS_GPIO, channel | 1); /* reset */
DDS_WRITE(DDS_GPIO, channel);
#endif
#ifdef CONFIG_DDS_AD9858
/*
* 2GHz divider disable
* SYNCLK disable
* Mixer power-down
* Phase detect power down
*/
DDS_WRITE(DDS_CFR0, 0x78);
DDS_WRITE(DDS_CFR1, 0x00);
DDS_WRITE(DDS_CFR2, 0x00);
DDS_WRITE(DDS_CFR3, 0x00);
DDS_WRITE(DDS_FUD, 0);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_CFR1H, 0x0000); /* Enable cosine output */
DDS_WRITE(DDS_CFR2L, 0x8900); /* Enable matched latency */
DDS_WRITE(DDS_CFR2H, 0x0080); /* Enable profile mode */
DDS_WRITE(DDS_ASF, 0x0fff); /* Set amplitude to maximum */
DDS_WRITE(DDS_CFR4H, 0x0105); /* Enable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005); /* Disable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
#endif
}
void dds_init_sync(long long int timestamp, int bus_channel, int channel, int sync_delay)
{
#ifdef CONFIG_DDS_AD9914
long long int now;
now = timestamp - DURATION_INIT_SYNC;
#ifdef CONFIG_DDS_ONEHOT_SEL
channel = 1 << channel;
#endif
channel <<= 1;
DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(DDS_CFR4H, 0x0105); /* Enable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005); /* Disable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
DDS_WRITE(DDS_CFR2L, 0x8b00); /* Enable matched latency and sync_out*/
DDS_WRITE(DDS_FUD, 0);
/* Set cal with sync and set sync_out and sync_in delay */
DDS_WRITE(DDS_USR0, 0x0840 | (sync_delay & 0x3f));
DDS_WRITE(DDS_FUD, 0);
DDS_WRITE(DDS_CFR4H, 0x0105); /* Enable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
now += DURATION_DAC_CAL;
DDS_WRITE(DDS_CFR4H, 0x0005); /* Disable DAC calibration */
DDS_WRITE(DDS_FUD, 0);
DDS_WRITE(DDS_CFR1H, 0x0000); /* Enable cosine output */
DDS_WRITE(DDS_CFR2H, 0x0080); /* Enable profile mode */
DDS_WRITE(DDS_ASF, 0x0fff); /* Set amplitude to maximum */
DDS_WRITE(DDS_FUD, 0);
#endif
}
/* Compensation to keep phase continuity when switching from absolute or tracking
* to continuous phase mode. */
static unsigned int continuous_phase_comp[CONFIG_RTIO_DDS_COUNT][CONFIG_DDS_CHANNELS_PER_BUS];
static void dds_set_one(long long int now, long long int ref_time,
int bus_channel, int channel,
unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude)
{
unsigned int channel_enc;
if((channel < 0) || (channel >= CONFIG_DDS_CHANNELS_PER_BUS))
artiq_raise_from_c("DDSError", "Attempted to set invalid DDS channel", 0, 0, 0);
if((bus_channel < CONFIG_RTIO_FIRST_DDS_CHANNEL)
|| (bus_channel >= (CONFIG_RTIO_FIRST_DDS_CHANNEL+CONFIG_RTIO_DDS_COUNT)))
artiq_raise_from_c("DDSError", "Attempted to use invalid DDS bus", 0, 0, 0);
#ifdef CONFIG_DDS_ONEHOT_SEL
channel_enc = 1 << channel;
#else
channel_enc = channel;
#endif
DDS_WRITE(DDS_GPIO, channel_enc << 1);
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_FTW0, ftw & 0xff);
DDS_WRITE(DDS_FTW1, (ftw >> 8) & 0xff);
DDS_WRITE(DDS_FTW2, (ftw >> 16) & 0xff);
DDS_WRITE(DDS_FTW3, (ftw >> 24) & 0xff);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_FTWL, ftw & 0xffff);
DDS_WRITE(DDS_FTWH, (ftw >> 16) & 0xffff);
#endif
/* We need the RTIO fine timestamp clock to be phase-locked
* to DDS SYSCLK, and divided by an integer CONFIG_DDS_RTIO_CLK_RATIO.
*/
if(phase_mode == PHASE_MODE_CONTINUOUS) {
/* Do not clear phase accumulator on FUD */
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_CFR2, 0x00);
#endif
#ifdef CONFIG_DDS_AD9914
/* Disable autoclear phase accumulator and enables OSK. */
DDS_WRITE(DDS_CFR1L, 0x0108);
#endif
pow += continuous_phase_comp[bus_channel-CONFIG_RTIO_FIRST_DDS_CHANNEL][channel];
} else {
long long int fud_time;
/* Clear phase accumulator on FUD */
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_CFR2, 0x40);
#endif
#ifdef CONFIG_DDS_AD9914
/* Enable autoclear phase accumulator and enables OSK. */
DDS_WRITE(DDS_CFR1L, 0x2108);
#endif
fud_time = now + 2*DURATION_WRITE;
pow -= (ref_time - fud_time)*CONFIG_DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH);
if(phase_mode == PHASE_MODE_TRACKING)
pow += ref_time*CONFIG_DDS_RTIO_CLK_RATIO*ftw >> (32-DDS_POW_WIDTH);
continuous_phase_comp[bus_channel-CONFIG_RTIO_FIRST_DDS_CHANNEL][channel] = pow;
}
#ifdef CONFIG_DDS_AD9858
DDS_WRITE(DDS_POW0, pow & 0xff);
DDS_WRITE(DDS_POW1, (pow >> 8) & 0x3f);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_POW, pow);
#endif
#ifdef CONFIG_DDS_AD9914
DDS_WRITE(DDS_ASF, amplitude);
#endif
DDS_WRITE(DDS_FUD, 0);
}
struct dds_set_params {
int bus_channel;
int channel;
unsigned int ftw;
unsigned int pow;
int phase_mode;
unsigned int amplitude;
};
static int batch_mode;
static int batch_count;
static long long int batch_ref_time;
static struct dds_set_params batch[DDS_MAX_BATCH];
void dds_batch_enter(long long int timestamp)
{
if(batch_mode)
artiq_raise_from_c("DDSError", "DDS batch entered twice", 0, 0, 0);
batch_mode = 1;
batch_count = 0;
batch_ref_time = timestamp;
}
void dds_batch_exit(void)
{
long long int now;
int i;
if(!batch_mode)
artiq_raise_from_c("DDSError", "DDS batch exited twice", 0, 0, 0);
batch_mode = 0;
/* + FUD time */
now = batch_ref_time - batch_count*(DURATION_PROGRAM + DURATION_WRITE);
for(i=0;i<batch_count;i++) {
dds_set_one(now, batch_ref_time,
batch[i].bus_channel, batch[i].channel,
batch[i].ftw, batch[i].pow, batch[i].phase_mode,
batch[i].amplitude);
now += DURATION_PROGRAM + DURATION_WRITE;
}
}
void dds_set(long long int timestamp, int bus_channel, int channel,
unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude)
{
if(batch_mode) {
if(batch_count >= DDS_MAX_BATCH)
artiq_raise_from_c("DDSError", "Too many commands in DDS batch", 0, 0, 0);
/* timestamp parameter ignored (determined by batch) */
batch[batch_count].bus_channel = bus_channel;
batch[batch_count].channel = channel;
batch[batch_count].ftw = ftw;
batch[batch_count].pow = pow;
batch[batch_count].phase_mode = phase_mode;
batch[batch_count].amplitude = amplitude;
batch_count++;
} else {
dds_set_one(timestamp - DURATION_PROGRAM, timestamp,
bus_channel, channel,
ftw, pow, phase_mode, amplitude);
}
}
#endif /* CONFIG_RTIO_DDS_COUNT */

View File

@ -1,70 +0,0 @@
#ifndef __DDS_H
#define __DDS_H
#include <hw/common.h>
#include <generated/csr.h>
#include <generated/mem.h>
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
/* Maximum number of commands in a batch */
#define DDS_MAX_BATCH 16
#ifdef CONFIG_DDS_AD9858
#define DDS_CFR0 0x00
#define DDS_CFR1 0x01
#define DDS_CFR2 0x02
#define DDS_CFR3 0x03
#define DDS_FTW0 0x0a
#define DDS_FTW1 0x0b
#define DDS_FTW2 0x0c
#define DDS_FTW3 0x0d
#define DDS_POW0 0x0e
#define DDS_POW1 0x0f
#define DDS_FUD 0x40
#define DDS_GPIO 0x41
#endif
#ifdef CONFIG_DDS_AD9914
#define DDS_CFR1L 0x01
#define DDS_CFR1H 0x03
#define DDS_CFR2L 0x05
#define DDS_CFR2H 0x07
#define DDS_CFR3L 0x09
#define DDS_CFR3H 0x0b
#define DDS_CFR4L 0x0d
#define DDS_CFR4H 0x0f
#define DDS_FTWL 0x2d
#define DDS_FTWH 0x2f
#define DDS_POW 0x31
#define DDS_ASF 0x33
#define DDS_USR0 0x6d
#define DDS_FUD 0x80
#define DDS_GPIO 0x81
#endif
#ifdef CONFIG_DDS_AD9858
#define DDS_POW_WIDTH 14
#endif
#ifdef CONFIG_DDS_AD9914
#define DDS_POW_WIDTH 16
#endif
enum {
PHASE_MODE_CONTINUOUS = 0,
PHASE_MODE_ABSOLUTE = 1,
PHASE_MODE_TRACKING = 2
};
void dds_init(long long int timestamp, int bus_channel, int channel);
void dds_init_sync(long long int timestamp, int bus_channel, int channel,
int sync_delay);
void dds_batch_enter(long long int timestamp);
void dds_batch_exit(void);
void dds_set(long long int timestamp, int bus_channel, int channel,
unsigned int ftw, unsigned int pow, int phase_mode, unsigned int amplitude);
#endif /* CONFIG_RTIO_DDS_COUNT */
#endif /* __DDS_H */

View File

@ -1,4 +1,5 @@
from random import Random
import numpy
from artiq.language.core import delay, at_mu, kernel
from artiq.sim import time
@ -18,6 +19,12 @@ class Core:
time.manager.timeline.clear()
return r
def seconds_to_mu(self, seconds):
return numpy.int64(seconds//self.ref_period)
def mu_to_seconds(self, mu):
return mu*self.ref_period
class Input:
def __init__(self, dmgr, name):

View File

@ -83,7 +83,7 @@ class _PulseLogger(EnvExperiment):
if not hasattr(self.parent_test, "first_timestamp"):
self.parent_test.first_timestamp = t
origin = self.parent_test.first_timestamp
t_usec = round(mu_to_seconds(t-origin, self.core)*1000000)
t_usec = round(self.core.mu_to_seconds(t-origin)*1000000)
self.parent_test.output_list.append((self.name, t_usec, l, f))
def on(self, t, f):

View File

@ -38,7 +38,7 @@ class RTT(EnvExperiment):
t1 = self.ttl_inout.timestamp_mu()
if t1 < 0:
raise PulseNotReceived()
self.set_dataset("rtt", mu_to_seconds(t1 - t0))
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
class Loopback(EnvExperiment):
@ -62,7 +62,7 @@ class Loopback(EnvExperiment):
t1 = self.loop_in.timestamp_mu()
if t1 < 0:
raise PulseNotReceived()
self.set_dataset("rtt", mu_to_seconds(t1 - t0))
self.set_dataset("rtt", self.core.mu_to_seconds(t1 - t0))
class ClockGeneratorLoopback(EnvExperiment):
@ -93,7 +93,7 @@ class PulseRate(EnvExperiment):
@kernel
def run(self):
self.core.reset()
dt = seconds_to_mu(300*ns)
dt = self.core.seconds_to_mu(300*ns)
while True:
for i in range(10000):
try:
@ -104,7 +104,7 @@ class PulseRate(EnvExperiment):
self.core.break_realtime()
break
else:
self.set_dataset("pulse_rate", mu_to_seconds(dt))
self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt))
return
@ -118,7 +118,7 @@ class PulseRateDDS(EnvExperiment):
@kernel
def run(self):
self.core.reset()
dt = seconds_to_mu(5*us)
dt = self.core.seconds_to_mu(5*us)
while True:
delay(10*ms)
for i in range(1250):
@ -132,7 +132,7 @@ class PulseRateDDS(EnvExperiment):
self.core.break_realtime()
break
else:
self.set_dataset("pulse_rate", mu_to_seconds(dt//2))
self.set_dataset("pulse_rate", self.core.mu_to_seconds(dt//2))
return
@ -403,7 +403,7 @@ class CoredeviceTest(ExperimentCase):
self.execute(TimeKeepsRunning)
t2 = self.dataset_mgr.get("time_at_start")
dead_time = mu_to_seconds(t2 - t1, self.device_mgr.get("core"))
dead_time = self.device_mgr.get("core").mu_to_seconds(t2 - t1)
print(dead_time)
self.assertGreater(dead_time, 1*ms)
self.assertLess(dead_time, 2500*ms)
@ -434,7 +434,7 @@ class RPCTiming(EnvExperiment):
t1 = self.core.get_rtio_counter_mu()
self.nop()
t2 = self.core.get_rtio_counter_mu()
self.ts[i] = mu_to_seconds(t2 - t1)
self.ts[i] = self.core.mu_to_seconds(t2 - t1)
def run(self):
self.ts = [0. for _ in range(self.repeats)]

View File

@ -0,0 +1,4 @@
# RUN: %python -m artiq.compiler.testbench.llvmgen %s
def f():
return float(1.0)

View File

@ -13,8 +13,8 @@ int()
# CHECK-L: int:<constructor int>(1.0:float):numpy.int?
int(1.0)
# CHECK-L: int:<constructor int>(1.0:float, width=64:numpy.int?):numpy.int64
int(1.0, width=64)
# CHECK-L: int64:<function int64>(1.0:float):numpy.int64
int64(1.0)
# CHECK-L: float:<constructor float {}>():float
float()

View File

@ -0,0 +1,5 @@
# RUN: %python -m artiq.compiler.testbench.inferencer +mono %s >%t
# RUN: OutputCheck %s --file-to-check=%t
# CHECK-L: numpy.int64
int64(2)**32

View File

@ -1,10 +1,6 @@
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
# RUN: OutputCheck %s --file-to-check=%t
a = 1
# CHECK-L: ${LINE:+1}: error: the width argument of int() must be an integer literal
int(1.0, width=a)
# CHECK-L: ${LINE:+1}: error: the argument of len() must be of an iterable type
len(1)

View File

@ -28,10 +28,10 @@
# CHECK-L: ${LINE:+1}: error: cannot coerce list(elt='a) to a numeric type
[] - 1.0
# CHECK-L: ${LINE:+2}: error: expression of type numpy.int? has to be coerced to float, which makes assignment invalid
# CHECK-L: ${LINE:+2}: error: the result of this operation has type float, which cannot be assigned to a left-hand side of type numpy.int?
# CHECK-L: ${LINE:+1}: note: expression of type float
a = 1; a += 1.0
# CHECK-L: ${LINE:+2}: error: the result of this operation has type (numpy.int?, float), which makes assignment to a slot of type (numpy.int?,) invalid
# CHECK-L: ${LINE:+2}: error: the result of this operation has type (numpy.int?, float), which cannot be assigned to a left-hand side of type (numpy.int?,)
# CHECK-L: ${LINE:+1}: note: expression of type (float,)
b = (1,); b += (1.0,)

View File

@ -7,7 +7,7 @@ assert bool() is False
assert int() is 0
assert int(1.0) is 1
#ARTIQ#assert int(1, width=64) << 40 is 1099511627776
#ARTIQ#assert int64(1) << 40 is 1099511627776
#ARTIQ#assert float() is 0.0
#ARTIQ#assert float(1) is 1.0

View File

@ -1,5 +0,0 @@
# RUN: %python -m artiq.compiler.testbench.jit %s
# REQUIRES: time
assert seconds_to_mu(2.0) == 2000000
assert mu_to_seconds(1500000) == 1.5

View File

@ -12,8 +12,8 @@ build:
requirements:
build:
- migen 0.4
- misoc 0.4
- migen 0.5.dev
- misoc 0.5.dev
- llvm-or1k
- binutils-or1k-linux >=2.27
- rust-core-or1k

View File

@ -1,14 +0,0 @@
#!/bin/bash
BUILD_SETTINGS_FILE=$HOME/.m-labs/build_settings.sh
[ -f $BUILD_SETTINGS_FILE ] && . $BUILD_SETTINGS_FILE
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/kc705-nist_qc1
mkdir -p $SOC_PREFIX
$PYTHON -m artiq.gateware.targets.kc705 -H nist_qc1 --toolchain vivado $MISOC_EXTRA_VIVADO_CMDLINE
cp misoc_nist_qc1_kc705/gateware/top.bit $SOC_PREFIX
cp misoc_nist_qc1_kc705/software/bios/bios.bin $SOC_PREFIX
cp misoc_nist_qc1_kc705/software/runtime/runtime.fbi $SOC_PREFIX
wget -P $SOC_PREFIX https://raw.githubusercontent.com/jordens/bscan_spi_bitstreams/master/bscan_spi_xc7k325t.bit

View File

@ -1,27 +0,0 @@
package:
name: artiq-kc705-nist_qc1
version: {{ environ.get("GIT_DESCRIBE_TAG", "") }}
source:
git_url: ../..
build:
noarch_python: true
number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}
string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }}
requirements:
build:
- migen 0.4
- misoc 0.4
- llvm-or1k
- binutils-or1k-linux >=2.27
- rust-core-or1k
- cargo
run:
- artiq {{ "{tag} py_{number}+git{hash}".format(tag=environ.get("GIT_DESCRIBE_TAG"), number=environ.get("GIT_DESCRIBE_NUMBER"), hash=environ.get("GIT_DESCRIBE_HASH")[1:]) if "GIT_DESCRIBE_TAG" in environ else "" }}
about:
home: https://m-labs.hk/artiq
license: GPL
summary: 'Bitstream, BIOS and runtime for NIST_QC1 on the KC705 board'

View File

@ -12,8 +12,8 @@ build:
requirements:
build:
- migen 0.4
- misoc 0.4
- migen 0.5.dev
- misoc 0.5.dev
- llvm-or1k
- binutils-or1k-linux >=2.27
- rust-core-or1k

View File

@ -3,12 +3,12 @@
BUILD_SETTINGS_FILE=$HOME/.m-labs/build_settings.sh
[ -f $BUILD_SETTINGS_FILE ] && . $BUILD_SETTINGS_FILE
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/pipistrello-nist_qc1
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/pipistrello-demo
mkdir -p $SOC_PREFIX
$PYTHON -m artiq.gateware.targets.pipistrello $MISOC_EXTRA_ISE_CMDLINE
cp misoc_nist_qc1_pipistrello/gateware/top.bit $SOC_PREFIX
cp misoc_nist_qc1_pipistrello/software/bios/bios.bin $SOC_PREFIX
cp misoc_nist_qc1_pipistrello/software/runtime/runtime.fbi $SOC_PREFIX
cp misoc_demo_pipistrello/gateware/top.bit $SOC_PREFIX
cp misoc_demo_pipistrello/software/bios/bios.bin $SOC_PREFIX
cp misoc_demo_pipistrello/software/runtime/runtime.fbi $SOC_PREFIX
wget -P $SOC_PREFIX https://raw.githubusercontent.com/jordens/bscan_spi_bitstreams/master/bscan_spi_xc6slx45.bit

View File

@ -1,5 +1,5 @@
package:
name: artiq-pipistrello-nist_qc1
name: artiq-pipistrello-demo
version: {{ environ.get("GIT_DESCRIBE_TAG", "") }}
source:
@ -12,8 +12,8 @@ build:
requirements:
build:
- migen 0.4
- misoc 0.4
- migen 0.5.dev
- misoc 0.5.dev
- llvm-or1k
- binutils-or1k-linux >=2.27
- rust-core-or1k
@ -24,4 +24,4 @@ requirements:
about:
home: http://m-labs.hk/artiq
license: GPL
summary: 'Bitstream, BIOS and runtime for NIST_QC1 on the Pipistrello board'
summary: 'Bitstream, BIOS and runtime for the Pipistrello board'

View File

@ -10,7 +10,7 @@ A number of Python features can be used inside a kernel for compilation and exec
* Booleans
* 32-bit signed integers (default size)
* 64-bit signed integers (use ``int(n, width=64)`` to convert)
* 64-bit signed integers (use ``numpy.int64`` to convert)
* Double-precision floating point numbers
* Lists of any supported types
* User-defined classes, with attributes of any supported types (attributes that are not used anywhere in the kernel are ignored)
@ -36,7 +36,7 @@ The Python types correspond to ARTIQ type annotations as follows:
+-------------+-------------------------+
| bool | TBool |
+-------------+-------------------------+
| int | TInt32, TInt64 |
| int | TInt32 or TInt64 |
+-------------+-------------------------+
| float | TFloat |
+-------------+-------------------------+
@ -46,6 +46,33 @@ The Python types correspond to ARTIQ type annotations as follows:
+-------------+-------------------------+
| range | TRange32, TRange64 |
+-------------+-------------------------+
| numpy.int32 | TInt32 |
+-------------+-------------------------+
| numpy.int64 | TInt64 |
+-------------+-------------------------+
| numpy.float64 | TFloat |
+-------------+-------------------------+
Pitfalls
--------
The ARTIQ compiler accepts *nearly* a strict subset of Python 3. However, by necessity there
is a number of differences that can lead to bugs.
Arbitrary-length integers are not supported at all on the core device; all integers are
either 32-bit or 64-bit. This especially affects calculations that result in a 32-bit signed
overflow; if the compiler detects a constant that doesn't fit into 32 bits, the entire expression
will be upgraded to 64-bit arithmetics, however if all constants are small, 32-bit arithmetics
will be used even if the result will overflow. Overflows are not detected.
The result of calling the builtin ``round`` function is different when used with
the builtin ``float`` type and the ``numpy.float64`` type on the host interpreter; ``round(1.0)``
returns an integer value 1, whereas ``round(numpy.float64(1.0))`` returns a floating point value
``numpy.float64(1.0)``. Since both ``float`` and ``numpy.float64`` are mapped to
the builtin ``float`` type on the core device, this can lead to problems in functions marked
``@portable``; the workaround is to explicitly cast the argument of ``round`` to ``float``:
``round(float(numpy.float64(1.0)))`` returns an integer on the core device as well as on the host
interpreter.
Asynchronous RPCs
-----------------
@ -124,7 +151,7 @@ In the synthetic example above, the compiler will be able to detect that the res
@kernel
def loop(self):
precomputed_delay_mu = seconds_to_mu(self.worker.interval / 5.0)
precomputed_delay_mu = self.core.seconds_to_mu(self.worker.interval / 5.0)
for _ in range(100):
delay_mu(precomputed_delay_mu)
self.worker.work()

View File

@ -29,13 +29,13 @@ All boards have a serial interface running at 115200bps 8-N-1 that can be used f
KC705
-----
The main target board for the ARTIQ core device is the KC705 development board from Xilinx. It supports the NIST QC1 hardware via an adapter, and the NIST CLOCK and QC2 hardware (FMC).
The main target board for the ARTIQ core device is the KC705 development board from Xilinx. It supports the NIST CLOCK and QC2 hardware (FMC).
Common problems
+++++++++++++++
* The SW13 switches on the board need to be set to 00001.
* When connected, QC1 and CLOCK adapters break the JTAG chain due to TDI not being connect to TDO on the FMC mezzanine.
* When connected, CLOCK adapter breaks the JTAG chain due to TDI not being connect to TDO on the FMC mezzanine.
* On some boards, the JTAG USB connector is not correctly soldered.
VADJ
@ -44,31 +44,6 @@ VADJ
With the NIST CLOCK and QC2 adapters, for safe operation of the DDS buses (to prevent damage to the IO banks of the FPGA), the FMC VADJ rail of the KC705 should be changed to 3.3V. Plug the Texas Instruments USB-TO-GPIO PMBus adapter into the PMBus connector in the corner of the KC705 and use the Fusion Digital Power Designer software to configure (requires Windows). Write to chip number U55 (address 52), channel 4, which is the VADJ rail, to make it 3.3V instead of 2.5V. Power cycle the KC705 board to check that the startup voltage on the VADJ rail is now 3.3V.
NIST QC1
++++++++
With the QC1 hardware, the TTL lines are mapped as follows:
+--------------+------------+--------------+
| RTIO channel | TTL line | Capability |
+==============+============+==============+
| 0 | PMT0 | Input |
+--------------+------------+--------------+
| 1 | PMT1 | Input |
+--------------+------------+--------------+
| 2-16 | TTL0-14 | Output |
+--------------+------------+--------------+
| 17 | SMA_GPIO_N | Input+Output |
+--------------+------------+--------------+
| 18 | LED | Output |
+--------------+------------+--------------+
| 19 | TTL15 | Clock |
+--------------+------------+--------------+
There are no SPI channels.
The DDS bus is on channel 20.
NIST CLOCK
++++++++++
@ -190,33 +165,27 @@ The low-cost Pipistrello FPGA board can be used as a lower-cost but slower alter
.. warning:: The Pipistrello draws a high current over USB, and that current increases when the FPGA design is active. If you experience problems such as intermittent board freezes or USB errors, try connecting it to a self-powered USB hub.
When plugged to an adapter, the NIST QC1 hardware can be used. The TTL lines are mapped to RTIO channels as follows:
The TTL lines are mapped to RTIO channels as follows:
+--------------+------------+--------------+
| RTIO channel | TTL line | Capability |
+==============+============+==============+
| 0 | PMT0 | Input |
| 0-1 | B0-1 | Input+Output |
+--------------+------------+--------------+
| 1 | PMT1 | Input |
| 2-14 | B2-14 | Output |
+--------------+------------+--------------+
| 2-16 | TTL0-14 | Output |
| 15 | USER_LED_1 | Output |
+--------------+------------+--------------+
| 17 | EXT_LED | Output |
| 16 | USER_LED_2 | Output |
+--------------+------------+--------------+
| 18 | USER_LED_1 | Output |
| 17 | USER_LED_3 | Output |
+--------------+------------+--------------+
| 19 | USER_LED_2 | Output |
| 18 | USER_LED_4 | Output |
+--------------+------------+--------------+
| 20 | USER_LED_3 | Output |
+--------------+------------+--------------+
| 21 | USER_LED_4 | Output |
+--------------+------------+--------------+
| 22 | TTL15 | Clock |
| 19 | B15 | Clock |
+--------------+------------+--------------+
The input only limitation on channels 0 and 1 comes from the QC-DAQ adapter. When the adapter is not used (and physically unplugged from the Pipistrello board), the corresponding pins on the Pipistrello can be used as outputs. Do not configure these channels as outputs when the adapter is plugged, as this would cause electrical contention.
The board can accept an external RTIO clock connected to PMT2. If the DDS box does not drive the PMT2 pair, use XTRIG and patch the XTRIG transceiver output on the adapter board onto C:15 disconnecting PMT2.
The board can accept an external RTIO clock connected to C15.
The board has one RTIO SPI bus on the PMOD connector, compliant to PMOD
Interface Type 2 (SPI) and 2A (expanded SPI):
@ -224,7 +193,5 @@ Interface Type 2 (SPI) and 2A (expanded SPI):
+--------------+--------+--------+--------+--------+
| RTIO channel | CS_N | MOSI | MISO | CLK |
+==============+========+========+========+========+
| 23 | PMOD_0 | PMOD_1 | PMOD_2 | PMOD_3 |
| 16 | PMOD_0 | PMOD_1 | PMOD_2 | PMOD_3 |
+--------------+--------+--------+--------+--------+
The DDS bus is on channel 24.

View File

@ -43,14 +43,13 @@ Then prepare to create a new conda environment with the ARTIQ package and the ma
choose a suitable name for the environment, for example ``artiq-main`` if you intend to track the main label or ``artiq-2016-04-01`` if you consider the environment a snapshot of ARTIQ on 2016-04-01.
Choose the package containing the binaries for your hardware:
* ``artiq-pipistrello-nist_qc1`` for the `Pipistrello <http://pipistrello.saanlima.com/>`_ board with the NIST adapter to SCSI cables and AD9858 DDS chips.
* ``artiq-kc705-nist_qc1`` for the `KC705 <http://www.xilinx.com/products/boards-and-kits/ek-k7-kc705-g.html>`_ board with the NIST adapter to SCSI cables and AD9858 DDS chips.
* ``artiq-pipistrello-demo`` for the `Pipistrello <http://pipistrello.saanlima.com/>`_ board.
* ``artiq-kc705-nist_clock`` for the KC705 board with the NIST "clock" FMC backplane and AD9914 DDS chips.
* ``artiq-kc705-nist_qc2`` for the KC705 board with the NIST QC2 FMC backplane and AD9914 DDS chips.
Conda will create the environment, automatically resolve, download, and install the necessary dependencies and install the packages you select::
$ conda create -n artiq-main artiq-pipistrello-nist_qc1
$ conda create -n artiq-main artiq-pipistrello-demo
After the installation, activate the newly created environment by name.
On Unix::
@ -80,7 +79,7 @@ When upgrading ARTIQ or when testing different versions it is recommended that n
Keep previous environments around until you are certain that they are not needed anymore and a new environment is known to work correctly.
You can create a new conda environment specifically to test a certain version of ARTIQ::
$ conda create -n artiq-test-1.0rc2 artiq-pipistrello-nist_qc1=1.0rc2
$ conda create -n artiq-test-1.0rc2 artiq-pipistrello-demo=1.0rc2
Switching between conda environments using ``$ source deactivate artiq-1.0rc2`` and ``$ source activate artiq-1.0rc1`` is the recommended way to roll back to previous versions of ARTIQ.
You can list the environments you have created using::
@ -132,11 +131,11 @@ Then, you can flash the board:
* For the Pipistrello board::
$ artiq_flash -t pipistrello -m nist_qc1
$ artiq_flash -t pipistrello -m demo
* For the KC705 board (selecting the appropriate hardware peripheral)::
$ artiq_flash -t kc705 -m [nist_qc1/nist_clock/nist_qc2]
$ artiq_flash -t kc705 -m [nist_clock/nist_qc2]
The SW13 switches also need to be set to 00001.

View File

@ -48,9 +48,9 @@ and the ARTIQ kernels.
* Install LLVM and Clang: ::
$ cd ~/artiq-dev
$ git clone -b artiq-3.8 https://github.com/m-labs/llvm-or1k
$ git clone -b artiq-3.9 https://github.com/m-labs/llvm-or1k
$ cd llvm-or1k
$ git clone -b artiq-3.8 https://github.com/m-labs/clang-or1k tools/clang
$ git clone -b artiq-3.9 https://github.com/m-labs/clang-or1k tools/clang
$ mkdir build
$ cd build
@ -171,7 +171,7 @@ These steps are required to generate gateware bitstream (``.bit``) files, build
* For KC705::
$ python3.5 -m artiq.gateware.targets.kc705 -H nist_qc1 # or nist_qc2
$ python3.5 -m artiq.gateware.targets.kc705 -H nist_clock # or nist_qc2
.. note:: Add ``--toolchain ise`` if you wish to use ISE instead of Vivado.

View File

@ -36,7 +36,7 @@ The wall clock keeps running across experiments.
Absolute timestamps can be large numbers.
They are represented internally as 64-bit integers with a resolution of typically a nanosecond and a range of hundreds of years.
Conversions between such a large integer number and a floating point representation can cause loss of precision through cancellation.
When computing the difference of absolute timestamps, use ``mu_to_seconds(t2-t1)``, not ``mu_to_seconds(t2)-mu_to_seconds(t1)`` (see :meth:`artiq.language.core.mu_to_seconds`).
When computing the difference of absolute timestamps, use ``self.core.mu_to_seconds(t2-t1)``, not ``self.core.mu_to_seconds(t2)-self.core.mu_to_seconds(t1)`` (see :meth:`artiq.coredevice.Core.mu_to_seconds`).
When accumulating time, do it in machine units and not in SI units, so that rounding errors do not accumulate.
The following basic example shows how to place output events on the timeline.