Skip to content

Commit

Permalink
Merge branch 'master' into drtio
Browse files Browse the repository at this point in the history
sbourdeauducq committed Nov 22, 2016
2 parents 02adae7 + ef971ef commit 3459793
Showing 69 changed files with 871 additions and 1,000 deletions.
6 changes: 6 additions & 0 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions artiq/compiler/analyses/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .constness import Constness
from .domination import DominatorTree
from .devirtualization import Devirtualization
from .invariant_detection import InvariantDetection
6 changes: 6 additions & 0 deletions artiq/compiler/analyses/constness.py
Original file line number Diff line number Diff line change
@@ -17,6 +17,12 @@ def visit_Assign(self, node):
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:
49 changes: 49 additions & 0 deletions artiq/compiler/analyses/invariant_detection.py
Original file line number Diff line number Diff line change
@@ -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)
10 changes: 2 additions & 8 deletions artiq/compiler/builtins.py
Original file line number Diff line number Diff line change
@@ -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")

9 changes: 7 additions & 2 deletions artiq/compiler/module.py
Original file line number Diff line number Diff line change
@@ -40,7 +40,8 @@ def from_filename(cls, filename, engine=None):
return cls(source.Buffer(f.read(), filename, 1), engine=engine)

class Module:
def __init__(self, src, ref_period=1e-6):
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
self.name = src.name
@@ -60,6 +61,7 @@ def __init__(self, src, ref_period=1e-6):
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)
@@ -73,13 +75,16 @@ def __init__(self, src, ref_period=1e-6):
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."""
llvm_ir_generator = transforms.LLVMIRGenerator(
engine=self.engine, module_name=self.name, target=target,
embedding_map=self.embedding_map)
return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True)
return llvm_ir_generator.process(self.artiq_ir,
attribute_writeback=self.attribute_writeback)

def __repr__(self):
printer = types.TypePrinter()
4 changes: 2 additions & 2 deletions artiq/compiler/prelude.py
Original file line number Diff line number Diff line change
@@ -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(),
5 changes: 1 addition & 4 deletions artiq/compiler/targets.py
Original file line number Diff line number Diff line change
@@ -86,14 +86,11 @@ def target_machine(self):
llmachine = lltarget.create_target_machine(
features=",".join(["+{}".format(f) for f in self.features]),
reloc="pic", codemodel="default")
llmachine.set_verbose(True)
llmachine.set_asm_verbosity(True)
return llmachine

def optimize(self, llmodule):
llmachine = self.target_machine()
llpassmgr = llvm.create_module_pass_manager()
llmachine.target_data.add_pass(llpassmgr)
llmachine.add_analysis_passes(llpassmgr)

# Register our alias analysis passes.
llpassmgr.add_basic_alias_analysis_pass()
17 changes: 2 additions & 15 deletions artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -1599,7 +1599,8 @@ def visit_builtin_call(self, node):
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 @@ def body_gen(index):
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):
74 changes: 37 additions & 37 deletions artiq/compiler/transforms/inferencer.py
Original file line number Diff line number Diff line change
@@ -622,14 +622,28 @@ def simple_form(info, arg_types=[], return_type=builtins.TNone()):

self._unify(node.type, builtins.TBool(),
node.loc, None)
elif 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?")
]
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? 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 @@ def simple_form(info, arg_types=[], return_type=builtins.TNone()):
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 @@ def makenotes(printer, typea, typeb, loca, locb):
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,36 +1044,41 @@ def visit_AugAssign(self, node):
if coerced:
return_type, target_type, value_type = coerced

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(target_type)
node.target.type.unify(return_type)
except types.UnificationError as e:
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",
"expression of type {typea} has to be coerced to {typeb}, "
"which makes assignment invalid",
"the result of this operation has type {typeb}, "
"which cannot be assigned to a left-hand side of type {typea}",
{"typea": printer.name(node.target.type),
"typeb": printer.name(target_type)},
"typeb": printer.name(return_type)},
node.op.loc, [node.target.loc], [note])
self.engine.process(diag)
return

try:
node.target.type.unify(return_type)
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)},
{"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",
"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(return_type)},
"typeb": printer.name(target_type)},
node.op.loc, [node.target.loc], [note])
self.engine.process(diag)
return
59 changes: 31 additions & 28 deletions artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -37,9 +37,16 @@ def memoized(self, *args):
class DebugInfoEmitter:
def __init__(self, llmodule):
self.llmodule = llmodule
self.llsubprograms = []
self.llcompileunit = None
self.cache = {}

llident = self.llmodule.add_named_metadata('llvm.ident')
llident.add(self.emit_metadata(["ARTIQ"]))

llflags = self.llmodule.add_named_metadata('llvm.module.flags')
llflags.add(self.emit_metadata([2, "Debug Info Version", 3]))
llflags.add(self.emit_metadata([2, "Dwarf Version", 4]))

def emit_metadata(self, operands):
def map_operand(operand):
if operand is None:
@@ -67,14 +74,13 @@ def emit_file(self, source_buffer):
})

@memoize
def emit_compile_unit(self, source_buffer, llsubprograms):
def emit_compile_unit(self, source_buffer):
return self.emit_debug_info("DICompileUnit", {
"language": ll.DIToken("DW_LANG_Python"),
"file": self.emit_file(source_buffer),
"producer": "ARTIQ",
"runtimeVersion": 0,
"emissionKind": 2, # full=1, lines only=2
"subprograms": self.emit_metadata(llsubprograms)
}, is_distinct=True)

@memoize
@@ -86,21 +92,26 @@ def emit_subroutine_type(self, typ):
@memoize
def emit_subprogram(self, func, llfunc):
source_buffer = func.loc.source_buffer

if self.llcompileunit is None:
self.llcompileunit = self.emit_compile_unit(source_buffer)
llcompileunits = self.llmodule.add_named_metadata('llvm.dbg.cu')
llcompileunits.add(self.llcompileunit)

display_name = "{}{}".format(func.name, types.TypePrinter().name(func.type))
llsubprogram = self.emit_debug_info("DISubprogram", {
return self.emit_debug_info("DISubprogram", {
"name": func.name,
"linkageName": llfunc.name,
"type": self.emit_subroutine_type(func.type),
"file": self.emit_file(source_buffer),
"line": func.loc.line(),
"unit": self.llcompileunit,
"scope": self.emit_file(source_buffer),
"scopeLine": func.loc.line(),
"isLocal": func.is_internal,
"isDefinition": True,
"variables": self.emit_metadata([])
}, is_distinct=True)
self.llsubprograms.append(llsubprogram)
return llsubprogram

@memoize
def emit_loc(self, loc, scope):
@@ -110,18 +121,6 @@ def emit_loc(self, loc, scope):
"scope": scope
})

def finalize(self, source_buffer):
llident = self.llmodule.add_named_metadata('llvm.ident')
llident.add(self.emit_metadata(["ARTIQ"]))

llflags = self.llmodule.add_named_metadata('llvm.module.flags')
llflags.add(self.emit_metadata([2, "Debug Info Version", 3]))
llflags.add(self.emit_metadata([2, "Dwarf Version", 4]))

llcompile_units = self.llmodule.add_named_metadata('llvm.dbg.cu')
llcompile_units.add(self.emit_compile_unit(source_buffer, tuple(self.llsubprograms)))


class LLVMIRGenerator:
def __init__(self, engine, module_name, target, embedding_map):
self.engine = engine
@@ -323,6 +322,8 @@ def llbuiltin(self, name):
llty = ll.FunctionType(llvoid, [])
elif name == "llvm.floor.f64":
llty = ll.FunctionType(lldouble, [lldouble])
elif name == "llvm.round.f64":
llty = ll.FunctionType(lldouble, [lldouble])
elif name == "llvm.pow.f64":
llty = ll.FunctionType(lldouble, [lldouble, lldouble])
elif name == "llvm.powi.f64":
@@ -347,8 +348,6 @@ def llbuiltin(self, name):
llty = ll.FunctionType(lli32, [llptr])
elif name == "strcmp":
llty = ll.FunctionType(lli32, [llptr, llptr])
elif name == "lround":
llty = ll.FunctionType(lli32, [lldouble])
elif name == "send_rpc":
llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr])
elif name == "send_async_rpc":
@@ -410,9 +409,6 @@ def process(self, functions, attribute_writeback):
for func in functions:
self.process_function(func)

if any(functions):
self.debug_info_emitter.finalize(functions[0].loc.source_buffer)

if attribute_writeback and self.embedding_map is not None:
self.emit_attribute_writeback()

@@ -655,7 +651,7 @@ def llptr_to_var(self, llenv, env_ty, var_name, var_type=None):
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
inbounds=True)
llouterenv = self.llbuilder.load(llptr)
llouterenv.set_metadata('invariant.load', self.empty_metadata)
llouterenv.set_metadata('unconditionally.invariant.load', self.empty_metadata)
llouterenv.set_metadata('nonnull', self.empty_metadata)
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)

@@ -799,7 +795,7 @@ def process_GetAttr(self, insn):
inbounds=True, name="ptr.{}".format(insn.name))
llvalue = self.llbuilder.load(llptr, name="val.{}".format(insn.name))
if types.is_instance(typ) and attr in typ.constant_attributes:
llvalue.set_metadata('invariant.load', self.empty_metadata)
llvalue.set_metadata('unconditionally.invariant.load', self.empty_metadata)
if isinstance(llvalue.type, ll.PointerType):
self.mark_dereferenceable(llvalue)
return llvalue
@@ -852,6 +848,8 @@ def process_SetElem(self, insn):

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)
@@ -1044,16 +1042,17 @@ def process_Builtin(self, insn):
name=insn.name)
elif insn.op == "round":
llarg = self.map(insn.operands[0])
return self.llbuilder.call(self.llbuiltin("lround"), [llarg],
name=insn.name)
llvalue = self.llbuilder.call(self.llbuiltin("llvm.round.f64"), [llarg])
return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type),
name=insn.name)
elif insn.op == "globalenv":
def get_outer(llenv, env_ty):
if "$outer" in env_ty.params:
outer_index = list(env_ty.params.keys()).index("$outer")
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
inbounds=True)
llouterenv = self.llbuilder.load(llptr)
llouterenv.set_metadata('invariant.load', self.empty_metadata)
llouterenv.set_metadata('unconditionally.invariant.load', self.empty_metadata)
llouterenv.set_metadata('nonnull', self.empty_metadata)
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)
else:
@@ -1387,6 +1386,7 @@ def _quote(self, value, typ, path):
def _quote_attributes():
llglobal = None
llfields = []
emit_as_constant = True
for attr in typ.attributes:
if attr == "__objectid__":
objectid = self.embedding_map.store_object(value)
@@ -1407,13 +1407,16 @@ def _quote_attributes():
not types.is_c_function(typ.attributes[attr]))
if is_class_function:
attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue)
if not (types.is_instance(typ) and attr in typ.constant_attributes):
emit_as_constant = False
llattrvalue = self._quote(attrvalue, typ.attributes[attr],
lambda: path() + [attr])
llfields.append(llattrvalue)
if is_class_function:
llclosureptr = self.get_global_closure_ptr(typ, attr)
llclosureptr.initializer = llattrvalue

llglobal.global_constant = emit_as_constant
llglobal.initializer = ll.Constant(llty.pointee, llfields)
llglobal.linkage = "private"
return llglobal
7 changes: 3 additions & 4 deletions artiq/coredevice/ad5360.py
Original file line number Diff line number Diff line change
@@ -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 @@ def set(self, values, op=_AD5360_CMD_DATA):
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
37 changes: 6 additions & 31 deletions artiq/coredevice/analyzer.py
Original file line number Diff line number Diff line change
@@ -182,10 +182,7 @@ def add_dds_channel(self, name, dds_channel_nr):
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 @@ def _gpio_to_channels(self, gpio):
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 @@ def process_message(self, message):
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:
24 changes: 22 additions & 2 deletions artiq/coredevice/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os, sys
import numpy

from pythonparser import diagnostic

@@ -81,15 +82,17 @@ def __init__(self, dmgr, ref_period, external_clock=False,
self.core = self
self.comm.core = self

def compile(self, function, args, kwargs, set_result=None, with_attr_writeback=True):
def compile(self, function, args, kwargs, set_result=None, attribute_writeback=True):
try:
engine = _DiagnosticEngine(all_errors_are_fatal=True)

stitcher = Stitcher(engine=engine, core=self, dmgr=self.dmgr)
stitcher.stitch_call(function, args, kwargs, set_result)
stitcher.finalize()

module = Module(stitcher, ref_period=self.ref_period)
module = Module(stitcher,
ref_period=self.ref_period,
attribute_writeback=attribute_writeback)
target = OR1KTarget()

library = target.compile_and_link([module])
@@ -122,6 +125,23 @@ def set_result(new_result):

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()
342 changes: 248 additions & 94 deletions artiq/coredevice/dds.py

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion artiq/coredevice/exceptions.py
Original file line number Diff line number Diff line change
@@ -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."""
8 changes: 3 additions & 5 deletions artiq/coredevice/spi.py
Original file line number Diff line number Diff line change
@@ -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,16 +58,15 @@ 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)
self.xfer_period_mu = numpy.int64(0)

@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):
4 changes: 2 additions & 2 deletions artiq/dashboard/moninj.py
Original file line number Diff line number Diff line change
@@ -219,10 +219,10 @@ def __setitem__(self, k, v):
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(
6 changes: 3 additions & 3 deletions artiq/devices/pdq2/mediator.py
Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ def create_segment(self, name=None):

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 @@ def advance(self):
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 @@ def advance(self):
# 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))
15 changes: 10 additions & 5 deletions artiq/examples/master/device_db.pyon
Original file line number Diff line number Diff line change
@@ -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}
},

4 changes: 2 additions & 2 deletions artiq/examples/master/idle_kernel.py
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@ class IdleKernel(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("led")

@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()
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ def run(self):
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:
2 changes: 1 addition & 1 deletion artiq/examples/master/repository/utilities/dds_setter.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ def build(self):
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(
2 changes: 1 addition & 1 deletion artiq/frontend/artiq_compile.py
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ def main():

object_map, kernel_library, _, _ = \
core.compile(exp.run, [exp_inst], {},
with_attr_writeback=False)
attribute_writeback=False)
except CompileError as error:
return
finally:
158 changes: 158 additions & 0 deletions artiq/frontend/artiq_devtool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env python3.5

# This script makes the following assumptions:
# * miniconda is installed remotely at ~/miniconda
# * misoc and artiq are installed remotely via conda

import sys
import argparse
import subprocess
import socket
import select
import threading
import paramiko

from artiq.tools import verbosity_args, init_logger, logger
from random import Random


def get_argparser():
parser = argparse.ArgumentParser(description="ARTIQ core device "
"development tool")

verbosity_args(parser)

parser.add_argument("--host", nargs=1, metavar="HOST",
type=str, default="lab.m-labs.hk",
help="SSH host where the development board is located")
parser.add_argument("--serial", nargs=1, metavar="SERIAL",
type=str, default="/dev/ttyUSB0",
help="TTY device corresponding to the development board")
parser.add_argument("--ip", nargs=1, metavar="IP",
type=str, default="kc705.lab.m-labs.hk",
help="IP address corresponding to the development board")

parser.add_argument("actions", metavar="ACTION",
type=str, default=[], nargs="+",
help="actions to perform (sequence of: build boot connect)")

return parser


def main():
args = get_argparser().parse_args()
init_logger(args)

ssh = None
def get_ssh():
nonlocal ssh
if ssh is not None:
return ssh
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(args.host)
return ssh

sftp = None
def get_sftp():
nonlocal sftp
if sftp is not None:
return sftp
sftp = get_ssh().open_sftp()
return sftp

rng = Random()
tmp = "artiq" + "".join([rng.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(6)])
env = "bash -c 'export PATH=$HOME/miniconda/bin:$PATH; exec $0 $*' "

def run_command(cmd):
logger.info("Executing {}".format(cmd))
chan = get_ssh().get_transport().open_session()
chan.set_combine_stderr(True)
chan.exec_command(cmd.format(tmp=tmp, env=env, serial=args.serial, ip=args.ip))
return chan.makefile()

def drain(chan):
while True:
char = chan.read(1)
if char == b"":
break
sys.stderr.write(char.decode("utf-8", errors='replace'))

for action in args.actions:
if action == "build":
logger.info("Building runtime")
try:
subprocess.check_call(["python3", "-m", "artiq.gateware.targets.kc705",
"-H", "nist_clock",
"--no-compile-gateware",
"--output-dir", "/tmp/kc705"])
except subprocess.CalledProcessError:
logger.error("Build failed")
sys.exit(1)

elif action == "boot":
logger.info("Uploading runtime")
get_sftp().mkdir("/tmp/{tmp}".format(tmp=tmp))
get_sftp().put("/tmp/kc705/software/runtime/runtime.bin",
"/tmp/{tmp}/runtime.bin".format(tmp=tmp))

logger.info("Booting runtime")
flterm = run_command(
"{env} python3 flterm.py {serial} " +
"--kernel /tmp/{tmp}/runtime.bin --upload-only")
artiq_flash = run_command(
"{env} artiq_flash start")
drain(flterm)

elif action == "connect":
def forwarder(port):
listener = socket.socket()
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(('localhost', port))
listener.listen(1)
while True:
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:
try:
r, w, x = select.select([local_stream, remote_stream], [], [])
if local_stream in r:
data = local_stream.recv(1024)
if data == b"":
break
remote_stream.send(data)
if remote_stream in r:
data = remote_stream.recv(1024)
if data == b"":
break
local_stream.send(data)
except Exception as e:
logger.exception("Forward error on port %s", port)
local_stream.close()
remote_stream.close()

for port in (1381, 1382):
thread = threading.Thread(target=forwarder, args=(port,),
name="port-{}".format(port), daemon=True)
thread.start()

logger.info("Connecting to device")
flterm = run_command(
"{env} python3 flterm.py {serial} --output-only")
drain(flterm)

else:
logger.error("Unknown action {}".format(action))
sys.exit(1)

if __name__ == "__main__":
main()
6 changes: 2 additions & 4 deletions artiq/frontend/artiq_run.py
Original file line number Diff line number Diff line change
@@ -73,17 +73,15 @@ def compile(self):
with open(self.file, "r") as f:
llmodule = llvm.parse_assembly(f.read())
llmodule.verify()
return self.target.link([self.target.assemble(llmodule)],
init_fn="__modinit__")
return self.target.link([self.target.assemble(llmodule)])


class LLVMBitcodeRunner(FileRunner):
def compile(self):
with open(self.file, "rb") as f:
llmodule = llvm.parse_bitcode(f.read())
llmodule.verify()
return self.target.link([self.target.assemble(llmodule)],
init_fn="__modinit__")
return self.target.link([self.target.assemble(llmodule)])


class DummyScheduler:
7 changes: 0 additions & 7 deletions artiq/gateware/ad9xxx.py
Original file line number Diff line number Diff line change
@@ -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,
94 changes: 0 additions & 94 deletions artiq/gateware/nist_qc1.py

This file was deleted.

5 changes: 0 additions & 5 deletions artiq/gateware/rtio/phy/dds.py
Original file line number Diff line number Diff line change
@@ -56,11 +56,6 @@ def selected(c):
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)
89 changes: 16 additions & 73 deletions artiq/gateware/targets/kc705.py
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
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
from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi
from artiq import __version__ as artiq_version

@@ -81,7 +81,7 @@ def __init__(self, platform, rtio_internal_clk):
# The default user SMA voltage on KC705 is 2.5V, and the Migen platform
# follows this default. But since the SMAs are on the same bank as the DDS,
# which is set to 3.3V by reprogramming the KC705 power ICs, we need to
# redefine them here.
# redefine them here.
_sma33_io = [
("user_sma_gpio_p_33", 0, Pins("Y23"), IOStandard("LVCMOS33")),
("user_sma_gpio_n_33", 0, Pins("Y24"), IOStandard("LVCMOS33")),
@@ -137,12 +137,13 @@ def __init__(self, cpu_type="or1k", **kwargs):
self.register_kernel_cpu_csrdevice("i2c")
self.config["I2C_BUS_COUNT"] = 1

self.config["HAS_DDS"] = None

def add_rtio(self, rtio_channels):
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
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")

@@ -157,62 +158,6 @@ def add_rtio(self, rtio_channels):
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"] = True
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
@@ -272,8 +217,8 @@ def __init__(self, cpu_type="or1k", **kwargs):
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 11
self.config["DDS_AD9914"] = True
self.config["DDS_ONEHOT_SEL"] = True
self.config["DDS_AD9914"] = None
self.config["DDS_ONEHOT_SEL"] = None
phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
@@ -291,7 +236,7 @@ def __init__(self, cpu_type="or1k", **kwargs):
class NIST_QC2(_NIST_Ions):
"""
NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane
and 24 DDS channels. Two backplanes are used.
and 24 DDS channels. Two backplanes are used.
"""
def __init__(self, cpu_type="or1k", **kwargs):
_NIST_Ions.__init__(self, cpu_type, **kwargs)
@@ -308,19 +253,19 @@ def __init__(self, cpu_type="or1k", **kwargs):
platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))

# CLK0, CLK1 are for clock generators, on backplane SMP connectors
for i in range(2):
for i in range(2):
phy = ttl_simple.ClockGen(
platform.request("clkout", i))
self.submodules += phy
clock_generators.append(rtio.Channel.from_phy(phy))
clock_generators.append(rtio.Channel.from_phy(phy))

# user SMA on KC705 board
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))
@@ -350,8 +295,8 @@ def __init__(self, cpu_type="or1k", **kwargs):
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 2
self.config["DDS_CHANNELS_PER_BUS"] = 12
self.config["DDS_AD9914"] = True
self.config["DDS_ONEHOT_SEL"] = True
self.config["DDS_AD9914"] = None
self.config["DDS_ONEHOT_SEL"] = None
for backplane_offset in range(2):
phy = dds.AD9914(
platform.request("dds", backplane_offset), 12, onehot=True)
@@ -371,19 +316,17 @@ def __init__(self, cpu_type="or1k", **kwargs):
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 "
"nist_clock/nist_qc2 "
"(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
75 changes: 33 additions & 42 deletions artiq/gateware/targets/pipistrello.py
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
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 @@ def __init__(self, platform, clk_freq):
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 @@ def __init__(self, platform, clk_freq):
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 @@ def __init__(self, cpu_type="or1k", **kwargs):
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),
self.rtio_crg.rtiox4_stb)
elif i in (2,): # ttl2 can run on a 8x serdes if xtrig is not used
phy = ttl_serdes_spartan6.Inout_4X(platform.request("ttl", i),
self.rtio_crg.rtiox4_stb)
elif i in (2,):
phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i),
self.rtio_crg.rtiox8_stb)
else:
@@ -185,10 +195,6 @@ def __init__(self, cpu_type="or1k", **kwargs):
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,26 +212,12 @@ def __init__(self, cpu_type="or1k", **kwargs):
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=64, ififo_depth=64))

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"] = True
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(
@@ -235,13 +227,12 @@ def __init__(self, cpu_type="or1k", **kwargs):

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


26 changes: 0 additions & 26 deletions artiq/language/core.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,6 @@
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
6 changes: 4 additions & 2 deletions artiq/runtime.rs/build.rs
Original file line number Diff line number Diff line change
@@ -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") {
13 changes: 1 addition & 12 deletions artiq/runtime.rs/libksupport/api.rs
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ static mut API: &'static [(&'static str, *const ())] = &[

/* libm */
api!(sqrt),
api!(lround),
api!(round),

/* exceptions */
api!(_Unwind_Resume),
@@ -105,17 +105,6 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(rtio_input_timestamp),
api!(rtio_input_data),

#[cfg(rtio_dds_count)]
api!(dds_init),
#[cfg(rtio_dds_count)]
api!(dds_init_sync),
#[cfg(rtio_dds_count)]
api!(dds_batch_enter),
#[cfg(rtio_dds_count)]
api!(dds_batch_exit),
#[cfg(rtio_dds_count)]
api!(dds_set),

api!(i2c_init),
api!(i2c_start),
api!(i2c_stop),
1 change: 1 addition & 0 deletions artiq/runtime.rs/liblwip-sys/lib.rs
Original file line number Diff line number Diff line change
@@ -149,6 +149,7 @@ extern {
// nonstandard
pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16;
pub fn tcp_so_options_(pcb: *mut tcp_pcb) -> *mut u8;
pub fn tcp_nagle_disable_(pcb: *mut tcp_pcb);

pub fn udp_new() -> *mut udp_pcb;
pub fn udp_new_ip_type(type_: ip_addr_type) -> *mut udp_pcb;
1 change: 1 addition & 0 deletions artiq/runtime.rs/liblwip/lib.rs
Original file line number Diff line number Diff line change
@@ -549,6 +549,7 @@ impl TcpStream {
lwip_sys::tcp_recv(raw, Some(recv));
lwip_sys::tcp_sent(raw, Some(sent));
lwip_sys::tcp_err(raw, Some(err));
lwip_sys::tcp_nagle_disable_(raw);
TcpStream { raw: raw, state: state }
}
}
7 changes: 7 additions & 0 deletions artiq/runtime.rs/src/clock.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,13 @@ pub fn init() {
}
}

pub fn get_us() -> u64 {
unsafe {
csr::timer0::update_value_write(1);
(INIT - csr::timer0::value_read()) / (FREQ / 1_000_000)
}
}

pub fn get_ms() -> u64 {
unsafe {
csr::timer0::update_value_write(1);
2 changes: 1 addition & 1 deletion artiq/runtime.rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ pub extern "C" fn _Unwind_Resume() -> ! {

#[no_mangle]
pub unsafe extern fn rust_main() {
static mut LOG_BUFFER: [u8; 4096] = [0; 4096];
static mut LOG_BUFFER: [u8; 65536] = [0; 65536];
BufferLogger::new(&mut LOG_BUFFER[..])
.register(move || {
info!("booting ARTIQ...");
14 changes: 9 additions & 5 deletions artiq/runtime.rs/src/logger.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use core::{mem, ptr};
use core::cell::RefCell;
use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter};
use log::{self, Log, LogLevel, LogMetadata, LogRecord, LogLevelFilter};
use log_buffer::LogBuffer;
use clock;

pub struct BufferLogger {
buffer: RefCell<LogBuffer<&'static mut [u8]>>
@@ -57,10 +58,13 @@ impl Log for BufferLogger {
fn log(&self, record: &LogRecord) {
if self.enabled(record.metadata()) {
use core::fmt::Write;
writeln!(self.buffer.borrow_mut(), "{:>5}({}): {}",
record.level(), record.target(), record.args()).unwrap();
println!("{:>5}({}): {}",
record.level(), record.target(), record.args());
writeln!(self.buffer.borrow_mut(),
"[{:12}us] {:>5}({}): {}",
clock::get_us(), record.level(), record.target(), record.args()).unwrap();
if record.level() <= LogLevel::Info {
println!("[{:12}us] {:>5}({}): {}",
clock::get_us(), record.level(), record.target(), record.args());
}
}
}
}
7 changes: 4 additions & 3 deletions artiq/runtime.rs/src/session.rs
Original file line number Diff line number Diff line change
@@ -114,7 +114,7 @@ fn host_read(stream: &mut TcpStream) -> io::Result<host::Request> {
Ok(request)
}

fn host_write(stream: &mut TcpStream, reply: host::Reply) -> io::Result<()> {
fn host_write(stream: &mut Write, reply: host::Reply) -> io::Result<()> {
trace!("comm->host {:?}", reply);
reply.write_to(stream)
}
@@ -389,8 +389,9 @@ fn process_kern_message(waiter: Waiter,
match stream {
None => unexpected!("unexpected RPC in flash kernel"),
Some(ref mut stream) => {
try!(host_write(stream, host::Reply::RpcRequest { async: async }));
try!(rpc::send_args(&mut BufWriter::new(stream), service, tag, data));
let writer = &mut BufWriter::new(stream);
try!(host_write(writer, host::Reply::RpcRequest { async: async }));
try!(rpc::send_args(writer, service, tag, data));
if !async {
session.kernel_state = KernelState::RpcWait
}
2 changes: 1 addition & 1 deletion artiq/runtime/Makefile
Original file line number Diff line number Diff line change
@@ -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
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o i2c.o

RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b
263 changes: 0 additions & 263 deletions artiq/runtime/dds.c

This file was deleted.

70 changes: 0 additions & 70 deletions artiq/runtime/dds.h

This file was deleted.

29 changes: 26 additions & 3 deletions artiq/runtime/ksupport_glue.c
Original file line number Diff line number Diff line change
@@ -70,10 +70,33 @@ int dl_iterate_phdr (int (*callback)(struct dl_phdr_info *, size_t, void *), voi
}

/* called by kernel */
long lround(double x);
long lround(double x)
double round(double x);
double round(double x)
{
return x < 0 ? floor(x) : ceil(x);
union {double f; uint64_t i;} u = {x};
int e = u.i >> 52 & 0x7ff;
double y;

if (e >= 0x3ff+52)
return x;
if (u.i >> 63)
x = -x;
if (e < 0x3ff-1) {
/* we don't do it in ARTIQ */
/* raise inexact if x!=0 */
// FORCE_EVAL(x + 0x1p52);
return 0*u.f;
}
y = (double)(x + 0x1p52) - 0x1p52 - x;
if (y > 0.5)
y = y + x - 1;
else if (y <= -0.5)
y = y + x + 1;
else
y = y + x;
if (u.i >> 63)
y = -y;
return y;
}

/* called by kernel */
4 changes: 4 additions & 0 deletions artiq/runtime/main.c
Original file line number Diff line number Diff line change
@@ -177,6 +177,10 @@ u8_t* tcp_so_options_(struct tcp_pcb *pcb) {
return &pcb->so_options;
}

void tcp_nagle_disable_(struct tcp_pcb *pcb) {
tcp_nagle_disable(pcb);
}

int main(void)
{
irq_setmask(0);
7 changes: 7 additions & 0 deletions artiq/sim/devices.py
Original file line number Diff line number Diff line change
@@ -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 @@ def run(self, k_function, k_args, k_kwargs):
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):
2 changes: 1 addition & 1 deletion artiq/test/coredevice/test_portability.py
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@ def _append(self, t, l, f):
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):
24 changes: 13 additions & 11 deletions artiq/test/coredevice/test_rtio.py
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ def run(self):
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 @@ def run(self):
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 @@ def build(self):
@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 @@ def run(self):
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 @@ def build(self):
@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 @@ def run(self):
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 @@ def test_time_keeps_running(self):
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 @@ def bench(self):
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)]
@@ -450,6 +450,8 @@ class RPCTest(ExperimentCase):
"timings are dependent on CPU load and network conditions")
def test_rpc_timing(self):
self.execute(RPCTiming)
self.assertGreater(self.dataset_mgr.get("rpc_time_mean"), 100*ns)
self.assertLess(self.dataset_mgr.get("rpc_time_mean"), 15*ms)
self.assertLess(self.dataset_mgr.get("rpc_time_stddev"), 2*ms)
rpc_time_mean = self.dataset_mgr.get("rpc_time_mean")
print(rpc_time_mean)
self.assertGreater(rpc_time_mean, 100*ns)
self.assertLess(rpc_time_mean, 2*ms)
self.assertLess(self.dataset_mgr.get("rpc_time_stddev"), 1*ms)
4 changes: 4 additions & 0 deletions artiq/test/lit/codegen/noop_coercion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# RUN: %python -m artiq.compiler.testbench.llvmgen %s

def f():
return float(1.0)
26 changes: 26 additions & 0 deletions artiq/test/lit/embedding/invariant_propagation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s
# RUN: OutputCheck %s --file-to-check=%t.ll

from artiq.language.core import *
from artiq.language.types import *

class Class:
kernel_invariants = {"foo"}

def __init__(self):
self.foo = True

@kernel
def run(self):
if self.foo:
print("bar")
else:
# Make sure all the code for this branch will be completely elided:
# CHECK-NOT: baz
print("baz")

obj = Class()

@kernel
def entrypoint():
obj.run()
4 changes: 2 additions & 2 deletions artiq/test/lit/inferencer/builtin_calls.py
Original file line number Diff line number Diff line change
@@ -13,8 +13,8 @@
# 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()
5 changes: 5 additions & 0 deletions artiq/test/lit/inferencer/cast.py
Original file line number Diff line number Diff line change
@@ -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
4 changes: 0 additions & 4 deletions artiq/test/lit/inferencer/error_builtin_calls.py
Original file line number Diff line number Diff line change
@@ -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)

4 changes: 2 additions & 2 deletions artiq/test/lit/inferencer/error_coerce.py
Original file line number Diff line number Diff line change
@@ -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,)
2 changes: 1 addition & 1 deletion artiq/test/lit/integration/builtin.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@

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
5 changes: 0 additions & 5 deletions artiq/test/lit/time/conversion.py

This file was deleted.

4 changes: 2 additions & 2 deletions conda/artiq-kc705-nist_clock/meta.yaml
Original file line number Diff line number Diff line change
@@ -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
14 changes: 0 additions & 14 deletions conda/artiq-kc705-nist_qc1/build.sh

This file was deleted.

27 changes: 0 additions & 27 deletions conda/artiq-kc705-nist_qc1/meta.yaml

This file was deleted.

4 changes: 2 additions & 2 deletions conda/artiq-kc705-nist_qc2/meta.yaml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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'
4 changes: 2 additions & 2 deletions conda/artiq/meta.yaml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ requirements:
- binutils-or1k-linux
run:
- python >=3.5.2
- llvmlite-artiq 0.10.0.dev py35_24
- llvmlite-artiq 0.12.0.dev py35_29
- lit
- outputcheck
- scipy
@@ -37,7 +37,7 @@ requirements:
- pygit2
- aiohttp
- binutils-or1k-linux
- pythonparser
- pythonparser 1.0
- levenshtein

test:
65 changes: 60 additions & 5 deletions doc/manual/compiler.rst
Original file line number Diff line number Diff line change
@@ -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
-----------------
@@ -54,7 +81,7 @@ If an RPC returns no value, it can be invoked in a way that does not block until
execution, but only until it is queued. (Submitting asynchronous RPCs too rapidly, as well as
submitting asynchronous RPCs with arguments that are too large, can still block until completion.)

To define an asynchronous RPC, use the ``@rpc`` annotation with a flag:
To define an asynchronous RPC, use the ``@rpc`` annotation with a flag: ::

@rpc(flags={"async"})
def record_result(x):
@@ -87,7 +114,7 @@ Kernel invariants

The compiler attempts to remove or hoist out of loops any redundant memory load operations, as well as propagate known constants into function bodies, which can enable further optimization. However, it must make conservative assumptions about code that it is unable to observe, because such code can change the value of the attribute, making the optimization invalid.

When an attribute is known to never change while the kernel is running, it can be marked as a *kernel invariant* to enable more aggressive optimization for this specific attribute: ::
When an attribute is known to never change while the kernel is running, it can be marked as a *kernel invariant* to enable more aggressive optimization for this specific attribute. ::

class Converter:
kernel_invariants = {"ratio"}
@@ -99,4 +126,32 @@ When an attribute is known to never change while the kernel is running, it can b
def convert(self, value):
return value * self.ratio ** 2

In the synthetic example above, the compiler will be able to detect that the result of evaluating ``self.ratio ** 2`` never changes and replace it with a constant, removing an expensive floating-point operation.
In the synthetic example above, the compiler will be able to detect that the result of evaluating ``self.ratio ** 2`` never changes and replace it with a constant, removing an expensive floating-point operation. ::

class Worker:
kernel_invariants = {"interval"}

def __init__(self, interval=1.0*us):
self.interval = interval

def work(self):
# something useful

class Looper:
def __init__(self, worker):
self.worker = worker

@kernel
def loop(self):
for _ in range(100):
delay(self.worker.interval / 5.0)
self.worker.work()

In the synthetic example above, the compiler will be able to detect that the result of evaluating ``self.interval / 5.0`` never changes, even though it neither knows the value of ``self.worker.interval`` beforehand nor can it see through the ``self.worker.work()`` function call, and hoist the expensive floating-point division out of the loop, transforming the code for ``loop`` into an equivalent of the following: ::

@kernel
def loop(self):
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()
57 changes: 12 additions & 45 deletions doc/manual/core_device.rst
Original file line number Diff line number Diff line change
@@ -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
++++++++++

@@ -166,41 +141,33 @@ 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):

+--------------+--------+--------+--------+--------+
| 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.
11 changes: 5 additions & 6 deletions doc/manual/installing.rst
Original file line number Diff line number Diff line change
@@ -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.

6 changes: 3 additions & 3 deletions doc/manual/installing_from_source.rst
Original file line number Diff line number Diff line change
@@ -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.

2 changes: 1 addition & 1 deletion doc/manual/rtio.rst
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
"artiq_coreconfig=artiq.frontend.artiq_coreconfig:main",
"artiq_corelog=artiq.frontend.artiq_corelog:main",
"artiq_ctlmgr=artiq.frontend.artiq_ctlmgr:main",
"artiq_devtool=artiq.frontend.artiq_devtool:main",
"artiq_influxdb=artiq.frontend.artiq_influxdb:main",
"artiq_master=artiq.frontend.artiq_master:main",
"artiq_mkfs=artiq.frontend.artiq_mkfs:main",

0 comments on commit 3459793

Please sign in to comment.