From 8e77e561cdd015d4fd807051ca01010ec29b60b5 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 25 Feb 2016 18:46:49 +0000 Subject: [PATCH 001/125] test: bring back test_loopback_count (fixes #295). --- artiq/test/coredevice/test_rtio.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index ba05b8f73..a4e90b236 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -101,6 +101,28 @@ class Watchdog(EnvExperiment): pass +class LoopbackCount(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("ttl_inout") + self.setattr_argument("npulses") + + def set_count(self, count): + self.set_dataset("count", count) + + @kernel + def run(self): + self.loop_out.output() + delay(5*us) + with parallel: + self.loop_in.gate_rising(10*us) + with sequential: + for i in range(self.npulses): + delay(25*ns) + self.loop_out.pulse(25*ns) + self.set_dataset("count", self.loop_in.count()) + + class Underflow(EnvExperiment): def build(self): self.setattr_device("core") @@ -182,6 +204,12 @@ class CoredeviceTest(ExperimentCase): self.assertGreater(rate, 100*ns) self.assertLess(rate, 2500*ns) + def test_loopback_count(self): + npulses = 2 + self.execute(LoopbackCount, npulses=npulses) + count = self.dataset_mgr.get("count") + self.assertEqual(count, npulses) + def test_underflow(self): with self.assertRaises(RTIOUnderflow): self.execute(Underflow) From 919a49b6bc5d731b09d08a6aba26442c8834df0d Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 25 Feb 2016 19:43:52 +0000 Subject: [PATCH 002/125] compiler: quell excessively detailed diagnostics. --- artiq/compiler/types.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index f5adf79e5..460c07ae7 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -694,42 +694,45 @@ class TypePrinter(object): self.map = {} self.recurse_guard = set() - def name(self, typ): + def name(self, typ, depth=0, max_depth=1): typ = typ.find() if isinstance(typ, TVar): if typ not in self.map: self.map[typ] = "'%s" % next(self.gen) return self.map[typ] elif isinstance(typ, TInstance): - if typ in self.recurse_guard: + if typ in self.recurse_guard or depth >= max_depth: return "".format(typ.name) else: self.recurse_guard.add(typ) - attrs = ", ".join(["{}: {}".format(attr, self.name(typ.attributes[attr])) - for attr in typ.attributes]) - return "".format(typ.name, attrs) + attrs = ",\n\t\t".join(["{}: {}".format(attr, self.name(typ.attributes[attr], + depth + 1)) + for attr in typ.attributes]) + return "".format(typ.name, attrs) elif isinstance(typ, TMono): if typ.params == {}: return typ.name else: return "%s(%s)" % (typ.name, ", ".join( - ["%s=%s" % (k, self.name(typ.params[k])) for k in typ.params])) + ["%s=%s" % (k, self.name(typ.params[k], depth + 1)) for k in typ.params])) elif isinstance(typ, TTuple): if len(typ.elts) == 1: - return "(%s,)" % self.name(typ.elts[0]) + return "(%s,)" % self.name(typ.elts[0], depth + 1) else: - return "(%s)" % ", ".join(list(map(self.name, typ.elts))) + return "(%s)" % ", ".join([self.name(typ, depth + 1) for typ in typ.elts]) elif isinstance(typ, (TFunction, TRPCFunction, TCFunction)): args = [] - args += [ "%s:%s" % (arg, self.name(typ.args[arg])) for arg in typ.args] - args += ["?%s:%s" % (arg, self.name(typ.optargs[arg])) for arg in typ.optargs] - signature = "(%s)->%s" % (", ".join(args), self.name(typ.ret)) + args += [ "%s:%s" % (arg, self.name(typ.args[arg], depth + 1)) + for arg in typ.args] + args += ["?%s:%s" % (arg, self.name(typ.optargs[arg], depth + 1)) + for arg in typ.optargs] + signature = "(%s)->%s" % (", ".join(args), self.name(typ.ret, depth + 1)) delay = typ.delay.find() if isinstance(delay, TVar): - signature += " delay({})".format(self.name(delay)) + signature += " delay({})".format(self.name(delay, depth + 1)) elif not (delay.is_fixed() and iodelay.is_zero(delay.duration)): - signature += " " + self.name(delay) + signature += " " + self.name(delay, depth + 1) if isinstance(typ, TRPCFunction): return "[rpc #{}]{}".format(typ.service, signature) @@ -740,11 +743,12 @@ class TypePrinter(object): elif isinstance(typ, TBuiltinFunction): return "".format(typ.name) elif isinstance(typ, (TConstructor, TExceptionConstructor)): - if typ in self.recurse_guard: + if typ in self.recurse_guard or depth >= max_depth: return "".format(typ.name) else: self.recurse_guard.add(typ) - attrs = ", ".join(["{}: {}".format(attr, self.name(typ.attributes[attr])) + attrs = ", ".join(["{}: {}".format(attr, self.name(typ.attributes[attr], + depth + 1)) for attr in typ.attributes]) return "".format(typ.name, attrs) elif isinstance(typ, TBuiltin): From d899d7307e4bd192b3500f37ff5bdbea180289bc Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 25 Feb 2016 19:56:12 +0000 Subject: [PATCH 003/125] compiler.types: TDelay is always unifiable with self. --- artiq/compiler/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index 460c07ae7..fdb225d9c 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -527,7 +527,7 @@ class TDelay(Type): elif self.is_fixed() and other.is_fixed() and \ self.duration.fold() == other.duration.fold(): pass - else: + elif self is not other: raise UnificationError(self, other) def fold(self, accum, fn): From f838b8be497ac68206565d0f0582873b7136ccbf Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 25 Feb 2016 19:56:45 +0000 Subject: [PATCH 004/125] compiler.embedding: cache attribute types (fixes #276). --- artiq/compiler/embedding.py | 203 ++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 100 deletions(-) diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 7cd53eaaa..17155d767 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -295,6 +295,102 @@ class StitchingInferencer(Inferencer): super().__init__(engine) self.value_map = value_map self.quote = quote + self.attr_type_cache = {} + + def _compute_value_type(self, object_value, object_type, object_loc, attr_name, loc): + if not hasattr(object_value, attr_name): + if attr_name.startswith('_'): + names = set(filter(lambda name: not name.startswith('_'), + dir(object_value))) + else: + names = set(dir(object_value)) + suggestion = suggest_identifier(attr_name, names) + + note = diagnostic.Diagnostic("note", + "attribute accessed here", {}, + loc) + if suggestion is not None: + diag = diagnostic.Diagnostic("error", + "host object does not have an attribute '{attr}'; " + "did you mean '{suggestion}'?", + {"attr": attr_name, "suggestion": suggestion}, + object_loc, notes=[note]) + else: + diag = diagnostic.Diagnostic("error", + "host object does not have an attribute '{attr}'", + {"attr": attr_name}, + object_loc, notes=[note]) + self.engine.process(diag) + return + + # Figure out what ARTIQ type does the value of the attribute have. + # We do this by quoting it, as if to serialize. This has some + # overhead (i.e. synthesizing a source buffer), but has the advantage + # of having the host-to-ARTIQ mapping code in only one place and + # also immediately getting proper diagnostics on type errors. + attr_value = getattr(object_value, attr_name) + if inspect.ismethod(attr_value) and types.is_instance(object_type): + # In cases like: + # class c: + # @kernel + # def f(self): pass + # we want f to be defined on the class, not on the instance. + attributes = object_type.constructor.attributes + attr_value = attr_value.__func__ + else: + attributes = object_type.attributes + + attr_value_type = None + + if isinstance(attr_value, list): + # Fast path for lists of scalars. + IS_FLOAT = 1 + IS_INT32 = 2 + IS_INT64 = 4 + + state = 0 + for elt in attr_value: + if elt.__class__ == float: + state |= IS_FLOAT + elif elt.__class__ == int: + if -2**31 < elt < 2**31-1: + state |= IS_INT32 + elif -2**63 < elt < 2**63-1: + state |= IS_INT64 + else: + state = -1 + break + else: + state = -1 + + if state == IS_FLOAT: + attr_value_type = builtins.TList(builtins.TFloat()) + elif state == IS_INT32: + attr_value_type = builtins.TList(builtins.TInt32()) + elif state == IS_INT64: + attr_value_type = builtins.TList(builtins.TInt64()) + + if attr_value_type is None: + # Slow path. We don't know what exactly is the attribute value, + # so we quote it only for the error message that may possibly result. + ast = self.quote(attr_value, object_loc.expanded_from) + + def proxy_diagnostic(diag): + note = diagnostic.Diagnostic("note", + "while inferring a type for an attribute '{attr}' of a host object", + {"attr": attr_name}, + loc) + diag.notes.append(note) + + self.engine.process(diag) + + proxy_engine = diagnostic.Engine() + proxy_engine.process = proxy_diagnostic + Inferencer(engine=proxy_engine).visit(ast) + IntMonomorphizer(engine=proxy_engine).visit(ast) + attr_value_type = ast.type + + return attributes, attr_value_type def _unify_attribute(self, result_type, value_node, attr_name, attr_loc, loc): # The inferencer can only observe types, not values; however, @@ -304,108 +400,15 @@ class StitchingInferencer(Inferencer): # its type, we now interrogate every host object we have to ensure # that we can successfully serialize the value of the attribute we # are now adding at the code generation stage. - # - # FIXME: We perform exhaustive checks of every known host object every - # time an attribute access is visited, which is potentially quadratic. - # This is done because it is simpler than performing the checks only when: - # * a previously unknown attribute is encountered, - # * a previously unknown host object is encountered; - # which would be the optimal solution. - object_type = value_node.type.find() for object_value, object_loc in self.value_map[object_type]: - attr_value_type = None - if not hasattr(object_value, attr_name): - if attr_name.startswith('_'): - names = set(filter(lambda name: not name.startswith('_'), - dir(object_value))) - else: - names = set(dir(object_value)) - suggestion = suggest_identifier(attr_name, names) - - note = diagnostic.Diagnostic("note", - "attribute accessed here", {}, - loc) - if suggestion is not None: - diag = diagnostic.Diagnostic("error", - "host object does not have an attribute '{attr}'; " - "did you mean '{suggestion}'?", - {"attr": attr_name, "suggestion": suggestion}, - object_loc, notes=[note]) - else: - diag = diagnostic.Diagnostic("error", - "host object does not have an attribute '{attr}'", - {"attr": attr_name}, - object_loc, notes=[note]) - self.engine.process(diag) - return - - # Figure out what ARTIQ type does the value of the attribute have. - # We do this by quoting it, as if to serialize. This has some - # overhead (i.e. synthesizing a source buffer), but has the advantage - # of having the host-to-ARTIQ mapping code in only one place and - # also immediately getting proper diagnostics on type errors. - attr_value = getattr(object_value, attr_name) - if inspect.ismethod(attr_value) and types.is_instance(object_type): - # In cases like: - # class c: - # @kernel - # def f(self): pass - # we want f to be defined on the class, not on the instance. - attributes = object_type.constructor.attributes - attr_value = attr_value.__func__ - is_method = True - else: - attributes = object_type.attributes - is_method = False - - if isinstance(attr_value, list): - # Fast path for lists of scalars. - IS_FLOAT = 1 - IS_INT32 = 2 - IS_INT64 = 4 - - state = 0 - for elt in attr_value: - if elt.__class__ == float: - state |= IS_FLOAT - elif elt.__class__ == int: - if -2**31 < elt < 2**31-1: - state |= IS_INT32 - elif -2**63 < elt < 2**63-1: - state |= IS_INT64 - else: - state = -1 - break - else: - state = -1 - - if state == IS_FLOAT: - attr_value_type = builtins.TList(builtins.TFloat()) - elif state == IS_INT32: - attr_value_type = builtins.TList(builtins.TInt32()) - elif state == IS_INT64: - attr_value_type = builtins.TList(builtins.TInt64()) - - if attr_value_type is None: - # Slow path. We don't know what exactly is the attribute value, - # so we quote it only for the error message that may possibly result. - ast = self.quote(attr_value, object_loc.expanded_from) - - def proxy_diagnostic(diag): - note = diagnostic.Diagnostic("note", - "while inferring a type for an attribute '{attr}' of a host object", - {"attr": attr_name}, - loc) - diag.notes.append(note) - - self.engine.process(diag) - - proxy_engine = diagnostic.Engine() - proxy_engine.process = proxy_diagnostic - Inferencer(engine=proxy_engine).visit(ast) - IntMonomorphizer(engine=proxy_engine).visit(ast) - attr_value_type = ast.type + attr_type_key = (id(object_value), attr_name) + try: + attributes, attr_value_type = self.attr_type_cache[attr_type_key] + except KeyError: + attributes, attr_value_type = \ + self._compute_value_type(object_value, object_type, object_loc, attr_name, loc) + self.attr_type_cache[attr_type_key] = attributes, attr_value_type if attr_name not in attributes: # We just figured out what the type should be. Add it. From 6bd16e448e167d75a1e5e0432dc6619544ff5c8a Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 25 Feb 2016 20:02:28 +0000 Subject: [PATCH 005/125] Commit missing parts of 919a49b6. --- artiq/test/lit/inferencer/error_with_arity.py | 2 +- artiq/test/lit/inferencer/error_with_exn.py | 2 +- artiq/test/lit/inferencer/error_with_self.py | 2 +- artiq/test/lit/iodelay/class.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/artiq/test/lit/inferencer/error_with_arity.py b/artiq/test/lit/inferencer/error_with_arity.py index b056073ae..c91a3996b 100644 --- a/artiq/test/lit/inferencer/error_with_arity.py +++ b/artiq/test/lit/inferencer/error_with_arity.py @@ -9,7 +9,7 @@ class contextmgr: pass def foo(): - # CHECK-L: ${LINE:+2}: error: function '__enter__(self:, n1:'a)->NoneType delay('b)' must accept 1 positional argument and no optional arguments + # CHECK-L: ${LINE:+2}: error: function '__enter__(self:, n1:'a)->NoneType delay('b)' must accept 1 positional argument and no optional arguments # CHECK-L: ${LINE:+1}: error: function '__exit__(self:, n1:'c, n2:'d)->NoneType delay('e)' must accept 4 positional arguments and no optional arguments with contextmgr(): pass diff --git a/artiq/test/lit/inferencer/error_with_exn.py b/artiq/test/lit/inferencer/error_with_exn.py index 5aa8ed1ec..27d6b1b54 100644 --- a/artiq/test/lit/inferencer/error_with_exn.py +++ b/artiq/test/lit/inferencer/error_with_exn.py @@ -11,6 +11,6 @@ class contextmgr: def foo(): # CHECK-L: ${LINE:+2}: error: cannot unify int(width='a) with NoneType - # CHECK-L: ${LINE:+1}: note: exception handling via context managers is not supported; the argument 'n3' of function '__exit__(self:, n1:NoneType, n2:NoneType, n3:int(width='a))->NoneType delay('b)' will always be None + # CHECK-L: ${LINE:+1}: note: exception handling via context managers is not supported; the argument 'n3' of function '__exit__(self:, n1:NoneType, n2:NoneType, n3:int(width='a))->NoneType delay('b)' will always be None with contextmgr(): pass diff --git a/artiq/test/lit/inferencer/error_with_self.py b/artiq/test/lit/inferencer/error_with_self.py index afe53e531..1bf087c26 100644 --- a/artiq/test/lit/inferencer/error_with_self.py +++ b/artiq/test/lit/inferencer/error_with_self.py @@ -11,7 +11,7 @@ class contextmgr: def foo(): contextmgr.__enter__(1) # CHECK-L: ${LINE:+3}: error: cannot unify with int(width='a) while inferring the type for self argument - # CHECK-L: ${LINE:+2}: note: expression of type + # CHECK-L: ${LINE:+2}: note: expression of type # CHECK-L: ${LINE:+1}: note: reference to an instance with a method '__enter__(self:int(width='a))->NoneType delay('b)' with contextmgr(): pass diff --git a/artiq/test/lit/iodelay/class.py b/artiq/test/lit/iodelay/class.py index 6bcb9339b..63fab1862 100644 --- a/artiq/test/lit/iodelay/class.py +++ b/artiq/test/lit/iodelay/class.py @@ -1,7 +1,7 @@ # RUN: %python -m artiq.compiler.testbench.signature %s >%t # RUN: OutputCheck %s --file-to-check=%t -# CHECK-L: g: (i:)->NoneType delay(1000000 mu) +# CHECK-L: g: (i:)->NoneType delay(1000000 mu) def g(i): i.f(1.0) From 82a8e819ac25126c8b29d9b3c929ef6d714447ae Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 25 Feb 2016 20:15:40 +0000 Subject: [PATCH 006/125] transforms.llvm_ir_generator: use private linkage instead of internal. This reduces the size of symbol tables (internal adds an STB_LOCAL symbol, but private doesn't). --- artiq/compiler/transforms/llvm_ir_generator.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 6f8e0633a..0c0d532cd 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -485,7 +485,7 @@ class LLVMIRGenerator: ]) llrpcattr.global_constant = True llrpcattr.unnamed_addr = True - llrpcattr.linkage = 'internal' + llrpcattr.linkage = 'private' return llrpcattr @@ -499,14 +499,14 @@ class LLVMIRGenerator: llrpcattrs + [ll.Constant(llrpcattrty.as_pointer(), None)]) llrpcattrary.global_constant = True llrpcattrary.unnamed_addr = True - llrpcattrary.linkage = 'internal' + llrpcattrary.linkage = 'private' llobjectaryty = ll.ArrayType(llptr, len(llobjects[typ]) + 1) llobjectary = ll.GlobalVariable(self.llmodule, llobjectaryty, name="objects.{}".format(type_name)) llobjectary.initializer = ll.Constant(llobjectaryty, llobjects[typ] + [ll.Constant(llptr, None)]) - llobjectary.linkage = 'internal' + llobjectary.linkage = 'private' lldesc = ll.GlobalVariable(self.llmodule, lldescty, name="desc.{}".format(type_name)) @@ -515,7 +515,7 @@ class LLVMIRGenerator: llobjectary.bitcast(llptr.as_pointer()) ]) lldesc.global_constant = True - lldesc.linkage = 'internal' + lldesc.linkage = 'private' lldescs.append(lldesc) llglobaldescty = ll.ArrayType(lldescty.as_pointer(), len(lldescs) + 1) @@ -529,7 +529,7 @@ class LLVMIRGenerator: self.llfunction = self.map(func) if func.is_internal: - self.llfunction.linkage = 'internal' + self.llfunction.linkage = 'private' self.llfunction.attributes.add('uwtable') From a5bf502917650df00e1a3e1dde7ce59852999bf8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 26 Feb 2016 14:29:51 +0800 Subject: [PATCH 007/125] test/LoopbackCount: request correct devices --- artiq/test/coredevice/test_rtio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index a4e90b236..ff50bf51c 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -104,7 +104,8 @@ class Watchdog(EnvExperiment): class LoopbackCount(EnvExperiment): def build(self): self.setattr_device("core") - self.setattr_device("ttl_inout") + self.setattr_device("loop_in") + self.setattr_device("loop_out") self.setattr_argument("npulses") def set_count(self, count): From fb929c8599f2df03fcdbb5f69225d14dece40c95 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 26 Feb 2016 13:11:10 +0100 Subject: [PATCH 008/125] gateware/spi: stubs --- artiq/gateware/targets/pipistrello.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index 288a7b3c8..dbb840aab 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -19,7 +19,7 @@ from misoc.targets.pipistrello import * from artiq.gateware.soc import AMPSoC from artiq.gateware import rtio, nist_qc1 -from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds +from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds, spi from artiq import __artiq_dir__ as artiq_dir from artiq import __version__ as artiq_version @@ -152,12 +152,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512, ofifo_depth=4)) - # ttl2 can run on a 8x serdes if xtrig is not used - for i in range(15): + # the last five ttls are used for SPI and a ClockGen + for i in range(11): if i in (0, 1): phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i), self.rtio_crg.rtiox4_stb) - elif i in (2,): + elif i in (2,): # ttl2 can run on a 8x serdes if xtrig is not used phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i), self.rtio_crg.rtiox8_stb) else: @@ -192,6 +192,20 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd ofifo_depth=512, ififo_depth=4)) + spi_pins = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) + # cs_n can be multiple bits wide, one-hot + # absence of miso indicates bidirectional mosi + self.comb += [ + platform.request("ttl", 11).eq(spi_pins.cs_n), + platform.request("ttl", 12).eq(spi_pins.clk), + platform.request("ttl", 13).eq(spi_pins.mosi), + spi_pins.miso.eq(platform.request("ttl", 14)), + ] + phy = spi.SPIMaster(spi_pins) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy( + phy, ofifo_depth=4, ififo_depth=4)) + self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.LogChannel()) From 313a696a540b7c35241d76e98060f813f04da680 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 26 Feb 2016 13:10:10 +0100 Subject: [PATCH 009/125] examples/notebook: cleanup, and fix spelling --- examples/artiq_ipython_notebook.ipynb | 113 ++++++++++++++++---------- 1 file changed, 71 insertions(+), 42 deletions(-) diff --git a/examples/artiq_ipython_notebook.ipynb b/examples/artiq_ipython_notebook.ipynb index e4a58d237..1d6026090 100644 --- a/examples/artiq_ipython_notebook.ipynb +++ b/examples/artiq_ipython_notebook.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "collapsed": false }, @@ -24,6 +24,7 @@ "import asyncio\n", "import datetime\n", "import glob\n", + "from pprint import pprint\n", "\n", "import numpy as np\n", "np.set_printoptions(precision=3)\n", @@ -39,19 +40,6 @@ "from artiq.master.worker_db import DeviceManager" ] }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# let's assume artiq_master and artiq_ctlmgr are already running\n", - "# move to a location where we have our artiq setup\n", - "os.chdir(os.path.expanduser(\"~/work/nist/artiq/run\"))" - ] - }, { "cell_type": "code", "execution_count": 3, @@ -60,21 +48,35 @@ }, "outputs": [], "source": [ - "# you can directly use the artiq controller infrastructure\n", + "# let's assume artiq_master and artiq_ctlmgr are already running\n", + "# then move to a location where we have our artiq setup\n", + "os.chdir(os.path.expanduser(\"~/work/nist/artiq/run\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# we can directly use the artiq controller infrastructure\n", "# and access any artiq device\n", "\n", - "# you can have artiq prepare that for you:\n", - "\n", + "# we can have artiq prepare that connection for us:\n", "ddb = DeviceDB(\"device_db.pyon\")\n", "devmgr = DeviceManager(ddb)\n", "lda = devmgr.get(\"lda\")\n", "lda.set_attenuation(42)\n", "assert lda.get_attenuation() == 42\n", "\n", - "# ... or you can wire it up yourself if you know where it is\n", + "# ... or we can wire it up ourselves if you know where it is\n", "assert ddb.get(\"lda\")[\"host\"] == \"::1\"\n", "assert ddb.get(\"lda\")[\"port\"] == 3253\n", "\n", + "# there are different Client types tailored to different use cases:\n", + "\n", "# synchronous\n", "lda = Client(\"::1\", 3253)\n", "assert lda.get_attenuation() == 42\n", @@ -93,7 +95,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": { "collapsed": false }, @@ -102,8 +104,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "current schedule {}\n", - "experiments ['__pycache__/', 'flopping_f_simulation.py', 'notebook_test.py', '.git/', 'idle.elf', 'transport.py', 'idle.py', 'speed_benchmark.py', 'test_raise.py']\n" + "current schedule\n", + "{}\n", + "experiments:\n", + "['ex/',\n", + " 'test_analyzer.py',\n", + " 'notebook_test.py',\n", + " 'speed_benchmark.py',\n", + " 'histograms.py',\n", + " 'arguments_demo.py',\n", + " '.git/',\n", + " '__pycache__/',\n", + " 'flopping_f_simulation.py',\n", + " 'test_crash.py',\n", + " 'run_forever.py',\n", + " 'transport.py',\n", + " 'pdq2_simple.py']\n" ] } ], @@ -114,13 +130,15 @@ " Client(\"::1\", 3251, \"master_\" + i) for i in\n", " \"schedule experiment_db dataset_db\".split()]\n", "\n", - "print(\"current schedule\", schedule.get_status())\n", - "print(\"experiments\", exps.list_directory(\"repository\"))" + "print(\"current schedule\")\n", + "pprint(schedule.get_status())\n", + "print(\"experiments:\")\n", + "pprint(exps.list_directory(\"repository\"))" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": { "collapsed": false }, @@ -129,12 +147,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "current schedule {131: {'priority': 0, 'status': 'preparing', 'repo_msg': None, 'pipeline': 'main', 'due_date': None, 'flush': False, 'expid': {'file': 'repository/flopping_f_simulation.py', 'arguments': {'noise_amplitude': 0.1, 'F0': 1500}, 'log_level': 30, 'class_name': 'FloppingF'}}}\n" + "current schedule\n", + "{4722: {'due_date': None,\n", + " 'expid': {'arguments': {'F0': 1500, 'noise_amplitude': 0.3},\n", + " 'class_name': 'FloppingF',\n", + " 'file': 'repository/flopping_f_simulation.py',\n", + " 'log_level': 30},\n", + " 'flush': False,\n", + " 'pipeline': 'main',\n", + " 'priority': 0,\n", + " 'repo_msg': None,\n", + " 'status': 'preparing'}}\n" ] } ], "source": [ - "# we can submit experiments to be run:\n", + "# we can submit experiments to be run\n", "\n", "expid = dict(\n", " file=\"repository/flopping_f_simulation.py\",\n", @@ -142,18 +170,19 @@ " log_level=logging.WARNING,\n", " arguments=dict(\n", " F0=1500,\n", - " noise_amplitude=.1,\n", + " noise_amplitude=.3,\n", " ),\n", ")\n", "if not schedule.get_status():\n", " rid = schedule.submit(pipeline_name=\"main\", expid=expid,\n", " priority=0, due_date=None, flush=False)\n", - "print(\"current schedule\", schedule.get_status())" + "print(\"current schedule\")\n", + "pprint(schedule.get_status())" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": { "collapsed": false }, @@ -167,7 +196,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": { "collapsed": false }, @@ -176,14 +205,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "flopping_f 1499.996784076909\n" + "flopping_f: 1499.944285221012\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAECCAYAAAAFL5eMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xt4VOWBP/DvOXOfTC4ECJckJCFcE4FKEsyqIKuywtbd\nLRZXYWVlpf1x2fpYeFCUi6GR1a3bZ+tPH9tKf219DF2wVfyt9de1NdIahSgwBeRiuIdLICGEXOY+\nZ+ac3x9JJkRCZpLMBOad7+d5eB4yM+ecN2/OfOed97znfSVN0zQQEZFQ5JtdACIiij6GOxGRgBju\nREQCYrgTEQmI4U5EJCCGOxGRgPThXhAIBLB27VrU1dVBr9fjhRdegNfrxbJly5CbmwsAWLhwIebN\nmxfrshIRUYSkcOPcP/74Y3zwwQf48Y9/jN27d2P79u2YOXMmXC4XlixZMkjFJCKivgjbcs/NzUUw\nGISmaXA4HDAYDDhy5AjOnDmDyspK5OTkYP369bBarYNRXiIiikDYlnt9fT1WrlwJl8uFlpYWvPHG\nGzhz5gwmTpyIgoIC/OxnP0NrayvWrl07WGUmIqIwwl5QffPNNzFz5kz84Q9/wPvvv4+1a9di1qxZ\nKCgoAADMmTMHNTU1MS8oERFFLmy3TGpqKvT69pclJydDURQsX74cGzduxNSpU1FdXY3CwsJe92G3\n26NTWiKiBFNUVNSv7cJ2y7jdbqxbtw6NjY0IBAJ4/PHHkZeXh/LychgMBgwfPhzl5eVISkq64T7s\ndnu/Cyga1kUX1kUX1kUX1kWXgdRF2Ja71WrFK6+8ct3j27Zt69cBiYgo9ngTExGRgBjuREQCYrgT\nEQmI4U5EJCCGOxGRgBjuREQCYrgTEQmI4U5EJCCGOxGRgBjuREQCYrgTEQmI4U5EJCCGOxGRgBju\nREQCYrgTEQmI4U5EJCCGOxGRgBjuREQCYrgTEQmI4U5EJCCGOxGRgBjuREQCYrgTEQmI4U5EJCB9\nuBcEAgGsXbsWdXV10Ov1eOGFF6DT6fDss89ClmWMHz8eZWVlg1FWIiKKUNiW+yeffAJVVbF9+3as\nXLkSP/7xj/HSSy9h9erV2Lp1K1RVRWVlZdgDub1KVAo82JSACo8vAKdHgcPth6ZpN7tIRBRlfiWI\nY2evQlXFeX+Hbbnn5uYiGAxC0zQ4HA7o9XocPHgQxcXFAIBZs2Zh9+7duP/++3vdzyPrf49Rw5Iw\ndnQqIAHNbV5cbfPCYtLjgdJc3FucDYupvTg+JYgT55phMemROzoVOlkCADS1evDJX+rwl2MNMBp0\nSEkyIiXJhNQkI9KSTUi1mRAMqjh2rhk1tc04VdcCVdUgyxJ0soTRw224a+po3Dl1NDKGWNDY7MHR\n2quovdiKsZmpKCkYCYtJD03TsP94I97deQJfnrzS7fcYkW5F6W2jcMdtI1GQmw6d7vrPR68vAKNB\nB7mj3ED7h9vuLy/i48+b8L69Gi6PArcvgIK8dHzrnnxkZST3WG/n6tvw6YGLqGt04mqbF02tHpgM\nOvx1UTbuLc7GkBRzuD9hN4dPXcFvKo8jI92KJ/6uEFazoU/bA8ChU1fwP7trcW9xNoonj+jz9tGy\n408nUN/kxsoF025aGW4ml0eBBsBq0nc71yKlBFRcbfMiY4gFkhT59s0OLxqbPWhz+eFw+9Hq9KGp\ntf397PYGMPMbmbhnelbofdsTt1eB2dh7uQPB9vINSTbBoNeFHnd6FOw5Uo+as1fh8wfhU4LwK0EE\ngxoCQRVBVYMkASaDLpQT95eMwaTc9OuOoaoaqg7UoeL3R3G52YNxWan4zj9MQeHYoRHXh9ur4MS5\nFnx19ipcHgV5o1OQn5mGrAxbt3xQVQ37vmrAf1edwvFzzTAb9bCY9Eiy6DEpNx1Fk0bgtvyhMBvD\nxnJEJC1MU7S+vh4rV66Ey+VCS0sLfvazn+Gpp55CVVUVAODzzz/Hjh078PLLL99wH3a7He/t9eLU\nhVY4Pe0teFkCUm0mONx+BIIaksx63Dl1NC5eceHY2WYEgioAwGLSYeKYdEACvjzRiEg/WCUJyMqw\nwWTQQVUBJaii7rIjtH1KkhFtLn+3bYx6GdMnZeDyVQ9OX2wFAEwYk4aUJBN0soSgquHI6SZ4fIHQ\n67NGJGPMyGSkJ5tx4bITZy61orHZA6tZj7zRqRibmYoWhw9fHL4Ef0ANHUuvk2DQy/D4ggCAOwpH\nYuY3MiFLEoKahuY2L6r2X8DJC62hbWQJSEs2welW4A+okGUJJZPbT4ixmakYOzoVNqvxuroIBlWc\nvNCC//rDMfzl2OXQ4xlDLFi1cDpuyx8WUZ2eudiKt37/FfZ91RCq40UPTMI/3jehz+HS6vRh9579\nmPvXd/QpWDodOnkF6366CwCw9QdzkWoz9Xkf/RUMqnB6lAEds6m1PRyzMmww6HWw2+0oKioKu51f\nCeKLw/Wo3HcOB45dhqq1nxdJFiMy0i2YnJOOSbnpmDBmCFJtRlhMekiSBI8vgLrLTlxodOLUhRYc\nO9uMUxda4A+oyBhiwd3TMnHXtNHQyRLO1reh9pIDDpcfyUlGpCYZYTDIOHm+BV/VXkV9kztsOceM\nTMY/z5uMGYUjQ39fTdNw6NQVvPfnU9j3VQNMRh3GjEhG7qgUJFkM8PgC8PgCuHDpCtyKDpebPVBV\nDXqdhDEjU5CfmYqrbV4cPNGIQLDvLezJuen4h3vyMTzNghaHD1fbvPjDF2dx8nwL9DoZBXnpocbc\n3dNGY+q4YfB2fHgEgip0kgRZJ0HTgKZWLxqb3bjc7OmWK9cy6mWMGpaEkUOTMHyIBfuPXUZdowsA\nkD0iGaqqweMLwOH2Q+nIBoNexuJ5kzF/9jgAiPi86EnYcP/3f/93mEwmrFq1Cg0NDVi8eDEcDgeq\nq6sBAB9//DGqq6uxYcOGG+7DbrcDaP/jtrmDkGUJVpMMnSzB4QnCftKFfSeccHpVSBIwcogBOcNN\n8AVUnG/040pbe5hmDjViWp4VhWMs0MkS3D4VLl8Qbq8Kl0+FyxuEqrW/LmuoEWZj91a1yxtEzQUP\njp7z4IojgNHpRmQPM2JEmgHnGn04cs6DK20BSBJQOMaCOycnY3R697AMBDXUNvhQU+dB3RU/GtsC\n3U40m1nGsFQDnJ4gmhwBdNbu0GQ9pnaUPdWqh14HaBpQc8GDXV85UdfU/YMGaA/PcaPMmJZnxZjh\nJiSZ2+vM41dxqNYN+0kXGlq6d3eZDBJMBhlmowwJgNMbhMvb9aGSN8KE2VNScOqSF58edUDTgNvz\nrUi36WHUyzDoJXj9Ktw+NVS/Lo8KpzeIFlf7B1FuhgnTxyXh4wOtaHUHMSnLjG/9VTrMht57+Zra\nFBw668HJi15c6Ph9i8Yl4W+L03pt5X2dP6DiJ/+vIVSeJfcNR+6IwQv3D+0t2HPCiX+8eygmZVn6\nvH1Tm4L/88dGePzt5/vQZD0yhxpx77QUpFpv3Go7fNaND/Y0w6u0n1SZQw2wmXXwdPy9mp0BBNXu\n20gSYNRL8CnadY+PSDMgNUmHM/U++AORhaXZKCF7mAnDUvSwmuSOfzokW3RItsoIqkDV4TYcPOOG\npgEWk4xUqw4pVh3a3EHUN7efr6PSDVBVoLFNgapef5wks4z0ZD1SLDq0uAKob1ZCv9vIIQYUZFsw\nbrQZFqMMg06CXt/+7VyW2j/sNLS/V5WAhsutCqq/cuL4RW+Pv9NtORbcNy0VQ2x6nL/iw4f21h7f\njz0xGSRkpBkwZpgRWcPa36P1zQouXfWjvllBkyMQqludDEzJtaJ0og0jh3TlSiCo4fwVH05e8uFM\nvReTsi2YVZgSer6/4R62/Z+amgq9vv1lycnJCAQCKCgowJ49ezBjxgxUVVWhtLQ07IF6K+Dsu9u/\nIp6ua0FmRjJslu5dBa1OH/yKiuFD+v5G+rpZYZ6va3TCZNBhWNqNj3XHNf8PqhoarrrQ3OZD5nAb\n0pK7QsbrD6D2UhsMOhljM1MhSdJ1n8QlJcBj39LwVe1VnK5rhSxLkCUJRoMO0ydmdNvfte7+q/YP\ny0tXXDh1oRWn6lpw5mIbmh1euDwKXN4AVFXFkGQL8jLNGJZqwf0zxmDa+OGhffxd7VX853/9BftP\nuXqtE71OQqrNhNvy07Dg3vGYPjEDkiThoQd8eLliH748eQW//LgF3/n7QpTeNuq6lrimafhozzm8\n8eGXoW8chWOHorGpFfaTLqiyFWv/uQRJlsi6iN5470u0uILIHmHD+QYnzCkjUVQ0NqJto+HNP/0J\nqgq8s+sq1i2ZgZKCkRFv63D7seZ/V8HjV3HXtNG42urFufr2MDzdoOCpR27HHbeNum67Lw5fwo7q\nvTAZdPj2X+fivpIxyB7RvStPCQRx6kJr+7l0sRVOtwK3V4HbG0CazYSsDBuyMmwYMzIF47LTQt2g\nfiWIvxy7jD1H6qHXycgZlYLcUSkYkmxCm9uPNqcfbl8AeaNTkJ2RHPZb2px72rsT3648jlMXWtDY\n4kV9swJZAu6aNhrz78nHxJz2LpJAUEVdoxN+JQizUQ+rWY/jNYfxV3eUdNtnIKii7rITZpMeI9Kt\nEdd3pwV/C5xvcOCjPeegqhqGJJuQlmzC2MxU5I1ODb2uCMDfz9Gw//hlON0KTEYdzEYddDoZqqoh\n2NFEH5pixrA0S9hzVtM0tLn8qG9yYUR60g3fz3f0+GhXw7g/wrbc3W431q1bh8bGRgQCATz++OMo\nLCzEhg0boCgK8vPzsXnz5l6/Wg/kq4VobrW68ClBnLrQAo8vAK8vCJ8SgMVkQKrNiJQkI1JtJtgs\nhhv+fYNBFVs/rMF7fz6JoKph6rhh+JcHC5E7OgV6nQyvL4Cf7vgSO/edh81iwNK/vw2lU0bBZjFg\n9+d7UXkkiL1HG5A9IhmbvluKjCG9v3EPnbqCdT/ZhawMG5569HY8/eqnmHdnLlZ+e3D63b3+AB5Z\n/3ukp5jR5vJDVTVsfOIOTJ+UEXZbJaDi+S27cfhUExbcOx6Pf7MAQHsAbHm7Cn/c3wZ/QMXfzRyL\nx+ZOCl0POXi8ET/4xeeQZQkv/K87MTnv+r7jW5mmaXB5FKhae3doOLfae+RmGkhdhG25W61WvPLK\nK9c9XlFR0a8D0q3FZNChIC/yi0dfp9PJePybBbivJBu/eP8I9n3VgFWvfAKp45oKALQ4fBifnYa1\n/1zSrdVlMshY/y/F+OX7h/H+p6fx03e/RNl3bvwt0K8E8drbByBLwPcfvR15o1MhS8C5eke/y99X\ntRfboKoa7pwyCjMKRqL8F59j86++wIsr78KknBuHrqZp+Mk7B3H4VBPunDoKi+dNDj0nSRKKx9vw\nwD234+WKvfjdp6fx+11nMCk3HQV56fjdp6ehacD6JTPiLtiB9t+vp2tBFFu8iYmiIisjGWXfKUXZ\nd0pxz+1ZKMgbCotRD6WjJfrD793d49dpnSzhu9+agsm56dj3VQPOXmq74TE+3ncel5pcePDusZiY\nkw6jQYdRw2w4e6lt0IaonjjfAgAYl52GaROG47klM6AEVPz6f2p63W7PkXpU7j2HcdlpWLVweo9d\nG7mjUvCfT92Dx+ZOQn5WKo6eacJvPz4Bf0DFM4uLcPvE8N8OiDpFZ8wNUYfiySP6NTxywb3j8cIv\nv8COP5/EqoXTr3s+GFSx408nYNDL+Pa940OP54xKxu4v24eJDk0d+DWZcE5e6Aj3rDQA7b/v1HHD\ncOBEI07XtQ+p/TpN0/DbnScAAKsevb3XoW5mkx6PzJmIR+ZMhMPtx8ETjUi1mTAlwhFNRJ3Ycqdb\nQvHkEcgekYxP/nIBjc2e657f9eVF1De5cV/JGKRfM7Z/zIj2UQVnB6lr5sT5FlhMOmQOt4Ue6xy2\n9n8/OdnjNkdON+HY2WbcUTgSY0am9PianiRbjbh7WiaDnfqF4U63BFmW8NDscQiqGv676lS35zRN\nwzs7T0CWgIc6grRTzqj2ESPn6m/cnRMtHl8AFy47MDYzrVu3yvSJGcgeYUPV/jpcabn+g+mdjlb7\ngvvGX/ccUaww3OmWcc/0LAxNNeMPn9fC6e4aZ2yvuYwzF9tw97RMjBqW1G2bnI6W8NlLsW+5n65r\nhaYB47PTuj0uyxK+dU/7B9MHn53u9tyZi62w11xG4dihvV5wJYo2hjvdMgx6Gf8wKx9efxD/XXU6\nNKa4t5bvqGFJ0OtknB2ElnvoYmpW2nXPzZ6ehTSbCR9W13abRylU9nvZaqfBxQuqdEt5oDQHb390\nDNs/OoZ3/3QCI4dacb7BiaJJGd1uNumk18nIyrDhXIMjNI9QrJzsCPevt9wBwGjQ4cG787D1wxps\n/bAGxZNHQFU1fHagDrmjUlAUwTh4omhiuNMtxWo2YMMTd+DD6rOoa3SgrtEJnSzh0b+ZeMNtckam\noPZSGy43uzFyaNINXzdQJy80I8msv+Ex5t2Zh9/uPIHffXoav/u0q3tmwb3j+zV/DtFAMNzplnNb\n/rDQZGaa1j7T37WzAn7dmJGdF1UdMQt3l0dBXaMLU8cNu+G3g5QkI15ccReOn2uGy6vA4w3Aajbg\n7m9kxqRMRL1huNMtTZKkXoMdAHI6wv1sfRtmFEY+z0tfnKq7cZfMtSaMGYIJY4bEpAxEfcELqhT3\nckbFfsTMyWvuTCWKBwx3insZQ6wwGXUxHTHT20gZolsRw53inixLGDMiGRcuO0OLvETbyQstSLYa\n+jXdLNHNwHAnIeSMTEEgqOLSld7npu+PoKqhvsmNMSNTOOqF4gbDnYTQuXDF+Ybo97t33pSUbO37\nerNENwvDnYTQucKNwx3Z8mh94XS3h3ukK0UR3QoY7iQEW0erujOIo8nVsai7zcIFJyh+MNxJCJ3r\n7jo90Q93p6f92wBb7hRPGO4khKQYhrvLEwCA6xZuJ7qVMdxJCKGWeyz63Dta7jZeUKU4wnAnIXQu\nwBybljsvqFL8YbiTEEwGHQx6ORTE0eQMXVBluFP8YLiTMGwWQ4wuqLLlTvGH4U7CsFkNsRkK6WbL\nneJP2Cl/33vvPezYsQOSJMHn86Gmpgbbt2/HsmXLkJubCwBYuHAh5s2bF+uyEvXKZjGirtEFTdOi\nOk2A08uWO8WfsOE+f/58zJ8/HwBQXl6OBQsW4PDhw3jiiSewZMmSWJePKGJJFgNUVYPH175IRrS4\n3Ar0OhkmQ+/zyhPdSiLuljl06BBOnjyJhx9+GEeOHMGf//xnPPbYY1i/fj3cbncsy0gUka7hkNHt\nmnF6/LBZDJw0jOJKxOG+ZcsWPPnkkwCAadOm4ZlnnsHWrVuRnZ2N1157LWYFJIpUZ7i7vNENd5cn\nwC4ZijsRLbPncDhQW1uLkpISAMD999+P5OT2WfjmzJmDzZs3h92H3W4fQDHFwrroEs26aGttbd/n\ngcO4eskclX1qmoY2tw9JJi3mfzeeF11YFwMXUbjv3bsXpaWloZ+XLl2KjRs3YsqUKaiurkZhYWHY\nfRQVFfW/lAKx2+2siw7RrovzzlOoOnwYmdl5KJoyOir79PoDULfVYcTwtJj+3XhedGFddBnIh1xE\n4X7mzBlkZ2eHfv7BD36A8vJyGAwGDB8+HOXl5f0uAFG0xKLPPTQjZBQv0BINhojCfenSpd1+njx5\nMrZt2xaTAhH1VywmDwvdwMR5ZSjO8CYmEkZoTvdohjtvYKI4xXAnYYRGy0Qx3DtH3jDcKd4w3EkY\nnSslRbPPnUvsUbxiuJMwurplojenO5fYo3jFcCdhmI066GQpNhdULRGNPSC6ZTDcSRiSJEV9Zki2\n3CleMdxJKElmQ1SnH+Di2BSvGO4klM6Wu6ZpUdlfqOXOce4UZxjuJBSbxYhAUIVPCUZlf5197tGc\nQphoMDDcSSjRHuvu8iiwmvXQyZzul+ILw52E0jlNQLQuqjo9Cm9gorjEcCeh2KI8v4zLo/BiKsUl\nhjsJpXPIYjS6ZYKqBrc3wGGQFJcY7iSUrpkhB36XqtvLG5gofjHcSSi2KPa5d80IyZY7xR+GOwkl\nmn3uLg8nDaP4xXAnoUQz3Du7dngDE8UjhjsJxWbtnPZ34H3uLk8AQPuUBkTxhuFOQum6iSkw4H2x\n5U7xjOFOQrGY9JCk6IyWYZ87xTOGOwlFliUkmQ1R6nPnEnsUvxjuJJxozenOJfYonjHcSTg2S3Ra\n7i623CmOMdxJODaLEX4lCCUwsGl/nexzpzgW9r7q9957Dzt27IAkSfD5fKipqcGvf/1rvPjii5Bl\nGePHj0dZWdlglJUoIqGZIT0KhiTr+r0fl0eBXifDZOj/PohulrAt9/nz56OiogJvvfUWCgsLsWHD\nBrz++utYvXo1tm7dClVVUVlZORhlJYpI6EamAfa7Oz1+2CwGSBLncqf4E3G3zKFDh3Dy5Ek8/PDD\nOHLkCIqLiwEAs2bNQnV1dcwKSNRX0Vqww+UJsEuG4lbE4b5lyxY8+eST1z2elJQEh8MR1UIRDURS\nFKYg0DQt1HInikcRzWXqcDhQW1uLkpISAIAsd30muFwupKSkhN2H3W7vZxHFw7roEou6uNroBAAc\nOnIckvtCv/bhD6gIBDUEFfeg/b14XnRhXQxcROG+d+9elJaWhn6ePHky9u7di5KSElRVVXV77kaK\nior6X0qB2O121kWHWNWFW1eHD/buw/CRmSgqGtuvfTS1egBcxOiRwwbl78XzogvrostAPuQiCvcz\nZ84gOzs79PPatWuxceNGKIqC/Px8zJ07t98FIIq2aPS5cxgkxbuIwn3p0qXdfs7NzUVFRUVMCkQ0\nUDbrwPvcuxbqYLhTfOJNTCSczpWTBjIU0uVluFN8Y7iTcKKxjqq7o9Vv5VzuFKcY7iQci6m9t9Hj\n6/+c7p3bdu6LKN4w3Ek4Br0Mg16OTribGe4UnxjuJCSLST+gcHez5U5xjuFOQrKY9PB42S1DiYvh\nTkIaaMu984PBynCnOMVwJyF1hrumaf3ani13incMdxKSxayHqgE+pX8LdjDcKd4x3ElIAx0O6fEF\nIEuAyciFOig+MdxJSNYohLvZpOdCHRS3GO4kpFDLvZ8jZjy+ALtkKK4x3ElI0eiWYbhTPGO4k5AG\nHO5ehjvFN4Y7Calz2oD+hHswqMIfUBnuFNcY7iSkzmB296PPncMgSQQMdxLSQLplOj8QOGkYxTOG\nOwlpIOHOljuJgOFOQopGuHNeGYpnDHcS0oC6ZdhyJwEw3ElIVnP/b2JitwyJgOFOQgqNlulPt4yX\n4U7xj+FOQjIbo3BBlaNlKI4x3ElIsizBYtJxtAwlrIjO3i1btmDnzp1QFAWLFi1CQUEBli1bhtzc\nXADAwoULMW/evFiWk6jP+rsaE8OdRBD27N2zZw/279+P7du3w+1245e//CVUVcUTTzyBJUuWDEIR\nifrHYtLDxQuqlKDCnr2fffYZJkyYgJUrV8LlcuHpp5/GO++8g9raWlRWViInJwfr16+H1WodjPIS\nRcxi0uNKq7fP2zHcSQRh+9ybm5tx+PBhvPrqq9i0aRPWrFmDadOm4ZlnnsHWrVuRnZ2N1157bTDK\nStQnFpMBPn8QQbVv66gy3EkEYc/etLQ05OfnQ6/XIy8vDyaTCffccw/S09MBAHPmzMHmzZvDHshu\ntw+8tIJgXXSJZV34vE4AwOdf7IPZGPnYgYbGqwCAmqOHoNcN3kpMPC+6sC4GLmy4FxUVoaKiAkuW\nLEFDQwM8Hg+WLVuGjRs3YurUqaiurkZhYWHYAxUVFUWlwPHObrezLjrEui7+9JUdx+suYOLk2zAs\nzRLxdts+q4Je58cdM4pjVrav43nRhXXRZSAfcmHDffbs2di3bx8WLFgATdOwadMmDBkyBOXl5TAY\nDBg+fDjKy8v7XQCiWOnvnO5ursJEAojoDF6zZs11j23bti3qhSGKpv7OL8Ml9kgEvImJhNXfRbIZ\n7iQChjsJq2t+GSXibTRNY7iTEBjuJKz+dMv4AypUVWO4U9xjuJOwrP3olvFwiT0SBMOdhNUZ0H2Z\n9pc3MJEoGO4krP50yzDcSRQMdxKWtR/j3EPrp5oNMSkT0WBhuJOw2HKnRMZwJ2H1K9y5xB4JguFO\nwurPTUxuttxJEAx3EpZBL0MnS+yWoYTEcCdhSZLU56X2QhdUGe4U5xjuJDSLuX/hzpuYKN4x3Elo\n/W25s1uG4h3DnYTW53DnaBkSBMOdhGYx6REIalACwYhez5Y7iYLhTkILTfsb4XDIznA3M9wpzjHc\nSWh9vZHJ41NgMuqgkwdvYWyiWGC4k9CsfQ53LtRBYmC4k9BC0/72oVuG4U4iYLiT0PreLcNwJzEw\n3ElofQl3VdXg8QUZ7iQEhjsJrS/h7vVzGCSJg+FOQutLuHNeGRJJRGfxli1bsHPnTiiKgkWLFqGk\npATPPvssZFnG+PHjUVZWFutyEvVLX1Zj4rwyJJKwLfc9e/Zg//792L59OyoqKnDp0iW89NJLWL16\nNbZu3QpVVVFZWTkYZSXqs77M6c67U0kkYcP9s88+w4QJE7By5UqsWLECs2fPxtGjR1FcXAwAmDVr\nFqqrq2NeUKL+6E+3DMOdRBD2LG5ubsbFixfxxhtv4Pz581ixYgVUVQ09n5SUBIfDEdNCEvWXxdS+\n0HVE4c5Jw0ggYc/itLQ05OfnQ6/XIy8vDyaTCQ0NDaHnXS4XUlJSwh7IbrcPrKQCYV10iXVdePzt\nDZFLDVfCHuvoGTcA4HJDHez21piWqyc8L7qwLgYubLgXFRWhoqICS5YsQUNDAzweD0pLS7Fnzx7M\nmDEDVVVVKC0tDXugoqKiqBQ43tntdtZFh8Goi2BQBd65CJPFFvZYl31nAFzFpPH5KJqeFdNyfR3P\niy6siy4D+ZALG+6zZ8/Gvn37sGDBAmiahk2bNiEzMxMbNmyAoijIz8/H3Llz+10AoljS6WQYDbrQ\nwte94WgZEklEZ/GaNWuue6yioiLqhSGKBatJH9FoGTcvqJJAeBMTCS/S1Zg4WoZEwnAn4UUc7l7e\noUriYLiT8CxmPbz+ADRN6/V1bLmTSBjuJDyrWQ9NCz/W3eVR2l9vMQxGsYhiiuFOwrN1hLXTrfT6\nOodHgVFnNQAKAAAKbklEQVQvw2TQDUaxiGKK4U7Cs1mNAACH29/r61xuJfRaonjHcCfhhVrunjAt\nd7cfyVZ2yZAYGO4kvEjCPahqcHnZcidxMNxJeJ2B3Vufu9urQNO6PgiI4h3DnYRn6+hqcXlu3Ofe\nGfzJbLmTIBjuJLzO1rijl5Z758VWG/vcSRAMdxJeJH3ubLmTaBjuJLzkUJ/7jbtl2HIn0TDcSXid\ngd1ry73juWQLW+4kBoY7Cc+g18Fo0PXacney5U6CYbhTQrBZDL223B3scyfBMNwpIdishl7HubPP\nnUTDcKeEkGw1wuVVoKo9T/vbOSMk71AlUTDcKSHYLAZoGm64lqrD7YcscaEOEgfDnRJCUmja354v\nqjrcCpIsRsiyNJjFIooZhjslhNBwyBv0uzvdfva3k1AY7pQQQjcy9TC/jKZpcHoUTvdLQmG4U0Lo\nbQoCnxKEElB5MZWEwnCnhNDb5GGheWV4dyoJJKKhAQ899BBsNhsAICsrC4sXL8ayZcuQm5sLAFi4\ncCHmzZsXs0ISDZStl/llOMadRBQ23P3+9hP/rbfeCj3229/+Fk888QSWLFkSs4IRRVPXnO49tNxD\nY9wZ7iSOsOFeU1MDt9uNpUuXIhgMYtWqVThy5Ahqa2tRWVmJnJwcrF+/HlardTDKS9QvvfW5d7bm\nOfUAiSRsn7vZbMbSpUvxi1/8Aps2bcKaNWtQWFiIZ555Blu3bkV2djZee+21wSgrUb/ZLDdeaq9r\nXhm23EkcYVvuubm5yMnJCf0/LS0Ns2bNwogRIwAAc+bMwebNm8MeyG63D7Co4mBddBmsugh2TDtw\nsaHpumPWHHcAAOrrzsEuNQ5KeXrC86IL62Lgwob7u+++i+PHj6OsrAwNDQ1wOp1YuXIlysrKMHXq\nVFRXV6OwsDDsgYqKiqJS4Hhnt9tZFx0Guy7MO+oh6c3XHfNIw1EArfjG1MkoyBs6aOW5Fs+LLqyL\nLgP5kAsb7gsWLMBzzz2HRYsWQZZlvPTSSzCZTCgvL4fBYMDw4cNRXl7e7wIQDRab1dhjnzun+yUR\nhQ13g8GAH/3oR9c9vm3btpgUiChWbBYDGpvd1z3OoZAkIt7ERAnDZjXA5Q2E+t87uTpa7jbexEQC\nYbhTwugcDvn1se4Ojx9mow4GPd8OJA6ezZQwbjR5mMOtcF4ZEg7DnRJG15zu3VvuTrefY9xJOAx3\nShihOd2v6ZYJBlW4vQH2t5NwGO6UMLruUu3qluG8MiQqhjsljJ7ml+n8P8e4k2gY7pQwQhdUr+lz\nD41xt7DlTmJhuFPC6KnPvTPo2S1DomG4U8IIdctc2+fO6X5JUAx3ShhJPfS5c14ZEhXDnRKGrYdx\n7k72uZOgGO6UMHQ6GVazvtsdqhwKSaJiuFNCsVkMX+uWYZ87iYnhTgnFZjF2u6Dq4GgZEhTDnRKK\nzWqAxxdEIKgCaO9zl2UJFlPYpQ2I4grDnRJKZwu9c9pfp0dBstUASZJuZrGIoo7hTgklNL9MZ7i7\nFU4aRkJiuFNCufZGpstX3XBwul8SFDsaKaF0dsv86oOjOHb2KoKqhpxRKTe5VETRx3CnhNK54tKR\n000YPSwJj/7NRMz6RuZNLhVR9DHcKaGU3jYSh09dQfHkEZj1jUzodOyZJDEx3CmhDEk24+nHim92\nMYhiLqJwf+ihh2Cz2QAAWVlZWL58OZ599lnIsozx48ejrKwspoUkIqK+CRvufn/73XxvvfVW6LEV\nK1Zg9erVKC4uRllZGSorK3H//ffHrpRERNQnYTsca2pq4Ha7sXTpUixZsgQHDx7E0aNHUVzc/tV2\n1qxZqK6ujnlBiYgocmFb7mazGUuXLsXDDz+M2tpafPe734WmaaHnk5KS4HA4YlpIIiLqm7Dhnpub\ni5ycnND/09LScPTo0dDzLpcLKSkcJ0xEdCsJG+7vvvsujh8/jrKyMjQ0NMDpdOKuu+7Cnj17MGPG\nDFRVVaG0tDTsgex2e1QKLALWRRfWRRfWRRfWxcBJ2rV9LD1QFAXPPfccLl68CFmW8fTTTyMtLQ0b\nNmyAoijIz8/H5s2bOfESEdEtJGy4ExFR/OHteUREAmK4ExEJiOFORCQghjsRkYBiOnGYpmnYtGkT\njh07BqPRiH/7t39DdnZ2LA95SwkEAli3bh3q6uqgKAqWL1+OcePGJfS8PE1NTfj2t7+NX/3qV9Dp\ndAlbF1u2bMHOnTuhKAoWLVqEkpKShKyLQCCAtWvXoq6uDnq9Hi+88EJCnhcHDx7Ej370I1RUVODc\nuXM9/v6/+c1v8Pbbb8NgMGD58uWYPXt27zvVYuiPf/yj9uyzz2qapmkHDhzQVqxYEcvD3XLeffdd\n7cUXX9Q0TdNaW1u12bNna8uXL9f27t2raZqmPf/889pHH310M4s4qBRF0f71X/9Ve+CBB7TTp08n\nbF188cUX2vLlyzVN0zSXy6W99tprCVsXlZWV2ve//31N0zRt165d2pNPPplwdfHzn/9ce/DBB7VH\nHnlE0zStx9+/sbFRe/DBBzVFUTSHw6E9+OCDmt/v73W/Me2WsdvtmDlzJgBg2rRpOHz4cCwPd8uZ\nN28ennrqKQBAMBiETqdL6Hl5fvjDH2LhwoXIyMiApmkJWxefffYZJkyYgJUrV2LFihWYPXt2wtZF\nbm4ugsEgNE2Dw+GAXq9PuLrIycnB66+/Hvr5yJEj3X7/3bt348svv0RRURH0ej1sNhtyc3Nx7Nix\nXvcb03B3Op1ITk4O/azX66GqaiwPeUuxWCywWq1wOp146qmnsGrVqoSdl2fHjh0YOnQo7rrrrlAd\nXHsuJFJdNDc34/Dhw3j11VexadMmrFmzJmHrIikpCRcuXMDcuXPx/PPPY/HixQn3HpkzZw50Ol3o\n56///k6nEy6Xq1uWWq3WsPUS0z53m80Gl8sV+llVVchyYl3DvXTpEr73ve/hsccewze/+U38x3/8\nR+i5RJqXZ8eOHZAkCbt27cKxY8ewdu1aNDc3h55PpLpIS0tDfn4+9Ho98vLyYDKZ0NDQEHo+keri\nzTffxMyZM7Fq1So0NDRg8eLFUBQl9Hwi1UWnazOy8/e32WxwOp3XPd7rfmJWQgDTp0/HJ598AgA4\ncOAAJkyYEMvD3XKuXLmCpUuX4umnn8b8+fMBAJMnT8bevXsBAFVVVSgqKrqZRRw0W7duRUVFBSoq\nKjBp0iS8/PLLmDlzZkLWRVFRET799FMAQENDAzweD0pLS7Fnzx4AiVUXqampoYWAkpOTEQgEUFBQ\nkJB10amgoOC698WUKVNgt9vh9/vhcDhw+vRpjB8/vtf9xLTlPmfOHOzatQuPPvooAOCll16K5eFu\nOW+88Qba2trwk5/8BK+//jokScL69euxefPm0Lw8c+fOvdnFvGnWrl2LjRs3JlxdzJ49G/v27cOC\nBQtCI8oyMzO7zdeUKHXx+OOPY926dfinf/onBAIBrFmzBoWFhQlZF516el9IkoTFixdj0aJF0DQN\nq1evhtFo7HU/nFuGiEhAidUBTkSUIBjuREQCYrgTEQmI4U5EJCCGOxGRgBjuREQCYrgTEQmI4U5E\nJKD/D7Ip5VrpmcohAAAAAElFTkSuQmCC\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -197,12 +226,12 @@ "fig, ax = plt.subplots()\n", "d = datasets.get(\"flopping_f_brightness\")\n", "ax.plot(d)\n", - "print(\"flopping_f\", datasets.get(\"flopping_freq\"))" + "print(\"flopping_f:\", datasets.get(\"flopping_freq\"))" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": { "collapsed": false }, @@ -215,7 +244,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": { "collapsed": false }, @@ -237,8 +266,8 @@ " \"results\", t.strftime(\"%Y-%m-%d\"), #t.strftime(\"%H-%M\"),\n", " \"*\", \"{:09d}-FloppingF.h5\".format(rid))\n", "\n", - "# we would usually like to use pandas but our data doe not comply\n", - "# with the pandas metadata\n", + "# we would usually like to use pandas but our data does not have\n", + "# the metadata pandas want\n", "#d = pd.HDFStore(glob.glob(f)[0])\n", "\n", "with h5py.File(glob.glob(f)[0]) as f:\n", @@ -248,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": { "collapsed": false }, @@ -264,7 +293,7 @@ "source": [ "%%writefile repository/notebook_test.py\n", "\n", - "# we can also write experiments in the notebook ans submit them\n", + "# we can also write experiments in the notebook and submit them\n", "# we don't have submit-by-content yet (and there would be questions\n", "# about other modules that would need to be imported) so we just export\n", "# this cell and submit it by filename\n", @@ -281,7 +310,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": { "collapsed": false }, @@ -290,7 +319,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "133\n" + "4724\n" ] } ], From e7146cc999428bff820c1d1fd39998a51fcb0bfe Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 26 Feb 2016 17:03:08 +0100 Subject: [PATCH 010/125] gateware.spi: design sketch --- artiq/gateware/rtio/phy/spi.py | 13 ++++ artiq/gateware/spi.py | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 artiq/gateware/rtio/phy/spi.py create mode 100644 artiq/gateware/spi.py diff --git a/artiq/gateware/rtio/phy/spi.py b/artiq/gateware/rtio/phy/spi.py new file mode 100644 index 000000000..e72f602c2 --- /dev/null +++ b/artiq/gateware/rtio/phy/spi.py @@ -0,0 +1,13 @@ +from migen import * + +from artiq.gateware.spi import SPIMaster as SPIMasterWB +from artiq.gateware.rtio.phy.wishbone import RT2WB + + +class SPIMaster(Module): + def __init__(self, pads, onehot=False, **kwargs): + self.submodules._ll = ClockDomainsRenamer("rio")( + SPIMasterWB(pads, **kwargs)) + self.submodules._rt2wb = RT2WB(2, self._ll.bus) + self.rtlink = self._rt2wb.rtlink + self.probes = [] diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py new file mode 100644 index 000000000..c3c6f14b2 --- /dev/null +++ b/artiq/gateware/spi.py @@ -0,0 +1,109 @@ +from migen import * +from migen.genlib.fsm import * +from migen.genlib.misc import WaitTimer +from misoc.interconnect import wishbone + + +class SPIMaster(Module): + """SPI Master. + + Notes: + * If there is a miso wire in pads, the input and output are done with + two signals (a.k.a. 4-wire SPI), else mosi is used for both output + and input (a.k.a. 3-wire SPI). + * Every transfer consists of a 0-32 bit write followed by a 0-32 + bit read. + * cs_n is always asserted at the beginning and deasserted + at the end of the tranfer. + * cs_n handling is agnostic to whether it is one-hot or decoded + somewhere downstream. If it is decoded, "cs_n all deasserted" + should be handled accordingly (no slave selected). + If it is one-hot, asserting multiple slaves should only be attempted + if miso is either not connected between slaves or open collector. + * If config.cs_polarity == 0 (cs active low, the default), + "cs_n all deasserted" means "all cs_n bits high". + * The first bit output on mosi is always the MSB/LSB (depending on + config.lsb_first) of the data register, independent of + xfer.write_len. The last bit input from miso always ends up in + the LSB/MSB (respectively) of the data register, independent of + read_len. + * For 4-wire SPI only the sum of read_len and write_len matters. The + behavior is the same no matter how the transfer length is divided + between the two. For 3-wire SPI, the direction of mosi/miso is + switched from output to input after write_len cycles, at the + "output" clk edge corresponding to bit write_len + 1 of the transfer. + * Data output on mosi in 4-wire SPI during the read cycles is + undefined. Data in the data register outside the + least/most (depending on config.lsb_first) significant read_len + bits is undefined. + * The transfer is complete when the wishbone transaction is ack-ed. + * Input data from the last transaction can be read from the data + register at any time. + + Transaction Sequence: + * if desired, write the xfer register to change lengths and cs_n. + * write the data register (also for zero-length writes), + writing triggers the transfer and the transfer is complete when the + write is complete. + * if desired, read the data register + + Register address and bit map: + + config (address 0): + 1 offline: all pins high-z (reset=1) + 1 cs_polarity: active level of chip select (reset=0) + 1 clk_polarity: idle level for clk (reset=0) + 1 clk_phase: first edge after cs assertion to sample data on (reset=0) + (0, 0): idle low, output on falling, input on rising + (0, 1): idle low, output on rising, input on falling + (1, 0): idle high, output on rising, input on falling + (1, 1): idle high, output on falling, input on rising + 1 lsb_first: LSB is the first bit on the wire (reset=0) + 11 undefined + 16 speed: divider from this module's clock to the SPI clk + (minimum=2, reset=4) + clk pulses are asymmetric if speed is odd, favoring longer setup + over hold times + + xfer (address 1): + 16 cs: active high bit mask of chip selects to assert + 6 write_len: 0-32 bits + 2 undefined + 6 read_len: 0-32 bits + 2 undefined + + data (address 2): + 32 write/read data + """ + def __init__(self, pads, bus=None): + if bus is None: + bus = wishbone.Interface(data_width=32) + self.bus = bus + + ### + + +def _test_gen(bus): + yield from bus.write(0, 0 | (5 << 16)) + yield + yield from bus.write(1, 1 | (24 << 16) | (16 << 24)) + yield + yield from bus.write(2, 0x12345678) + yield + r = (yield from bus.read(2)) + print(r) + yield + + +class _TestPads: + def __init__(self): + self.cs_n = Signal(3) + self.clk = Signal() + self.mosi = Signal() + self.miso = Signal() + + +if __name__ == "__main__": + pads = _TestPads() + dut = SPIMaster(pads) + run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From ade3eda19a8f0b0728226cab5555a6c875f78042 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 27 Feb 2016 11:29:40 +0100 Subject: [PATCH 011/125] gateware.pipistrello: use pmod for spi --- artiq/gateware/targets/pipistrello.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index dbb840aab..5967ccdd7 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -152,8 +152,8 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512, ofifo_depth=4)) - # the last five ttls are used for SPI and a ClockGen - for i in range(11): + # 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) @@ -192,15 +192,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd ofifo_depth=512, ififo_depth=4)) - spi_pins = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) - # cs_n can be multiple bits wide, one-hot - # absence of miso indicates bidirectional mosi - self.comb += [ - platform.request("ttl", 11).eq(spi_pins.cs_n), - platform.request("ttl", 12).eq(spi_pins.clk), - platform.request("ttl", 13).eq(spi_pins.mosi), - spi_pins.miso.eq(platform.request("ttl", 14)), - ] + pmod = self.platform.request("pmod", 0) + spi_pins = Module() + spi_pins.clk = pmod.d[0] + spi_pins.mosi = pmod.d[1] + spi_pins.miso = pmod.d[2] + spi_pins.cs_n = pmod.d[3:] phy = spi.SPIMaster(spi_pins) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy( From 63e0c7c07c006ca877cb1ad6f1c89127063cb13c Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 27 Feb 2016 13:15:49 +0000 Subject: [PATCH 012/125] Revert wrong parts of 6bd16e44. --- artiq/test/lit/inferencer/error_with_self.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/test/lit/inferencer/error_with_self.py b/artiq/test/lit/inferencer/error_with_self.py index 1bf087c26..afe53e531 100644 --- a/artiq/test/lit/inferencer/error_with_self.py +++ b/artiq/test/lit/inferencer/error_with_self.py @@ -11,7 +11,7 @@ class contextmgr: def foo(): contextmgr.__enter__(1) # CHECK-L: ${LINE:+3}: error: cannot unify with int(width='a) while inferring the type for self argument - # CHECK-L: ${LINE:+2}: note: expression of type + # CHECK-L: ${LINE:+2}: note: expression of type # CHECK-L: ${LINE:+1}: note: reference to an instance with a method '__enter__(self:int(width='a))->NoneType delay('b)' with contextmgr(): pass From e421b229538d6b7ba5ed01dadf83fcd254771e35 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 27 Feb 2016 13:29:47 +0000 Subject: [PATCH 013/125] types.TypePrinter: don't waste screen space on empty attribute lists. --- artiq/compiler/types.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index fdb225d9c..0f0819b2a 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -703,12 +703,14 @@ class TypePrinter(object): elif isinstance(typ, TInstance): if typ in self.recurse_guard or depth >= max_depth: return "".format(typ.name) - else: + elif len(typ.attributes) > 0: self.recurse_guard.add(typ) attrs = ",\n\t\t".join(["{}: {}".format(attr, self.name(typ.attributes[attr], depth + 1)) for attr in typ.attributes]) return "".format(typ.name, attrs) + else: + return "".format(typ.name) elif isinstance(typ, TMono): if typ.params == {}: return typ.name @@ -745,12 +747,14 @@ class TypePrinter(object): elif isinstance(typ, (TConstructor, TExceptionConstructor)): if typ in self.recurse_guard or depth >= max_depth: return "".format(typ.name) - else: + elif len(typ.attributes) > 0: self.recurse_guard.add(typ) attrs = ", ".join(["{}: {}".format(attr, self.name(typ.attributes[attr], depth + 1)) for attr in typ.attributes]) return "".format(typ.name, attrs) + else: + return "".format(typ.name) elif isinstance(typ, TBuiltin): return "".format(typ.name) elif isinstance(typ, TValue): From 8bbffab8c8f133ac37ba70ba906ef9caedf6f369 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 27 Feb 2016 13:33:28 +0000 Subject: [PATCH 014/125] Fix tests. --- artiq/compiler/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index 0f0819b2a..ec4701852 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -710,6 +710,7 @@ class TypePrinter(object): for attr in typ.attributes]) return "".format(typ.name, attrs) else: + self.recurse_guard.add(typ) return "".format(typ.name) elif isinstance(typ, TMono): if typ.params == {}: @@ -754,6 +755,7 @@ class TypePrinter(object): for attr in typ.attributes]) return "".format(typ.name, attrs) else: + self.recurse_guard.add(typ) return "".format(typ.name) elif isinstance(typ, TBuiltin): return "".format(typ.name) From bd9ceb4e12d4fa2503cae096bbeda22700d3f3d9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 27 Feb 2016 22:47:16 +0100 Subject: [PATCH 015/125] gateware.spi: add complete spi master logic --- artiq/gateware/spi.py | 304 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 270 insertions(+), 34 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index c3c6f14b2..d0df75509 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -1,3 +1,5 @@ +from itertools import product + from migen import * from migen.genlib.fsm import * from migen.genlib.misc import WaitTimer @@ -8,18 +10,22 @@ class SPIMaster(Module): """SPI Master. Notes: - * If there is a miso wire in pads, the input and output are done with - two signals (a.k.a. 4-wire SPI), else mosi is used for both output - and input (a.k.a. 3-wire SPI). - * Every transfer consists of a 0-32 bit write followed by a 0-32 + * M = 32 is the data width (width of the data register, + maximum write bits, maximum read bits) + * If there is a miso wire in pads, the input and output can be done + with two signals (a.k.a. 4-wire SPI), else mosi must be used for + both output and input (a.k.a. 3-wire SPI) and config.half_duplex + needs to be set. + * Every transfer consists of a 0-M bit write followed by a 0-M bit read. * cs_n is always asserted at the beginning and deasserted - at the end of the tranfer. + at the end of the transfer. * cs_n handling is agnostic to whether it is one-hot or decoded somewhere downstream. If it is decoded, "cs_n all deasserted" should be handled accordingly (no slave selected). If it is one-hot, asserting multiple slaves should only be attempted if miso is either not connected between slaves or open collector. + cs can also be handled independently through other means. * If config.cs_polarity == 0 (cs active low, the default), "cs_n all deasserted" means "all cs_n bits high". * The first bit output on mosi is always the MSB/LSB (depending on @@ -32,24 +38,26 @@ class SPIMaster(Module): between the two. For 3-wire SPI, the direction of mosi/miso is switched from output to input after write_len cycles, at the "output" clk edge corresponding to bit write_len + 1 of the transfer. - * Data output on mosi in 4-wire SPI during the read cycles is - undefined. Data in the data register outside the - least/most (depending on config.lsb_first) significant read_len - bits is undefined. - * The transfer is complete when the wishbone transaction is ack-ed. + * Data output on mosi in 4-wire SPI during the read cycles is what + is found in the data register at the time. + Data in the data register outside the least/most (depending + on config.lsb_first) significant read_len bits is what is + seen on miso during the write cycles. + * When the transfer is complete the wishbone transaction is ack-ed. * Input data from the last transaction can be read from the data register at any time. Transaction Sequence: - * if desired, write the xfer register to change lengths and cs_n. - * write the data register (also for zero-length writes), - writing triggers the transfer and the transfer is complete when the - write is complete. - * if desired, read the data register + * If desired, write the config register to set up the core. + * If desired, write the xfer register to change lengths and cs_n. + * Write the data register (also for zero-length writes), + writing triggers the transfer and when the transfer is complete the + write is ack-ed. + * If desired, read the data register. Register address and bit map: - config (address 0): + config (address 2): 1 offline: all pins high-z (reset=1) 1 cs_polarity: active level of chip select (reset=0) 1 clk_polarity: idle level for clk (reset=0) @@ -59,40 +67,252 @@ class SPIMaster(Module): (1, 0): idle high, output on rising, input on falling (1, 1): idle high, output on falling, input on rising 1 lsb_first: LSB is the first bit on the wire (reset=0) - 11 undefined - 16 speed: divider from this module's clock to the SPI clk - (minimum=2, reset=4) - clk pulses are asymmetric if speed is odd, favoring longer setup - over hold times + 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) + 10 undefined + 16 clk_load: clock load value to divide from this module's clock + to the SPI write clk clk pulses are asymmetric + if a divider is odd, favoring longer setup over hold times. + clk/spi_clk == clk_load + 2 (reset=0) xfer (address 1): 16 cs: active high bit mask of chip selects to assert - 6 write_len: 0-32 bits - 2 undefined - 6 read_len: 0-32 bits - 2 undefined + 8 write_len: 0-M bits + 8 read_len: 0-M bits - data (address 2): - 32 write/read data + data (address 0): + M write/read data """ - def __init__(self, pads, bus=None): + def __init__(self, pads, bus=None, data_width=32): if bus is None: - bus = wishbone.Interface(data_width=32) + bus = wishbone.Interface(data_width=data_width) self.bus = bus ### + # State machine + wb_we = Signal() + start = Signal() + active = Signal() + + fsm = FSM("IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(bus.cyc & bus.stb, + NextState("ACK"), + If(bus.we, + wb_we.eq(1), + If(bus.adr == 0, # data register + NextState("START"), + ) + ) + ) + ) + fsm.act("START", + start.eq(1), + NextState("ACTIVE"), + ) + fsm.act("ACTIVE", + If(~active, + bus.ack.eq(1), + NextState("IDLE"), + ) + ) + fsm.act("ACK", + bus.ack.eq(1), + NextState("IDLE"), + ) + + # Wishbone + config = Record([ + ("offline", 1), + ("cs_polarity", 1), + ("clk_polarity", 1), + ("clk_phase", 1), + ("lsb_first", 1), + ("half_duplex", 1), + ("padding", 10), + ("clk_load", 16), + ]) + config.offline.reset = 1 + assert len(config) <= len(bus.dat_w) + + xfer = Record([ + ("cs", 16), + ("write_length", 8), + ("read_length", 8), + ]) + assert len(xfer) <= len(bus.dat_w) + + data = Signal.like(bus.dat_w) + + wb_data = Array([data, xfer.raw_bits(), config.raw_bits()])[bus.adr] + self.comb += bus.dat_r.eq(wb_data) + self.sync += If(wb_we, wb_data.eq(bus.dat_w)) + + # SPI + write_count = Signal.like(xfer.write_length) + read_count = Signal.like(xfer.read_length) + clk_count = Signal.like(config.clk_load) + clk = Signal(reset=1) # idle high + phase = Signal() + edge = Signal() + write = Signal() + read = Signal() + miso = Signal() + miso_i = Signal() + mosi_o = Signal() + + self.comb += [ + phase.eq(clk ^ config.clk_phase), + edge.eq(active & (clk_count == 0)), + write.eq(write_count != 0), + read.eq(read_count != 0), + ] + + self.sync += [ + If(start, + write_count.eq(xfer.write_length), + read_count.eq(xfer.read_length), + active.eq(1), + ), + If(active, + clk_count.eq(clk_count - 1), + ), + If(start | edge, + # setup time passes during phase 0 + # use the lsb to bias that time to favor longer setup times + clk_count.eq(config.clk_load[1:] + + (config.clk_load[0] & phase)), + clk.eq(~clk), # idle high + If(phase, + data.eq(Mux(config.lsb_first, + Cat(data[1:], miso), + Cat(miso, data[:-1]))), + mosi_o.eq(Mux(config.lsb_first, data[0], data[-1])), + If(write, + write_count.eq(write_count - 1), + ), + ).Else( + miso.eq(miso_i), + If(~write & read, + read_count.eq(read_count - 1), + ), + ), + ), + If(~clk & edge & ~write & ~read, # always from low clk + active.eq(0), + ), + ] + + # I/O + cs_n_t = TSTriple(len(pads.cs_n)) + self.specials += cs_n_t.get_tristate(pads.cs_n) + clk_t = TSTriple() + self.specials += clk_t.get_tristate(pads.clk) + mosi_t = TSTriple() + self.specials += mosi_t.get_tristate(pads.mosi) + + self.comb += [ + cs_n_t.oe.eq(~config.offline), + clk_t.oe.eq(~config.offline), + mosi_t.oe.eq(~config.offline & (write | ~config.half_duplex)), + cs_n_t.o.eq((xfer.cs & Replicate(active, len(xfer.cs))) ^ + Replicate(~config.cs_polarity, len(xfer.cs))), + clk_t.o.eq((clk & active) ^ config.clk_polarity), + miso_i.eq(Mux(config.half_duplex, mosi_t.i, + getattr(pads, "miso", mosi_t.i))), + mosi_t.o.eq(mosi_o), + ] + + +SPI_CONFIG_ADDR = 2 +SPI_XFER_ADDR = 1 +SPI_DATA_ADDR = 0 +SPI_OFFLINE = 1 << 0 +SPI_CS_POLARITY = 1 << 1 +SPI_CLK_POLARITY = 1 << 2 +SPI_CLK_PHASE = 1 << 3 +SPI_LSB_FIRST = 1 << 4 +SPI_HALF_DUPLEX = 1 << 5 + + +def SPI_CLK_LOAD(i): + return i << 16 + + +def SPI_CS(i): + return i << 0 + + +def SPI_WRITE_LENGTH(i): + return i << 16 + + +def SPI_READ_LENGTH(i): + return i << 24 + def _test_gen(bus): - yield from bus.write(0, 0 | (5 << 16)) + yield from bus.write(SPI_CONFIG_ADDR, + 1*SPI_CLK_PHASE | 0*SPI_LSB_FIRST | + 1*SPI_HALF_DUPLEX | SPI_CLK_LOAD(3)) yield - yield from bus.write(1, 1 | (24 << 16) | (16 << 24)) + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | + SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(0)) yield - yield from bus.write(2, 0x12345678) + yield from bus.write(SPI_DATA_ADDR, 0x90000000) yield - r = (yield from bus.read(2)) - print(r) + print(hex((yield from bus.read(SPI_DATA_ADDR)))) yield + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | + SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(4)) + yield + yield from bus.write(SPI_DATA_ADDR, 0x81000000) + yield + print(hex((yield from bus.read(SPI_DATA_ADDR)))) + yield + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | + SPI_WRITE_LENGTH(0) | SPI_READ_LENGTH(4)) + yield + yield from bus.write(SPI_DATA_ADDR, 0x90000000) + yield + print(hex((yield from bus.read(SPI_DATA_ADDR)))) + yield + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | + SPI_WRITE_LENGTH(32) | SPI_READ_LENGTH(0)) + yield + yield from bus.write(SPI_DATA_ADDR, 0x87654321) + yield + print(hex((yield from bus.read(SPI_DATA_ADDR)))) + yield + + return + for cpol, cpha, lsb, clk in product( + (0, 1), (0, 1), (0, 1), (0, 1)): + yield from bus.write(SPI_CONFIG_ADDR, + cpol*SPI_CLK_POLARITY | cpha*SPI_CLK_PHASE | + lsb*SPI_LSB_FIRST | SPI_CLK_LOAD(clk)) + for wlen, rlen, wdata in product((0, 8, 32), (0, 8, 32), + (0, 0xffffffff, 0xdeadbeef)): + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | + SPI_WRITE_LENGTH(wlen) | + SPI_READ_LENGTH(rlen)) + yield from bus.write(SPI_DATA_ADDR, wdata) + rdata = yield from bus.read(SPI_DATA_ADDR) + len = (wlen + rlen) % 32 + mask = (1 << len) - 1 + if lsb: + shift = (wlen + rlen) % 32 + else: + shift = 0 + a = (wdata >> wshift) & wmask + b = (rdata >> rshift) & rmask + if a != b: + print("ERROR", end=" ") + print(cpol, cpha, lsb, clk, wlen, rlen, + hex(wdata), hex(rdata), hex(a), hex(b)) + class _TestPads: @@ -104,6 +324,22 @@ class _TestPads: if __name__ == "__main__": + from migen.fhdl.specials import Tristate + + class T(Module): + def __init__(self, t): + oe = Signal() + self.comb += [ + t.target.eq(t.o), + oe.eq(t.oe), + t.i.eq(t.o), + ] + Tristate.lower = staticmethod(lambda dr: T(dr)) + + from migen.fhdl.verilog import convert pads = _TestPads() dut = SPIMaster(pads) + dut.comb += pads.miso.eq(pads.mosi) + #print(convert(dut)) + run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From 3b6999ac06ecc09b9587e6b5c464a4fb880b6211 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 20:40:06 +0100 Subject: [PATCH 016/125] gateware.spi: refactor, sim verified --- artiq/gateware/spi.py | 454 ++++++++++++++++++++++++++---------------- 1 file changed, 283 insertions(+), 171 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index d0df75509..86ae26562 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -1,11 +1,158 @@ from itertools import product from migen import * -from migen.genlib.fsm import * -from migen.genlib.misc import WaitTimer +from migen.genlib.fsm import FSM, NextState from misoc.interconnect import wishbone +class SPIClockGen(Module): + def __init__(self, width): + self.load = Signal(width) + self.bias = Signal() # bias this clock phase to longer times + self.edge = Signal() + self.clk = Signal(reset=1) + + cnt = Signal.like(self.load) + self.comb += [ + self.edge.eq(cnt == 0), + ] + self.sync += [ + cnt.eq(cnt - 1), + If(self.edge, + cnt.eq(self.load[1:] + + (self.load[0] & self.bias)), + self.clk.eq(~self.clk), + ) + ] + + +class SPIRegister(Module): + def __init__(self, width): + self.data = Signal(width) + self.o = Signal() + self.i = Signal() + self.lsb = Signal() + self.shift = Signal() + self.sample = Signal() + + self.comb += [ + self.o.eq(Mux(self.lsb, self.data[0], self.data[-1])), + ] + self.sync += [ + If(self.shift, + If(self.lsb, + self.data[:-1].eq(self.data[1:]), + ).Else( + self.data[1:].eq(self.data[:-1]), + ) + ), + If(self.sample, + If(self.lsb, + self.data[-1].eq(self.i), + ).Else( + self.data[0].eq(self.i), + ) + ) + ] + + +class SPIBitCounter(Module): + def __init__(self, width): + self.n_read = Signal(width) + self.n_write = Signal(width) + self.read = Signal() + self.write = Signal() + self.done = Signal() + + self.comb += [ + self.write.eq(self.n_write != 0), + self.read.eq(self.n_read != 0), + self.done.eq(~(self.write | self.read)), + ] + self.sync += [ + If(self.write, + self.n_write.eq(self.n_write - 1), + ).Elif(self.read, + self.n_read.eq(self.n_read - 1), + ) + ] + + +class SPIMachine(Module): + def __init__(self, data_width, clock_width, bits_width): + ce = CEInserter() + self.submodules.cg = ce(SPIClockGen(clock_width)) + self.submodules.reg = ce(SPIRegister(data_width)) + self.submodules.bits = ce(SPIBitCounter(bits_width)) + self.div_write = Signal.like(self.cg.load) + self.div_read = Signal.like(self.cg.load) + self.clk_phase = Signal() + self.start = Signal() + self.cs = Signal() + self.oe = Signal() + self.done = Signal() + + # # # + + fsm = CEInserter()(FSM("IDLE")) + self.submodules += fsm + + fsm.act("IDLE", + If(self.start, + If(self.clk_phase, + NextState("WAIT"), + ).Else( + NextState("SETUP"), + ) + ) + ) + fsm.act("SETUP", + self.reg.sample.eq(1), + NextState("HOLD"), + ) + fsm.act("HOLD", + If(self.bits.done & ~self.start, + If(self.clk_phase, + NextState("IDLE"), + ).Else( + NextState("WAIT"), + ) + ).Else( + self.reg.shift.eq(1), + NextState("SETUP"), + ) + ) + fsm.act("WAIT", + If(self.bits.done, + NextState("IDLE"), + ).Else( + NextState("SETUP"), + ) + ) + + write0 = Signal() + self.sync += [ + If(self.cg.edge & self.reg.shift, + write0.eq(self.bits.write), + ) + ] + self.comb += [ + self.cg.ce.eq(self.start | self.cs | ~self.cg.edge), + If(self.bits.write | ~self.bits.read, + self.cg.load.eq(self.div_write), + ).Else( + self.cg.load.eq(self.div_read), + ), + self.cg.bias.eq(fsm.before_entering("SETUP")), + fsm.ce.eq(self.cg.edge), + self.cs.eq(~fsm.ongoing("IDLE")), + self.reg.ce.eq(self.cg.edge), + self.bits.ce.eq(self.cg.edge & self.reg.sample), + self.done.eq(self.cg.edge & self.bits.done & fsm.ongoing("HOLD")), + self.oe.eq(write0 | self.bits.write), + ] + + class SPIMaster(Module): """SPI Master. @@ -15,50 +162,61 @@ class SPIMaster(Module): * If there is a miso wire in pads, the input and output can be done with two signals (a.k.a. 4-wire SPI), else mosi must be used for both output and input (a.k.a. 3-wire SPI) and config.half_duplex - needs to be set. - * Every transfer consists of a 0-M bit write followed by a 0-M - bit read. - * cs_n is always asserted at the beginning and deasserted - at the end of the transfer. + must to be set when reading data is desired. + * Every transfer consists of a write_length 0-M bit write followed + by a read_length 0-M bit read. + * cs_n is asserted at the beginning and deasserted at the end of the + transfer if there is no other transfer pending. * cs_n handling is agnostic to whether it is one-hot or decoded somewhere downstream. If it is decoded, "cs_n all deasserted" should be handled accordingly (no slave selected). If it is one-hot, asserting multiple slaves should only be attempted - if miso is either not connected between slaves or open collector. - cs can also be handled independently through other means. + if miso is either not connected between slaves, or open collector, + or correctly multiplexed externally. * If config.cs_polarity == 0 (cs active low, the default), "cs_n all deasserted" means "all cs_n bits high". + * cs is not mandatory in pads. Framing and chip selection can also + be handled independently through other means. * The first bit output on mosi is always the MSB/LSB (depending on config.lsb_first) of the data register, independent of xfer.write_len. The last bit input from miso always ends up in the LSB/MSB (respectively) of the data register, independent of read_len. * For 4-wire SPI only the sum of read_len and write_len matters. The - behavior is the same no matter how the transfer length is divided - between the two. For 3-wire SPI, the direction of mosi/miso is - switched from output to input after write_len cycles, at the - "output" clk edge corresponding to bit write_len + 1 of the transfer. + behavior is the same no matter how the total transfer length is + divided between the two. For 3-wire SPI, the direction of mosi/miso + is switched from output to input after write_len cycles, at the + "shift_out" clk edge corresponding to bit write_length + 1 of the + transfer. * Data output on mosi in 4-wire SPI during the read cycles is what is found in the data register at the time. Data in the data register outside the least/most (depending - on config.lsb_first) significant read_len bits is what is + on config.lsb_first) significant read_length bits is what is seen on miso during the write cycles. - * When the transfer is complete the wishbone transaction is ack-ed. - * Input data from the last transaction can be read from the data - register at any time. + * The SPI data register is double-buffered: once a transfer completes, + the previous transfer's read data is available in the data register + and new write data can be written, queuing a new transfer. Transfers + submitted this way are chained and executed without deasserting cs. + * A wishbone transaction is ack-ed when the transfer has been written + to the intermediate buffer. It will be started when there are no + other transactions being executed. Transaction Sequence: * If desired, write the config register to set up the core. * If desired, write the xfer register to change lengths and cs_n. * Write the data register (also for zero-length writes), - writing triggers the transfer and when the transfer is complete the - write is ack-ed. + writing triggers the transfer and when the transfer is accepted to + the inermediate buffer, the write is ack-ed. * If desired, read the data register. + * If desired write xfer and data for the next, chained, transfer Register address and bit map: config (address 2): 1 offline: all pins high-z (reset=1) + 1 active: cs/transfer active + 1 pending: transfer pending in intermediate buffer, bus writes will + block 1 cs_polarity: active level of chip select (reset=0) 1 clk_polarity: idle level for clk (reset=0) 1 clk_phase: first edge after cs assertion to sample data on (reset=0) @@ -68,11 +226,11 @@ class SPIMaster(Module): (1, 1): idle high, output on falling, input on rising 1 lsb_first: LSB is the first bit on the wire (reset=0) 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) - 10 undefined - 16 clk_load: clock load value to divide from this module's clock - to the SPI write clk clk pulses are asymmetric - if a divider is odd, favoring longer setup over hold times. - clk/spi_clk == clk_load + 2 (reset=0) + 12 div_write: counter load value to divide this module's clock + to the SPI write clk. clk pulses are asymmetric + if the value is odd, favoring longer setup over hold times. + f_clk/f_spi_write == div_write + 2 (reset=0) + 12 div_read: ditto for the read clock xfer (address 1): 16 cs: active high bit mask of chip selects to assert @@ -89,50 +247,18 @@ class SPIMaster(Module): ### - # State machine - wb_we = Signal() - start = Signal() - active = Signal() - - fsm = FSM("IDLE") - self.submodules += fsm - - fsm.act("IDLE", - If(bus.cyc & bus.stb, - NextState("ACK"), - If(bus.we, - wb_we.eq(1), - If(bus.adr == 0, # data register - NextState("START"), - ) - ) - ) - ) - fsm.act("START", - start.eq(1), - NextState("ACTIVE"), - ) - fsm.act("ACTIVE", - If(~active, - bus.ack.eq(1), - NextState("IDLE"), - ) - ) - fsm.act("ACK", - bus.ack.eq(1), - NextState("IDLE"), - ) - # Wishbone config = Record([ ("offline", 1), + ("active", 1), + ("pending", 1), ("cs_polarity", 1), ("clk_polarity", 1), ("clk_phase", 1), ("lsb_first", 1), ("half_duplex", 1), - ("padding", 10), - ("clk_load", 16), + ("div_write", 12), + ("div_read", 12), ]) config.offline.reset = 1 assert len(config) <= len(bus.dat_w) @@ -144,101 +270,96 @@ class SPIMaster(Module): ]) assert len(xfer) <= len(bus.dat_w) - data = Signal.like(bus.dat_w) - - wb_data = Array([data, xfer.raw_bits(), config.raw_bits()])[bus.adr] - self.comb += bus.dat_r.eq(wb_data) - self.sync += If(wb_we, wb_data.eq(bus.dat_w)) - # SPI - write_count = Signal.like(xfer.write_length) - read_count = Signal.like(xfer.read_length) - clk_count = Signal.like(config.clk_load) - clk = Signal(reset=1) # idle high - phase = Signal() - edge = Signal() - write = Signal() - read = Signal() - miso = Signal() - miso_i = Signal() - mosi_o = Signal() + spi = SPIMachine(data_width, clock_width=len(config.div_read), + bits_width=len(xfer.read_length)) + self.submodules += spi + + wb_we = Signal() + pending = Signal() + cs = Signal.like(xfer.cs) + data_read = Signal.like(spi.reg.data) + data_write = Signal.like(spi.reg.data) self.comb += [ - phase.eq(clk ^ config.clk_phase), - edge.eq(active & (clk_count == 0)), - write.eq(write_count != 0), - read.eq(read_count != 0), + wb_we.eq(bus.cyc & bus.stb & bus.we & (~pending | spi.done)), + bus.dat_r.eq( + Array([data_read, xfer.raw_bits(), config.raw_bits() + ])[bus.adr]), + spi.start.eq(pending & (~spi.cs | spi.done)), + spi.clk_phase.eq(config.clk_phase), + spi.reg.lsb.eq(config.lsb_first), + spi.div_write.eq(config.div_write), + spi.div_read.eq(config.div_read), ] - self.sync += [ - If(start, - write_count.eq(xfer.write_length), - read_count.eq(xfer.read_length), - active.eq(1), + bus.ack.eq(~bus.we | ~pending | spi.done), + If(wb_we, + Array([data_write, xfer.raw_bits(), config.raw_bits() + ])[bus.adr].eq(bus.dat_w) ), - If(active, - clk_count.eq(clk_count - 1), + config.active.eq(spi.cs), + config.pending.eq(pending), + If(spi.done, + data_read.eq(spi.reg.data), ), - If(start | edge, - # setup time passes during phase 0 - # use the lsb to bias that time to favor longer setup times - clk_count.eq(config.clk_load[1:] + - (config.clk_load[0] & phase)), - clk.eq(~clk), # idle high - If(phase, - data.eq(Mux(config.lsb_first, - Cat(data[1:], miso), - Cat(miso, data[:-1]))), - mosi_o.eq(Mux(config.lsb_first, data[0], data[-1])), - If(write, - write_count.eq(write_count - 1), - ), - ).Else( - miso.eq(miso_i), - If(~write & read, - read_count.eq(read_count - 1), - ), - ), + If(spi.start, + cs.eq(xfer.cs), + spi.bits.n_write.eq(xfer.write_length), + spi.bits.n_read.eq(xfer.read_length), + spi.reg.data.eq(data_write), + pending.eq(0), ), - If(~clk & edge & ~write & ~read, # always from low clk - active.eq(0), + If(wb_we & (bus.adr == 0), # data register + pending.eq(1), ), ] # I/O - cs_n_t = TSTriple(len(pads.cs_n)) - self.specials += cs_n_t.get_tristate(pads.cs_n) + if hasattr(pads, "cs_n"): + cs_n_t = TSTriple(len(pads.cs_n)) + self.specials += cs_n_t.get_tristate(pads.cs_n) + self.comb += [ + cs_n_t.oe.eq(~config.offline), + cs_n_t.o.eq((cs & Replicate(spi.cs, len(cs))) ^ + Replicate(~config.cs_polarity, len(cs))), + ] + clk_t = TSTriple() self.specials += clk_t.get_tristate(pads.clk) mosi_t = TSTriple() self.specials += mosi_t.get_tristate(pads.mosi) self.comb += [ - cs_n_t.oe.eq(~config.offline), clk_t.oe.eq(~config.offline), - mosi_t.oe.eq(~config.offline & (write | ~config.half_duplex)), - cs_n_t.o.eq((xfer.cs & Replicate(active, len(xfer.cs))) ^ - Replicate(~config.cs_polarity, len(xfer.cs))), - clk_t.o.eq((clk & active) ^ config.clk_polarity), - miso_i.eq(Mux(config.half_duplex, mosi_t.i, - getattr(pads, "miso", mosi_t.i))), - mosi_t.o.eq(mosi_o), + mosi_t.oe.eq(~config.offline & spi.cs & + (spi.oe | ~config.half_duplex)), + clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), + spi.reg.i.eq(Mux(config.half_duplex, mosi_t.i, + getattr(pads, "miso", mosi_t.i))), + mosi_t.o.eq(spi.reg.o), ] -SPI_CONFIG_ADDR = 2 -SPI_XFER_ADDR = 1 -SPI_DATA_ADDR = 0 -SPI_OFFLINE = 1 << 0 -SPI_CS_POLARITY = 1 << 1 -SPI_CLK_POLARITY = 1 << 2 -SPI_CLK_PHASE = 1 << 3 -SPI_LSB_FIRST = 1 << 4 -SPI_HALF_DUPLEX = 1 << 5 +SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) +( + SPI_OFFLINE, + SPI_ACTIVE, + SPI_PENDING, + SPI_CS_POLARITY, + SPI_CLK_POLARITY, + SPI_CLK_PHASE, + SPI_LSB_FIRST, + SPI_HALF_DUPLEX, +) = (1 << i for i in range(8)) -def SPI_CLK_LOAD(i): - return i << 16 +def SPI_DIV_WRITE(i): + return i << 8 + + +def SPI_DIV_READ(i): + return i << 20 def SPI_CS(i): @@ -253,53 +374,44 @@ def SPI_READ_LENGTH(i): return i << 24 -def _test_gen(bus): - yield from bus.write(SPI_CONFIG_ADDR, - 1*SPI_CLK_PHASE | 0*SPI_LSB_FIRST | - 1*SPI_HALF_DUPLEX | SPI_CLK_LOAD(3)) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | - SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(0)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x90000000) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | - SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(4)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x81000000) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | - SPI_WRITE_LENGTH(0) | SPI_READ_LENGTH(4)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x90000000) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | - SPI_WRITE_LENGTH(32) | SPI_READ_LENGTH(0)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x87654321) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) +def _test_xfer(bus, cs, wlen, rlen, wdata): + yield from bus.write(SPI_XFER_ADDR, SPI_CS(cs) | + SPI_WRITE_LENGTH(wlen) | SPI_READ_LENGTH(rlen)) + yield from bus.write(SPI_DATA_ADDR, wdata) yield + +def _test_read(bus, sync=SPI_ACTIVE | SPI_PENDING): + while (yield from bus.read(SPI_CONFIG_ADDR)) & sync: + pass + return (yield from bus.read(SPI_DATA_ADDR)) + + +def _test_gen(bus): + yield from bus.write(SPI_CONFIG_ADDR, + 0*SPI_CLK_PHASE | 0*SPI_LSB_FIRST | + 1*SPI_HALF_DUPLEX | + SPI_DIV_WRITE(3) | SPI_DIV_READ(5)) + yield from _test_xfer(bus, 0b01, 4, 0, 0x90000000) + print(hex((yield from _test_read(bus)))) + yield from _test_xfer(bus, 0b10, 0, 4, 0x90000000) + print(hex((yield from _test_read(bus)))) + yield from _test_xfer(bus, 0b11, 4, 4, 0x81000000) + print(hex((yield from _test_read(bus)))) + yield from _test_xfer(bus, 0b01, 8, 32, 0x87654321) + yield from _test_xfer(bus, 0b01, 0, 32, 0x12345678) + print(hex((yield from _test_read(bus, SPI_PENDING)))) + print(hex((yield from _test_read(bus, SPI_ACTIVE)))) return for cpol, cpha, lsb, clk in product( (0, 1), (0, 1), (0, 1), (0, 1)): yield from bus.write(SPI_CONFIG_ADDR, cpol*SPI_CLK_POLARITY | cpha*SPI_CLK_PHASE | - lsb*SPI_LSB_FIRST | SPI_CLK_LOAD(clk)) + lsb*SPI_LSB_FIRST | SPI_DIV_WRITE(clk) | + SPI_DIV_READ(clk)) for wlen, rlen, wdata in product((0, 8, 32), (0, 8, 32), (0, 0xffffffff, 0xdeadbeef)): - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | - SPI_WRITE_LENGTH(wlen) | - SPI_READ_LENGTH(rlen)) - yield from bus.write(SPI_DATA_ADDR, wdata) - rdata = yield from bus.read(SPI_DATA_ADDR) + rdata = (yield from _test_xfer(bus, 0b1, wlen, rlen, wdata, True)) len = (wlen + rlen) % 32 mask = (1 << len) - 1 if lsb: @@ -336,10 +448,10 @@ if __name__ == "__main__": ] Tristate.lower = staticmethod(lambda dr: T(dr)) - from migen.fhdl.verilog import convert pads = _TestPads() dut = SPIMaster(pads) dut.comb += pads.miso.eq(pads.mosi) - #print(convert(dut)) + # from migen.fhdl.verilog import convert + # print(convert(dut)) run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From f8732acece7cd5d980a40b56768960e7e5a94b62 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 21:06:20 +0100 Subject: [PATCH 017/125] rtio.spi: drop unused argument --- artiq/gateware/rtio/phy/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/rtio/phy/spi.py b/artiq/gateware/rtio/phy/spi.py index e72f602c2..92ab7a548 100644 --- a/artiq/gateware/rtio/phy/spi.py +++ b/artiq/gateware/rtio/phy/spi.py @@ -5,7 +5,7 @@ from artiq.gateware.rtio.phy.wishbone import RT2WB class SPIMaster(Module): - def __init__(self, pads, onehot=False, **kwargs): + def __init__(self, pads, **kwargs): self.submodules._ll = ClockDomainsRenamer("rio")( SPIMasterWB(pads, **kwargs)) self.submodules._rt2wb = RT2WB(2, self._ll.bus) From 312e09150e075a86c833c1fc49bb7f8406a192d0 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 21:17:53 +0100 Subject: [PATCH 018/125] kc705/clock: add spi bus for dac on ams101 --- artiq/gateware/targets/kc705.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 719876220..ba3751a27 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -19,7 +19,7 @@ from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict from artiq.gateware.soc import AMPSoC from artiq.gateware import rtio, nist_qc1, nist_clock, nist_qc2 -from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds +from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi from artiq import __artiq_dir__ as artiq_dir from artiq import __version__ as artiq_version @@ -237,6 +237,22 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) + self.config["RTIO_SPI_LDAC_CHANNEL"] = len(rtio_channels) + ldac_n = self.platform.request("xadc_gpio", 0) + phy = ttl_simple.Output(ldac_n) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + + self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) + spi_pins = Module() + spi_pins.clk = self.platform.request("xadc_gpio", 1) + spi_pins.mosi = self.platform.request("xadc_gpio", 2) + spi_pins.cs_n = self.platform.request("xadc_gpio", 3) + phy = spi.SPIMaster(spi_pins) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy( + phy, ofifo_depth=4, ififo_depth=4)) + self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) self.config["DDS_CHANNEL_COUNT"] = 11 self.config["DDS_AD9914"] = True From d5893d15fbc383c8fbe7f743218f49d3efd227b9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 22:41:17 +0100 Subject: [PATCH 019/125] gateware.kc705: make xadc/ams an extension header --- artiq/gateware/targets/kc705.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index ba3751a27..3cc948aab 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -80,6 +80,18 @@ class _RTIOCRG(Module, AutoCSR): ] +_ams101_dac = [ + ("ams101_dac", 0, + + Subsignal("ldac", Pins("XADC:GPIO0")), + Subsignal("clk", Pins("XADC:GPIO1")), + Subsignal("mosi", Pins("XADC:GPIO2")), + Subsignal("cs_n", Pins("XADC:GPIO3")), + IOStandard("LVTTL") + ) +] + + class _NIST_Ions(MiniSoC, AMPSoC): csr_map = { "rtio": None, # mapped on Wishbone instead @@ -115,6 +127,8 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.platform.request("user_led", 0), self.platform.request("user_led", 1))) + self.platform.add_extension(_ams101_dac) + def add_rtio(self, rtio_channels): self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) self.submodules.rtio = rtio.RTIO(rtio_channels) @@ -237,19 +251,15 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) - self.config["RTIO_SPI_LDAC_CHANNEL"] = len(rtio_channels) - ldac_n = self.platform.request("xadc_gpio", 0) - phy = ttl_simple.Output(ldac_n) + spi_pins = self.platform.request("ams101_dac", 0) + phy = ttl_simple.Output(spi_pins.ldac) self.submodules += phy + self.config["RTIO_SPI_LDAC_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy(phy)) - self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) - spi_pins = Module() - spi_pins.clk = self.platform.request("xadc_gpio", 1) - spi_pins.mosi = self.platform.request("xadc_gpio", 2) - spi_pins.cs_n = self.platform.request("xadc_gpio", 3) phy = spi.SPIMaster(spi_pins) self.submodules += phy + self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) From 9a881aa430c18b141ceb9ee804f765d953465be0 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:36:18 +0100 Subject: [PATCH 020/125] gateware.spi: simpler clk bias --- artiq/gateware/spi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 86ae26562..ebfa9a8b0 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -20,7 +20,7 @@ class SPIClockGen(Module): cnt.eq(cnt - 1), If(self.edge, cnt.eq(self.load[1:] + - (self.load[0] & self.bias)), + (self.load[0] & (self.clk ^ self.bias))), self.clk.eq(~self.clk), ) ] @@ -143,7 +143,7 @@ class SPIMachine(Module): ).Else( self.cg.load.eq(self.div_read), ), - self.cg.bias.eq(fsm.before_entering("SETUP")), + self.cg.bias.eq(self.clk_phase), fsm.ce.eq(self.cg.edge), self.cs.eq(~fsm.ongoing("IDLE")), self.reg.ce.eq(self.cg.edge), From 8d7e92ebaebba8a756b0dc5c4af082e0069a1a13 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:37:00 +0100 Subject: [PATCH 021/125] pipistrello: set RTIO_SPI_CHANNEL --- artiq/gateware/targets/pipistrello.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index 5967ccdd7..d7e177517 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -200,6 +200,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd spi_pins.cs_n = pmod.d[3:] phy = spi.SPIMaster(spi_pins) self.submodules += phy + self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) From d63a63531a079fc8be12023ac60f6ed1745c2ac1 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:38:36 +0100 Subject: [PATCH 022/125] spi: add runtime support --- artiq/runtime/Makefile | 2 +- artiq/runtime/ksupport.c | 4 ++++ artiq/runtime/spi.c | 50 ++++++++++++++++++++++++++++++++++++++++ artiq/runtime/spi.h | 20 ++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 artiq/runtime/spi.c create mode 100644 artiq/runtime/spi.h diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 6c13b631b..46e3aedb9 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o dds.o + bridge.o rtio.o ttl.o dds.o spi.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index d5825f746..bcdd7d028 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -15,6 +15,7 @@ #include "artiq_personality.h" #include "ttl.h" #include "dds.h" +#include "spi.h" #include "rtio.h" double round(double x); @@ -121,6 +122,9 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, + {"spi_write", &spi_write}, + {"spi_read", &spi_read}, + {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/spi.c b/artiq/runtime/spi.c new file mode 100644 index 000000000..a72e3a4f8 --- /dev/null +++ b/artiq/runtime/spi.c @@ -0,0 +1,50 @@ +#include +#include + +#include "artiq_personality.h" +#include "rtio.h" +#include "log.h" +#include "spi.h" + + +#define DURATION_WRITE (1 << CONFIG_RTIO_FINE_TS_WIDTH) + +void spi_write(long long int timestamp, int channel, int addr, + unsigned int data) +{ + rtio_chan_sel_write(CONFIG_RTIO_SPI_CHANNEL + channel); + rtio_o_address_write(addr); + rtio_o_data_write(data); + rtio_o_timestamp_write(timestamp); + rtio_write_and_process_status(timestamp, channel); +} + + +unsigned int spi_read(long long int timestamp, int channel, int addr) +{ + int status; + long long int time_limit = timestamp + DURATION_WRITE; + unsigned int r; + + spi_write(timestamp, channel, addr | SPI_WB_READ, 0); + + while((status = rtio_i_status_read())) { + if(rtio_i_status_read() & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RTIO overflow at channel {0}", + channel, 0, 0); + } + if(rtio_get_counter() >= time_limit) { + /* check empty flag again to prevent race condition. + * now we are sure that the time limit has been exceeded. + */ + if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) + return -1; + } + /* input FIFO is empty - keep waiting */ + } + r = rtio_i_data_read(); + rtio_i_re_write(1); + return r; +} diff --git a/artiq/runtime/spi.h b/artiq/runtime/spi.h new file mode 100644 index 000000000..6a5ba08f1 --- /dev/null +++ b/artiq/runtime/spi.h @@ -0,0 +1,20 @@ +#ifndef __SPI_H +#define __SPI_H + +#include +#include +#include + +#define SPI_ADDR_DATA 0 +#define SPI_ADDR_XFER 1 +#define SPI_ADDR_CONFIG 2 +#define SPI_WB_READ (1 << 2) + +#define SPI_XFER_CS(x) (x) +#define SPI_XFER_WRITE_LENGTH(x) ((x) << 16) +#define SPI_XFER_READ_LENGTH(x) ((x) << 24) + +void spi_write(long long int timestamp, int channel, int address, unsigned int data); +unsigned int spi_read(long long int timestamp, int channel, int address); + +#endif /* __SPI_H */ From 8b2b27845761109cca45f0f18029e16db0199669 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:38:50 +0100 Subject: [PATCH 023/125] spi: add coredevice support --- artiq/coredevice/spi.py | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 artiq/coredevice/spi.py diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py new file mode 100644 index 000000000..6265ff3b6 --- /dev/null +++ b/artiq/coredevice/spi.py @@ -0,0 +1,88 @@ +from artiq.language.core import * +from artiq.language.types import * + + +@syscall +def spi_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def spi_read(time_mu: TInt64, channel: TInt32, addr: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") + + +SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) +( + SPI_OFFLINE, + SPI_ACTIVE, + SPI_PENDING, + SPI_CS_POLARITY, + SPI_CLK_POLARITY, + SPI_CLK_PHASE, + SPI_LSB_FIRST, + SPI_HALF_DUPLEX, +) = (1 << i for i in range(8)) + + +class SPIMaster: + """Core device Serial Peripheral Interface (SPI) bus master. + + :param ref_period: clock period of the SPI core. + :param channel: channel number of the SPI bus to control. + """ + def __init__(self, dmgr, ref_period, channel): + self.core = dmgr.get("core") + self.ref_period_mu = int(seconds_to_mu(ref_period, self.core), 64) + self.channel = channel + self.write_div = 0 + self.read_div = 0 + # a full transfer takes prep_mu + xfer_mu + self.prep_mu = int(0, 64) + # chaned transfers can happen every xfer_mu + self.xfer_mu = int(0, 64) + # The second transfer of a chain be written ref_period_mu + # after the first. Read data is available every xfer_mu starting + # a bit before prep_mu + xfer_mu. + + @portable + def predict_xfer_mu(self, write_length, read_length): + # this is only the intrinsic bit cycle duration + return self.ref_period_mu*( + write_length*self.write_div + + read_length*self.read_div) + + @portable + def predict_prep_mu(self, write_div): + return self.ref_period_mu*( + 2 + # intermediate transfers + # one write_div for the wait+idle cycle + self.write_div) + + @kernel + def set_config(self, flags=0, write_div=6, read_div=6): + self.write_div = write_div + self.read_div = read_div + self.prep_mu = self.predict_prep_mu(write_div) + spi_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 8) | ((read_div - 2) << 20)) + delay_mu(self.ref_period_mu) + + @kernel + def set_xfer(self, chip_select=0, write_length=0, read_length=0): + self.xfer_mu = self.predict_xfer_mu(write_length, read_length) + spi_write(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) + delay_mu(self.ref_period_mu) + + @kernel + def write(self, data): + spi_write(now_mu(), self.channel, SPI_DATA_ADDR, data) + delay_mu(self.prep_mu + self.xfer_mu) + + @kernel + def read(self): + r = spi_read(now_mu(), self.channel, SPI_DATA_ADDR) + delay_mu(self.ref_period_mu) + return r From 9a1d6a51a49a05d8bf5bd0642bb19cb58cffecc1 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 01:51:33 +0100 Subject: [PATCH 024/125] gateware.spi: shorten counters --- artiq/gateware/spi.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index ebfa9a8b0..8a758d003 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -226,16 +226,18 @@ class SPIMaster(Module): (1, 1): idle high, output on falling, input on rising 1 lsb_first: LSB is the first bit on the wire (reset=0) 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) - 12 div_write: counter load value to divide this module's clock - to the SPI write clk. clk pulses are asymmetric - if the value is odd, favoring longer setup over hold times. - f_clk/f_spi_write == div_write + 2 (reset=0) - 12 div_read: ditto for the read clock + 8 undefined + 8 div_write: counter load value to divide this module's clock + to the SPI write clk. + f_clk/f_spi_write == 2*(div_write + 1) (reset=0) + 8 div_read: ditto for the read clock xfer (address 1): 16 cs: active high bit mask of chip selects to assert - 8 write_len: 0-M bits - 8 read_len: 0-M bits + 6 write_len: 0-M bits + 2 undefined + 6 read_len: 0-M bits + 2 undefined data (address 0): M write/read data @@ -257,16 +259,19 @@ class SPIMaster(Module): ("clk_phase", 1), ("lsb_first", 1), ("half_duplex", 1), - ("div_write", 12), - ("div_read", 12), + ("padding", 8), + ("div_write", 8), + ("div_read", 8), ]) config.offline.reset = 1 assert len(config) <= len(bus.dat_w) xfer = Record([ ("cs", 16), - ("write_length", 8), - ("read_length", 8), + ("write_length", 6), + ("padding0", 2), + ("read_length", 6), + ("padding1", 2), ]) assert len(xfer) <= len(bus.dat_w) @@ -355,11 +360,11 @@ SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) def SPI_DIV_WRITE(i): - return i << 8 + return i << 16 def SPI_DIV_READ(i): - return i << 20 + return i << 24 def SPI_CS(i): From 5480099f1bbddb848c389d7a0395973ed6469013 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 02:28:19 +0100 Subject: [PATCH 025/125] gateware.spi: rewrite counter bias for timing --- artiq/gateware/spi.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 8a758d003..dabb89a40 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -13,14 +13,21 @@ class SPIClockGen(Module): self.clk = Signal(reset=1) cnt = Signal.like(self.load) + bias = Signal() + zero = Signal() self.comb += [ - self.edge.eq(cnt == 0), + zero.eq(cnt == 0), + self.edge.eq(zero & ~bias), ] self.sync += [ - cnt.eq(cnt - 1), + If(zero, + bias.eq(0), + ).Else( + cnt.eq(cnt - 1), + ), If(self.edge, - cnt.eq(self.load[1:] + - (self.load[0] & (self.clk ^ self.bias))), + cnt.eq(self.load[1:]), + bias.eq(self.load[0] & (self.clk ^ self.bias)), self.clk.eq(~self.clk), ) ] From ad34927b0a7b12e81ee33c757be5d44701f48c16 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 11:35:49 +0100 Subject: [PATCH 026/125] spi: RTIO_SPI_CHANNEL -> RTIO_FIRST_SPI_CHANNEL --- artiq/gateware/targets/kc705.py | 2 +- artiq/gateware/targets/pipistrello.py | 2 +- artiq/runtime/spi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 3cc948aab..42b5af981 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -259,7 +259,7 @@ class NIST_CLOCK(_NIST_Ions): phy = spi.SPIMaster(spi_pins) self.submodules += phy - self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) + self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index d7e177517..ceb39d83f 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -200,7 +200,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd spi_pins.cs_n = pmod.d[3:] phy = spi.SPIMaster(spi_pins) self.submodules += phy - self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) + self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) diff --git a/artiq/runtime/spi.c b/artiq/runtime/spi.c index a72e3a4f8..20d694454 100644 --- a/artiq/runtime/spi.c +++ b/artiq/runtime/spi.c @@ -12,7 +12,7 @@ void spi_write(long long int timestamp, int channel, int addr, unsigned int data) { - rtio_chan_sel_write(CONFIG_RTIO_SPI_CHANNEL + channel); + rtio_chan_sel_write(CONFIG_RTIO_FIRST_SPI_CHANNEL + channel); rtio_o_address_write(addr); rtio_o_data_write(data); rtio_o_timestamp_write(timestamp); From 948fefa69a3cc8348a4cb42763aac8edcd9e4400 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 11:48:29 +0100 Subject: [PATCH 027/125] gateware.spi: style --- artiq/gateware/spi.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index dabb89a40..f485d345a 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -282,10 +282,9 @@ class SPIMaster(Module): ]) assert len(xfer) <= len(bus.dat_w) - # SPI - spi = SPIMachine(data_width, clock_width=len(config.div_read), - bits_width=len(xfer.read_length)) - self.submodules += spi + self.submodules.spi = spi = SPIMachine( + data_width, clock_width=len(config.div_read), + bits_width=len(xfer.read_length)) wb_we = Signal() pending = Signal() @@ -344,12 +343,12 @@ class SPIMaster(Module): self.comb += [ clk_t.oe.eq(~config.offline), + clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), mosi_t.oe.eq(~config.offline & spi.cs & (spi.oe | ~config.half_duplex)), - clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), + mosi_t.o.eq(spi.reg.o), spi.reg.i.eq(Mux(config.half_duplex, mosi_t.i, getattr(pads, "miso", mosi_t.i))), - mosi_t.o.eq(spi.reg.o), ] @@ -438,32 +437,31 @@ def _test_gen(bus): hex(wdata), hex(rdata), hex(a), hex(b)) - class _TestPads: def __init__(self): - self.cs_n = Signal(3) + self.cs_n = Signal(2) self.clk = Signal() self.mosi = Signal() self.miso = Signal() +class _TestTristate(Module): + def __init__(self, t): + oe = Signal() + self.comb += [ + t.target.eq(t.o), + oe.eq(t.oe), + t.i.eq(t.o), + ] + if __name__ == "__main__": from migen.fhdl.specials import Tristate - class T(Module): - def __init__(self, t): - oe = Signal() - self.comb += [ - t.target.eq(t.o), - oe.eq(t.oe), - t.i.eq(t.o), - ] - Tristate.lower = staticmethod(lambda dr: T(dr)) - pads = _TestPads() dut = SPIMaster(pads) dut.comb += pads.miso.eq(pads.mosi) # from migen.fhdl.verilog import convert # print(convert(dut)) + Tristate.lower = _TestTristate run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From aeae565d35c02d70c05a44a70c3aef41ccc16930 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 11:53:36 +0100 Subject: [PATCH 028/125] runtime/spi: don't apply channel offset --- artiq/runtime/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/spi.c b/artiq/runtime/spi.c index 20d694454..17e86dea7 100644 --- a/artiq/runtime/spi.c +++ b/artiq/runtime/spi.c @@ -12,7 +12,7 @@ void spi_write(long long int timestamp, int channel, int addr, unsigned int data) { - rtio_chan_sel_write(CONFIG_RTIO_FIRST_SPI_CHANNEL + channel); + rtio_chan_sel_write(channel); rtio_o_address_write(addr); rtio_o_data_write(data); rtio_o_timestamp_write(timestamp); From eb01b0bfee1454f26baf441fd0f24c65b6715378 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 12:41:30 +0100 Subject: [PATCH 029/125] gateware.spi: cleanup doc --- artiq/gateware/spi.py | 56 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index f485d345a..328419559 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -166,10 +166,6 @@ class SPIMaster(Module): Notes: * M = 32 is the data width (width of the data register, maximum write bits, maximum read bits) - * If there is a miso wire in pads, the input and output can be done - with two signals (a.k.a. 4-wire SPI), else mosi must be used for - both output and input (a.k.a. 3-wire SPI) and config.half_duplex - must to be set when reading data is desired. * Every transfer consists of a write_length 0-M bit write followed by a read_length 0-M bit read. * cs_n is asserted at the beginning and deasserted at the end of the @@ -184,29 +180,37 @@ class SPIMaster(Module): "cs_n all deasserted" means "all cs_n bits high". * cs is not mandatory in pads. Framing and chip selection can also be handled independently through other means. + * If there is a miso wire in pads, the input and output can be done + with two signals (a.k.a. 4-wire SPI), else mosi must be used for + both output and input (a.k.a. 3-wire SPI) and config.half_duplex + must to be set when reading data is desired. + * For 4-wire SPI only the sum of read_length and write_length matters. + The behavior is the same no matter how the total transfer length is + divided between the two. For 3-wire SPI, the direction of mosi/miso + is switched from output to input after write_len cycles, at the + "shift_out" clk edge corresponding to bit write_length + 1 of the + transfer. * The first bit output on mosi is always the MSB/LSB (depending on config.lsb_first) of the data register, independent of xfer.write_len. The last bit input from miso always ends up in the LSB/MSB (respectively) of the data register, independent of read_len. - * For 4-wire SPI only the sum of read_len and write_len matters. The - behavior is the same no matter how the total transfer length is - divided between the two. For 3-wire SPI, the direction of mosi/miso - is switched from output to input after write_len cycles, at the - "shift_out" clk edge corresponding to bit write_length + 1 of the - transfer. * Data output on mosi in 4-wire SPI during the read cycles is what is found in the data register at the time. Data in the data register outside the least/most (depending on config.lsb_first) significant read_length bits is what is seen on miso during the write cycles. - * The SPI data register is double-buffered: once a transfer completes, - the previous transfer's read data is available in the data register - and new write data can be written, queuing a new transfer. Transfers - submitted this way are chained and executed without deasserting cs. + * The SPI data register is double-buffered: Once a transfer has + started, new write data can be written, queuing a new transfer. + Transfers submitted this way are chained and executed without + deasserting cs. Once a transfer completes, the previous transfer's + read data is available in the data register. * A wishbone transaction is ack-ed when the transfer has been written to the intermediate buffer. It will be started when there are no - other transactions being executed. + other transactions being executed. Writes take one cycle when + there is either no transfer being executed, no data in the + intermediate buffer, or a transfer just completing. Reads always + finish in one cycle. Transaction Sequence: * If desired, write the config register to set up the core. @@ -215,28 +219,30 @@ class SPIMaster(Module): writing triggers the transfer and when the transfer is accepted to the inermediate buffer, the write is ack-ed. * If desired, read the data register. - * If desired write xfer and data for the next, chained, transfer + * If desired, write data for the next, chained, transfer. Register address and bit map: config (address 2): 1 offline: all pins high-z (reset=1) - 1 active: cs/transfer active + 1 active: cs/transfer active (read-only) 1 pending: transfer pending in intermediate buffer, bus writes will - block + block (read-only) 1 cs_polarity: active level of chip select (reset=0) - 1 clk_polarity: idle level for clk (reset=0) + 1 clk_polarity: idle level of clk (reset=0) 1 clk_phase: first edge after cs assertion to sample data on (reset=0) + (clk_polarity, clk_phase) == (CPOL, CPHA) in Freescale language. (0, 0): idle low, output on falling, input on rising (0, 1): idle low, output on rising, input on falling (1, 0): idle high, output on rising, input on falling (1, 1): idle high, output on falling, input on rising + There is never a clk edge during a cs edge. 1 lsb_first: LSB is the first bit on the wire (reset=0) 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) 8 undefined 8 div_write: counter load value to divide this module's clock - to the SPI write clk. - f_clk/f_spi_write == 2*(div_write + 1) (reset=0) + to generate the SPI write clk (reset=0) + f_clk/f_spi_write == div_write + 2 8 div_read: ditto for the read clock xfer (address 1): @@ -338,12 +344,14 @@ class SPIMaster(Module): clk_t = TSTriple() self.specials += clk_t.get_tristate(pads.clk) - mosi_t = TSTriple() - self.specials += mosi_t.get_tristate(pads.mosi) - self.comb += [ clk_t.oe.eq(~config.offline), clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), + ] + + mosi_t = TSTriple() + self.specials += mosi_t.get_tristate(pads.mosi) + self.comb += [ mosi_t.oe.eq(~config.offline & spi.cs & (spi.oe | ~config.half_duplex)), mosi_t.o.eq(spi.reg.o), From df7d15d1fecb8e707f9618a28d344afb0e40bafb Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 13:54:36 +0100 Subject: [PATCH 030/125] runtime: refactor spi into rt2wb --- artiq/coredevice/rt2wb.py | 14 ++++++++++ artiq/coredevice/spi.py | 45 +++++++++++++------------------- artiq/runtime/Makefile | 2 +- artiq/runtime/ksupport.c | 6 ++--- artiq/runtime/{spi.c => rt2wb.c} | 30 ++++++++++----------- artiq/runtime/rt2wb.h | 10 +++++++ artiq/runtime/spi.h | 20 -------------- 7 files changed, 60 insertions(+), 67 deletions(-) create mode 100644 artiq/coredevice/rt2wb.py rename artiq/runtime/{spi.c => rt2wb.c} (56%) create mode 100644 artiq/runtime/rt2wb.h delete mode 100644 artiq/runtime/spi.h diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py new file mode 100644 index 000000000..150afd1fb --- /dev/null +++ b/artiq/coredevice/rt2wb.py @@ -0,0 +1,14 @@ +from artiq.language.core import * +from artiq.language.types import * + + +@syscall +def rt2wb_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def rt2wb_read_sync(time_mu: TInt64, channel: TInt32, addr: TInt32, + duration_mu: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 6265ff3b6..4145a5513 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,16 +1,6 @@ from artiq.language.core import * from artiq.language.types import * - - -@syscall -def spi_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 - ) -> TNone: - raise NotImplementedError("syscall not simulated") - - -@syscall -def spi_read(time_mu: TInt64, channel: TInt32, addr: TInt32) -> TInt32: - raise NotImplementedError("syscall not simulated") +from artiq.coredevice.rt2wb import * SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -34,14 +24,14 @@ class SPIMaster: """ def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") - self.ref_period_mu = int(seconds_to_mu(ref_period, self.core), 64) + self.ref_period_mu = seconds_to_mu(ref_period, self.core) self.channel = channel self.write_div = 0 self.read_div = 0 # a full transfer takes prep_mu + xfer_mu - self.prep_mu = int(0, 64) - # chaned transfers can happen every xfer_mu - self.xfer_mu = int(0, 64) + self.prep_mu = 0 + # chained transfers can happen every xfer_mu + self.xfer_mu = 0 # The second transfer of a chain be written ref_period_mu # after the first. Read data is available every xfer_mu starting # a bit before prep_mu + xfer_mu. @@ -49,40 +39,41 @@ class SPIMaster: @portable def predict_xfer_mu(self, write_length, read_length): # this is only the intrinsic bit cycle duration - return self.ref_period_mu*( + return int(self.ref_period_mu*( write_length*self.write_div + - read_length*self.read_div) + read_length*self.read_div)) @portable def predict_prep_mu(self, write_div): - return self.ref_period_mu*( + return int(self.ref_period_mu*( 2 + # intermediate transfers # one write_div for the wait+idle cycle - self.write_div) + self.write_div)) @kernel def set_config(self, flags=0, write_div=6, read_div=6): self.write_div = write_div self.read_div = read_div self.prep_mu = self.predict_prep_mu(write_div) - spi_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 8) | ((read_div - 2) << 20)) + rt2wb_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 8) | ((read_div - 2) << 20)) delay_mu(self.ref_period_mu) @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): self.xfer_mu = self.predict_xfer_mu(write_length, read_length) - spi_write(now_mu(), self.channel, SPI_XFER_ADDR, - chip_select | (write_length << 16) | (read_length << 24)) + rt2wb_write(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) delay_mu(self.ref_period_mu) @kernel def write(self, data): - spi_write(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(self.prep_mu + self.xfer_mu) + rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR, data) + delay_mu(int(self.prep_mu + self.xfer_mu)) @kernel - def read(self): - r = spi_read(now_mu(), self.channel, SPI_DATA_ADDR) + def read_sync(self): + r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR, + int(self.ref_period_mu)) delay_mu(self.ref_period_mu) return r diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 46e3aedb9..045a162fe 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o dds.o spi.o + bridge.o rtio.o ttl.o dds.o rt2wb.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index bcdd7d028..892f66ccb 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -15,8 +15,8 @@ #include "artiq_personality.h" #include "ttl.h" #include "dds.h" -#include "spi.h" #include "rtio.h" +#include "rt2wb.h" double round(double x); @@ -122,8 +122,8 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"spi_write", &spi_write}, - {"spi_read", &spi_read}, + {"rt2wb_write", &rt2wb_write}, + {"rt2wb_read_sync", &rt2wb_read_sync}, {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/spi.c b/artiq/runtime/rt2wb.c similarity index 56% rename from artiq/runtime/spi.c rename to artiq/runtime/rt2wb.c index 17e86dea7..0758bb6d5 100644 --- a/artiq/runtime/spi.c +++ b/artiq/runtime/rt2wb.c @@ -1,15 +1,11 @@ #include -#include #include "artiq_personality.h" #include "rtio.h" -#include "log.h" -#include "spi.h" +#include "rt2wb.h" -#define DURATION_WRITE (1 << CONFIG_RTIO_FINE_TS_WIDTH) - -void spi_write(long long int timestamp, int channel, int addr, +void rt2wb_write(long long int timestamp, int channel, int addr, unsigned int data) { rtio_chan_sel_write(channel); @@ -20,31 +16,33 @@ void spi_write(long long int timestamp, int channel, int addr, } -unsigned int spi_read(long long int timestamp, int channel, int addr) +unsigned int rt2wb_read_sync(long long int timestamp, int channel, + int addr, int duration) { int status; - long long int time_limit = timestamp + DURATION_WRITE; - unsigned int r; + unsigned int data; - spi_write(timestamp, channel, addr | SPI_WB_READ, 0); + rt2wb_write(timestamp, channel, addr, 0); while((status = rtio_i_status_read())) { - if(rtio_i_status_read() & RTIO_I_STATUS_OVERFLOW) { + if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", - "RTIO overflow at channel {0}", + "RTIO WB overflow on channel {0}", channel, 0, 0); } - if(rtio_get_counter() >= time_limit) { + if(rtio_get_counter() >= timestamp + duration) { /* check empty flag again to prevent race condition. * now we are sure that the time limit has been exceeded. */ if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - return -1; + artiq_raise_from_c("InternalError", + "RTIO WB read failed on channel {0}", + channel, 0, 0); } /* input FIFO is empty - keep waiting */ } - r = rtio_i_data_read(); + data = rtio_i_data_read(); rtio_i_re_write(1); - return r; + return data; } diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h new file mode 100644 index 000000000..f2cbe57d4 --- /dev/null +++ b/artiq/runtime/rt2wb.h @@ -0,0 +1,10 @@ +#ifndef __RT2WB_H +#define __RT2WB_H + +void rt2wb_write(long long int timestamp, int channel, int address, + unsigned int data); +unsigned int rt2wb_read_sync(long long int timestamp, int channel, int address, + int duration); + +#endif /* __RT2WB_H */ + diff --git a/artiq/runtime/spi.h b/artiq/runtime/spi.h deleted file mode 100644 index 6a5ba08f1..000000000 --- a/artiq/runtime/spi.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __SPI_H -#define __SPI_H - -#include -#include -#include - -#define SPI_ADDR_DATA 0 -#define SPI_ADDR_XFER 1 -#define SPI_ADDR_CONFIG 2 -#define SPI_WB_READ (1 << 2) - -#define SPI_XFER_CS(x) (x) -#define SPI_XFER_WRITE_LENGTH(x) ((x) << 16) -#define SPI_XFER_READ_LENGTH(x) ((x) << 24) - -void spi_write(long long int timestamp, int channel, int address, unsigned int data); -unsigned int spi_read(long long int timestamp, int channel, int address); - -#endif /* __SPI_H */ From 785691ab98d9db852b8bdf3acb7fe41682b47224 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 29 Feb 2016 21:32:48 +0800 Subject: [PATCH 031/125] fix indentation --- artiq/master/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/master/scheduler.py b/artiq/master/scheduler.py index 8b7c52553..6e41a8d48 100644 --- a/artiq/master/scheduler.py +++ b/artiq/master/scheduler.py @@ -198,7 +198,7 @@ class PrepareStage(TaskObject): await self.pool.state_changed.wait() elif isinstance(run, float): await asyncio_wait_or_cancel([self.pool.state_changed.wait()], - timeout=run) + timeout=run) else: if run.flush: run.status = RunStatus.flushing From 572c49f475bc0528fa6f27fa7f2929006f45f1fb Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 29 Feb 2016 21:35:23 +0800 Subject: [PATCH 032/125] use m-labs setup for defaults --- artiq/frontend/artiq_flash.py | 2 +- artiq/gateware/targets/kc705.py | 2 +- artiq/runtime/main.c | 4 +- examples/master/device_db.pyon | 72 +++++++++++++++++---------------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index 82cb27939..00e217bcf 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -37,7 +37,7 @@ Prerequisites: """) parser.add_argument("-t", "--target", default="kc705", help="target board, default: %(default)s") - parser.add_argument("-m", "--adapter", default="qc2", + parser.add_argument("-m", "--adapter", default="clock", help="target adapter, default: %(default)s") parser.add_argument("-f", "--storage", help="write file to storage area") parser.add_argument("-d", "--dir", help="look for files in this directory") diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 719876220..6d2367cc4 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -315,7 +315,7 @@ def main(): "+ NIST Ions QC1/CLOCK/QC2 hardware adapters") builder_args(parser) soc_kc705_args(parser) - parser.add_argument("-H", "--hw-adapter", default="qc1", + parser.add_argument("-H", "--hw-adapter", default="clock", help="hardware adapter type: qc1/clock/qc2 " "(default: %(default)s)") args = parser.parse_args() diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 372176732..5897b1eef 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -137,9 +137,9 @@ static void network_init(void) struct ip4_addr gateway_ip; init_macadr(); - fsip_or_default(&local_ip, "ip", 192, 168, 0, 42); + fsip_or_default(&local_ip, "ip", 192, 168, 1, 50); fsip_or_default(&netmask, "netmask", 255, 255, 255, 0); - fsip_or_default(&gateway_ip, "gateway", 192, 168, 0, 1); + fsip_or_default(&gateway_ip, "gateway", 192, 168, 1, 1); lwip_init(); diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index 59b32de2f..da3417e9c 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -1,12 +1,12 @@ # This is an example device database that needs to be adapted to your setup. -# The RTIO channel numbers here are for NIST QC1 on KC705. +# The RTIO channel numbers here are for NIST CLOCK on KC705. { "comm": { "type": "local", "module": "artiq.coredevice.comm_tcp", "class": "Comm", - "arguments": {"host": "192.168.0.42"} + "arguments": {"host": "kc705.lab.m-labs.hk"} }, "core": { "type": "local", @@ -15,73 +15,70 @@ "arguments": {"ref_period": 1e-9} }, - "pmt0": { - "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLInOut", - "arguments": {"channel": 0} - }, - "pmt1": { - "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLInOut", - "arguments": {"channel": 1} - }, - "ttl0": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 2}, + "arguments": {"channel": 0}, "comment": "This is a fairly long comment to test word wrapping in GUI." }, "ttl1": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 3}, + "arguments": {"channel": 1}, "comment": "Hello World" }, "ttl2": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 4} + "arguments": {"channel": 2} }, "ttl3": { "type": "local", "module": "artiq.coredevice.ttl", - "class": "TTLOut", - "arguments": {"channel": 5} + "class": "TTLInOut", + "arguments": {"channel": 3} }, + "ttl4": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 6} + "arguments": {"channel": 4} }, "ttl5": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", + "arguments": {"channel": 5} + }, + "ttl6": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 6} + }, + "ttl7": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLInOut", "arguments": {"channel": 7} }, + + "ttl_sma": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLInOut", - "arguments": {"channel": 17} + "arguments": {"channel": 18} }, + "led": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 18} - }, - "ttl15": { - "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLClockGen", "arguments": {"channel": 19} }, @@ -94,21 +91,21 @@ "dds0": { "type": "local", "module": "artiq.coredevice.dds", - "class": "AD9858", - "arguments": {"sysclk": 1e9, "channel": 0}, + "class": "AD9914", + "arguments": {"sysclk": 3e9, "channel": 0}, "comment": "Comments work in DDS panel as well" }, "dds1": { "type": "local", "module": "artiq.coredevice.dds", - "class": "AD9858", - "arguments": {"sysclk": 1e9, "channel": 1} + "class": "AD9914", + "arguments": {"sysclk": 3e9, "channel": 1} }, "dds2": { "type": "local", "module": "artiq.coredevice.dds", - "class": "AD9858", - "arguments": {"sysclk": 1e9, "channel": 2} + "class": "AD9914", + "arguments": {"sysclk": 3e9, "channel": 2} }, "qc_q1_0": { @@ -160,7 +157,12 @@ "ttl_out": "ttl0", "ttl_out_serdes": "ttl0", - "pmt": "pmt0", + "loop_out": "ttl0", + "loop_in": "ttl3", + #"loop_clock_out": "TODO", + "loop_clock_in": "ttl7", + + "pmt": "ttl3", "bd_dds": "dds0", "bd_sw": "ttl0", "bdd_dds": "dds1", From c226aeb0d4f716b2fb4d3e6a73dde09e8e3e7baf Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 14:55:29 +0100 Subject: [PATCH 033/125] coredevice/spi: read_sync read bit --- artiq/coredevice/spi.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 4145a5513..b75de123c 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -15,6 +15,8 @@ SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) SPI_HALF_DUPLEX, ) = (1 << i for i in range(8)) +SPI_RT2WB_READ = 1 << 2 + class SPIMaster: """Core device Serial Peripheral Interface (SPI) bus master. @@ -73,7 +75,7 @@ class SPIMaster: @kernel def read_sync(self): - r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR, - int(self.ref_period_mu)) + r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | + SPI_RT2WB_READ, int(self.ref_period_mu)) delay_mu(self.ref_period_mu) return r From 6903a1d88aacfc994a062fb1f8a9f3f00c4cbd7a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 14:56:04 +0100 Subject: [PATCH 034/125] runtime/rt2wb: accurate exception strings --- artiq/runtime/rt2wb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 0758bb6d5..e96ce3d3a 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -28,7 +28,7 @@ unsigned int rt2wb_read_sync(long long int timestamp, int channel, if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", - "RTIO WB overflow on channel {0}", + "RT2WB overflow on channel {0}", channel, 0, 0); } if(rtio_get_counter() >= timestamp + duration) { @@ -37,7 +37,7 @@ unsigned int rt2wb_read_sync(long long int timestamp, int channel, */ if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) artiq_raise_from_c("InternalError", - "RTIO WB read failed on channel {0}", + "RT2WB read failed on channel {0}", channel, 0, 0); } /* input FIFO is empty - keep waiting */ From 1b08e65fa1ffc616e2b74c0f875b6193ce7a01a3 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 14:56:29 +0100 Subject: [PATCH 035/125] gateware/rt2wb: only input when active --- artiq/gateware/rtio/phy/wishbone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 1b58525f6..429e2c8ce 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -39,6 +39,6 @@ class RT2WB(Module): wb.cyc.eq(active), wb.stb.eq(active), - self.rtlink.i.stb.eq(wb.ack & ~wb.we), + self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From 4467f91cbfef9f57af5a29d8888555db680714d0 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 29 Feb 2016 22:21:10 +0800 Subject: [PATCH 036/125] coredevice: do not give up on UTF-8 errors in log. Closes #300 --- artiq/coredevice/comm_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index b4059fdcc..f4e1d2eee 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -158,7 +158,7 @@ class CommGeneric: return self._read_chunk(self._read_int32()) def _read_string(self): - return self._read_bytes()[:-1].decode('utf-8') + return self._read_bytes()[:-1].decode("utf-8") # # Writer interface @@ -242,7 +242,7 @@ class CommGeneric: self._read_header() self._read_expect(_D2HMsgType.LOG_REPLY) - return self._read_chunk(self._read_length).decode("utf-8") + return self._read_chunk(self._read_length).decode("utf-8", "replace") def clear_log(self): self._write_empty(_H2DMsgType.LOG_CLEAR) From f73228f248fa2ce5d29a906dfc4973b913a1881a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 15:24:17 +0100 Subject: [PATCH 037/125] gateware/rt2wb: support combinatorial ack --- artiq/gateware/rtio/phy/wishbone.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 429e2c8ce..4eb69ceed 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -23,6 +23,9 @@ class RT2WB(Module): active = Signal() self.sync.rio += [ + If(active & wb.ack, + active.eq(0), + ), If(self.rtlink.o.stb, active.eq(1), wb.adr.eq(self.rtlink.o.address[:address_width]), @@ -30,15 +33,11 @@ class RT2WB(Module): wb.dat_w.eq(self.rtlink.o.data), wb.sel.eq(2**len(wb.sel) - 1) ), - If(wb.ack, - active.eq(0) - ) ] self.comb += [ - self.rtlink.o.busy.eq(active), + self.rtlink.o.busy.eq(active & ~wb.ack), wb.cyc.eq(active), wb.stb.eq(active), - self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From cb8815cc65cac4caa1e8108f81bc9590e53f63a8 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 16:44:04 +0100 Subject: [PATCH 038/125] Revert "gateware/rt2wb: support combinatorial ack" This reverts commit f73228f248fa2ce5d29a906dfc4973b913a1881a. --- artiq/gateware/rtio/phy/wishbone.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 4eb69ceed..429e2c8ce 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -23,9 +23,6 @@ class RT2WB(Module): active = Signal() self.sync.rio += [ - If(active & wb.ack, - active.eq(0), - ), If(self.rtlink.o.stb, active.eq(1), wb.adr.eq(self.rtlink.o.address[:address_width]), @@ -33,11 +30,15 @@ class RT2WB(Module): wb.dat_w.eq(self.rtlink.o.data), wb.sel.eq(2**len(wb.sel) - 1) ), + If(wb.ack, + active.eq(0) + ) ] self.comb += [ - self.rtlink.o.busy.eq(active & ~wb.ack), + self.rtlink.o.busy.eq(active), wb.cyc.eq(active), wb.stb.eq(active), + self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From a0083f450102264e99f717bf2533226bc5ed6782 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 16:44:11 +0100 Subject: [PATCH 039/125] Revert "gateware/rt2wb: only input when active" This reverts commit 1b08e65fa1ffc616e2b74c0f875b6193ce7a01a3. --- artiq/gateware/rtio/phy/wishbone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 429e2c8ce..1b58525f6 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -39,6 +39,6 @@ class RT2WB(Module): wb.cyc.eq(active), wb.stb.eq(active), - self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), + self.rtlink.i.stb.eq(wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From d0d56bd3fe4108967497a1fa26d2805aff33f245 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Mar 2016 00:19:50 +0800 Subject: [PATCH 040/125] doc: update install instructions --- doc/manual/installing.rst | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 751480058..33246d27f 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -40,13 +40,16 @@ If your ``$PATH`` misses reference the ``miniconda3/bin`` or ``anaconda3/bin`` y $ export PATH=$HOME/miniconda3/bin:$PATH -Installing the host side software -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Installing the ARTIQ packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For this, you need to add our Anaconda repository to your conda configuration:: $ conda config --add channels http://conda.anaconda.org/m-labs/channel/main - $ conda config --add channels http://conda.anaconda.org/m-labs/channel/dev + +.. note:: + To use the development versions of ARTIQ, also add the ``dev`` channel (http://conda.anaconda.org/m-labs/channel/dev). + Development versions contain more features, but are not as well-tested and are more likely to contain bugs or inconsistencies. Then you can install the ARTIQ package, it will pull all the necessary dependencies. @@ -60,7 +63,12 @@ Then you can install the ARTIQ package, it will pull all the necessary dependenc $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-kc705-nist_qc1; \ echo "Created environment $ENV for ARTIQ" -* For the KC705 board with the FMC backplane and AD9914 DDS chips:: +* For the KC705 board with the "clock" FMC backplane and AD9914 DDS chips:: + + $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-kc705-nist_clock; \ + echo "Created environment $ENV for ARTIQ" + +* For the KC705 board with the QC2 FMC backplane and AD9914 DDS chips:: $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-kc705-nist_qc2; \ echo "Created environment $ENV for ARTIQ" @@ -89,7 +97,9 @@ You now need to flash 3 things on the FPGA board: 2. The BIOS 3. The ARTIQ runtime -First you need to :ref:`install openocd `. Then, you can flash the board: +They are all shipped in our Conda packages, along with the required flash proxy bitstreams. + +First you need to install OpenOCD. Then, you can flash the board: * For the Pipistrello board:: @@ -97,11 +107,9 @@ First you need to :ref:`install openocd `. Then, you can flash * For the KC705 board:: - $ artiq_flash + $ artiq_flash -m [qc1/clock/qc2] -Next step (for KC705) is to flash MAC and IP addresses to the board: - -* See :ref:`those instructions ` to flash MAC and IP addresses. +For the KC705, the next step is to flash the MAC and IP addresses to the board. See :ref:`those instructions `. .. _install-from-sources: From a1e1f2b387ebd1a1c02d94233eee3e0f5f5226a9 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Mar 2016 00:28:40 +0800 Subject: [PATCH 041/125] doc: insist that output() must be called on TTLInOut. Closes #297 --- artiq/coredevice/ttl.py | 15 +++++++++++++-- doc/manual/getting_started_core.rst | 4 ++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index be410d919..91c8c5531 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -91,6 +91,11 @@ class TTLInOut: This should be used with bidirectional channels. + Note that the channel is in input mode by default. If you need to drive a + signal, you must call ``output``. If the channel is in output mode most of + the time in your setup, it is a good idea to call ``output`` in the + startup kernel. + :param channel: channel number """ def __init__(self, dmgr, channel): @@ -107,12 +112,18 @@ class TTLInOut: @kernel def output(self): - """Set the direction to output.""" + """Set the direction to output. + + There must be a delay of at least one RTIO clock cycle before any + other command can be issued.""" self.set_oe(True) @kernel def input(self): - """Set the direction to input.""" + """Set the direction to input. + + There must be a delay of at least one RTIO clock cycle before any + other command can be issued.""" self.set_oe(False) @kernel diff --git a/doc/manual/getting_started_core.rst b/doc/manual/getting_started_core.rst index 2189e65bd..4dde970cc 100644 --- a/doc/manual/getting_started_core.rst +++ b/doc/manual/getting_started_core.rst @@ -108,6 +108,10 @@ Create a new file ``rtio.py`` containing the following: :: delay(2*us) +.. note:: + If ``ttl0`` is a bidirectional channel (``TTLInOut``), it is in input (non-driving) mode by default. You need to call ``self.ttl0.output()`` as explained above for the LED. + + Connect an oscilloscope or logic analyzer to TTL0 and run ``artiq_run.py led.py``. Notice that the generated signal's period is precisely 4 microseconds, and that it has a duty cycle of precisely 50%. This is not what you would expect if the delay and the pulse were implemented with CPU-controlled GPIO: overhead from the loop management, function calls, etc. would increase the signal's period, and asymmetry in the overhead would cause duty cycle distortion. Instead, inside the core device, output timing is generated by the gateware and the CPU only programs switching commands with certain timestamps that the CPU computes. This guarantees precise timing as long as the CPU can keep generating timestamps that are increasing fast enough. In case it fails to do that (and attempts to program an event with a timestamp in the past), the :class:`artiq.coredevice.exceptions.RTIOUnderflow` exception is raised. The kernel causing it may catch it (using a regular ``try... except...`` construct), or it will be propagated to the host. From dd570720ac0d3aa27f621a36a692906b73379992 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 17:29:37 +0100 Subject: [PATCH 042/125] gateware.spi: ack only in cycles --- artiq/gateware/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 328419559..fd8f612c4 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -310,7 +310,7 @@ class SPIMaster(Module): spi.div_read.eq(config.div_read), ] self.sync += [ - bus.ack.eq(~bus.we | ~pending | spi.done), + bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), If(wb_we, Array([data_write, xfer.raw_bits(), config.raw_bits() ])[bus.adr].eq(bus.dat_w) From 5fad570f5e2b7fdd65294ef5ed2eb29ef9da49a8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Mar 2016 00:35:26 +0800 Subject: [PATCH 043/125] targets/kc705-nist_clock: add clock generator on LA32 for testing purposes --- artiq/gateware/nist_clock.py | 6 ++---- artiq/gateware/targets/kc705.py | 4 ++++ doc/manual/core_device.rst | 2 ++ examples/master/device_db.pyon | 9 ++++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/nist_clock.py b/artiq/gateware/nist_clock.py index 363a339b5..3b621ac41 100644 --- a/artiq/gateware/nist_clock.py +++ b/artiq/gateware/nist_clock.py @@ -48,10 +48,8 @@ fmc_adapter_io = [ Subsignal("n", Pins("LPC:CLK1_M2C_N")), IOStandard("LVDS")), - ("la32", 0, - Subsignal("p", Pins("LPC:LA32_P")), - Subsignal("n", Pins("LPC:LA32_N")), - IOStandard("LVDS")), + ("la32_p", 0, Pins("LPC:LA32_P"), IOStandard("LVTTL")), + ("la32_n", 0, Pins("LPC:LA32_N"), IOStandard("LVTTL")), ("spi", 0, Subsignal("clk", Pins("LPC:LA13_N")), diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 6d2367cc4..6dd32def8 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -237,6 +237,10 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) + phy = ttl_simple.ClockGen(platform.request("la32_p")) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) self.config["DDS_CHANNEL_COUNT"] = 11 self.config["DDS_AD9914"] = True diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index f22763151..8000a6770 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -64,6 +64,8 @@ With the CLOCK hardware, the TTL lines are mapped as follows: +--------------------+-----------------------+--------------+ | 19 | LED | Output | +--------------------+-----------------------+--------------+ +| 20 | LA32_P | Clock | ++--------------------+-----------------------+--------------+ Pipistrello diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index da3417e9c..4dbb92ab7 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -74,6 +74,13 @@ "class": "TTLInOut", "arguments": {"channel": 18} }, + "ttl_clock_la32_p": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLClockGen", + "arguments": {"channel": 20} + }, + "led": { "type": "local", @@ -159,7 +166,7 @@ "loop_out": "ttl0", "loop_in": "ttl3", - #"loop_clock_out": "TODO", + "loop_clock_out": "ttl_clock_la32_p", "loop_clock_in": "ttl7", "pmt": "ttl3", From e11366869df33391a08174e8a78ab515965a4b91 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 17:54:42 +0100 Subject: [PATCH 044/125] coredevice/spi: clean up api --- artiq/coredevice/spi.py | 83 ++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index b75de123c..d6ebeb7db 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,6 +1,6 @@ from artiq.language.core import * -from artiq.language.types import * -from artiq.coredevice.rt2wb import * +from artiq.language.units import MHz +from artiq.coredevice.rt2wb import rt2wb_write, rt2wb_read_sync SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -26,56 +26,63 @@ class SPIMaster: """ def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") + self.ref_period = ref_period self.ref_period_mu = seconds_to_mu(ref_period, self.core) self.channel = channel - self.write_div = 0 - self.read_div = 0 - # a full transfer takes prep_mu + xfer_mu - self.prep_mu = 0 - # chained transfers can happen every xfer_mu - self.xfer_mu = 0 - # The second transfer of a chain be written ref_period_mu - # after the first. Read data is available every xfer_mu starting - # a bit before prep_mu + xfer_mu. - - @portable - def predict_xfer_mu(self, write_length, read_length): - # this is only the intrinsic bit cycle duration - return int(self.ref_period_mu*( - write_length*self.write_div + - read_length*self.read_div)) - - @portable - def predict_prep_mu(self, write_div): - return int(self.ref_period_mu*( - 2 + # intermediate transfers - # one write_div for the wait+idle cycle - self.write_div)) + self.write_period_mu = 0 + self.read_period_mu = 0 + self.xfer_period_mu = 0 + # A full transfer takes write_period_mu + xfer_period_mu. + # Chained transfers can happen every xfer_period_mu. + # The second transfer of a chain can be written 2*ref_period_mu + # after the first. Read data is available every xfer_period_mu starting + # a bit after xfer_period_mu (depending on clk_phase). + # To chain transfers together, new data must be written before + # pending transfer's read data becomes available. @kernel - def set_config(self, flags=0, write_div=6, read_div=6): - self.write_div = write_div - self.read_div = read_div - self.prep_mu = self.predict_prep_mu(write_div) + def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): + write_div = round(1/(write_freq*self.ref_period)) + read_div = round(1/(read_freq*self.ref_period)) + self.set_config_mu(flags, write_div, read_div) + + @kernel + def set_config_mu(self, flags=0, write_div=6, read_div=6): rt2wb_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 8) | ((read_div - 2) << 20)) - delay_mu(self.ref_period_mu) + ((write_div - 2) << 16) | ((read_div - 2) << 24)) + self.write_period_mu = int(write_div*self.ref_period_mu) + self.read_period_mu = int(read_div*self.ref_period_mu) + delay_mu(2*self.ref_period_mu) + + @portable + def get_xfer_period_mu(self, write_length, read_length): + return int(write_length*self.write_period_mu + + read_length*self.read_period_mu) @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): - self.xfer_mu = self.predict_xfer_mu(write_length, read_length) rt2wb_write(now_mu(), self.channel, SPI_XFER_ADDR, chip_select | (write_length << 16) | (read_length << 24)) - delay_mu(self.ref_period_mu) + self.xfer_period_mu = self.get_xfer_period_mu( + write_length, read_length) + delay_mu(int(2*self.ref_period_mu)) @kernel def write(self, data): rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(int(self.prep_mu + self.xfer_mu)) + delay_mu(int(self.write_period_mu + self.xfer_period_mu)) + + @kernel + def read_async(self): + rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + delay_mu(int(2*self.ref_period_mu)) @kernel def read_sync(self): - r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | - SPI_RT2WB_READ, int(self.ref_period_mu)) - delay_mu(self.ref_period_mu) - return r + return rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | + SPI_RT2WB_READ, int(2*self.ref_period_mu)) + + @kernel + def get_config_sync(self): + return rt2wb_read_sync(now_mu(), self.channel, SPI_CONFIG_ADDR | + SPI_RT2WB_READ, int(2*self.ref_period_mu)) From d3c94827eb239e589393f974f107978d39ad6ccf Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 17:58:54 +0100 Subject: [PATCH 045/125] runtime/ttl: simplify ttl_get() a bit --- artiq/runtime/ttl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c index 577ab1eeb..db75207d4 100644 --- a/artiq/runtime/ttl.c +++ b/artiq/runtime/ttl.c @@ -38,7 +38,7 @@ long long int ttl_get(int channel, long long int time_limit) rtio_chan_sel_write(channel); while((status = rtio_i_status_read())) { - if(rtio_i_status_read() & RTIO_I_STATUS_OVERFLOW) { + if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", "RTIO overflow at channel {0}", From 5dae9f8aa8c2682e2a1b493dd63160d38516dddc Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:16:29 +0100 Subject: [PATCH 046/125] runtime: refactor rt2wb/dds --- artiq/runtime/Makefile | 2 +- artiq/runtime/dds.c | 19 ++++++------------- artiq/runtime/rt2wb.c | 34 +++++----------------------------- artiq/runtime/rt2wb.h | 2 ++ artiq/runtime/rtio.c | 38 ++++++++++++++++++++++++++++++++++++++ artiq/runtime/rtio.h | 3 +++ 6 files changed, 55 insertions(+), 43 deletions(-) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 045a162fe..7915f4d0e 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o dds.o rt2wb.o + bridge.o rtio.o ttl.o rt2wb.o dds.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index 396e652eb..fb9d34b05 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -2,7 +2,7 @@ #include #include "artiq_personality.h" -#include "rtio.h" +#include "rt2wb.h" #include "log.h" #include "dds.h" @@ -26,10 +26,7 @@ #endif #define DDS_WRITE(addr, data) do { \ - rtio_o_address_write(addr); \ - rtio_o_data_write(data); \ - rtio_o_timestamp_write(now); \ - rtio_write_and_process_status(now, CONFIG_RTIO_DDS_CHANNEL); \ + rt2wb_write(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ now += DURATION_WRITE; \ } while(0) @@ -37,8 +34,6 @@ void dds_init(long long int timestamp, int channel) { long long int now; - rtio_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); - now = timestamp - DURATION_INIT; #ifdef CONFIG_DDS_ONEHOT_SEL @@ -94,10 +89,10 @@ static void dds_set_one(long long int now, long long int ref_time, unsigned int { unsigned int channel_enc; - if(channel >= CONFIG_DDS_CHANNEL_COUNT) { - core_log("Attempted to set invalid DDS channel\n"); - return; - } + if(channel >= CONFIG_DDS_CHANNEL_COUNT) { + core_log("Attempted to set invalid DDS channel\n"); + return; + } #ifdef CONFIG_DDS_ONEHOT_SEL channel_enc = 1 << channel; #else @@ -190,7 +185,6 @@ void dds_batch_exit(void) if(!batch_mode) artiq_raise_from_c("DDSBatchError", "DDS batch error", 0, 0, 0); - rtio_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); /* + FUD time */ now = batch_ref_time - batch_count*(DURATION_PROGRAM + DURATION_WRITE); for(i=0;i= timestamp + duration) { - /* check empty flag again to prevent race condition. - * now we are sure that the time limit has been exceeded. - */ - if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("InternalError", - "RT2WB read failed on channel {0}", - channel, 0, 0); - } - /* input FIFO is empty - keep waiting */ - } + rtio_output(timestamp, channel, addr, 0); + rtio_input_wait(timestamp + duration, channel); data = rtio_i_data_read(); rtio_i_re_write(1); return data; diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h index f2cbe57d4..afae9a5a7 100644 --- a/artiq/runtime/rt2wb.h +++ b/artiq/runtime/rt2wb.h @@ -1,6 +1,8 @@ #ifndef __RT2WB_H #define __RT2WB_H +#include "rtio.h" + void rt2wb_write(long long int timestamp, int channel, int address, unsigned int data); unsigned int rt2wb_read_sync(long long int timestamp, int channel, int address, diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 76514d19a..1680dbb6f 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -39,6 +39,44 @@ void rtio_process_exceptional_status(int status, long long int timestamp, int ch } } + +void rtio_output(long long int timestamp, int channel, unsigned int addr, + unsigned int data) +{ + rtio_chan_sel_write(channel); + rtio_o_timestamp_write(timestamp); + rtio_o_address_write(addr); + rtio_o_data_write(data); + rtio_write_and_process_status(timestamp, channel); +} + + +void rtio_input_wait(long long int timeout, int channel) +{ + int status; + + rtio_chan_sel_write(channel); + while((status = rtio_i_status_read())) { + if(status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + } + if(rtio_get_counter() >= timeout) { + /* check empty flag again to prevent race condition. + * now we are sure that the time limit has been exceeded. + */ + if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) + artiq_raise_from_c("InternalError", + "RTIO input timeout on channel {0}", + channel, 0, 0); + } + /* input FIFO is empty - keep waiting */ + } +} + + void rtio_log_va(long long int timestamp, const char *fmt, va_list args) { // This executes on the kernel CPU's stack, which is specifically designed diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 87d8a9f37..1214b40c8 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -17,6 +17,9 @@ long long int rtio_get_counter(void); void rtio_process_exceptional_status(int status, long long int timestamp, int channel); void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); +void rtio_output(long long int timestamp, int channel, unsigned int address, + unsigned int data); +void rtio_input_wait(long long int timeout, int channel); static inline void rtio_write_and_process_status(long long int timestamp, int channel) { From ecedbbef4c735f342ab3ec2714638b9f70726a4f Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:20:07 +0100 Subject: [PATCH 047/125] runtime/ttl: use rtio_output and rtio_input_wait --- artiq/runtime/ttl.c | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c index db75207d4..bdd6d82b3 100644 --- a/artiq/runtime/ttl.c +++ b/artiq/runtime/ttl.c @@ -6,53 +6,24 @@ void ttl_set_o(long long int timestamp, int channel, int value) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_address_write(0); - rtio_o_data_write(value); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 0, value); } void ttl_set_oe(long long int timestamp, int channel, int oe) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_address_write(1); - rtio_o_data_write(oe); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 1, oe); } void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_address_write(2); - rtio_o_data_write(sensitivity); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 2, sensitivity); } long long int ttl_get(int channel, long long int time_limit) { long long int r; - int status; - rtio_chan_sel_write(channel); - while((status = rtio_i_status_read())) { - if(status & RTIO_I_STATUS_OVERFLOW) { - rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RTIO overflow at channel {0}", - channel, 0, 0); - } - if(rtio_get_counter() >= time_limit) { - /* check empty flag again to prevent race condition. - * now we are sure that the time limit has been exceeded. - */ - if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - return -1; - } - /* input FIFO is empty - keep waiting */ - } + rtio_input_wait(time_limit, channel); r = rtio_i_timestamp_read(); rtio_i_re_write(1); return r; @@ -60,8 +31,5 @@ long long int ttl_get(int channel, long long int time_limit) void ttl_clock_set(long long int timestamp, int channel, int ftw) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_data_write(ftw); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 0, ftw); } From 16537d347e094b5fd454df0d4c85187f81e80756 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:48:26 +0100 Subject: [PATCH 048/125] coredevice.spi: cleanup --- artiq/coredevice/spi.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index d6ebeb7db..330ddd168 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,4 +1,5 @@ -from artiq.language.core import * +from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, + delay_mu) from artiq.language.units import MHz from artiq.coredevice.rt2wb import rt2wb_write, rt2wb_read_sync @@ -83,6 +84,11 @@ class SPIMaster: SPI_RT2WB_READ, int(2*self.ref_period_mu)) @kernel - def get_config_sync(self): + def _get_config_sync(self): return rt2wb_read_sync(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, int(2*self.ref_period_mu)) + + @kernel + def _get_xfer_sync(self): + return rt2wb_read_sync(now_mu(), self.channel, SPI_XFER_ADDR | + SPI_RT2WB_READ, int(2*self.ref_period_mu)) From 6c899e6ba64ff83d9fb14ccbe048e14650d0e7c9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:49:15 +0100 Subject: [PATCH 049/125] runtime/rtio: fix rtio_input_wait(), add RTIOTimeout --- artiq/coredevice/__init__.py | 7 ++++--- artiq/coredevice/exceptions.py | 11 +++++++++++ artiq/runtime/rt2wb.c | 14 +++++++++++++- artiq/runtime/rtio.c | 14 ++++++-------- artiq/runtime/rtio.h | 2 +- artiq/runtime/ttl.c | 9 ++++++++- 6 files changed, 43 insertions(+), 14 deletions(-) diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index 76e782a3f..aa4ab5066 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,12 +1,13 @@ -from artiq.coredevice import exceptions, dds +from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, RTIOCollisionError, RTIOOverflow, - DDSBatchError, CacheError) + DDSBatchError, CacheError, + RTIOTimeout) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) __all__ = [] __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollisionError", - "RTIOOverflow", "DDSBatchError", "CacheError"] + "RTIOOverflow", "DDSBatchError", "CacheError", "RTIOTimeout"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 05d9d1c29..3b5dba547 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -108,6 +108,17 @@ class RTIOOverflow(Exception): """ artiq_builtin = True +class RTIOTimeout(Exception): + """Raised when an RTIO input operation does not complete within the expected + time. This is only raised by channels where a response is guaranteed, such + as RT2WB (DDS and SPI). + + This does not interrupt operations further than cancelling the current read + attempt. Reading can be reattempted after the exception is caught, and + events that have arrived in the meantime will be retrieved. + """ + artiq_builtin = True + class DDSBatchError(Exception): """Raised when attempting to start a DDS batch while already in a batch, or when too many commands are batched. diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 50f4fee55..30742ed24 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -16,8 +16,20 @@ unsigned int rt2wb_read_sync(long long int timestamp, int channel, int addr, int duration) { unsigned int data; + int status; + rtio_output(timestamp, channel, addr, 0); - rtio_input_wait(timestamp + duration, channel); + + status = rtio_input_wait(timestamp + duration, channel); + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RT2WB read overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + artiq_raise_from_c("RTIOTimeout", + "RT2WB read timeout on channel {0}", + channel, 0, 0); + data = rtio_i_data_read(); rtio_i_re_write(1); return data; diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 1680dbb6f..00eecbc2f 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -51,7 +51,7 @@ void rtio_output(long long int timestamp, int channel, unsigned int addr, } -void rtio_input_wait(long long int timeout, int channel) +int rtio_input_wait(long long int timeout, int channel) { int status; @@ -59,21 +59,19 @@ void rtio_input_wait(long long int timeout, int channel) while((status = rtio_i_status_read())) { if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RTIO input overflow on channel {0}", - channel, 0, 0); + break; } if(rtio_get_counter() >= timeout) { /* check empty flag again to prevent race condition. * now we are sure that the time limit has been exceeded. */ - if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("InternalError", - "RTIO input timeout on channel {0}", - channel, 0, 0); + status = rtio_i_status_read(); + if(status & RTIO_I_STATUS_EMPTY) + break; } /* input FIFO is empty - keep waiting */ } + return status; } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 1214b40c8..d311fbb55 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -19,7 +19,7 @@ void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); -void rtio_input_wait(long long int timeout, int channel); +int rtio_input_wait(long long int timeout, int channel); static inline void rtio_write_and_process_status(long long int timestamp, int channel) { diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c index bdd6d82b3..603eb2a8c 100644 --- a/artiq/runtime/ttl.c +++ b/artiq/runtime/ttl.c @@ -22,8 +22,15 @@ void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity) long long int ttl_get(int channel, long long int time_limit) { long long int r; + int status = rtio_input_wait(time_limit, channel); + + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + return -1; - rtio_input_wait(time_limit, channel); r = rtio_i_timestamp_read(); rtio_i_re_write(1); return r; From 6dd1eb2e928476fdc4c364fae26b8bc14c0fea54 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 20:45:41 +0100 Subject: [PATCH 050/125] artiq_flash: use term 'gateware' --- artiq/frontend/artiq_flash.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index 00e217bcf..b29128a35 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -6,7 +6,6 @@ import os import subprocess import tempfile -import artiq from artiq import __artiq_dir__ as artiq_dir from artiq.frontend.bit2bin import bit2bin @@ -18,13 +17,13 @@ def get_argparser(): epilog="""\ Valid actions: - * proxy: load the flash proxy bitstream - * bitstream: write bitstream to flash + * proxy: load the flash proxy gateware bitstream + * gateware: write gateware bitstream to flash * bios: write bios to flash * runtime: write runtime to flash * storage: write storage image to flash - * load: load bitstream into device (volatile but fast) - * start: trigger the target to (re)load its bitstream from flash + * load: load gateware bitstream into device (volatile but fast) + * start: trigger the target to (re)load its gateware bitstream from flash Prerequisites: @@ -42,7 +41,7 @@ Prerequisites: parser.add_argument("-f", "--storage", help="write file to storage area") parser.add_argument("-d", "--dir", help="look for files in this directory") parser.add_argument("ACTION", nargs="*", - default="proxy bitstream bios runtime start".split(), + default="proxy gateware bios runtime start".split(), help="actions to perform, default: %(default)s") return parser @@ -55,7 +54,7 @@ def main(): "kc705": { "chip": "xc7k325t", "start": "xc7_program xc7.tap", - "bitstream": 0x000000, + "gateware": 0x000000, "bios": 0xaf0000, "runtime": 0xb00000, "storage": 0xb80000, @@ -63,7 +62,7 @@ def main(): "pipistrello": { "chip": "xc6slx45", "start": "xc6s_program xc6s.tap", - "bitstream": 0x000000, + "gateware": 0x000000, "bios": 0x170000, "runtime": 0x180000, "storage": 0x200000, @@ -83,23 +82,23 @@ def main(): proxy_base = "bscan_spi_{}.bit".format(config["chip"]) proxy = None for p in [opts.dir, os.path.expanduser("~/.migen"), - "/usr/local/share/migen", "/usr/share/migen"]: + "/usr/local/share/migen", "/usr/share/migen"]: proxy_ = os.path.join(p, proxy_base) if os.access(proxy_, os.R_OK): proxy = "jtagspi_init 0 {}".format(proxy_) break if not proxy: raise SystemExit( - "proxy bitstream {} not found".format(proxy_base)) + "proxy gateware bitstream {} not found".format(proxy_base)) prog.append(proxy) - elif action == "bitstream": + elif action == "gateware": bin = os.path.join(opts.dir, "top.bin") if not os.access(bin, os.R_OK): bin = tempfile.mkstemp()[1] bit = os.path.join(opts.dir, "top.bit") conv = True prog.append("jtagspi_program {} 0x{:x}".format( - bin, config["bitstream"])) + bin, config["gateware"])) elif action == "bios": prog.append("jtagspi_program {} 0x{:x}".format( os.path.join(opts.dir, "bios.bin"), config["bios"])) From 8fa98f64860ea14c9d747d4e5d5c5a57ab1c11f9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 20:50:45 +0100 Subject: [PATCH 051/125] doc: use term 'gateware' FPGA newcomers are not used to the term 'bitstream'. To insist that this file is the result of the gateware compilation and thus the binary FPGA format, add the term 'gateware' as a prefix. --- doc/manual/installing.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 33246d27f..43f7540dd 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -93,11 +93,11 @@ Preparing the core device FPGA board You now need to flash 3 things on the FPGA board: -1. The FPGA bitstream +1. The FPGA gateware bitstream 2. The BIOS 3. The ARTIQ runtime -They are all shipped in our Conda packages, along with the required flash proxy bitstreams. +They are all shipped in our Conda packages, along with the required flash proxy gateware bitstreams. First you need to install OpenOCD. Then, you can flash the board: @@ -169,11 +169,11 @@ and the ARTIQ kernels. Preparing the core device FPGA board ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -These steps are required to generate bitstream (``.bit``) files, build the MiSoC BIOS and ARTIQ runtime, and flash FPGA boards. If the board is already flashed, you may skip those steps and go directly to `Installing the host-side software`. +These steps are required to generate gateware bitstream (``.bit``) files, build the MiSoC BIOS and ARTIQ runtime, and flash FPGA boards. If the board is already flashed, you may skip those steps and go directly to `Installing the host-side software`. * Install the FPGA vendor tools (e.g. Xilinx ISE and/or Vivado): - * Get Xilinx tools from http://www.xilinx.com/support/download/index.htm. ISE can build bitstreams both for boards using the Spartan-6 (Pipistrello) and 7-series devices (KC705), while Vivado supports only boards using 7-series devices. + * Get Xilinx tools from http://www.xilinx.com/support/download/index.htm. ISE can build gateware bitstreams both for boards using the Spartan-6 (Pipistrello) and 7-series devices (KC705), while Vivado supports only boards using 7-series devices. * The Pipistrello is supported by Webpack, the KC705 is not. @@ -208,9 +208,9 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC .. _install-flash-proxy: -* Install the required flash proxy bitstreams: +* Install the required flash proxy gateware bitstreams: - The purpose of the flash proxy bitstream is to give programming software fast JTAG access to the flash connected to the FPGA. + The purpose of the flash proxy gateware bitstream is to give programming software fast JTAG access to the flash connected to the FPGA. * Pipistrello and KC705: @@ -243,7 +243,7 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC :ref:`installing the host-side software `. -* Build the bitstream, BIOS and runtime by running: +* Build the gateware bitstream, BIOS and runtime by running: :: $ cd ~/artiq-dev @@ -270,7 +270,7 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC .. note:: The `-t` option specifies the board your are targeting. Available options are ``kc705`` and ``pipistrello``. -* Check that the board boots by running a serial terminal program (you may need to press its FPGA reconfiguration button or power-cycle it to load the bitstream that was newly written into the flash): :: +* Check that the board boots by running a serial terminal program (you may need to press its FPGA reconfiguration button or power-cycle it to load the gateware bitstream that was newly written into the flash): :: $ make -C ~/artiq-dev/misoc/tools # do only once $ ~/artiq-dev/misoc/tools/flterm --port /dev/ttyUSB1 From 7ef21f03b98e5af196f080a19456500a75fc9aeb Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:19:39 +0100 Subject: [PATCH 052/125] nist_clock: add SPIMasters to spi buses --- artiq/gateware/targets/kc705.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 39a8ae3b2..5213adaae 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -250,18 +250,24 @@ class NIST_CLOCK(_NIST_Ions): self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) - spi_pins = self.platform.request("ams101_dac", 0) - phy = ttl_simple.Output(spi_pins.ldac) + ams101_dac = self.platform.request("ams101_dac", 0) + phy = ttl_simple.Output(ams101_dac.ldac) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) - phy = spi.SPIMaster(spi_pins) + phy = spi.SPIMaster(ams101_dac) self.submodules += phy self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) + for i in range(3): + phy = spi.SPIMaster(self.platform.request("spi", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy( + phy, ofifo_depth=128, ififo_depth=128)) + phy = ttl_simple.ClockGen(platform.request("la32_p")) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) From 12252abc8f97765afe2b91709d1c36ef6cc10a42 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:21:18 +0100 Subject: [PATCH 053/125] nist_clock: rename spi*.ce to spi*.cs_n --- artiq/gateware/nist_clock.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/nist_clock.py b/artiq/gateware/nist_clock.py index 3b621ac41..808f5abb9 100644 --- a/artiq/gateware/nist_clock.py +++ b/artiq/gateware/nist_clock.py @@ -53,21 +53,21 @@ fmc_adapter_io = [ ("spi", 0, Subsignal("clk", Pins("LPC:LA13_N")), - Subsignal("ce", Pins("LPC:LA14_N")), + Subsignal("cs_n", Pins("LPC:LA14_N")), Subsignal("mosi", Pins("LPC:LA17_CC_P")), Subsignal("miso", Pins("LPC:LA17_CC_N")), IOStandard("LVTTL")), ("spi", 1, Subsignal("clk", Pins("LPC:LA23_N")), - Subsignal("ce", Pins("LPC:LA23_P")), + Subsignal("cs_n", Pins("LPC:LA23_P")), Subsignal("mosi", Pins("LPC:LA18_CC_N")), Subsignal("miso", Pins("LPC:LA18_CC_P")), IOStandard("LVTTL")), ("spi", 2, Subsignal("clk", Pins("LPC:LA27_P")), - Subsignal("ce", Pins("LPC:LA26_P")), + Subsignal("cs_n", Pins("LPC:LA26_P")), Subsignal("mosi", Pins("LPC:LA27_N")), Subsignal("miso", Pins("LPC:LA26_N")), IOStandard("LVTTL")), From da22ec73dfcd82e6d4b06235d1a7983c36a817b5 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:22:08 +0100 Subject: [PATCH 054/125] gateware.spi: rework wb bus sequence --- artiq/gateware/spi.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index fd8f612c4..5e2e5541a 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -292,14 +292,12 @@ class SPIMaster(Module): data_width, clock_width=len(config.div_read), bits_width=len(xfer.read_length)) - wb_we = Signal() pending = Signal() cs = Signal.like(xfer.cs) data_read = Signal.like(spi.reg.data) data_write = Signal.like(spi.reg.data) self.comb += [ - wb_we.eq(bus.cyc & bus.stb & bus.we & (~pending | spi.done)), bus.dat_r.eq( Array([data_read, xfer.raw_bits(), config.raw_bits() ])[bus.adr]), @@ -310,13 +308,6 @@ class SPIMaster(Module): spi.div_read.eq(config.div_read), ] self.sync += [ - bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), - If(wb_we, - Array([data_write, xfer.raw_bits(), config.raw_bits() - ])[bus.adr].eq(bus.dat_w) - ), - config.active.eq(spi.cs), - config.pending.eq(pending), If(spi.done, data_read.eq(spi.reg.data), ), @@ -327,9 +318,19 @@ class SPIMaster(Module): spi.reg.data.eq(data_write), pending.eq(0), ), - If(wb_we & (bus.adr == 0), # data register - pending.eq(1), + bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), + If(bus.ack, + bus.ack.eq(0), ), + If(bus.we & bus.ack, + Array([data_write, xfer.raw_bits(), config.raw_bits() + ])[bus.adr].eq(bus.dat_w), + If(bus.adr == 0, # data register + pending.eq(1), + ), + ), + config.active.eq(spi.cs), + config.pending.eq(pending), ] # I/O From 764795a8fe7fbeab94d4688a7b1d43cbea71f944 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:32:53 +0100 Subject: [PATCH 055/125] examples: update device_db for nist_clock spi --- examples/master/device_db.pyon | 47 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index 4dbb92ab7..a78c74eba 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -74,14 +74,6 @@ "class": "TTLInOut", "arguments": {"channel": 18} }, - "ttl_clock_la32_p": { - "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLClockGen", - "arguments": {"channel": 20} - }, - - "led": { "type": "local", "module": "artiq.coredevice.ttl", @@ -89,6 +81,45 @@ "arguments": {"channel": 19} }, + "ams101_ldac": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 20} + }, + "ams101_spi": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 21} + }, + + "spi0": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 22} + }, + "spi1": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 23} + }, + "spi2": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 24} + }, + + "ttl_clock_la32_p": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLClockGen", + "arguments": {"channel": 25} + }, + "dds_bus": { "type": "local", "module": "artiq.coredevice.dds", From 7d7a710a561b77618d7a321c64ca77ea5a868441 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 00:21:32 +0100 Subject: [PATCH 056/125] runtime/rt2wb: use input/output terminology and add (async) input --- artiq/coredevice/rt2wb.py | 12 ++++++--- artiq/coredevice/spi.py | 57 ++++++++++++++++++++++----------------- artiq/runtime/dds.c | 2 +- artiq/runtime/ksupport.c | 5 ++-- artiq/runtime/rt2wb.c | 39 ++++++++++++++++++++------- artiq/runtime/rt2wb.h | 6 ++--- 6 files changed, 77 insertions(+), 44 deletions(-) diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py index 150afd1fb..80d1cb041 100644 --- a/artiq/coredevice/rt2wb.py +++ b/artiq/coredevice/rt2wb.py @@ -3,12 +3,16 @@ from artiq.language.types import * @syscall -def rt2wb_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 - ) -> TNone: +def rt2wb_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: raise NotImplementedError("syscall not simulated") @syscall -def rt2wb_read_sync(time_mu: TInt64, channel: TInt32, addr: TInt32, - duration_mu: TInt32) -> TInt32: +def rt2wb_input(channel: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") + + +@syscall +def rt2wb_input_sync(timeout_mu: TInt64, channel: TInt32) -> TInt32: raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 330ddd168..c0daba932 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,7 +1,7 @@ from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, - delay_mu) + delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rt2wb import rt2wb_write, rt2wb_read_sync +from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input, rt2wb_input_sync SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -28,11 +28,11 @@ class SPIMaster: def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") self.ref_period = ref_period - self.ref_period_mu = seconds_to_mu(ref_period, self.core) + self.ref_period_mu = int(seconds_to_mu(ref_period, self.core)) self.channel = channel - self.write_period_mu = 0 - self.read_period_mu = 0 - self.xfer_period_mu = 0 + self.write_period_mu = int(0) + self.read_period_mu = int(0) + self.xfer_period_mu = int(0) # A full transfer takes write_period_mu + xfer_period_mu. # Chained transfers can happen every xfer_period_mu. # The second transfer of a chain can be written 2*ref_period_mu @@ -49,11 +49,11 @@ class SPIMaster: @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): - rt2wb_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 16) | ((read_div - 2) << 24)) + rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 16) | ((read_div - 2) << 24)) self.write_period_mu = int(write_div*self.ref_period_mu) self.read_period_mu = int(read_div*self.ref_period_mu) - delay_mu(2*self.ref_period_mu) + delay_mu(3*self.ref_period_mu) @portable def get_xfer_period_mu(self, write_length, read_length): @@ -62,33 +62,40 @@ class SPIMaster: @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): - rt2wb_write(now_mu(), self.channel, SPI_XFER_ADDR, - chip_select | (write_length << 16) | (read_length << 24)) - self.xfer_period_mu = self.get_xfer_period_mu( - write_length, read_length) - delay_mu(int(2*self.ref_period_mu)) + rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) + self.xfer_period_mu = self.get_xfer_period_mu(write_length, + read_length) + delay_mu(3*self.ref_period_mu) @kernel def write(self, data): - rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(int(self.write_period_mu + self.xfer_period_mu)) + rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR, data) + delay_mu(3*self.ref_period_mu) @kernel - def read_async(self): - rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) - delay_mu(int(2*self.ref_period_mu)) + def read(self): + rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + delay_mu(3*self.ref_period_mu) + + @kernel + def input(self): + return rt2wb_input(self.channel) + + @kernel + def _rt2wb_read_sync(self, addr=0): + t = now_mu() + rt2wb_output(t, self.channel, addr | SPI_RT2WB_READ, 0) + return rt2wb_input_sync(t + 3*self.ref_period_mu, self.channel) @kernel def read_sync(self): - return rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | - SPI_RT2WB_READ, int(2*self.ref_period_mu)) + return self._rt2wb_read_sync(SPI_DATA_ADDR) @kernel def _get_config_sync(self): - return rt2wb_read_sync(now_mu(), self.channel, SPI_CONFIG_ADDR | - SPI_RT2WB_READ, int(2*self.ref_period_mu)) + return self._rt2wb_read_sync(SPI_CONFIG_ADDR) @kernel def _get_xfer_sync(self): - return rt2wb_read_sync(now_mu(), self.channel, SPI_XFER_ADDR | - SPI_RT2WB_READ, int(2*self.ref_period_mu)) + return self._rt2wb_read_sync(SPI_XFER_ADDR) diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index fb9d34b05..f76da5724 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -26,7 +26,7 @@ #endif #define DDS_WRITE(addr, data) do { \ - rt2wb_write(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ + rt2wb_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ now += DURATION_WRITE; \ } while(0) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 892f66ccb..a95744450 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -122,8 +122,9 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"rt2wb_write", &rt2wb_write}, - {"rt2wb_read_sync", &rt2wb_read_sync}, + {"rt2wb_output", &rt2wb_output}, + {"rt2wb_input", &rt2wb_input}, + {"rt2wb_input_sync", &rt2wb_input_sync}, {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 30742ed24..3956028db 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -5,29 +5,50 @@ #include "rt2wb.h" -void rt2wb_write(long long int timestamp, int channel, int addr, +void rt2wb_output(long long int timestamp, int channel, int addr, unsigned int data) { rtio_output(timestamp, channel, addr, data); } -unsigned int rt2wb_read_sync(long long int timestamp, int channel, int addr, - int duration) +unsigned int rt2wb_input(int channel) { unsigned int data; int status; - rtio_output(timestamp, channel, addr, 0); - - status = rtio_input_wait(timestamp + duration, channel); - if (status & RTIO_I_STATUS_OVERFLOW) + rtio_chan_sel_write(channel); + status = rtio_i_status_read(); + if (status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", - "RT2WB read overflow on channel {0}", + "RT2WB input overflow on channel {0}", channel, 0, 0); + } if (status & RTIO_I_STATUS_EMPTY) artiq_raise_from_c("RTIOTimeout", - "RT2WB read timeout on channel {0}", + "RT2WB input timeout on channel {0}", + channel, 0, 0); + + data = rtio_i_data_read(); + rtio_i_re_write(1); + return data; +} + + +unsigned int rt2wb_input_sync(long long int timeout, int channel) +{ + unsigned int data; + int status; + + status = rtio_input_wait(timeout, channel); + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RT2WB input overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + artiq_raise_from_c("RTIOTimeout", + "RT2WB input timeout on channel {0}", channel, 0, 0); data = rtio_i_data_read(); diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h index afae9a5a7..d421397f4 100644 --- a/artiq/runtime/rt2wb.h +++ b/artiq/runtime/rt2wb.h @@ -3,10 +3,10 @@ #include "rtio.h" -void rt2wb_write(long long int timestamp, int channel, int address, +void rt2wb_output(long long int timestamp, int channel, int addr, unsigned int data); -unsigned int rt2wb_read_sync(long long int timestamp, int channel, int address, - int duration); +unsigned int rt2wb_input(int channel); +unsigned int rt2wb_input_sync(long long int timeout, int channel); #endif /* __RT2WB_H */ From f2ec8692c059f8581943c47fddaeeb8362445824 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 01:52:46 +0100 Subject: [PATCH 057/125] nist_clock: disable spi1/2 --- artiq/gateware/targets/kc705.py | 2 +- examples/master/device_db.pyon | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 5213adaae..5872e0f5f 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -262,7 +262,7 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) - for i in range(3): + for i in range(1): # spi1 and spi2 collide in pinout with ttl phy = spi.SPIMaster(self.platform.request("spi", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy( diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index a78c74eba..5debbc7d3 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -100,24 +100,12 @@ "class": "SPIMaster", "arguments": {"channel": 22} }, - "spi1": { - "type": "local", - "module": "artiq.coredevice.spi", - "class": "SPIMaster", - "arguments": {"channel": 23} - }, - "spi2": { - "type": "local", - "module": "artiq.coredevice.spi", - "class": "SPIMaster", - "arguments": {"channel": 24} - }, "ttl_clock_la32_p": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLClockGen", - "arguments": {"channel": 25} + "arguments": {"channel": 23} }, "dds_bus": { From dc70029b910ff19da6f0b92e0ebf71a013288721 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Mar 2016 05:22:12 +0000 Subject: [PATCH 058/125] transforms.asttyped_rewriter: set loc for ForT (fixes #302). --- artiq/compiler/transforms/asttyped_rewriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/asttyped_rewriter.py b/artiq/compiler/transforms/asttyped_rewriter.py index 70790a613..43455143f 100644 --- a/artiq/compiler/transforms/asttyped_rewriter.py +++ b/artiq/compiler/transforms/asttyped_rewriter.py @@ -477,7 +477,7 @@ class ASTTypedRewriter(algorithm.Transformer): target=node.target, iter=node.iter, body=node.body, orelse=node.orelse, trip_count=None, trip_interval=None, keyword_loc=node.keyword_loc, in_loc=node.in_loc, for_colon_loc=node.for_colon_loc, - else_loc=node.else_loc, else_colon_loc=node.else_colon_loc) + else_loc=node.else_loc, else_colon_loc=node.else_colon_loc, loc=node.loc) return node def visit_withitem(self, node): From b0526c3354d63de9f5f7503e287e9a5e4ec361cb Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Mar 2016 14:49:04 +0800 Subject: [PATCH 059/125] protocols/pipe_ipc: fix resource leak on Windows --- artiq/protocols/pipe_ipc.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/artiq/protocols/pipe_ipc.py b/artiq/protocols/pipe_ipc.py index 4ac416a09..c32f74b8f 100644 --- a/artiq/protocols/pipe_ipc.py +++ b/artiq/protocols/pipe_ipc.py @@ -109,7 +109,6 @@ else: # windows # mode or not. self.address = "\\\\.\\pipe\\artiq-{}-{}".format(os.getpid(), next(_pipe_count)) - self.server = None self.ready = asyncio.Event() self.write_buffer = b"" @@ -118,11 +117,12 @@ else: # windows async def _autoclose(self): await self.process.wait() - if self.server is not None: - self.server[0].close() - self.server = None + self.server[0].close() + del self.server if self.ready.is_set(): self.writer.close() + del self.reader + del self.writer async def create_subprocess(self, *args, **kwargs): loop = asyncio.get_event_loop() @@ -150,8 +150,12 @@ else: # windows # There is still a race condition in the AsyncioParentComm # creation/destruction, but it is unlikely to cause problems # in most practical cases. - assert self.server is not None - self.server = None + if self.ready.is_set(): + # A child already connected before. We should have shut down + # the server, but asyncio won't let us do that. + # Drop connections immediately instead. + writer.close() + return self.reader = reader self.writer = writer if self.write_buffer: From c7d48a1765e17366405a2d9ba68829b9f1555c2d Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Mar 2016 19:03:10 +0800 Subject: [PATCH 060/125] coredevice/TTLOut: add dummy output function --- artiq/coredevice/ttl.py | 4 ++++ artiq/test/coredevice/test_rtio.py | 1 + 2 files changed, 5 insertions(+) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 91c8c5531..28248a820 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -37,6 +37,10 @@ class TTLOut: # in RTIO cycles self.o_previous_timestamp = int(0, width=64) + @kernel + def output(self): + pass + @kernel def set_o(self, o): ttl_set_o(now_mu(), self.channel, o) diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index ff50bf51c..50958392f 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -8,6 +8,7 @@ from math import sqrt from artiq.experiment import * from artiq.test.hardware_testbench import ExperimentCase + artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY") From 7e16da4a7783bfea2e866d7a9f023531726b489d Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Mar 2016 12:26:18 +0000 Subject: [PATCH 061/125] transforms.llvm_ir_generator: ignore assignments of None (fixes #309). --- artiq/compiler/transforms/llvm_ir_generator.py | 5 ++++- artiq/test/lit/codegen/assign_none.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 artiq/test/lit/codegen/assign_none.py diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 0c0d532cd..c59e16689 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -655,8 +655,11 @@ class LLVMIRGenerator: def process_SetLocal(self, insn): env = insn.environment() - llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name) llvalue = self.map(insn.value()) + if isinstance(llvalue.type, ll.VoidType): + # We store NoneType as {} but return it as void. So, bail out here. + return ll.Constant(ll.LiteralStructType([]), []) + llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name) if isinstance(llvalue, ll.Block): llvalue = ll.BlockAddress(self.llfunction, llvalue) if llptr.type.pointee != llvalue.type: diff --git a/artiq/test/lit/codegen/assign_none.py b/artiq/test/lit/codegen/assign_none.py new file mode 100644 index 000000000..000e87ac7 --- /dev/null +++ b/artiq/test/lit/codegen/assign_none.py @@ -0,0 +1,6 @@ +# RUN: %python -m artiq.compiler.testbench.llvmgen %s + +def f(): + pass +def g(): + a = f() From c2fe9a08ae1dffa77a2ad9392c94de3bf78457e5 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 14:14:38 +0100 Subject: [PATCH 062/125] gateware.spi: delay only writes to data register, update doc --- artiq/gateware/spi.py | 59 +++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 5e2e5541a..e476adbb9 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -205,12 +205,15 @@ class SPIMaster(Module): Transfers submitted this way are chained and executed without deasserting cs. Once a transfer completes, the previous transfer's read data is available in the data register. - * A wishbone transaction is ack-ed when the transfer has been written - to the intermediate buffer. It will be started when there are no - other transactions being executed. Writes take one cycle when - there is either no transfer being executed, no data in the - intermediate buffer, or a transfer just completing. Reads always - finish in one cycle. + * Writes to the config register take effect immediately. Writes to xfer + and data are synchronized to the start of a transfer. + * A wishbone data register write is ack-ed when the transfer has + been written to the intermediate buffer. It will be started when + there are no other transactions being executed, either starting + a new SPI transfer of chained to an in-flight transfer. + Writes take two cycles unless the write is to the data register + and another chained transfer is pending and the transfer being + executed is not complete. Reads always finish in two cycles. Transaction Sequence: * If desired, write the config register to set up the core. @@ -218,16 +221,17 @@ class SPIMaster(Module): * Write the data register (also for zero-length writes), writing triggers the transfer and when the transfer is accepted to the inermediate buffer, the write is ack-ed. - * If desired, read the data register. - * If desired, write data for the next, chained, transfer. + * If desired, read the data register corresponding to the last + completed transfer. + * If desired, change xfer register for the next transfer. + * If desired, write data queuing the next (possibly chained) transfer. Register address and bit map: config (address 2): 1 offline: all pins high-z (reset=1) 1 active: cs/transfer active (read-only) - 1 pending: transfer pending in intermediate buffer, bus writes will - block (read-only) + 1 pending: transfer pending in intermediate buffer (read-only) 1 cs_polarity: active level of chip select (reset=0) 1 clk_polarity: idle level of clk (reset=0) 1 clk_phase: first edge after cs assertion to sample data on (reset=0) @@ -246,18 +250,18 @@ class SPIMaster(Module): 8 div_read: ditto for the read clock xfer (address 1): - 16 cs: active high bit mask of chip selects to assert - 6 write_len: 0-M bits + 16 cs: active high bit mask of chip selects to assert (reset=0) + 6 write_len: 0-M bits (reset=0) 2 undefined - 6 read_len: 0-M bits + 6 read_len: 0-M bits (reset=0) 2 undefined data (address 0): - M write/read data + M write/read data (reset=0) """ - def __init__(self, pads, bus=None, data_width=32): + def __init__(self, pads, bus=None): if bus is None: - bus = wishbone.Interface(data_width=data_width) + bus = wishbone.Interface(data_width=32) self.bus = bus ### @@ -289,7 +293,8 @@ class SPIMaster(Module): assert len(xfer) <= len(bus.dat_w) self.submodules.spi = spi = SPIMachine( - data_width, clock_width=len(config.div_read), + data_width=len(bus.dat_w), + clock_width=len(config.div_read), bits_width=len(xfer.read_length)) pending = Signal() @@ -318,15 +323,21 @@ class SPIMaster(Module): spi.reg.data.eq(data_write), pending.eq(0), ), - bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), + # wb.ack a transaction if any of the following: + # a) reading, + # b) writing to non-data register + # c) writing to data register and no pending transfer + # d) writing to data register and pending and swapping buffers + bus.ack.eq(bus.cyc & bus.stb & + (~bus.we | (bus.adr != 0) | ~pending | spi.done)), If(bus.ack, bus.ack.eq(0), - ), - If(bus.we & bus.ack, - Array([data_write, xfer.raw_bits(), config.raw_bits() - ])[bus.adr].eq(bus.dat_w), - If(bus.adr == 0, # data register - pending.eq(1), + If(bus.we, + Array([data_write, xfer.raw_bits(), config.raw_bits() + ])[bus.adr].eq(bus.dat_w), + If(bus.adr == 0, # data register + pending.eq(1), + ), ), ), config.active.eq(spi.cs), From 324660ab407f6900cd7845b6967e18cb4ef1f739 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 14:44:07 +0100 Subject: [PATCH 063/125] rt2wb, exceptions: remove RTIOTimeout Assume that rt2wb transactions either collide and are then reported (https://github.com/m-labs/artiq/issues/308) or that they complete and the delay with which they complete does not matter. If a transaction is ack'ed with a delay because the WB core's downstream logic is busy, that may lead to a later collision with another WB transaction. --- artiq/coredevice/__init__.py | 5 ++--- artiq/coredevice/exceptions.py | 11 ---------- artiq/coredevice/rt2wb.py | 5 ----- artiq/coredevice/spi.py | 39 +++++++++++++++------------------- artiq/runtime/ksupport.c | 1 - artiq/runtime/rt2wb.c | 38 ++++++--------------------------- 6 files changed, 26 insertions(+), 73 deletions(-) diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index aa4ab5066..88de8cf7c 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,13 +1,12 @@ from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, RTIOCollisionError, RTIOOverflow, - DDSBatchError, CacheError, - RTIOTimeout) + DDSBatchError, CacheError) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) __all__ = [] __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollisionError", - "RTIOOverflow", "DDSBatchError", "CacheError", "RTIOTimeout"] + "RTIOOverflow", "DDSBatchError", "CacheError"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 3b5dba547..05d9d1c29 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -108,17 +108,6 @@ class RTIOOverflow(Exception): """ artiq_builtin = True -class RTIOTimeout(Exception): - """Raised when an RTIO input operation does not complete within the expected - time. This is only raised by channels where a response is guaranteed, such - as RT2WB (DDS and SPI). - - This does not interrupt operations further than cancelling the current read - attempt. Reading can be reattempted after the exception is caught, and - events that have arrived in the meantime will be retrieved. - """ - artiq_builtin = True - class DDSBatchError(Exception): """Raised when attempting to start a DDS batch while already in a batch, or when too many commands are batched. diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py index 80d1cb041..5bc3e6f7a 100644 --- a/artiq/coredevice/rt2wb.py +++ b/artiq/coredevice/rt2wb.py @@ -11,8 +11,3 @@ def rt2wb_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 @syscall def rt2wb_input(channel: TInt32) -> TInt32: raise NotImplementedError("syscall not simulated") - - -@syscall -def rt2wb_input_sync(timeout_mu: TInt64, channel: TInt32) -> TInt32: - raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index c0daba932..c28cf828d 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,7 +1,7 @@ from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input, rt2wb_input_sync +from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -55,17 +55,12 @@ class SPIMaster: self.read_period_mu = int(read_div*self.ref_period_mu) delay_mu(3*self.ref_period_mu) - @portable - def get_xfer_period_mu(self, write_length, read_length): - return int(write_length*self.write_period_mu + - read_length*self.read_period_mu) - @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR, chip_select | (write_length << 16) | (read_length << 24)) - self.xfer_period_mu = self.get_xfer_period_mu(write_length, - read_length) + self.xfer_period_mu = int(write_length*self.write_period_mu + + read_length*self.read_period_mu) delay_mu(3*self.ref_period_mu) @kernel @@ -74,28 +69,28 @@ class SPIMaster: delay_mu(3*self.ref_period_mu) @kernel - def read(self): + def read_async(self): + # every read_async() must be matched by an input_async() rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) delay_mu(3*self.ref_period_mu) @kernel - def input(self): + def input_async(self): + # matches the preeeding read_async() return rt2wb_input(self.channel) - @kernel - def _rt2wb_read_sync(self, addr=0): - t = now_mu() - rt2wb_output(t, self.channel, addr | SPI_RT2WB_READ, 0) - return rt2wb_input_sync(t + 3*self.ref_period_mu, self.channel) - @kernel def read_sync(self): - return self._rt2wb_read_sync(SPI_DATA_ADDR) - - @kernel - def _get_config_sync(self): - return self._rt2wb_read_sync(SPI_CONFIG_ADDR) + rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + return rt2wb_input(self.channel) @kernel def _get_xfer_sync(self): - return self._rt2wb_read_sync(SPI_XFER_ADDR) + rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0) + return rt2wb_input(self.channel) + + @kernel + def _get_config_sync(self): + rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, + 0) + return rt2wb_input(self.channel) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index a95744450..e15352bb5 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -124,7 +124,6 @@ static const struct symbol runtime_exports[] = { {"rt2wb_output", &rt2wb_output}, {"rt2wb_input", &rt2wb_input}, - {"rt2wb_input_sync", &rt2wb_input_sync}, {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 3956028db..6760149c4 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -18,38 +18,14 @@ unsigned int rt2wb_input(int channel) int status; rtio_chan_sel_write(channel); - status = rtio_i_status_read(); - if (status & RTIO_I_STATUS_OVERFLOW) { - rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RT2WB input overflow on channel {0}", - channel, 0, 0); + while((status = rtio_i_status_read())) { + if(status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RT2WB input overflow on channel {0}", + channel, 0, 0); + } } - if (status & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("RTIOTimeout", - "RT2WB input timeout on channel {0}", - channel, 0, 0); - - data = rtio_i_data_read(); - rtio_i_re_write(1); - return data; -} - - -unsigned int rt2wb_input_sync(long long int timeout, int channel) -{ - unsigned int data; - int status; - - status = rtio_input_wait(timeout, channel); - if (status & RTIO_I_STATUS_OVERFLOW) - artiq_raise_from_c("RTIOOverflow", - "RT2WB input overflow on channel {0}", - channel, 0, 0); - if (status & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("RTIOTimeout", - "RT2WB input timeout on channel {0}", - channel, 0, 0); data = rtio_i_data_read(); rtio_i_re_write(1); From 29776fae3f171afa221fffffd36c20ecb933563a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 15:38:40 +0100 Subject: [PATCH 064/125] coredevice.spi: unused import --- artiq/coredevice/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index c28cf828d..5b0c6bb88 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,4 +1,4 @@ -from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, +from artiq.language.core import (kernel, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input From aa10791ddf56fdcb36d3fb452a3b9e2008abc4c4 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 15:40:35 +0100 Subject: [PATCH 065/125] rtio: rm rtio_write_and_process_status --- artiq/runtime/rtio.c | 7 ++++++- artiq/runtime/rtio.h | 10 ---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 00eecbc2f..0670a4e2b 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -43,11 +43,16 @@ void rtio_process_exceptional_status(int status, long long int timestamp, int ch void rtio_output(long long int timestamp, int channel, unsigned int addr, unsigned int data) { + int status; + rtio_chan_sel_write(channel); rtio_o_timestamp_write(timestamp); rtio_o_address_write(addr); rtio_o_data_write(data); - rtio_write_and_process_status(timestamp, channel); + rtio_o_we_write(1); + status = rtio_o_status_read(); + if(status) + rtio_process_exceptional_status(status, timestamp, channel); } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index d311fbb55..9cebb6f2f 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -21,14 +21,4 @@ void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); int rtio_input_wait(long long int timeout, int channel); -static inline void rtio_write_and_process_status(long long int timestamp, int channel) -{ - int status; - - rtio_o_we_write(1); - status = rtio_o_status_read(); - if(status) - rtio_process_exceptional_status(status, timestamp, channel); -} - #endif /* __RTIO_H */ From 8adef1278133bcda56b55ac82bfd1d30d2dd3dd9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 16:34:05 +0100 Subject: [PATCH 066/125] runtime: refactor ttl*() * remove rt2wb_output * remove ttl_*() ttl.c ttl.h * use rtio_output() and rtio_input_timestamp() * adapt coredevice/compiler layer * adapt bridge to not artiq_raise_from_c() --- artiq/coredevice/rt2wb.py | 6 ------ artiq/coredevice/rtio.py | 13 ++++++++++++ artiq/coredevice/spi.py | 3 ++- artiq/coredevice/ttl.py | 36 ++++++++------------------------- artiq/runtime/Makefile | 2 +- artiq/runtime/bridge.c | 14 ++++++++----- artiq/runtime/dds.c | 4 ++-- artiq/runtime/ksupport.c | 10 ++-------- artiq/runtime/rt2wb.c | 7 ------- artiq/runtime/rt2wb.h | 3 --- artiq/runtime/rtio.c | 15 ++++++++++++-- artiq/runtime/rtio.h | 2 +- artiq/runtime/ttl.c | 42 --------------------------------------- artiq/runtime/ttl.h | 10 ---------- 14 files changed, 51 insertions(+), 116 deletions(-) create mode 100644 artiq/coredevice/rtio.py delete mode 100644 artiq/runtime/ttl.c delete mode 100644 artiq/runtime/ttl.h diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py index 5bc3e6f7a..318d8bf0e 100644 --- a/artiq/coredevice/rt2wb.py +++ b/artiq/coredevice/rt2wb.py @@ -2,12 +2,6 @@ from artiq.language.core import * from artiq.language.types import * -@syscall -def rt2wb_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 - ) -> TNone: - raise NotImplementedError("syscall not simulated") - - @syscall def rt2wb_input(channel: TInt32) -> TInt32: raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/rtio.py b/artiq/coredevice/rtio.py new file mode 100644 index 000000000..194818507 --- /dev/null +++ b/artiq/coredevice/rtio.py @@ -0,0 +1,13 @@ +from artiq.language.core import syscall +from artiq.language.types import TInt64, TInt32, TNone + + +@syscall +def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64: + raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 5b0c6bb88..5998997e1 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,7 +1,8 @@ from artiq.language.core import (kernel, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input +from artiq.coredevice.rtio import rtio_output as rt2wb_output +from artiq.coredevice.rt2wb import rt2wb_input SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 91c8c5531..1b3a6eecd 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -1,26 +1,6 @@ from artiq.language.core import * from artiq.language.types import * - - -@syscall -def ttl_set_o(time_mu: TInt64, channel: TInt32, enabled: TBool) -> TNone: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_set_oe(time_mu: TInt64, channel: TInt32, enabled: TBool) -> TNone: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_set_sensitivity(time_mu: TInt64, channel: TInt32, sensitivity: TInt32) -> TNone: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_get(channel: TInt32, time_limit_mu: TInt64) -> TInt64: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_clock_set(time_mu: TInt64, channel: TInt32, ftw: TInt32) -> TNone: - raise NotImplementedError("syscall not simulated") +from artiq.coredevice.rtio import rtio_output, rtio_input_timestamp class TTLOut: @@ -39,7 +19,7 @@ class TTLOut: @kernel def set_o(self, o): - ttl_set_o(now_mu(), self.channel, o) + rtio_output(now_mu(), self.channel, 0, 1 if o else 0) self.o_previous_timestamp = now_mu() @kernel @@ -108,7 +88,7 @@ class TTLInOut: @kernel def set_oe(self, oe): - ttl_set_oe(now_mu(), self.channel, oe) + rtio_output(now_mu(), self.channel, 1, 1 if oe else 0) @kernel def output(self): @@ -128,7 +108,7 @@ class TTLInOut: @kernel def set_o(self, o): - ttl_set_o(now_mu(), self.channel, o) + rtio_output(now_mu(), self.channel, 0, 1 if o else 0) self.o_previous_timestamp = now_mu() @kernel @@ -170,7 +150,7 @@ class TTLInOut: @kernel def _set_sensitivity(self, value): - ttl_set_sensitivity(now_mu(), self.channel, value) + rtio_output(now_mu(), self.channel, 2, 1 if value else 0) self.i_previous_timestamp = now_mu() @kernel @@ -226,7 +206,7 @@ class TTLInOut: """Poll the RTIO input during all the previously programmed gate openings, and returns the number of registered events.""" count = 0 - while ttl_get(self.channel, self.i_previous_timestamp) >= 0: + while rtio_input_timestamp(self.i_previous_timestamp, self.channel) >= 0: count += 1 return count @@ -237,7 +217,7 @@ class TTLInOut: If the gate is permanently closed, returns a negative value. """ - return ttl_get(self.channel, self.i_previous_timestamp) + return rtio_input_timestamp(self.i_previous_timestamp, self.channel) class TTLClockGen: @@ -288,7 +268,7 @@ class TTLClockGen: that are not powers of two cause jitter of one RTIO clock cycle at the output. """ - ttl_clock_set(now_mu(), self.channel, frequency) + rtio_output(now_mu(), self.channel, 0, frequency) self.previous_timestamp = now_mu() @kernel diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 7915f4d0e..4411790dc 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o rt2wb.o dds.o + bridge.o rtio.o rt2wb.o dds.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 708e35473..02be42e4c 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -1,21 +1,25 @@ #include "mailbox.h" #include "messages.h" #include "rtio.h" -#include "ttl.h" #include "dds.h" #include "bridge.h" #define TIME_BUFFER (8000 << CONFIG_RTIO_FINE_TS_WIDTH) -static void dds_write(int addr, int data) +static void rtio_output_blind(int channel, int addr, int data) { - rtio_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); + rtio_chan_sel_write(channel); rtio_o_address_write(addr); rtio_o_data_write(data); rtio_o_timestamp_write(rtio_get_counter() + TIME_BUFFER); rtio_o_we_write(1); } +static void dds_write(int addr, int data) +{ + rtio_output_blind(CONFIG_RTIO_DDS_CHANNEL, addr, data); +} + static int dds_read(int addr) { int r; @@ -54,7 +58,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - ttl_set_oe(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value); + rtio_output_blind(msg->channel, 0, msg->value); mailbox_acknowledge(); break; } @@ -62,7 +66,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - ttl_set_o(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value); + rtio_output_blind(msg->channel, 1, msg->value); mailbox_acknowledge(); break; } diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index f76da5724..3f8e728c1 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -2,7 +2,7 @@ #include #include "artiq_personality.h" -#include "rt2wb.h" +#include "rtio.h" #include "log.h" #include "dds.h" @@ -26,7 +26,7 @@ #endif #define DDS_WRITE(addr, data) do { \ - rt2wb_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ + rtio_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ now += DURATION_WRITE; \ } while(0) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index e15352bb5..add01a13a 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -13,7 +13,6 @@ #include "messages.h" #include "bridge.h" #include "artiq_personality.h" -#include "ttl.h" #include "dds.h" #include "rtio.h" #include "rt2wb.h" @@ -110,19 +109,14 @@ static const struct symbol runtime_exports[] = { /* direct syscalls */ {"rtio_get_counter", &rtio_get_counter}, {"rtio_log", &rtio_log}, - - {"ttl_set_o", &ttl_set_o}, - {"ttl_set_oe", &ttl_set_oe}, - {"ttl_set_sensitivity", &ttl_set_sensitivity}, - {"ttl_get", &ttl_get}, - {"ttl_clock_set", &ttl_clock_set}, + {"rtio_output", &rtio_output}, + {"rtio_input_timestamp", &rtio_input_timestamp}, {"dds_init", &dds_init}, {"dds_batch_enter", &dds_batch_enter}, {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"rt2wb_output", &rt2wb_output}, {"rt2wb_input", &rt2wb_input}, {"cache_get", &cache_get}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 6760149c4..ef71f3a91 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -5,13 +5,6 @@ #include "rt2wb.h" -void rt2wb_output(long long int timestamp, int channel, int addr, - unsigned int data) -{ - rtio_output(timestamp, channel, addr, data); -} - - unsigned int rt2wb_input(int channel) { unsigned int data; diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h index d421397f4..158a12d7c 100644 --- a/artiq/runtime/rt2wb.h +++ b/artiq/runtime/rt2wb.h @@ -3,10 +3,7 @@ #include "rtio.h" -void rt2wb_output(long long int timestamp, int channel, int addr, - unsigned int data); unsigned int rt2wb_input(int channel); -unsigned int rt2wb_input_sync(long long int timeout, int channel); #endif /* __RT2WB_H */ diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 0670a4e2b..a87bba88b 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -56,8 +56,9 @@ void rtio_output(long long int timestamp, int channel, unsigned int addr, } -int rtio_input_wait(long long int timeout, int channel) +long long int rtio_input_timestamp(long long int timeout, int channel) { + long long int r; int status; rtio_chan_sel_write(channel); @@ -76,7 +77,17 @@ int rtio_input_wait(long long int timeout, int channel) } /* input FIFO is empty - keep waiting */ } - return status; + + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + return -1; + + r = rtio_i_timestamp_read(); + rtio_i_re_write(1); + return r; } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 9cebb6f2f..1aef9f830 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -19,6 +19,6 @@ void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); -int rtio_input_wait(long long int timeout, int channel); +long long int rtio_input_timestamp(long long int timeout, int channel); #endif /* __RTIO_H */ diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c deleted file mode 100644 index 603eb2a8c..000000000 --- a/artiq/runtime/ttl.c +++ /dev/null @@ -1,42 +0,0 @@ -#include - -#include "artiq_personality.h" -#include "rtio.h" -#include "ttl.h" - -void ttl_set_o(long long int timestamp, int channel, int value) -{ - rtio_output(timestamp, channel, 0, value); -} - -void ttl_set_oe(long long int timestamp, int channel, int oe) -{ - rtio_output(timestamp, channel, 1, oe); -} - -void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity) -{ - rtio_output(timestamp, channel, 2, sensitivity); -} - -long long int ttl_get(int channel, long long int time_limit) -{ - long long int r; - int status = rtio_input_wait(time_limit, channel); - - if (status & RTIO_I_STATUS_OVERFLOW) - artiq_raise_from_c("RTIOOverflow", - "RTIO input overflow on channel {0}", - channel, 0, 0); - if (status & RTIO_I_STATUS_EMPTY) - return -1; - - r = rtio_i_timestamp_read(); - rtio_i_re_write(1); - return r; -} - -void ttl_clock_set(long long int timestamp, int channel, int ftw) -{ - rtio_output(timestamp, channel, 0, ftw); -} diff --git a/artiq/runtime/ttl.h b/artiq/runtime/ttl.h deleted file mode 100644 index 9d95a32f2..000000000 --- a/artiq/runtime/ttl.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __TTL_H -#define __TTL_H - -void ttl_set_o(long long int timestamp, int channel, int value); -void ttl_set_oe(long long int timestamp, int channel, int oe); -void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity); -long long int ttl_get(int channel, long long int time_limit); -void ttl_clock_set(long long int timestamp, int channel, int ftw); - -#endif /* __TTL_H */ From 6f9656dcbecb74295728bb6ccb2e135d32f75fc7 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:19:06 +0100 Subject: [PATCH 067/125] bridge: fix ttl o/oe addresses --- artiq/runtime/bridge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 02be42e4c..82fe3a16a 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -58,7 +58,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 0, msg->value); + rtio_output_blind(msg->channel, 1, msg->value); mailbox_acknowledge(); break; } @@ -66,7 +66,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 1, msg->value); + rtio_output_blind(msg->channel, 0, msg->value); mailbox_acknowledge(); break; } From 3aebbbdb61a7f231f8779a9731233578be1ffa87 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:22:03 +0100 Subject: [PATCH 068/125] coredevice.ttl: fix sensitivity --- artiq/coredevice/ttl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 1b3a6eecd..d5db21a67 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -150,7 +150,7 @@ class TTLInOut: @kernel def _set_sensitivity(self, value): - rtio_output(now_mu(), self.channel, 2, 1 if value else 0) + rtio_output(now_mu(), self.channel, 2, value) self.i_previous_timestamp = now_mu() @kernel From 135643e3a66db7bcfb2ab2ba4feb0bfe5b2e262e Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:22:42 +0100 Subject: [PATCH 069/125] runtime: define constants for ttl addresses --- artiq/runtime/bridge.c | 5 +++-- artiq/runtime/ttl.h | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 artiq/runtime/ttl.h diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 82fe3a16a..9c2e6d921 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -1,6 +1,7 @@ #include "mailbox.h" #include "messages.h" #include "rtio.h" +#include "ttl.h" #include "dds.h" #include "bridge.h" @@ -58,7 +59,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 1, msg->value); + rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); mailbox_acknowledge(); break; } @@ -66,7 +67,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 0, msg->value); + rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); mailbox_acknowledge(); break; } diff --git a/artiq/runtime/ttl.h b/artiq/runtime/ttl.h new file mode 100644 index 000000000..87fa74b7c --- /dev/null +++ b/artiq/runtime/ttl.h @@ -0,0 +1,8 @@ +#ifndef __TTL_H +#define __TTL_H + +#define TTL_O_ADDR 0 +#define TTL_OE_ADDR 1 +#define TTL_SENSITIVITY_ADDR 2 + +#endif /* __TTL_H */ From 81b35be5744044840d918fb4b9e4b35aba4353a6 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:49:04 +0100 Subject: [PATCH 070/125] bridge: really fix O/OE --- artiq/runtime/bridge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 9c2e6d921..d1157cf65 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -59,7 +59,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); + rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); mailbox_acknowledge(); break; } @@ -67,7 +67,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); + rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); mailbox_acknowledge(); break; } From baf7b0dcf2f572a6d1383b209714f4e2685492bf Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 19:04:03 +0100 Subject: [PATCH 071/125] examples/tdr: adapt to compiler changes --- .../master/repository/coredevice_examples/tdr.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/master/repository/coredevice_examples/tdr.py b/examples/master/repository/coredevice_examples/tdr.py index a71628645..d0c435753 100644 --- a/examples/master/repository/coredevice_examples/tdr.py +++ b/examples/master/repository/coredevice_examples/tdr.py @@ -39,6 +39,7 @@ class TDR(EnvExperiment): n = 1000 # repetitions latency = 50e-9 # calibrated latency without a transmission line 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)) except PulseNotReceivedError: @@ -53,21 +54,19 @@ class TDR(EnvExperiment): @kernel def many(self, n, p): - t = [0 for i in range(2)] self.core.break_realtime() for i in range(n): - self.one(t, p) - self.t = t + self.one(p) @kernel - def one(self, t, p): + def one(self, p): t0 = now_mu() with parallel: self.pmt0.gate_both_mu(2*p) self.ttl2.pulse_mu(p) - for i in range(len(t)): + for i in range(len(self.t)): ti = self.pmt0.timestamp_mu() if ti <= 0: - raise PulseNotReceivedError - t[i] += ti - t0 + raise PulseNotReceivedError() + self.t[i] = int(self.t[i] + ti - t0) self.pmt0.count() # flush From f30dc4b39e5beb9604cc45dbd858f118592df444 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 19:21:50 +0100 Subject: [PATCH 072/125] runtime: rt2wb_input -> rtio_input_data --- artiq/coredevice/rt2wb.py | 7 ------- artiq/coredevice/rtio.py | 5 +++++ artiq/coredevice/spi.py | 31 +++++++++++++++---------------- artiq/runtime/Makefile | 2 +- artiq/runtime/ksupport.c | 4 +--- artiq/runtime/rt2wb.c | 26 -------------------------- artiq/runtime/rt2wb.h | 9 --------- artiq/runtime/rtio.c | 21 +++++++++++++++++++++ artiq/runtime/rtio.h | 11 +++++++++++ 9 files changed, 54 insertions(+), 62 deletions(-) delete mode 100644 artiq/coredevice/rt2wb.py delete mode 100644 artiq/runtime/rt2wb.c delete mode 100644 artiq/runtime/rt2wb.h diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py deleted file mode 100644 index 318d8bf0e..000000000 --- a/artiq/coredevice/rt2wb.py +++ /dev/null @@ -1,7 +0,0 @@ -from artiq.language.core import * -from artiq.language.types import * - - -@syscall -def rt2wb_input(channel: TInt32) -> TInt32: - raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/rtio.py b/artiq/coredevice/rtio.py index 194818507..978d80fd1 100644 --- a/artiq/coredevice/rtio.py +++ b/artiq/coredevice/rtio.py @@ -11,3 +11,8 @@ def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 @syscall def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64: raise NotImplementedError("syscall not simulated") + + +@syscall +def rtio_input_data(channel: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 5998997e1..dcd1acc27 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,8 +1,7 @@ from artiq.language.core import (kernel, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rtio import rtio_output as rt2wb_output -from artiq.coredevice.rt2wb import rt2wb_input +from artiq.coredevice.rtio import rtio_output, rtio_input_data SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -50,48 +49,48 @@ class SPIMaster: @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): - rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 16) | ((read_div - 2) << 24)) + rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 16) | ((read_div - 2) << 24)) self.write_period_mu = int(write_div*self.ref_period_mu) self.read_period_mu = int(read_div*self.ref_period_mu) delay_mu(3*self.ref_period_mu) @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): - rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR, - chip_select | (write_length << 16) | (read_length << 24)) + rtio_output(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) self.xfer_period_mu = int(write_length*self.write_period_mu + read_length*self.read_period_mu) delay_mu(3*self.ref_period_mu) @kernel def write(self, data): - rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR, data) + rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data) delay_mu(3*self.ref_period_mu) @kernel def read_async(self): # every read_async() must be matched by an input_async() - rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) delay_mu(3*self.ref_period_mu) @kernel def input_async(self): # matches the preeeding read_async() - return rt2wb_input(self.channel) + return rtio_input_data(self.channel) @kernel def read_sync(self): - rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) - return rt2wb_input(self.channel) + rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + return rtio_input_data(self.channel) @kernel def _get_xfer_sync(self): - rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0) - return rt2wb_input(self.channel) + rtio_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0) + return rtio_input_data(self.channel) @kernel def _get_config_sync(self): - rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, - 0) - return rt2wb_input(self.channel) + rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, + 0) + return rtio_input_data(self.channel) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 4411790dc..2a7b37e2e 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o rt2wb.o dds.o + bridge.o rtio.o dds.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index add01a13a..8ed50f131 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -15,7 +15,6 @@ #include "artiq_personality.h" #include "dds.h" #include "rtio.h" -#include "rt2wb.h" double round(double x); @@ -111,14 +110,13 @@ static const struct symbol runtime_exports[] = { {"rtio_log", &rtio_log}, {"rtio_output", &rtio_output}, {"rtio_input_timestamp", &rtio_input_timestamp}, + {"rtio_input_data", &rtio_input_data}, {"dds_init", &dds_init}, {"dds_batch_enter", &dds_batch_enter}, {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"rt2wb_input", &rt2wb_input}, - {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c deleted file mode 100644 index ef71f3a91..000000000 --- a/artiq/runtime/rt2wb.c +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include "artiq_personality.h" -#include "rtio.h" -#include "rt2wb.h" - - -unsigned int rt2wb_input(int channel) -{ - unsigned int data; - int status; - - rtio_chan_sel_write(channel); - while((status = rtio_i_status_read())) { - if(status & RTIO_I_STATUS_OVERFLOW) { - rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RT2WB input overflow on channel {0}", - channel, 0, 0); - } - } - - data = rtio_i_data_read(); - rtio_i_re_write(1); - return data; -} diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h deleted file mode 100644 index 158a12d7c..000000000 --- a/artiq/runtime/rt2wb.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __RT2WB_H -#define __RT2WB_H - -#include "rtio.h" - -unsigned int rt2wb_input(int channel); - -#endif /* __RT2WB_H */ - diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index a87bba88b..7a4faa78c 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -91,6 +91,27 @@ long long int rtio_input_timestamp(long long int timeout, int channel) } +unsigned int rtio_input_data(int channel) +{ + unsigned int data; + int status; + + rtio_chan_sel_write(channel); + while((status = rtio_i_status_read())) { + if(status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + } + } + + data = rtio_i_data_read(); + rtio_i_re_write(1); + return data; +} + + void rtio_log_va(long long int timestamp, const char *fmt, va_list args) { // This executes on the kernel CPU's stack, which is specifically designed diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 1aef9f830..253a64fad 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -19,6 +19,17 @@ void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); + +/* + * Waits at least until timeout and returns the timestamp of the first + * input event on the chanel, -1 if there was no event. + */ long long int rtio_input_timestamp(long long int timeout, int channel); +/* + * Assumes that there is or will be an event in the channel and returns only + * its data. + */ +unsigned int rtio_input_data(int channel); + #endif /* __RTIO_H */ From 2cc1dfaee3d72566cb5b147c7b51cf6dab55c422 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 19:40:32 +0100 Subject: [PATCH 073/125] kc705: move ttl channels together again, update doc --- artiq/gateware/targets/kc705.py | 8 ++++---- doc/manual/core_device.rst | 8 ++++---- examples/master/device_db.pyon | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 5872e0f5f..3bc8db6f2 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -256,6 +256,10 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) + phy = ttl_simple.ClockGen(platform.request("la32_p")) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + phy = spi.SPIMaster(ams101_dac) self.submodules += phy self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) @@ -268,10 +272,6 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=128, ififo_depth=128)) - phy = ttl_simple.ClockGen(platform.request("la32_p")) - self.submodules += phy - rtio_channels.append(rtio.Channel.from_phy(phy)) - self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) self.config["DDS_CHANNEL_COUNT"] = 11 self.config["DDS_AD9914"] = True diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index 8000a6770..c0369b184 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -64,7 +64,9 @@ With the CLOCK hardware, the TTL lines are mapped as follows: +--------------------+-----------------------+--------------+ | 19 | LED | Output | +--------------------+-----------------------+--------------+ -| 20 | LA32_P | Clock | +| 20 | AMS101_LDAC_B | Output | ++--------------------+-----------------------+--------------+ +| 21 | LA32_P | Clock | +--------------------+-----------------------+--------------+ @@ -101,6 +103,4 @@ When plugged to an adapter, the NIST QC1 hardware can be used. The TTL lines are 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 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. diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index 5debbc7d3..1644ed176 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -87,24 +87,24 @@ "class": "TTLOut", "arguments": {"channel": 20} }, - "ams101_spi": { + "ttl_clock_la32_p": { "type": "local", - "module": "artiq.coredevice.spi", - "class": "SPIMaster", + "module": "artiq.coredevice.ttl", + "class": "TTLClockGen", "arguments": {"channel": 21} }, - "spi0": { + "ams101_spi": { "type": "local", "module": "artiq.coredevice.spi", "class": "SPIMaster", "arguments": {"channel": 22} }, - "ttl_clock_la32_p": { + "spi0": { "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLClockGen", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", "arguments": {"channel": 23} }, From 0456169558e915ba811c6bb7304983e039db5add Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 21:29:09 +0100 Subject: [PATCH 074/125] coredevice.spi, doc/manual: add spi --- artiq/coredevice/spi.py | 160 +++++++++++++++++++++++++- doc/manual/core_drivers_reference.rst | 9 +- 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index dcd1acc27..f1ee52f85 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -21,9 +21,25 @@ SPI_RT2WB_READ = 1 << 2 class SPIMaster: """Core device Serial Peripheral Interface (SPI) bus master. + Owns one SPI bus. + + **Transfer Sequence**: + + * If desired, write the ``config`` register (:meth:`set_config`) + to configure and activate the core. + * If desired, write the ``xfer`` register (:meth:`set_xfer`) + to set ``cs_n``, ``write_length``, and ``read_length``. + * :meth:`write` to the ``data`` register (also for transfers with + zero bits to be written). Writing starts the transfer. + * If desired, :meth:`read_sync` (or :meth:`read_async` followed by a + :meth:`input_async` later) the ``data`` register corresponding to + the last completed transfer. + * If desired, :meth:`set_xfer` for the next transfer. + * If desired, :meth:`write` ``data`` queuing the next + (possibly chained) transfer. :param ref_period: clock period of the SPI core. - :param channel: channel number of the SPI bus to control. + :param channel: RTIO channel number of the SPI bus to control. """ def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") @@ -43,12 +59,69 @@ class SPIMaster: @kernel def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): + """Set the configuration register. + + * If ``config.cs_polarity`` == 0 (```cs`` active low, the default), + "``cs_n`` all deasserted" means "all ``cs_n`` bits high". + * ``cs_n`` is not mandatory in the pads supplied to the gateware core. + Framing and chip selection can also be handled independently + through other means, e.g. ``TTLOut``. + * If there is a ``miso`` wire in the pads supplied in the gateware, + input and output may be two signals ("4-wire SPI"), + otherwise ``mosi`` must be used for both output and input + ("3-wire SPI") and ``config.half_duplex`` must to be set + when reading data is desired or when the slave drives the + ``mosi`` signal at any point. + * The first bit output on ``mosi`` is always the MSB/LSB (depending + on ``config.lsb_first``) of the ``data`` register, independent of + ``xfer.write_length``. The last bit input from ``miso`` always ends + up in the LSB/MSB (respectively) of the ``data`` register, + independent of ``xfer.read_length``. + * Writes to the ``config`` register take effect immediately. + + **Configuration flags**: + + * :const:`SPI_OFFLINE`: all pins high-z (reset=1) + * :const:`SPI_ACTIVE`: transfer in progress (read-only) + * :const:`SPI_PENDING`: transfer pending in intermediate buffer + (read-only) + * :const:`SPI_CS_POLARITY`: active level of ``cs_n`` (reset=0) + * :const:`SPI_CLK_POLARITY`: idle level of ``clk`` (reset=0) + * :const:`SPI_CLK_PHASE`: first edge after ``cs`` assertion to sample + data on (reset=0). In Motorola/Freescale SPI language + (:const:`SPI_CLK_POLARITY`, :const:`SPI_CLK_PHASE`) == (CPOL, CPHA): + + - (0, 0): idle low, output on falling, input on rising + - (0, 1): idle low, output on rising, input on falling + - (1, 0): idle high, output on rising, input on falling + - (1, 1): idle high, output on falling, input on rising + * :const:`SPI_LSB_FIRST`: LSB is the first bit on the wire (reset=0) + * :const:`SPI_HALF_DUPLEX`: 3-wire SPI, in/out on ``mosi`` (reset=0) + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + + :param flags: A bit map of `SPI_*` flags. + :param write_freq: Desired SPI clock frequency during write bits. + :param read_freq: Desired SPI clock frequency during read bits. + """ write_div = round(1/(write_freq*self.ref_period)) read_div = round(1/(read_freq*self.ref_period)) self.set_config_mu(flags, write_div, read_div) @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): + """Set the ``config`` register (in SPI bus machine units). + + .. seealso:: :meth:`set_config` + + :param write_div: Counter load value to divide the RTIO + clock by to generate the SPI write clk. (minimum=2, reset=2) + ``f_rtio_clk/f_spi_write == write_div``. If ``write_div`` is odd, + the setup phase of the SPI clock is biased to longer lengths + by one RTIO clock cycle. + :param read_div: Ditto for the read clock. + """ rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | ((write_div - 2) << 16) | ((read_div - 2) << 24)) self.write_period_mu = int(write_div*self.ref_period_mu) @@ -57,6 +130,43 @@ class SPIMaster: @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): + """Set the ``xfer`` register. + + * Every transfer consists of a write of ``write_length`` bits + immediately followed by a read of ``read_length`` bits. + * ``cs_n`` is asserted at the beginning and deasserted at the end + of the transfer if there is no other transfer pending. + * ``cs_n`` handling is agnostic to whether it is one-hot or decoded + somewhere downstream. If it is decoded, "``cs_n`` all deasserted" + should be handled accordingly (no slave selected). + If it is one-hot, asserting multiple slaves should only be attempted + if ``miso`` is either not connected between slaves, or open + collector, or correctly multiplexed externally. + * For 4-wire SPI only the sum of ``read_length`` and ``write_length`` + matters. The behavior is the same (except for clock speeds) no matter + how the total transfer length is divided between the two. For + 3-wire SPI, the direction of ``mosi`` is switched from output to + input after ``write_length`` bits. + * Data output on ``mosi`` in 4-wire SPI during the read cycles is what + is found in the data register at the time. + Data in the ``data`` register outside the least/most (depending + on ``config.lsb_first``) significant ``read_length`` bits is what is + seen on ``miso`` (or ``mosi`` if ``config.half_duplex``) + during the write cycles. + * Writes to ``xfer`` are synchronized to the start of the next + (possibly chained) transfer. + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + + :param chip_select: Bit mask of chip selects to assert. Or number of + the chip select to assert if ``cs`` is decoded downstream. + (reset=0) + :param write_length: Number of bits to write during the next transfer. + (reset=0) + :param read_length: Number of bits to read during the next transfer. + (reset=0) + """ rtio_output(now_mu(), self.channel, SPI_XFER_ADDR, chip_select | (write_length << 16) | (read_length << 24)) self.xfer_period_mu = int(write_length*self.write_period_mu + @@ -65,24 +175,64 @@ class SPIMaster: @kernel def write(self, data): + """Write data to data register. + + * The ``data`` register and the shift register are 32 bits wide. + If there are no writes to the register, ``miso`` data reappears on + ``mosi`` after 32 cycles. + * A wishbone data register write is acknowledged when the + transfer has been written to the intermediate buffer. + It will be started when there are no other transactions being + executed, either beginning a new SPI transfer of chained + to an in-flight transfer. + * Writes take three ``ref_period`` cycles unless another + chained transfer is pending and the transfer being + executed is not complete. + * The SPI ``data`` register is double-buffered: Once a transfer has + started, new write data can be written, queuing a new transfer. + Transfers submitted this way are chained and executed without + deasserting ``cs`` in between. Once a transfer completes, + the previous transfer's read data is available in the + ``data`` register. + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + """ rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data) delay_mu(3*self.ref_period_mu) @kernel def read_async(self): - # every read_async() must be matched by an input_async() + """Trigger an asynchronous read from the data register. + + Reads always finish in two cycles. + + Every data register read triggered by a :meth:`read_async` + must be matched by a :meth:`input_async` to retrieve the data. + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + """ rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) delay_mu(3*self.ref_period_mu) @kernel def input_async(self): - # matches the preeeding read_async() + """Retrieves data written asynchronously. + + :meth:`input_async` must match a preeeding :meth:`read_async`. + """ return rtio_input_data(self.channel) @kernel def read_sync(self): - rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) - return rtio_input_data(self.channel) + """Read the ``data`` register synchronously. + + This is a shortcut for :meth:`read_async` followed by + :meth:`input_async`. + """ + self.read_async() + return self.input_async() @kernel def _get_xfer_sync(self): diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index 604d823b5..e88e077e5 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -19,11 +19,18 @@ These drivers are for the core device and the peripherals closely integrated int ---------------------------------- .. autoclass:: artiq.coredevice.dds._DDSGeneric - :members: + :members: .. automodule:: artiq.coredevice.dds :members: +:mod:`artiq.coredevice.spi` module +---------------------------------- + +.. automodule:: artiq.coredevice.spi + :members: + + :mod:`artiq.coredevice.exceptions` module ----------------------------------------- From 5ba753425d0c33b7005d8fea8f81b728206ccc23 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 21:38:51 +0100 Subject: [PATCH 075/125] runtime/rtio: rtio_process_exceptional_status() has only one user --- artiq/runtime/rtio.c | 5 +++-- artiq/runtime/rtio.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 7a4faa78c..6a136267c 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -15,7 +15,8 @@ long long int rtio_get_counter(void) return rtio_counter_read(); } -void rtio_process_exceptional_status(int status, long long int timestamp, int channel) +static void rtio_process_exceptional_status( + long long int timestamp, int channel, int status) { if(status & RTIO_O_STATUS_FULL) while(rtio_o_status_read() & RTIO_O_STATUS_FULL); @@ -52,7 +53,7 @@ void rtio_output(long long int timestamp, int channel, unsigned int addr, rtio_o_we_write(1); status = rtio_o_status_read(); if(status) - rtio_process_exceptional_status(status, timestamp, channel); + rtio_process_exceptional_status(timestamp, channel, status); } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 253a64fad..709a85be3 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -14,7 +14,6 @@ void rtio_init(void); long long int rtio_get_counter(void); -void rtio_process_exceptional_status(int status, long long int timestamp, int channel); void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, From d973eb879f7e4519998a42b9114e80b695d43ca8 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 22:42:00 +0100 Subject: [PATCH 076/125] coredevice.spi: docstring fix --- artiq/coredevice/spi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index f1ee52f85..49986ddcc 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -203,7 +203,7 @@ class SPIMaster: @kernel def read_async(self): - """Trigger an asynchronous read from the data register. + """Trigger an asynchronous read from the ``data`` register. Reads always finish in two cycles. @@ -218,7 +218,7 @@ class SPIMaster: @kernel def input_async(self): - """Retrieves data written asynchronously. + """Retrieves data read asynchronously from the ``data`` register. :meth:`input_async` must match a preeeding :meth:`read_async`. """ From 162ecdd574e9edbdaa8a9defd3a025c15c5fe2b3 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 2 Mar 2016 00:11:17 +0100 Subject: [PATCH 077/125] spi: cleanup, add frequency_to_div() --- artiq/coredevice/spi.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 49986ddcc..264481e21 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,4 +1,4 @@ -from artiq.language.core import (kernel, seconds_to_mu, now_mu, +from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz from artiq.coredevice.rtio import rtio_output, rtio_input_data @@ -57,6 +57,10 @@ class SPIMaster: # To chain transfers together, new data must be written before # pending transfer's read data becomes available. + @portable + def frequency_to_div(self, f): + return int(1/(f*self.ref_period)) + 1 + @kernel def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): """Set the configuration register. @@ -105,9 +109,8 @@ class SPIMaster: :param write_freq: Desired SPI clock frequency during write bits. :param read_freq: Desired SPI clock frequency during read bits. """ - write_div = round(1/(write_freq*self.ref_period)) - read_div = round(1/(read_freq*self.ref_period)) - self.set_config_mu(flags, write_div, read_div) + self.set_config_mu(flags, self.frequency_to_div(write_freq), + self.frequency_to_div(read_freq)) @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): @@ -174,7 +177,7 @@ class SPIMaster: delay_mu(3*self.ref_period_mu) @kernel - def write(self, data): + def write(self, data=0): """Write data to data register. * The ``data`` register and the shift register are 32 bits wide. From 1e4bccae20f72f3b18dea86204ad0a0c06845b17 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 2 Mar 2016 00:12:01 +0100 Subject: [PATCH 078/125] ad53xx: add --- artiq/coredevice/ad53xx.py | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 artiq/coredevice/ad53xx.py diff --git a/artiq/coredevice/ad53xx.py b/artiq/coredevice/ad53xx.py new file mode 100644 index 000000000..1f6984108 --- /dev/null +++ b/artiq/coredevice/ad53xx.py @@ -0,0 +1,71 @@ +from artiq.language.core import (kernel, portable, delay, delay_mu, int) +from artiq.language.units import ns +from artiq.coredevice import spi + + +_AD53xx_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_CS_POLARITY | + 0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE | + 0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX) + +_AD53xx_MODE_WRITE_X1 = 3 << 22 +_AD53xx_MODE_WRITE_C = 2 << 22 +_AD53xx_MODE_WRITE_M = 1 << 22 +_AD53xx_MODE_SPECIAL = 0 << 22 + +_AD53xx_GROUP = portable(lambda g: ((g + 1) << 19)) +_AD53xx_GROUP_ALL = 0 << 19 +_AD53xx_GROUP_01234 = 6 << 19 +_AD53xx_GROUP_1234 = 7 << 19 +_AD53xx_CHANNEL_ALL = 0 << 16 +_AD53xx_CHANNEL = portable(lambda g: g << 16) + +_AD53xx_SPECIAL_NOP = 0 << 16 +_AD53xx_SPECIAL_CONTROL = 1 << 16 +_AD53xx_SPECIAL_OFS0 = 2 << 16 +_AD53xx_SPECIAL_OFS1 = 3 << 16 +_AD53xx_SPECIAL_AB_SELECT = portable(lambda i: (i + 6) << 16) +_AD53xx_SPECIAL_AB_SELECT_ALL = 11 << 16 + +_AD53xx_READ_X1A = portable(lambda ch: (0x00 | (ch + 8)) << 7) +_AD53xx_READ_X1B = portable(lambda ch: (0x40 | (ch + 8)) << 7) +_AD53xx_READ_C = portable(lambda ch: (0x80 | (ch + 8)) << 7) +_AD53xx_READ_M = portable(lambda ch: (0xc0 | (ch + 8)) << 7) +_AD53xx_READ_CONTROL = 0x101 << 7 +_AD53xx_READ_OFS0 = 0x102 << 7 +_AD53xx_READ_OFS1 = 0x103 << 7 +_AD53xx_READ_AB_SELECT = portable(lambda i: (0x100 + (i + 6)) << 7) + + +class AD53xx: + def __init__(self, dmgr, spi_bus, ldac=None, + chip_select=0, write_div=4, read_div=6): + self.core = dmgr.get("core") + self.bus = dmgr.get(spi_bus) + # if ldac is not None: + ldac = dmgr.get(ldac) + self.ldac = ldac + self.chip_select = chip_select + self.write_div = write_div + self.read_div = read_div + + @kernel + def bus_setup(self): + self.bus.set_config_mu(_AD53xx_SPI_CONFIG, self.write_div, + self.read_div) + self.bus.set_xfer(self.chip_select, 24, 0) + + @kernel + def _channel_address(self, channel=0): + return int((channel + 8) << 16) + + @kernel + def write_x1(self, channel=0, value=0): + ch = self._channel_address(channel) + self.bus.write(_AD53xx_MODE_WRITE_X1 | ch | value) + delay_mu(int(self.bus.xfer_period_mu + self.bus.write_period_mu)) + + @kernel + def load(self): + self.ldac.off() + delay(20*ns) + self.ldac.on() From 946bd84b583cb237b077f619d7e5bab6a62996ee Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Mar 2016 11:45:31 +0800 Subject: [PATCH 079/125] protocols/pc_rpc: support retrieving selected target --- artiq/protocols/pc_rpc.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/artiq/protocols/pc_rpc.py b/artiq/protocols/pc_rpc.py index 962fe10de..c9f53b286 100644 --- a/artiq/protocols/pc_rpc.py +++ b/artiq/protocols/pc_rpc.py @@ -108,6 +108,7 @@ class Client: server_identification = self.__recv() self.__target_names = server_identification["targets"] self.__description = server_identification["description"] + self.__selected_target = None if target_name is not None: self.select_rpc_target(target_name) except: @@ -119,6 +120,12 @@ class Client: exactly once if the object was created with ``target_name=None``.""" target_name = _validate_target_name(target_name, self.__target_names) self.__socket.sendall((target_name + "\n").encode()) + self.__selected_target = target_name + + def get_selected_target(self): + """Returns the selected target, or ``None`` if no target has been + selected yet.""" + return self.__selected_target def get_rpc_id(self): """Returns a tuple (target_names, description) containing the @@ -197,6 +204,7 @@ class AsyncioClient: server_identification = await self.__recv() self.__target_names = server_identification["targets"] self.__description = server_identification["description"] + self.__selected_target = None if target_name is not None: self.select_rpc_target(target_name) except: @@ -209,6 +217,12 @@ class AsyncioClient: """ target_name = _validate_target_name(target_name, self.__target_names) self.__writer.write((target_name + "\n").encode()) + self.__selected_target = target_name + + def get_selected_target(self): + """Returns the selected target, or ``None`` if no target has been + selected yet.""" + return self.__selected_target def get_rpc_id(self): """Returns a tuple (target_names, description) containing the From d0d50d74eb5813d7cd35a5c8b5641643c7168ec8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Mar 2016 11:45:51 +0800 Subject: [PATCH 080/125] rpctool: interactive mode --- artiq/frontend/artiq_rpctool.py | 38 +++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/artiq/frontend/artiq_rpctool.py b/artiq/frontend/artiq_rpctool.py index 58514f881..1f5d9b394 100755 --- a/artiq/frontend/artiq_rpctool.py +++ b/artiq/frontend/artiq_rpctool.py @@ -3,10 +3,12 @@ import argparse import textwrap import sys +import traceback import numpy as np # Needed to use numpy in RPC call arguments on cmd line +import readline # This makes input() nicer import pprint -from artiq.protocols.pc_rpc import AutoTarget, Client +from artiq.protocols.pc_rpc import AutoTarget, Client, RemoteError def get_argparser(): @@ -17,7 +19,6 @@ def get_argparser(): parser.add_argument("port", type=int, help="TCP port to use to connect to the controller") subparsers = parser.add_subparsers(dest="action") - subparsers.required = True subparsers.add_parser("list-targets", help="list existing targets") parser_list_methods = subparsers.add_parser("list-methods", help="list target's methods") @@ -27,6 +28,10 @@ def get_argparser(): parser_call.add_argument("method", help="method name") parser_call.add_argument("args", nargs=argparse.REMAINDER, help="arguments") + parser_interactive = subparsers.add_parser("interactive", + help="enter interactive mode " + "(default)") + parser_interactive.add_argument("-t", "--target", help="target name") return parser @@ -81,8 +86,35 @@ def call_method(remote, method_name, args): pprint.pprint(ret) +def interactive(remote): + while True: + try: + cmd = input("({}) ".format(remote.get_selected_target())) + except EOFError: + return + class RemoteDict: + def __getitem__(self, k): + if k == "np": + return np + else: + return getattr(remote, k) + try: + result = eval(cmd, {}, RemoteDict()) + except Exception as e: + if isinstance(e, RemoteError): + print("Remote exception:") + print(str(e)) + else: + traceback.print_exc() + else: + if result is not None: + print(result) + + def main(): args = get_argparser().parse_args() + if not args.action: + args.target = None remote = Client(args.server, args.port, None) targets, description = remote.get_rpc_id() @@ -98,6 +130,8 @@ def main(): list_methods(remote) elif args.action == "call": call_method(remote, args.method, args.args) + elif args.action == "interactive" or not args.action: + interactive(remote) else: print("Unrecognized action: {}".format(args.action)) From 763a4d3011997c67c60b191b719bc4c25c252ab1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Mar 2016 11:47:34 +0800 Subject: [PATCH 081/125] rpctool: use pprint in interactive mode --- artiq/frontend/artiq_rpctool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/frontend/artiq_rpctool.py b/artiq/frontend/artiq_rpctool.py index 1f5d9b394..4c63f7428 100755 --- a/artiq/frontend/artiq_rpctool.py +++ b/artiq/frontend/artiq_rpctool.py @@ -99,7 +99,7 @@ def interactive(remote): else: return getattr(remote, k) try: - result = eval(cmd, {}, RemoteDict()) + ret = eval(cmd, {}, RemoteDict()) except Exception as e: if isinstance(e, RemoteError): print("Remote exception:") @@ -107,8 +107,8 @@ def interactive(remote): else: traceback.print_exc() else: - if result is not None: - print(result) + if ret is not None: + pprint.pprint(ret) def main(): From f5dee455f517a18e485f23f26c369567720f7b5a Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Mar 2016 17:12:22 +0800 Subject: [PATCH 082/125] test/worker: test exception logging --- artiq/test/test_worker.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/artiq/test/test_worker.py b/artiq/test/test_worker.py index efc2fa6e2..73c09b352 100644 --- a/artiq/test/test_worker.py +++ b/artiq/test/test_worker.py @@ -87,8 +87,12 @@ class WorkerCase(unittest.TestCase): _run_experiment("SimpleExperiment") def test_exception(self): - with self.assertRaises(WorkerInternalException): - _run_experiment("ExceptionTermination") + with self.assertLogs() as logs: + with self.assertRaises(WorkerInternalException): + _run_experiment("ExceptionTermination") + self.assertEqual(len(logs.records), 1) + self.assertIn("Terminating with exception (TypeError)", + logs.output[0]) def test_watchdog_no_timeout(self): _run_experiment("WatchdogNoTimeout") From 9969cd85de7a56c6a3a742d6cc8f681f4f0fc16f Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 2 Mar 2016 15:50:02 +0100 Subject: [PATCH 083/125] ad53xx: ldac may be none --- artiq/coredevice/ad53xx.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/ad53xx.py b/artiq/coredevice/ad53xx.py index 1f6984108..6ab325717 100644 --- a/artiq/coredevice/ad53xx.py +++ b/artiq/coredevice/ad53xx.py @@ -41,8 +41,8 @@ class AD53xx: chip_select=0, write_div=4, read_div=6): self.core = dmgr.get("core") self.bus = dmgr.get(spi_bus) - # if ldac is not None: - ldac = dmgr.get(ldac) + if ldac is not None: + ldac = dmgr.get(ldac) self.ldac = ldac self.chip_select = chip_select self.write_div = write_div From d3f36ce78452719c200432cda0de39d3ba0ccc24 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 2 Mar 2016 19:56:24 +0100 Subject: [PATCH 084/125] kc705: add false paths for ethernet phy * vivado prefers rsys_clk over sys_clk (despite the assignment hierarchy) (We need DONT_TOUCH and/or KEEP verilog annotations to fix this) --- artiq/gateware/targets/kc705.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 3bc8db6f2..497842058 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -9,6 +9,7 @@ from migen.genlib.cdc import MultiReg from migen.build.generic_platform import * from migen.build.xilinx.vivado import XilinxVivadoToolchain from migen.build.xilinx.ise import XilinxISEToolchain +from migen.fhdl.specials import Keep from misoc.interconnect.csr import * from misoc.interconnect import wishbone @@ -135,20 +136,22 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) - if isinstance(self.platform.toolchain, XilinxVivadoToolchain): - self.platform.add_platform_command(""" -create_clock -name rsys_clk -period 8.0 [get_nets {rsys_clk}] -create_clock -name rio_clk -period 8.0 [get_nets {rio_clk}] -set_false_path -from [get_clocks rsys_clk] -to [get_clocks rio_clk] -set_false_path -from [get_clocks rio_clk] -to [get_clocks rsys_clk] -""", rsys_clk=self.rtio.cd_rsys.clk, rio_clk=self.rtio.cd_rio.clk) - if isinstance(self.platform.toolchain, XilinxISEToolchain): - self.platform.add_platform_command(""" -NET "sys_clk" TNM_NET = "GRPrsys_clk"; -NET "{rio_clk}" TNM_NET = "GRPrio_clk"; -TIMESPEC "TSfix_cdc1" = FROM "GRPrsys_clk" TO "GRPrio_clk" TIG; -TIMESPEC "TSfix_cdc2" = FROM "GRPrio_clk" TO "GRPrsys_clk" TIG; -""", rio_clk=self.rtio_crg.cd_rtio.clk) + self.specials += [ + Keep(self.rtio.cd_rsys.clk), + Keep(self.rtio_crg.cd_rtio.clk), + Keep(self.ethphy.crg.cd_eth_rx.clk), + Keep(self.ethphy.crg.cd_eth_tx.clk), + ] + + self.platform.add_period_constraint(self.rtio.cd_rsys.clk, 8.) + self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 8.) + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 8.) + self.platform.add_false_path_constraints( + self.rtio.cd_rsys.clk, + self.rtio_crg.cd_rtio.clk, + self.ethphy.crg.cd_eth_rx.clk, + self.ethphy.crg.cd_eth_tx.clk) rtio_csrs = self.rtio.get_csrs() self.submodules.rtiowb = wishbone.CSRBank(rtio_csrs) From 0c97043a204b3ddb48a852fa63a9853f098f8c1f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 10:03:49 +0800 Subject: [PATCH 085/125] gateware/nist_clock: pin assignment corrections from David Leibrandt --- artiq/gateware/nist_clock.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/nist_clock.py b/artiq/gateware/nist_clock.py index 808f5abb9..dd8edaa22 100644 --- a/artiq/gateware/nist_clock.py +++ b/artiq/gateware/nist_clock.py @@ -9,7 +9,7 @@ fmc_adapter_io = [ ("ttl", 4, Pins("LPC:LA01_CC_N"), IOStandard("LVTTL")), ("ttl", 5, Pins("LPC:LA06_P"), IOStandard("LVTTL")), ("ttl", 6, Pins("LPC:LA06_N"), IOStandard("LVTTL")), - ("ttl", 7, Pins("LPC:LA27_P"), IOStandard("LVTTL")), + ("ttl", 7, Pins("LPC:LA01_CC_P"), IOStandard("LVTTL")), ("ttl", 8, Pins("LPC:LA10_P"), IOStandard("LVTTL")), ("ttl", 9, Pins("LPC:LA05_N"), IOStandard("LVTTL")), ("ttl", 10, Pins("LPC:LA05_P"), IOStandard("LVTTL")), @@ -66,9 +66,9 @@ fmc_adapter_io = [ IOStandard("LVTTL")), ("spi", 2, - Subsignal("clk", Pins("LPC:LA27_P")), - Subsignal("cs_n", Pins("LPC:LA26_P")), - Subsignal("mosi", Pins("LPC:LA27_N")), - Subsignal("miso", Pins("LPC:LA26_N")), + Subsignal("clk", Pins("LPC:LA26_N")), + Subsignal("cs_n", Pins("LPC:LA27_N")), + Subsignal("mosi", Pins("LPC:LA26_P")), + Subsignal("miso", Pins("LPC:LA27_P")), IOStandard("LVTTL")), ] From b83b113f3c60d37f363ba2681455497ad9dad772 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 10:48:17 +0800 Subject: [PATCH 086/125] gui/moninj: make widgets look less like buttons --- artiq/gui/moninj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gui/moninj.py b/artiq/gui/moninj.py index 9e5d0a733..43892876a 100644 --- a/artiq/gui/moninj.py +++ b/artiq/gui/moninj.py @@ -29,7 +29,7 @@ class _TTLWidget(QtWidgets.QFrame): QtWidgets.QFrame.__init__(self) - self.setFrameShape(QtWidgets.QFrame.Panel) + self.setFrameShape(QtWidgets.QFrame.Box) self.setFrameShadow(QtWidgets.QFrame.Raised) grid = QtWidgets.QGridLayout() From 9af12230c8f569f4171be32034e64de4522614d1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 13:19:17 +0800 Subject: [PATCH 087/125] soc: add timer to kernel CPU system --- artiq/gateware/soc.py | 11 +++++++++++ artiq/gateware/targets/kc705.py | 7 ++++--- artiq/gateware/targets/pipistrello.py | 6 ++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/soc.py b/artiq/gateware/soc.py index 4487101aa..e97faa0e5 100644 --- a/artiq/gateware/soc.py +++ b/artiq/gateware/soc.py @@ -1,5 +1,6 @@ from misoc.integration.soc_core import mem_decoder from misoc.cores import timer +from misoc.interconnect import wishbone from artiq.gateware import amp @@ -29,3 +30,13 @@ class AMPSoC: self.mailbox.i2) self.add_memory_region("mailbox", self.mem_map["mailbox"] | 0x80000000, 4) + + self.submodules.timer_kernel = timer.Timer() + timer_csrs = self.timer_kernel.get_csrs() + timerwb = wishbone.CSRBank(timer_csrs) + self.submodules += timerwb + self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["timer_kernel"]), + timerwb.bus) + self.add_csr_region("timer_kernel", + self.mem_map["timer_kernel"] | 0x80000000, 32, + timer_csrs) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 497842058..5854bee17 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -83,7 +83,6 @@ class _RTIOCRG(Module, AutoCSR): _ams101_dac = [ ("ams101_dac", 0, - Subsignal("ldac", Pins("XADC:GPIO0")), Subsignal("clk", Pins("XADC:GPIO1")), Subsignal("mosi", Pins("XADC:GPIO2")), @@ -95,6 +94,7 @@ _ams101_dac = [ class _NIST_Ions(MiniSoC, AMPSoC): csr_map = { + "timer_kernel": None, # mapped on Wishbone instead "rtio": None, # mapped on Wishbone instead "rtio_crg": 13, "kernel_cpu": 14, @@ -103,8 +103,9 @@ class _NIST_Ions(MiniSoC, AMPSoC): } csr_map.update(MiniSoC.csr_map) mem_map = { - "rtio": 0x20000000, # (shadow @0xa0000000) - "mailbox": 0x70000000 # (shadow @0xf0000000) + "timer_kernel": 0x10000000, # (shadow @0x90000000) + "rtio": 0x20000000, # (shadow @0xa0000000) + "mailbox": 0x70000000 # (shadow @0xf0000000) } mem_map.update(MiniSoC.mem_map) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index ceb39d83f..655ec22b7 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -103,6 +103,7 @@ TIMESPEC "TSfix_ise4" = FROM "GRPsys_clk" TO "GRPrtio_clk" TIG; class NIST_QC1(BaseSoC, AMPSoC): csr_map = { + "timer_kernel": None, # mapped on Wishbone instead "rtio": None, # mapped on Wishbone instead "rtio_crg": 10, "kernel_cpu": 11, @@ -111,8 +112,9 @@ class NIST_QC1(BaseSoC, AMPSoC): } csr_map.update(BaseSoC.csr_map) mem_map = { - "rtio": 0x20000000, # (shadow @0xa0000000) - "mailbox": 0x70000000 # (shadow @0xf0000000) + "timer_kernel": 0x10000000, # (shadow @0x90000000) + "rtio": 0x20000000, # (shadow @0xa0000000) + "mailbox": 0x70000000 # (shadow @0xf0000000) } mem_map.update(BaseSoC.mem_map) From b662a6fcbd15ad021c2ecd1a3bee1795929af914 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 15:10:50 +0800 Subject: [PATCH 088/125] gateware/nist_{clock,qc2}: do not conflict with KC705 I2C --- artiq/gateware/nist_clock.py | 4 ++-- artiq/gateware/nist_qc2.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/nist_clock.py b/artiq/gateware/nist_clock.py index dd8edaa22..b6db05efe 100644 --- a/artiq/gateware/nist_clock.py +++ b/artiq/gateware/nist_clock.py @@ -38,8 +38,8 @@ fmc_adapter_io = [ Subsignal("rst", Pins("LPC:LA25_P")), IOStandard("LVTTL")), - ("i2c", 0, - Subsignal("scl", Pins("LPC:IIC_SLC")), + ("i2c_fmc", 0, + Subsignal("scl", Pins("LPC:IIC_SCL")), Subsignal("sda", Pins("LPC:IIC_SDA")), IOStandard("LVCMOS25")), diff --git a/artiq/gateware/nist_qc2.py b/artiq/gateware/nist_qc2.py index 442d66ee6..9fba61b16 100644 --- a/artiq/gateware/nist_qc2.py +++ b/artiq/gateware/nist_qc2.py @@ -48,7 +48,7 @@ fmc_adapter_io = [ Subsignal("rst", Pins("LPC:LA25_P")), IOStandard("LVTTL")), - ("i2c", 0, + ("i2c_fmc", 0, Subsignal("scl", Pins("LPC:IIC_SCL")), Subsignal("sda", Pins("LPC:IIC_SDA")), IOStandard("LVCMOS25")), From a901971e58d3995d1df283c9e1e946e6205ea700 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 15:12:15 +0800 Subject: [PATCH 089/125] gateware/soc: factor code to connect CSR device to kernel CPU --- artiq/gateware/soc.py | 22 ++++++++++++++-------- artiq/gateware/targets/kc705.py | 8 +------- artiq/gateware/targets/pipistrello.py | 12 ++---------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/artiq/gateware/soc.py b/artiq/gateware/soc.py index e97faa0e5..908701ad2 100644 --- a/artiq/gateware/soc.py +++ b/artiq/gateware/soc.py @@ -32,11 +32,17 @@ class AMPSoC: self.mem_map["mailbox"] | 0x80000000, 4) self.submodules.timer_kernel = timer.Timer() - timer_csrs = self.timer_kernel.get_csrs() - timerwb = wishbone.CSRBank(timer_csrs) - self.submodules += timerwb - self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["timer_kernel"]), - timerwb.bus) - self.add_csr_region("timer_kernel", - self.mem_map["timer_kernel"] | 0x80000000, 32, - timer_csrs) + self.register_kernel_cpu_csrdevice("timer_kernel") + + def register_kernel_cpu_csrdevice(self, name): + # make sure the device is not getting connected to the comms-CPU already + assert self.csr_map[name] is None + + csrs = getattr(self, name).get_csrs() + bank = wishbone.CSRBank(csrs) + self.submodules += bank + self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map[name]), + bank.bus) + self.add_csr_region(name, + self.mem_map[name] | 0x80000000, 32, + csrs) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 5854bee17..e7268d3d0 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -134,6 +134,7 @@ class _NIST_Ions(MiniSoC, AMPSoC): def add_rtio(self, rtio_channels): self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) 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) @@ -154,13 +155,6 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.ethphy.crg.cd_eth_rx.clk, self.ethphy.crg.cd_eth_tx.clk) - rtio_csrs = self.rtio.get_csrs() - self.submodules.rtiowb = wishbone.CSRBank(rtio_csrs) - self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["rtio"]), - self.rtiowb.bus) - self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32, - rtio_csrs) - self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, self.get_native_sdram_if()) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index 655ec22b7..ed1966e33 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -209,20 +209,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.LogChannel()) - # RTIO core + # 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) - - # CPU connections - rtio_csrs = self.rtio.get_csrs() - self.submodules.rtiowb = wishbone.CSRBank(rtio_csrs) - self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["rtio"]), - self.rtiowb.bus) - self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32, - rtio_csrs) - self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, self.get_native_sdram_if()) From cfe72c72a24173ef96ff48a76f12d2a087c61c96 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 15:32:10 +0800 Subject: [PATCH 090/125] gateware/kc705: add I2C GPIO core for QC2 --- artiq/gateware/targets/kc705.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index e7268d3d0..3215c7ec0 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -291,8 +291,17 @@ class NIST_CLOCK(_NIST_Ions): class NIST_QC2(_NIST_Ions): """ NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane - and 12 DDS channels. Current implementation for single backplane. + and 12 DDS channels. Current implementation for single backplane. """ + csr_map = { + "i2c": None + } + csr_map.update(_NIST_Ions.csr_map) + mem_map = { + "i2c": 0x30000000 # (shadow @0xb0000000) + } + mem_map.update(_NIST_Ions.mem_map) + def __init__(self, cpu_type="or1k", **kwargs): _NIST_Ions.__init__(self, cpu_type, **kwargs) @@ -341,6 +350,10 @@ class NIST_QC2(_NIST_Ions): assert self.rtio.fine_ts_width <= 3 self.config["DDS_RTIO_CLK_RATIO"] = 24 >> self.rtio.fine_ts_width + i2c = platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.register_kernel_cpu_csrdevice("i2c") + def main(): parser = argparse.ArgumentParser( From 73bfbe51dbd66f3c652746a2fd925c2decca15ab Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 3 Mar 2016 08:33:28 +0000 Subject: [PATCH 091/125] compiler: reject lambdas used as kernel functions (fixes #313). --- artiq/compiler/embedding.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 17155d767..1058c6a70 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -742,6 +742,16 @@ class Stitcher: else: if hasattr(function, "artiq_embedded"): if function.artiq_embedded.function is not None: + if function.__name__ == "": + note = diagnostic.Diagnostic("note", + "lambda created here", {}, + self._function_loc(function.artiq_embedded.function)) + diag = diagnostic.Diagnostic("fatal", + "lambdas cannot be used as kernel functions", {}, + loc, + notes=[note]) + self.engine.process(diag) + # Insert the typed AST for the new function and restart inference. # It doesn't really matter where we insert as long as it is before # the final call. From 423ca03f3b7e0c022ab5b103b7f61c0108acfba4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 17:46:42 +0800 Subject: [PATCH 092/125] runtime: bit-banged i2c support (untested) --- artiq/gateware/targets/kc705.py | 1 + artiq/runtime/Makefile | 2 +- artiq/runtime/i2c.c | 194 ++++++++++++++++++++++++++++++++ artiq/runtime/i2c.h | 10 ++ artiq/runtime/ksupport.c | 9 +- 5 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 artiq/runtime/i2c.c create mode 100644 artiq/runtime/i2c.h diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 3215c7ec0..778cdc68d 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -353,6 +353,7 @@ class NIST_QC2(_NIST_Ions): i2c = platform.request("i2c") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.register_kernel_cpu_csrdevice("i2c") + self.config["I2C_BUS_COUNT"] = 1 def main(): diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 2a7b37e2e..58fca022f 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o dds.o + bridge.o rtio.o dds.o i2c.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/i2c.c b/artiq/runtime/i2c.c new file mode 100644 index 000000000..af8b9bed7 --- /dev/null +++ b/artiq/runtime/i2c.c @@ -0,0 +1,194 @@ +#include + +#include "rtio.h" +#include "i2c.h" + + +static void i2c_halfperiod() +{ + timer_kernel_en_write(0); + timer_kernel_load_write(CONFIG_CLOCK_FREQUENCY/10000); + timer_kernel_reload_write(0); + timer_kernel_en_write(1); + + timer_kernel_update_value_write(1); + while(timer_kernel_value_read() != 0) + timer_kernel_update_value_write(1); +} + +#if (defined CONFIG_I2C_BUS_COUNT) && (CONFIG_I2C_BUS_COUNT > 0) + +#define SDA_BIT (1 << (2*busno + 1)) +#define SCL_BIT (1 << (2*busno)) + +static int i2c_sda_i(int busno) +{ + if(busno >= CONFIG_I2C_BUS_COUNT) + return 1; + else + return i2c_in_read() & SDA_BIT; +} + +static void i2c_sda_oe(int busno, int oe) +{ + int reg; + + reg = i2c_oe_read(); + if(oe) + reg |= SDA_BIT; + else + reg &= ~SDA_BIT; + i2c_oe_write(reg); +} + +static void i2c_sda_o(int busno, int o) +{ + int reg; + + reg = i2c_out_read(); + if(o) + reg |= SDA_BIT; + else + reg &= ~SDA_BIT; + i2c_out_write(reg); +} + +static void i2c_scl_oe(int busno, int oe) +{ + int reg; + + reg = i2c_oe_read(); + if(oe) + reg |= SCL_BIT; + else + reg &= ~SCL_BIT; + i2c_oe_write(reg); +} + +static void i2c_scl_o(int busno, int o) +{ + int reg; + + reg = i2c_out_read(); + if(o) + reg |= SCL_BIT; + else + reg &= ~SCL_BIT; + i2c_out_write(reg); +} + +#else + +static int i2c_sda_i(int busno) +{ + return 1; +} +static void i2c_sda_oe(int busno, int oe) {} +static void i2c_sda_o(int busno, int o) {} +static void i2c_scl_oe(int busno, int oe) {} +static void i2c_scl_o(int busno, int o) {} + +#endif + + +int i2c_init(int busno) +{ + /* Set SCL as output, and high level */ + i2c_scl_o(busno, 1); + i2c_scl_oe(busno, 1); + /* Prepare a zero level on SDA so that i2c_sda_oe pulls it down */ + i2c_sda_o(busno, 0); + /* Release SDA */ + i2c_sda_oe(busno, 0); + + /* Check the I2C bus is ready */ + i2c_halfperiod(); + i2c_halfperiod(); + if(i2c_sda_i(busno)) + return 1; /* success */ + else + return 0; +} + +void i2c_start(int busno) +{ + /* Set SCL high then SDA low */ + i2c_scl_o(busno, 1); + i2c_halfperiod(); + i2c_sda_oe(busno, 1); + i2c_halfperiod(); +} + +void i2c_stop(int busno) +{ + /* First, make sure SCL is low, so that the target releases the SDA line */ + i2c_scl_o(busno, 0); + i2c_halfperiod(); + /* Set SCL high then SDA high */ + i2c_sda_oe(busno, 1); + i2c_scl_o(busno, 1); + i2c_halfperiod(); + i2c_sda_oe(busno, 0); + i2c_halfperiod(); +} + +int i2c_write(int busno, char b) +{ + int i; + + /* MSB first */ + for(i=7;i>=0;i--) { + /* Set SCL low and set our bit on SDA */ + i2c_scl_o(busno, 0); + i2c_sda_oe(busno, b & (1 << i) ? 0 : 1); + i2c_halfperiod(); + /* Set SCL high ; data is shifted on the rising edge of SCL */ + i2c_scl_o(busno, 1); + i2c_halfperiod(); + } + /* Check ack */ + /* Set SCL low, then release SDA so that the I2C target can respond */ + i2c_scl_o(busno, 0); + i2c_halfperiod(); + i2c_sda_oe(busno, 0); + /* Set SCL high and check for ack */ + i2c_scl_o(busno, 1); + i2c_halfperiod(); + /* returns 1 if acked (I2C target pulled SDA low) */ + return !i2c_sda_i(busno); +} + +char i2c_read(int busno, int ack) +{ + int i; + char b; + + /* Set SCL low first, otherwise setting SDA as input may cause a transition + * on SDA with SCL high which will be interpreted as START/STOP condition. + */ + i2c_scl_o(busno, 0); + i2c_halfperiod(); /* make sure SCL has settled low */ + i2c_sda_oe(busno, 0); + + b = 0; + /* MSB first */ + for(i=7;i>=0;i--) { + i2c_scl_o(busno, 0); + i2c_halfperiod(); + /* Set SCL high and shift data */ + i2c_scl_o(busno, 1); + i2c_halfperiod(); + if(i2c_sda_i(busno)) b |= (1 << i); + } + /* Send ack */ + /* Set SCL low and pull SDA low when acking */ + i2c_scl_o(busno, 0); + if(ack) + i2c_sda_oe(busno, 1); + i2c_halfperiod(); + /* then set SCL high */ + i2c_scl_o(busno, 1); + i2c_halfperiod(); + + return b; +} diff --git a/artiq/runtime/i2c.h b/artiq/runtime/i2c.h new file mode 100644 index 000000000..b30e25706 --- /dev/null +++ b/artiq/runtime/i2c.h @@ -0,0 +1,10 @@ +#ifndef __I2C_H +#define __I2C_H + +int i2c_init(int busno); +void i2c_start(int busno); +void i2c_stop(int busno); +int i2c_write(int busno, char b); +char i2c_read(int busno, int ack); + +#endif diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 8ed50f131..b61f956ee 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -13,8 +13,9 @@ #include "messages.h" #include "bridge.h" #include "artiq_personality.h" -#include "dds.h" #include "rtio.h" +#include "dds.h" +#include "i2c.h" double round(double x); @@ -117,6 +118,12 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, + {"i2c_init", &i2c_init}, + {"i2c_start", &i2c_start}, + {"i2c_stop", &i2c_stop}, + {"i2c_write", &i2c_write}, + {"i2c_read", &i2c_read}, + {"cache_get", &cache_get}, {"cache_put", &cache_put}, From c2fcefc31ffbf6e0c1773ec9a1bd299de6cbde07 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Mar 2016 19:48:06 +0800 Subject: [PATCH 093/125] runtime/rtio: cleanup include --- artiq/runtime/rtio.c | 1 + artiq/runtime/rtio.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 6a136267c..34aca2d78 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -1,5 +1,6 @@ #include +#include "artiq_personality.h" #include "rtio.h" void rtio_init(void) diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 709a85be3..a1ab8a90a 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -2,8 +2,6 @@ #define __RTIO_H #include -#include -#include "artiq_personality.h" #define RTIO_O_STATUS_FULL 1 #define RTIO_O_STATUS_UNDERFLOW 2 From dc6d1168241a6953035a7939d11b4c1c05db9b7e Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Thu, 3 Mar 2016 21:57:27 +0100 Subject: [PATCH 094/125] spi: have write() delay by transfer duration --- artiq/coredevice/spi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 264481e21..159bd4da4 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -198,11 +198,11 @@ class SPIMaster: the previous transfer's read data is available in the ``data`` register. - This method advances the timeline by the duration of the - RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + This method advances the timeline by the duration of the SPI transfer. + If a transfer is to be chained, the timeline needs to be rewound. """ rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(3*self.ref_period_mu) + delay_mu(self.xfer_period_mu + self.write_period_mu) @kernel def read_async(self): From 669fbaa4f12ac7580a631539575e421bb0e0b9ea Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 00:00:25 +0100 Subject: [PATCH 095/125] ad53xx->ad5360 and refactor --- artiq/coredevice/ad5360.py | 95 +++++++++++++++++++++++++++ artiq/coredevice/ad53xx.py | 71 -------------------- doc/manual/core_drivers_reference.rst | 5 ++ 3 files changed, 100 insertions(+), 71 deletions(-) create mode 100644 artiq/coredevice/ad5360.py delete mode 100644 artiq/coredevice/ad53xx.py diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py new file mode 100644 index 000000000..9e1b6781c --- /dev/null +++ b/artiq/coredevice/ad5360.py @@ -0,0 +1,95 @@ +from artiq.language.core import kernel, portable, delay +from artiq.language.units import ns +from artiq.coredevice import spi + + +_AD5360_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_CS_POLARITY | + 0*spi.SPI_CLK_POLARITY | 1*spi.SPI_CLK_PHASE | + 0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX) + +_AD5360_CMD_DATA = 3 << 22 +_AD5360_CMD_OFFSET = 2 << 22 +_AD5360_CMD_GAIN = 1 << 22 +_AD5360_CMD_SPECIAL = 0 << 22 + + +@portable +def _AD5360_WRITE_CHANNEL(c): + return (c + 8) << 16 + +_AD5360_SPECIAL_NOP = 0 << 16 +_AD5360_SPECIAL_CONTROL = 1 << 16 +_AD5360_SPECIAL_OFS0 = 2 << 16 +_AD5360_SPECIAL_OFS1 = 3 << 16 +_AD5360_SPECIAL_READ = 3 << 16 + + +@portable +def _AD5360_READ_CHANNEL(ch): + return (ch + 8) << 7 + +_AD5360_READ_X1A = 0x000 << 7 +_AD5360_READ_X1B = 0x040 << 7 +_AD5360_READ_OFFSET = 0x080 << 7 +_AD5360_READ_GAIN = 0x0c0 << 7 +_AD5360_READ_CONTROL = 0x101 << 7 +_AD5360_READ_OFS0 = 0x102 << 7 +_AD5360_READ_OFS1 = 0x103 << 7 + + +class AD5360: + """ + Support for the Analog devices AD53[67][0123] + multi-channel Digital to Analog Converters + """ + + def __init__(self, dmgr, spi_bus, ldac=None, chip_select=0): + self.core = dmgr.get("core") + self.bus = dmgr.get(spi_bus) + if ldac is not None: + ldac = dmgr.get(ldac) + self.ldac = ldac + self.chip_select = chip_select + + @kernel + def setup_bus(self, write_div=4, read_div=7): + # write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising) + # read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid) + self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div) + self.bus.set_xfer(self.chip_select, 24, 0) + + @kernel + def write_offsets(self, value=0x1fff): + value &= 0x3fff + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value + ) << 8) + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value + ) << 8) + + @kernel + def write_channel(self, channel=0, value=0, op=_AD5360_CMD_DATA): + channel &= 0x3f + value &= 0xffff + self.bus.write((op | _AD5360_WRITE_CHANNEL(channel) | value) << 8) + + @kernel + def write_channels(self, values, first=0, op=_AD5360_CMD_DATA): + for i in range(len(values)): + self.write_channel(i + first, values[i], op) + + @kernel + def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): + channel &= 0x3f + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | + _AD5360_READ_CHANNEL(channel)) << 8) + self.bus.set_xfer(self.chip_select, 0, 24) + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_NOP) << 8) + self.bus.read_async() + self.bus.set_xfer(self.chip_select, 24, 0) + return self.bus.input_async() & 0xffff + + @kernel + def load(self): + self.ldac.off() + delay(24*ns) + self.ldac.on() diff --git a/artiq/coredevice/ad53xx.py b/artiq/coredevice/ad53xx.py deleted file mode 100644 index 6ab325717..000000000 --- a/artiq/coredevice/ad53xx.py +++ /dev/null @@ -1,71 +0,0 @@ -from artiq.language.core import (kernel, portable, delay, delay_mu, int) -from artiq.language.units import ns -from artiq.coredevice import spi - - -_AD53xx_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_CS_POLARITY | - 0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE | - 0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX) - -_AD53xx_MODE_WRITE_X1 = 3 << 22 -_AD53xx_MODE_WRITE_C = 2 << 22 -_AD53xx_MODE_WRITE_M = 1 << 22 -_AD53xx_MODE_SPECIAL = 0 << 22 - -_AD53xx_GROUP = portable(lambda g: ((g + 1) << 19)) -_AD53xx_GROUP_ALL = 0 << 19 -_AD53xx_GROUP_01234 = 6 << 19 -_AD53xx_GROUP_1234 = 7 << 19 -_AD53xx_CHANNEL_ALL = 0 << 16 -_AD53xx_CHANNEL = portable(lambda g: g << 16) - -_AD53xx_SPECIAL_NOP = 0 << 16 -_AD53xx_SPECIAL_CONTROL = 1 << 16 -_AD53xx_SPECIAL_OFS0 = 2 << 16 -_AD53xx_SPECIAL_OFS1 = 3 << 16 -_AD53xx_SPECIAL_AB_SELECT = portable(lambda i: (i + 6) << 16) -_AD53xx_SPECIAL_AB_SELECT_ALL = 11 << 16 - -_AD53xx_READ_X1A = portable(lambda ch: (0x00 | (ch + 8)) << 7) -_AD53xx_READ_X1B = portable(lambda ch: (0x40 | (ch + 8)) << 7) -_AD53xx_READ_C = portable(lambda ch: (0x80 | (ch + 8)) << 7) -_AD53xx_READ_M = portable(lambda ch: (0xc0 | (ch + 8)) << 7) -_AD53xx_READ_CONTROL = 0x101 << 7 -_AD53xx_READ_OFS0 = 0x102 << 7 -_AD53xx_READ_OFS1 = 0x103 << 7 -_AD53xx_READ_AB_SELECT = portable(lambda i: (0x100 + (i + 6)) << 7) - - -class AD53xx: - def __init__(self, dmgr, spi_bus, ldac=None, - chip_select=0, write_div=4, read_div=6): - self.core = dmgr.get("core") - self.bus = dmgr.get(spi_bus) - if ldac is not None: - ldac = dmgr.get(ldac) - self.ldac = ldac - self.chip_select = chip_select - self.write_div = write_div - self.read_div = read_div - - @kernel - def bus_setup(self): - self.bus.set_config_mu(_AD53xx_SPI_CONFIG, self.write_div, - self.read_div) - self.bus.set_xfer(self.chip_select, 24, 0) - - @kernel - def _channel_address(self, channel=0): - return int((channel + 8) << 16) - - @kernel - def write_x1(self, channel=0, value=0): - ch = self._channel_address(channel) - self.bus.write(_AD53xx_MODE_WRITE_X1 | ch | value) - delay_mu(int(self.bus.xfer_period_mu + self.bus.write_period_mu)) - - @kernel - def load(self): - self.ldac.off() - delay(20*ns) - self.ldac.on() diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index e88e077e5..afc41aee0 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -30,6 +30,11 @@ These drivers are for the core device and the peripherals closely integrated int .. automodule:: artiq.coredevice.spi :members: +:mod:`artiq.coredevice.ad5360` module +------------------------------------- + +.. automodule:: artiq.coredevice.ad5360 + :members: :mod:`artiq.coredevice.exceptions` module ----------------------------------------- From 7ff0c89d51cce9ade35f1f7ca602e226da8922aa Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 00:03:48 +0100 Subject: [PATCH 096/125] kc705.clock: add all spi buses --- artiq/gateware/targets/kc705.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 778cdc68d..16ad41bf8 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -264,7 +264,7 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) - for i in range(1): # spi1 and spi2 collide in pinout with ttl + for i in range(3): phy = spi.SPIMaster(self.platform.request("spi", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy( From 6e44c5424dbadc4d6ca36bcbb0a497cad92849ec Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Mar 2016 08:37:38 +0000 Subject: [PATCH 097/125] coredevice.ttl: add missed int64 conversion. --- artiq/coredevice/ttl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index b841bde28..2cd308f55 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -238,7 +238,7 @@ class TTLClockGen: # in RTIO cycles self.previous_timestamp = int(0, width=64) - self.acc_width = 24 + self.acc_width = int(24, width=64) @portable def frequency_to_ftw(self, frequency): From 4352d1501625828ee1b550d33c0f36543215ff16 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 4 Mar 2016 16:59:35 +0800 Subject: [PATCH 098/125] coredevice/core: add ref_multiplier and coarse_ref_period attributes --- artiq/coredevice/core.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index ba03b223b..32c2501bd 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -60,11 +60,17 @@ class Core: The time machine unit is equal to this period. :param external_clock: whether the core device should switch to its external RTIO clock input instead of using its internal oscillator. + :param ref_multiplier: ratio between the RTIO fine timestamp frequency + and the RTIO coarse timestamp frequency (e.g. SERDES multiplication + factor). :param comm_device: name of the device used for communications. """ - def __init__(self, dmgr, ref_period, external_clock=False, comm_device="comm"): + def __init__(self, dmgr, ref_period, external_clock=False, + ref_multiplier=8, comm_device="comm"): self.ref_period = ref_period self.external_clock = external_clock + self.ref_multiplier = ref_multiplier + self.coarse_ref_period = ref_period*ref_multiplier self.comm = dmgr.get(comm_device) self.first_run = True From 33648277448d8beb438b0d68f3bdda837dab37b2 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 4 Mar 2016 16:59:59 +0800 Subject: [PATCH 099/125] ttl/TTLClockGen: fix FTW computation with ref_multiplier != 1 --- artiq/coredevice/ttl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 2cd308f55..9ac12ca48 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -245,14 +245,14 @@ class TTLClockGen: """Returns the frequency tuning word corresponding to the given frequency. """ - return round(2**self.acc_width*frequency*self.core.ref_period) + return round(2**self.acc_width*frequency*self.core.coarse_ref_period) @portable def ftw_to_frequency(self, ftw): """Returns the frequency corresponding to the given frequency tuning word. """ - return ftw/self.core.ref_period/2**self.acc_width + return ftw/self.core.coarse_ref_period/2**self.acc_width @kernel def set_mu(self, frequency): From ff4a46c278408673ab1dc687e75b880b14916607 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:16:12 +0800 Subject: [PATCH 100/125] runtime/i2c: make syscalls more ARTIQ-Python-friendly --- artiq/coredevice/exceptions.py | 4 ++++ artiq/runtime/i2c.c | 13 ++++++------- artiq/runtime/i2c.h | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 05d9d1c29..36314ed1a 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -113,3 +113,7 @@ class DDSBatchError(Exception): or when too many commands are batched. """ artiq_builtin = True + +class I2CError(Exception): + """Raised with a I2C transaction fails.""" + artiq_builtin = True diff --git a/artiq/runtime/i2c.c b/artiq/runtime/i2c.c index af8b9bed7..b1cfb733a 100644 --- a/artiq/runtime/i2c.c +++ b/artiq/runtime/i2c.c @@ -1,5 +1,6 @@ #include +#include "artiq_personality.h" #include "rtio.h" #include "i2c.h" @@ -91,7 +92,7 @@ static void i2c_scl_o(int busno, int o) {} #endif -int i2c_init(int busno) +void i2c_init(int busno) { /* Set SCL as output, and high level */ i2c_scl_o(busno, 1); @@ -104,10 +105,8 @@ int i2c_init(int busno) /* Check the I2C bus is ready */ i2c_halfperiod(); i2c_halfperiod(); - if(i2c_sda_i(busno)) - return 1; /* success */ - else - return 0; + if(!i2c_sda_i(busno)) + artiq_raise_from_c("I2CError", "SDA is stuck low") } void i2c_start(int busno) @@ -132,7 +131,7 @@ void i2c_stop(int busno) i2c_halfperiod(); } -int i2c_write(int busno, char b) +int i2c_write(int busno, int b) { int i; @@ -158,7 +157,7 @@ int i2c_write(int busno, char b) return !i2c_sda_i(busno); } -char i2c_read(int busno, int ack) +int i2c_read(int busno, int ack) { int i; char b; diff --git a/artiq/runtime/i2c.h b/artiq/runtime/i2c.h index b30e25706..c7aab6e22 100644 --- a/artiq/runtime/i2c.h +++ b/artiq/runtime/i2c.h @@ -1,10 +1,10 @@ #ifndef __I2C_H #define __I2C_H -int i2c_init(int busno); +void i2c_init(int busno); void i2c_start(int busno); void i2c_stop(int busno); -int i2c_write(int busno, char b); -char i2c_read(int busno, int ack); +int i2c_write(int busno, int b); +int i2c_read(int busno, int ack); #endif From 790269eee7ed37fe336872b34300aa8d77bfd0c2 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:17:08 +0800 Subject: [PATCH 101/125] master/worker_db: make arguments optional in DDB entries --- artiq/master/worker_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/master/worker_db.py b/artiq/master/worker_db.py index 487257077..fd41647b3 100644 --- a/artiq/master/worker_db.py +++ b/artiq/master/worker_db.py @@ -90,7 +90,7 @@ def _create_device(desc, device_mgr): if ty == "local": module = importlib.import_module(desc["module"]) device_class = getattr(module, desc["class"]) - return device_class(device_mgr, **desc["arguments"]) + return device_class(device_mgr, **desc.get("arguments", {})) elif ty == "controller": if desc.get("best_effort", False): cls = BestEffortClient From 2f1a2782d28698258cb6c92a2de3d6ef1150096e Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:17:41 +0800 Subject: [PATCH 102/125] coredevice: add I2C, PCA9548, TCA6424A drivers --- artiq/coredevice/i2c.py | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 artiq/coredevice/i2c.py diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py new file mode 100644 index 000000000..09f449b0c --- /dev/null +++ b/artiq/coredevice/i2c.py @@ -0,0 +1,75 @@ +from artiq.language.core import syscall, kernel +from artiq.language.types import TBool, TInt8, TInt32, TNone +from artiq.coredevice.exceptions import I2CError + + +@syscall +def i2c_init(busno: TInt32) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def i2c_start(busno: TInt32) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def i2c_stop(busno: TInt32) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def i2c_write(busno: TInt32, b: TInt32) -> TBool: + raise NotImplementedError("syscall not simulated") + + +@syscall +def i2c_read(busno: TInt32, ack: TBool) -> TInt32: + raise NotImplementedError("syscall not simulated") + + +class PCA9548: + def __init__(self, dmgr, busno=0, address=0x74): + self.core = dmgr.get("core") + self.busno = busno + self.address = address + + @kernel + def set(self, channel): + i2c_init(self.busno) + i2c_start(self.busno) + try: + if not i2c_write(self.busno, self.address): + raise I2CError("PCA9548 failed to ack address") + if not i2c_write(self.busno, 1 << channel): + raise I2CError("PCA9548 failed to ack control word") + finally: + i2c_stop(self.busno) + + +class TCA6424A: + def __init__(self, dmgr, busno=0, address=0x44): + self.core = dmgr.get("core") + self.busno = busno + self.address = address + + @kernel + def _write24(self, command, value): + i2c_init(self.busno) + i2c_start(self.busno) + try: + if not i2c_write(self.busno, self.address): + raise I2CError("TCA6424A failed to ack address") + if not i2c_write(self.busno, command): + raise I2CError("TCA6424A failed to ack command") + for i in range(3): + if not i2c_write(self.busno, value >> 16): + raise I2CError("TCA6424A failed to ack command") + value <<= 8 + finally: + i2c_stop(self.busno) + + @kernel + def set(self, outputs): + self._write24(0x8c, 0) # set all directions to output + self._write24(0x84, output) # set levels From 6b8efd10fd7add08fda0defa33e3817cb49b4e4c Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:18:50 +0800 Subject: [PATCH 103/125] runtime/i2c: fix artiq_raise_from_c invokation --- artiq/runtime/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/i2c.c b/artiq/runtime/i2c.c index b1cfb733a..22277432d 100644 --- a/artiq/runtime/i2c.c +++ b/artiq/runtime/i2c.c @@ -106,7 +106,7 @@ void i2c_init(int busno) i2c_halfperiod(); i2c_halfperiod(); if(!i2c_sda_i(busno)) - artiq_raise_from_c("I2CError", "SDA is stuck low") + artiq_raise_from_c("I2CError", "SDA is stuck low", 0, 0, 0); } void i2c_start(int busno) From a8a74d7840fa5dc7bf19a5ce52fb2303b4a92050 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:19:59 +0800 Subject: [PATCH 104/125] targets/kc705: enable I2C for all hardware adapters --- artiq/gateware/targets/kc705.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 16ad41bf8..bfa778794 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -94,8 +94,11 @@ _ams101_dac = [ class _NIST_Ions(MiniSoC, AMPSoC): csr_map = { - "timer_kernel": None, # mapped on Wishbone instead - "rtio": None, # mapped on Wishbone instead + # mapped on Wishbone instead + "timer_kernel": None, + "rtio": None, + "i2c": None, + "rtio_crg": 13, "kernel_cpu": 14, "rtio_moninj": 15, @@ -105,6 +108,7 @@ class _NIST_Ions(MiniSoC, AMPSoC): mem_map = { "timer_kernel": 0x10000000, # (shadow @0x90000000) "rtio": 0x20000000, # (shadow @0xa0000000) + "i2c": 0x30000000, # (shadow @0xb0000000) "mailbox": 0x70000000 # (shadow @0xf0000000) } mem_map.update(MiniSoC.mem_map) @@ -131,6 +135,11 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.platform.add_extension(_ams101_dac) + i2c = self.platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.register_kernel_cpu_csrdevice("i2c") + self.config["I2C_BUS_COUNT"] = 1 + def add_rtio(self, rtio_channels): self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) self.submodules.rtio = rtio.RTIO(rtio_channels) @@ -293,15 +302,6 @@ class NIST_QC2(_NIST_Ions): NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane and 12 DDS channels. Current implementation for single backplane. """ - csr_map = { - "i2c": None - } - csr_map.update(_NIST_Ions.csr_map) - mem_map = { - "i2c": 0x30000000 # (shadow @0xb0000000) - } - mem_map.update(_NIST_Ions.mem_map) - def __init__(self, cpu_type="or1k", **kwargs): _NIST_Ions.__init__(self, cpu_type, **kwargs) @@ -350,11 +350,6 @@ class NIST_QC2(_NIST_Ions): assert self.rtio.fine_ts_width <= 3 self.config["DDS_RTIO_CLK_RATIO"] = 24 >> self.rtio.fine_ts_width - i2c = platform.request("i2c") - self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) - self.register_kernel_cpu_csrdevice("i2c") - self.config["I2C_BUS_COUNT"] = 1 - def main(): parser = argparse.ArgumentParser( From df71b8203721a435f5608ce8ec509e146b99dbf3 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:43:13 +0800 Subject: [PATCH 105/125] coredevice/i2c: fix imports --- artiq/coredevice/i2c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index 09f449b0c..fb620e684 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -1,5 +1,5 @@ from artiq.language.core import syscall, kernel -from artiq.language.types import TBool, TInt8, TInt32, TNone +from artiq.language.types import TBool, TInt32, TNone from artiq.coredevice.exceptions import I2CError From 70f0a7447f63221398ed0440f8dee2d1b16c3b50 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:47:24 +0800 Subject: [PATCH 106/125] coredevice/PCA9548: fix I2C address --- artiq/coredevice/i2c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index fb620e684..c48b89672 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -29,7 +29,7 @@ def i2c_read(busno: TInt32, ack: TBool) -> TInt32: class PCA9548: - def __init__(self, dmgr, busno=0, address=0x74): + def __init__(self, dmgr, busno=0, address=0xe8): self.core = dmgr.get("core") self.busno = busno self.address = address From 200cddc34685bc29d4420a1a4546ad1f1a31ef5f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 00:51:13 +0800 Subject: [PATCH 107/125] coredevice/i2c: fix exception message --- artiq/coredevice/i2c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index c48b89672..b406b34ea 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -64,7 +64,7 @@ class TCA6424A: raise I2CError("TCA6424A failed to ack command") for i in range(3): if not i2c_write(self.busno, value >> 16): - raise I2CError("TCA6424A failed to ack command") + raise I2CError("TCA6424A failed to ack data") value <<= 8 finally: i2c_stop(self.busno) From 4ae3ca5f23b2a08657bda2556290e03fa0607c73 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 18:12:30 +0100 Subject: [PATCH 108/125] spi/ad5360: refactor, small fixes --- artiq/coredevice/ad5360.py | 41 +++++++++++++++++++--------------- artiq/coredevice/spi.py | 13 +++++------ examples/master/device_db.pyon | 11 +++++++-- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index 9e1b6781c..0df78f824 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -1,7 +1,8 @@ -from artiq.language.core import kernel, portable, delay -from artiq.language.units import ns +from artiq.language.core import kernel, portable, delay_mu from artiq.coredevice import spi +# Designed from the data sheets and somewhat after the linux kernel +# iio driver. _AD5360_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_CS_POLARITY | 0*spi.SPI_CLK_POLARITY | 1*spi.SPI_CLK_PHASE | @@ -21,7 +22,7 @@ _AD5360_SPECIAL_NOP = 0 << 16 _AD5360_SPECIAL_CONTROL = 1 << 16 _AD5360_SPECIAL_OFS0 = 2 << 16 _AD5360_SPECIAL_OFS1 = 3 << 16 -_AD5360_SPECIAL_READ = 3 << 16 +_AD5360_SPECIAL_READ = 5 << 16 @portable @@ -43,11 +44,12 @@ class AD5360: multi-channel Digital to Analog Converters """ - def __init__(self, dmgr, spi_bus, ldac=None, chip_select=0): + def __init__(self, dmgr, spi_device, ldac_device=None, + chip_select=1): self.core = dmgr.get("core") - self.bus = dmgr.get(spi_bus) - if ldac is not None: - ldac = dmgr.get(ldac) + self.bus = dmgr.get(spi_device) + if ldac_device is not None: + ldac = dmgr.get(ldac_device) self.ldac = ldac self.chip_select = chip_select @@ -58,32 +60,35 @@ class AD5360: self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div) self.bus.set_xfer(self.chip_select, 24, 0) + @kernel + def write(self, data): + self.bus.write(data << 8) + delay_mu(self.bus.ref_period_mu) # get to 20ns min cs high + @kernel def write_offsets(self, value=0x1fff): value &= 0x3fff - self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value - ) << 8) - self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value - ) << 8) + self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value) + self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value) @kernel def write_channel(self, channel=0, value=0, op=_AD5360_CMD_DATA): channel &= 0x3f value &= 0xffff - self.bus.write((op | _AD5360_WRITE_CHANNEL(channel) | value) << 8) + self.write(op | _AD5360_WRITE_CHANNEL(channel) | value) @kernel - def write_channels(self, values, first=0, op=_AD5360_CMD_DATA): + def write_channels(self, values, op=_AD5360_CMD_DATA): for i in range(len(values)): - self.write_channel(i + first, values[i], op) + self.write_channel(i, values[i], op) @kernel def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): channel &= 0x3f - self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | - _AD5360_READ_CHANNEL(channel)) << 8) + self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | + _AD5360_READ_CHANNEL(channel)) self.bus.set_xfer(self.chip_select, 0, 24) - self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_NOP) << 8) + self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_NOP) self.bus.read_async() self.bus.set_xfer(self.chip_select, 24, 0) return self.bus.input_async() & 0xffff @@ -91,5 +96,5 @@ class AD5360: @kernel def load(self): self.ldac.off() - delay(24*ns) + delay_mu(3*self.bus.ref_period_mu) self.ldac.on() diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 159bd4da4..6221c8f53 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -38,17 +38,16 @@ class SPIMaster: * If desired, :meth:`write` ``data`` queuing the next (possibly chained) transfer. - :param ref_period: clock period of the SPI core. :param channel: RTIO channel number of the SPI bus to control. """ - def __init__(self, dmgr, ref_period, channel): + def __init__(self, dmgr, channel): self.core = dmgr.get("core") - self.ref_period = ref_period - self.ref_period_mu = int(seconds_to_mu(ref_period, self.core)) + self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period, + self.core) self.channel = channel - self.write_period_mu = int(0) - self.read_period_mu = int(0) - self.xfer_period_mu = int(0) + self.write_period_mu = int(0, 64) + self.read_period_mu = int(0, 64) + self.xfer_period_mu = int(0, 64) # A full transfer takes write_period_mu + xfer_period_mu. # Chained transfers can happen every xfer_period_mu. # The second transfer of a chain can be written 2*ref_period_mu diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index 1644ed176..dce234bf2 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -81,7 +81,7 @@ "arguments": {"channel": 19} }, - "ams101_ldac": { + "ttl_ams101_ldac": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", @@ -94,7 +94,7 @@ "arguments": {"channel": 21} }, - "ams101_spi": { + "spi_ams101": { "type": "local", "module": "artiq.coredevice.spi", "class": "SPIMaster", @@ -108,6 +108,13 @@ "arguments": {"channel": 23} }, + "dac0": { + "type": "local", + "module": "artiq.coredevice.ad5360", + "class": "AD5360", + "arguments": {"spi_device": "spi0", "ldac_device": "ttl0"} + }, + "dds_bus": { "type": "local", "module": "artiq.coredevice.dds", From 710717ca9be375b54a920530ca4555f0e030490f Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 18:14:24 +0100 Subject: [PATCH 109/125] ad5360: add batched zero-length multi-channel set() --- artiq/coredevice/ad5360.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index 0df78f824..af9f4f9e7 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -98,3 +98,15 @@ class AD5360: self.ldac.off() delay_mu(3*self.bus.ref_period_mu) self.ldac.on() + + @kernel + def set(self, values, op=_AD5360_CMD_DATA): + # write() compensation + delay_mu(-len(values)*(self.bus.xfer_period_mu + + self.bus.write_period_mu + + self.bus.ref_period_mu) - + 3*self.bus.ref_period_mu) # latency alignment + self.write_channels(values, op) + delay_mu(3*self.bus.ref_period_mu) # latency alignment + self.load() + delay_mu(-3*self.bus.ref_period_mu) # load() compensation From e834a8834028ba329c7cbdcfe87a5f2355467ac7 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 18:15:35 +0100 Subject: [PATCH 110/125] ad5360: style --- artiq/coredevice/ad5360.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index af9f4f9e7..da045c8e0 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -44,8 +44,7 @@ class AD5360: multi-channel Digital to Analog Converters """ - def __init__(self, dmgr, spi_device, ldac_device=None, - chip_select=1): + def __init__(self, dmgr, spi_device, ldac_device=None, chip_select=1): self.core = dmgr.get("core") self.bus = dmgr.get(spi_device) if ldac_device is not None: From 725943fee24475186c619c6923b07aba4de8f874 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 18:53:05 +0100 Subject: [PATCH 111/125] ad5360: add busy and update timings --- artiq/coredevice/ad5360.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index da045c8e0..d70c90e25 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -1,4 +1,6 @@ -from artiq.language.core import kernel, portable, delay_mu +from artiq.language.core import (kernel, portable, delay_mu, delay, + seconds_to_mu) +from artiq.language.units import ns, us from artiq.coredevice import spi # Designed from the data sheets and somewhat after the linux kernel @@ -95,17 +97,22 @@ class AD5360: @kernel def load(self): self.ldac.off() - delay_mu(3*self.bus.ref_period_mu) + # t13 = 10ns ldac pulse width low + delay_mu(2*self.bus.ref_period_mu) self.ldac.on() @kernel def set(self, values, op=_AD5360_CMD_DATA): - # write() compensation + # compensate all delays that will be applied delay_mu(-len(values)*(self.bus.xfer_period_mu + self.bus.write_period_mu + self.bus.ref_period_mu) - - 3*self.bus.ref_period_mu) # latency alignment + 3*self.bus.ref_period_mu - + seconds_to_mu(1.5*us) - + seconds_to_mu(3*us)) self.write_channels(values, op) - delay_mu(3*self.bus.ref_period_mu) # latency alignment + 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.load() - delay_mu(-3*self.bus.ref_period_mu) # load() compensation + delay_mu(-2*self.bus.ref_period_mu + # load(), t13 + seconds_to_mu(3*us)) # t16 dac response time From eb2ec40b3aa91b09174242a73c557f2718754268 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 19:01:29 +0100 Subject: [PATCH 112/125] ad5360: un-factor write_channels --- artiq/coredevice/ad5360.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index d70c90e25..701f0d775 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -50,8 +50,7 @@ class AD5360: self.core = dmgr.get("core") self.bus = dmgr.get(spi_device) if ldac_device is not None: - ldac = dmgr.get(ldac_device) - self.ldac = ldac + self.ldac = dmgr.get(ldac_device) self.chip_select = chip_select @kernel @@ -78,11 +77,6 @@ class AD5360: value &= 0xffff self.write(op | _AD5360_WRITE_CHANNEL(channel) | value) - @kernel - def write_channels(self, values, op=_AD5360_CMD_DATA): - for i in range(len(values)): - self.write_channel(i, values[i], op) - @kernel def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): channel &= 0x3f @@ -110,7 +104,8 @@ class AD5360: 3*self.bus.ref_period_mu - seconds_to_mu(1.5*us) - seconds_to_mu(3*us)) - self.write_channels(values, op) + 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.load() From 18ccac717ba9d3a30651626e58483a54df9c71af Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 19:46:18 +0100 Subject: [PATCH 113/125] ad5360: t16 is a max --- artiq/coredevice/ad5360.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index 701f0d775..a72b8909a 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -102,12 +102,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) - - seconds_to_mu(3*us)) + 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.load() - delay_mu(-2*self.bus.ref_period_mu + # load(), t13 - seconds_to_mu(3*us)) # t16 dac response time + delay_mu(-2*self.bus.ref_period_mu) # load(), t13 From f2b4b975a3f02fb8e82e682e5927536362c2900b Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 4 Mar 2016 23:36:17 +0100 Subject: [PATCH 114/125] ad5360: add documentation and an example --- artiq/coredevice/ad5360.py | 64 +++++++++++++++++++ .../coredevice_examples/simple/ad5360.py | 17 +++++ 2 files changed, 81 insertions(+) create mode 100644 examples/master/repository/coredevice_examples/simple/ad5360.py diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index a72b8909a..b2dbe79c6 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -44,6 +44,12 @@ class AD5360: """ Support for the Analog devices AD53[67][0123] multi-channel Digital to Analog Converters + + :param spi_device: Name of the SPI bus this device is on. + :param ldac_device: Name of the TTL device that LDAC is connected to + (optional). Needs to be explicitly initialized to high. + :param chip_select: Value to drive on the chip select lines + during transactions. """ def __init__(self, dmgr, spi_device, ldac_device=None, chip_select=1): @@ -55,6 +61,16 @@ class AD5360: @kernel def setup_bus(self, write_div=4, read_div=7): + """Configure the SPI bus and the SPI transaction parameters + for this device. This method has to be called before any other method + if the bus has been used to access a different device in the meantime. + + This method advances the timeline by the duration of two + RTIO-to-Wishbone bus transactions. + + :param write_div: Write clock divider. + :param read_div: Read clock divider. + """ # write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising) # read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid) self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div) @@ -62,23 +78,56 @@ class AD5360: @kernel def write(self, data): + """Write 24 bits of data. + + This method advances the timeline by the duration of the SPI transfer + and the required CS high time. + """ self.bus.write(data << 8) delay_mu(self.bus.ref_period_mu) # get to 20ns min cs high @kernel def write_offsets(self, value=0x1fff): + """Write the OFS0 and OFS1 offset DACs. + + This method advances the timeline by twice the duration of + :meth:`write`. + + :param value: Value to set both offset registers to. + """ value &= 0x3fff self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value) self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value) @kernel def write_channel(self, channel=0, value=0, op=_AD5360_CMD_DATA): + """Write to a channel register. + + This method advances the timeline by the duration of :meth:`write`. + + :param channel: Channel number to write to. + :param value: 16 bit value to write to the register. + :param op: Operation to perform, one of :const:`_AD5360_CMD_DATA`, + :const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN` + (default: :const:`_AD5360_CMD_DATA`). + """ channel &= 0x3f value &= 0xffff self.write(op | _AD5360_WRITE_CHANNEL(channel) | value) @kernel def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): + """Read a channel register. + + This method advances the timeline by the duration of :meth:`write` plus + three RTIO-to-Wishbone transactions. + + :param channel: Channel number to read from. + :param op: Operation to perform, one of :const:`_AD5360_READ_X1A`, + :const:`_AD5360_READ_X1B`, :const:`_AD5360_READ_OFFSET`, + :const:`_AD5360_READ_GAIN` (default: :const:`_AD5360_READ_X1A`). + :return: The 16 bit register value. + """ channel &= 0x3f self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | _AD5360_READ_CHANNEL(channel)) @@ -90,6 +139,10 @@ class AD5360: @kernel def load(self): + """Pulse the LDAC line. + + This method advances the timeline by two RTIO clock periods (16 ns). + """ self.ldac.off() # t13 = 10ns ldac pulse width low delay_mu(2*self.bus.ref_period_mu) @@ -97,6 +150,17 @@ class AD5360: @kernel def set(self, values, op=_AD5360_CMD_DATA): + """Write to several channels and pulse LDAC to update the channels. + + This method does not advance the timeline. Write events are scheduled + in the past. The DACs will synchronously start changing their output + levels `now`. + + :param values: List of 16 bit values to write to the channels. + :param op: Operation to perform, one of :const:`_AD5360_CMD_DATA`, + :const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN` + (default: :const:`_AD5360_CMD_DATA`). + """ # compensate all delays that will be applied delay_mu(-len(values)*(self.bus.xfer_period_mu + self.bus.write_period_mu + diff --git a/examples/master/repository/coredevice_examples/simple/ad5360.py b/examples/master/repository/coredevice_examples/simple/ad5360.py new file mode 100644 index 000000000..5dd731c04 --- /dev/null +++ b/examples/master/repository/coredevice_examples/simple/ad5360.py @@ -0,0 +1,17 @@ +from artiq.experiment import * + + +class AD5360Test(EnvExperiment): + def build(self): + self.setattr_device("core") + self.dac = self.get_device("dac0") + self.setattr_device("led") + + @kernel + def run(self): + self.dac.setup_bus(write_div=30, read_div=40) + self.dac.write_offsets() + self.led.on() + delay(400*us) + self.led.off() + self.dac.set([i << 10 for i in range(40)]) From 125ab3e07646deabcd5f4005810556f3c4f17d81 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 19:00:14 +0800 Subject: [PATCH 115/125] runtime/i2c_read: fix MSB --- artiq/runtime/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/i2c.c b/artiq/runtime/i2c.c index 22277432d..c8ec3f131 100644 --- a/artiq/runtime/i2c.c +++ b/artiq/runtime/i2c.c @@ -160,7 +160,7 @@ int i2c_write(int busno, int b) int i2c_read(int busno, int ack) { int i; - char b; + unsigned char b; /* Set SCL low first, otherwise setting SDA as input may cause a transition * on SDA with SCL high which will be interpreted as START/STOP condition. From 36387afc12d52bde93a1c91aac92a9046f7449ee Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 19:00:38 +0800 Subject: [PATCH 116/125] examples/device_db: add PCA9548 --- examples/master/device_db.pyon | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index dce234bf2..ee475911a 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -15,6 +15,12 @@ "arguments": {"ref_period": 1e-9} }, + "i2c_switch": { + "type": "local", + "module": "artiq.coredevice.i2c", + "class": "PCA9548" + }, + "ttl0": { "type": "local", "module": "artiq.coredevice.ttl", From 683716017bf1687642bbc4ac581d158c631860aa Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 19:01:35 +0800 Subject: [PATCH 117/125] test: I2C/PCA9548 unittest --- artiq/coredevice/i2c.py | 13 +++++++++++++ artiq/test/coredevice/test_i2c.py | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 artiq/test/coredevice/test_i2c.py diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index b406b34ea..074a9aabe 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -46,6 +46,19 @@ class PCA9548: finally: i2c_stop(self.busno) + @kernel + def readback(self): + i2c_init(self.busno) + i2c_start(self.busno) + r = 0 + try: + if not i2c_write(self.busno, self.address | 1): + raise I2CError("PCA9548 failed to ack address") + r = i2c_read(self.busno, False) + finally: + i2c_stop(self.busno) + return r + class TCA6424A: def __init__(self, dmgr, busno=0, address=0x44): diff --git a/artiq/test/coredevice/test_i2c.py b/artiq/test/coredevice/test_i2c.py new file mode 100644 index 000000000..45955c132 --- /dev/null +++ b/artiq/test/coredevice/test_i2c.py @@ -0,0 +1,25 @@ +import os, unittest + +from artiq.experiment import * +from artiq.test.hardware_testbench import ExperimentCase + + +class I2CSwitch(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("i2c_switch") + + @kernel + def run(self): + passed = True + for i in range(8): + self.i2c_switch.set(i) + if self.i2c_switch.readback() != 1 << i: + passed = False + self.set_dataset("passed", passed) + + +class I2CTest(ExperimentCase): + def test_i2c_switch(self): + self.execute(I2CSwitch) + self.assertTrue(self.dataset_mgr.get("passed")) From 2770d9c729df0abf0c63283f399e9ec84acad1f7 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 5 Mar 2016 19:02:03 +0800 Subject: [PATCH 118/125] doc: I2C/QC2 --- artiq/coredevice/i2c.py | 23 +++++++++++++++++++++++ doc/manual/core_device.rst | 20 ++++++++++++++++++++ doc/manual/core_drivers_reference.rst | 6 ++++++ 3 files changed, 49 insertions(+) diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index 074a9aabe..2f9342ca9 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -29,6 +29,11 @@ def i2c_read(busno: TInt32, ack: TBool) -> TInt32: class PCA9548: + """Driver for the PCA9548 I2C bus switch. + + On the KC705, this chip is used for selecting the I2C buses on the two FMC + connectors. HPC=1, LPC=2. + """ def __init__(self, dmgr, busno=0, address=0xe8): self.core = dmgr.get("core") self.busno = busno @@ -36,6 +41,12 @@ class PCA9548: @kernel def set(self, channel): + """Select one channel. + + Selecting multiple channels is not supported by this driver. + + :param channel: channel number (0-7) + """ i2c_init(self.busno) i2c_start(self.busno) try: @@ -61,6 +72,10 @@ class PCA9548: class TCA6424A: + """Driver for the TCA6424A I2C I/O expander. + + On the NIST QC2 hardware, this chip is used for switching the directions + of TTL buffers.""" def __init__(self, dmgr, busno=0, address=0x44): self.core = dmgr.get("core") self.busno = busno @@ -84,5 +99,13 @@ class TCA6424A: @kernel def set(self, outputs): + """Drive all pins of the chip to the levels given by the + specified 24-bit word. + + On the QC2 hardware, the LSB of the word determines the direction of + TTL0 (on a given FMC card) and the MSB that of TTL23. + + A bit set to 1 means the TTL is an output. + """ self._write24(0x8c, 0) # set all directions to output self._write24(0x84, output) # set levels diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index c0369b184..c53ccbe23 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -29,6 +29,9 @@ 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). +NIST QC1 +++++++++ + With the QC1 hardware, the TTL lines are mapped as follows: +--------------+------------+--------------+ @@ -47,6 +50,9 @@ With the QC1 hardware, the TTL lines are mapped as follows: | 19 | TTL15 | Clock | +--------------+------------+--------------+ +NIST CLOCK +++++++++++ + With the CLOCK hardware, the TTL lines are mapped as follows: +--------------------+-----------------------+--------------+ @@ -70,6 +76,20 @@ With the CLOCK hardware, the TTL lines are mapped as follows: +--------------------+-----------------------+--------------+ +NIST QC2 +++++++++ + +With the QC2 hardware, the TTL lines are mapped as follows: + +TODO + +The QC2 hardware uses TCA6424A I2C I/O expanders to define the directions of its TTL buffers. There is one such expander per FMC card, and they are selected using the PCA9548 on the KC705. + +To avoid I/O contention, the startup kernel should first program the TCA6424A expanders and then call ``output()`` on all ``TTLInOut`` channels that should be configured as outputs. + +See :mod:`artiq.coredevice.i2c` for more details. + + Pipistrello ----------- diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index afc41aee0..e174930e4 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -36,6 +36,12 @@ These drivers are for the core device and the peripherals closely integrated int .. automodule:: artiq.coredevice.ad5360 :members: +:mod:`artiq.coredevice.i2c` module +---------------------------------- + +.. automodule:: artiq.coredevice.i2c + :members: + :mod:`artiq.coredevice.exceptions` module ----------------------------------------- From c73b0800197edc04c497372af7e671b5c9d3e65b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Mar 2016 00:17:45 +0800 Subject: [PATCH 119/125] doc/PCA9548: clarify channel selection --- artiq/coredevice/i2c.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index 2f9342ca9..e9de36161 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -43,7 +43,8 @@ class PCA9548: def set(self, channel): """Select one channel. - Selecting multiple channels is not supported by this driver. + Selecting multiple channels at the same time is not supported by this + driver. :param channel: channel number (0-7) """ From e8b59b00f650a48325fdc27a28c1c0cfd40019d1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Mar 2016 00:18:47 +0800 Subject: [PATCH 120/125] soc: use add_extra_software_packages, factor builder code --- artiq/gateware/soc.py | 13 +++++++++++++ artiq/gateware/targets/kc705.py | 12 +++--------- artiq/gateware/targets/pipistrello.py | 14 +++++--------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/artiq/gateware/soc.py b/artiq/gateware/soc.py index 908701ad2..b1b84f25e 100644 --- a/artiq/gateware/soc.py +++ b/artiq/gateware/soc.py @@ -1,8 +1,12 @@ +import os + from misoc.integration.soc_core import mem_decoder from misoc.cores import timer from misoc.interconnect import wishbone +from misoc.integration.builder import * from artiq.gateware import amp +from artiq import __artiq_dir__ as artiq_dir class AMPSoC: @@ -46,3 +50,12 @@ class AMPSoC: self.add_csr_region(name, self.mem_map[name] | 0x80000000, 32, csrs) + + +def build_artiq_soc(soc, argdict): + builder = Builder(soc, **argdict) + builder.add_extra_software_packages() + builder.add_software_package("liblwip", os.path.join(artiq_dir, "runtime", + "liblwip")) + builder.add_software_package("runtime", os.path.join(artiq_dir, "runtime")) + builder.build() diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index bfa778794..8f766ecce 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3.5 import argparse -import os from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer @@ -15,13 +14,12 @@ from misoc.interconnect.csr import * from misoc.interconnect import wishbone from misoc.cores import gpio from misoc.integration.soc_core import mem_decoder -from misoc.integration.builder import * 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 +from artiq.gateware.soc import AMPSoC, build_artiq_soc from artiq.gateware import rtio, nist_qc1, nist_clock, nist_qc2 from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi -from artiq import __artiq_dir__ as artiq_dir from artiq import __version__ as artiq_version @@ -375,11 +373,7 @@ def main(): sys.exit(1) soc = cls(**soc_kc705_argdict(args)) - builder = Builder(soc, **builder_argdict(args)) - builder.add_software_package("liblwip", os.path.join(artiq_dir, "runtime", - "liblwip")) - builder.add_software_package("runtime", os.path.join(artiq_dir, "runtime")) - builder.build() + build_artiq_soc(soc, builder_argdict(args)) if __name__ == "__main__": diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index ed1966e33..80985c04a 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -4,7 +4,6 @@ # Copyright (C) 2014, 2015 M-Labs Limited import argparse -import os from fractions import Fraction from migen import * @@ -15,12 +14,13 @@ from misoc.interconnect.csr import * from misoc.interconnect import wishbone from misoc.cores import gpio from misoc.integration.soc_core import mem_decoder -from misoc.targets.pipistrello import * +from misoc.targets.pipistrello import (BaseSoC, soc_pipistrello_args, + soc_pipistrello_argdict) +from misoc.integration.builder import builder_args, builder_argdict -from artiq.gateware.soc import AMPSoC +from artiq.gateware.soc import AMPSoC, build_artiq_soc from artiq.gateware import rtio, nist_qc1 from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds, spi -from artiq import __artiq_dir__ as artiq_dir from artiq import __version__ as artiq_version @@ -228,11 +228,7 @@ def main(): args = parser.parse_args() soc = NIST_QC1(**soc_pipistrello_argdict(args)) - builder = Builder(soc, **builder_argdict(args)) - builder.add_software_package("liblwip", os.path.join(artiq_dir, "runtime", - "liblwip")) - builder.add_software_package("runtime", os.path.join(artiq_dir, "runtime")) - builder.build() + build_artiq_soc(soc, builder_argdict(args)) if __name__ == "__main__": From 9ffa8cbb1110f382f4de90a535fa120f415e8d83 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 7 Mar 2016 20:27:22 +0000 Subject: [PATCH 121/125] test_loopback: bump RTT limit to 60ns. Turns out a short jumper wire on a backplane leads to RTT of 50ns exactly. --- artiq/test/coredevice/test_rtio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index 50958392f..f68742885 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -192,7 +192,7 @@ class CoredeviceTest(ExperimentCase): rtt = self.dataset_mgr.get("rtt") print(rtt) self.assertGreater(rtt, 0*ns) - self.assertLess(rtt, 50*ns) + self.assertLess(rtt, 60*ns) def test_clock_generator_loopback(self): self.execute(ClockGeneratorLoopback) From 739568fcb8c3f583e217e69bcd70fa7aa80dd2ae Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 7 Mar 2016 21:12:03 +0000 Subject: [PATCH 122/125] runtime: fix sloppy memory management in cache_put. --- artiq/runtime/session.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index 5c4333480..fb6e4dcaf 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -1027,7 +1027,6 @@ static int process_kmsg(struct msg_base *umsg) struct cache_row *row = NULL; for(struct cache_row *iter = cache; iter; iter = iter->next) { if(!strcmp(iter->key, request->key)) { - free(iter->elements); row = iter; break; } @@ -1042,11 +1041,14 @@ static int process_kmsg(struct msg_base *umsg) } if(!row->borrowed) { - if(request->length != 0) { - row->length = request->length; + row->length = request->length; + if(row->length != 0) { row->elements = calloc(row->length, sizeof(int32_t)); memcpy(row->elements, request->elements, sizeof(int32_t) * row->length); + } else { + free(row->elements); + row->elements = NULL; } reply.succeeded = 1; From 59d7f5f1e31b74b5c9e2fc7a2ad12b9155182266 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Mar 2016 14:04:51 +0800 Subject: [PATCH 123/125] test/coredevice/time_keeps_running: start new session to prevent now_mu save/restore --- artiq/test/coredevice/test_rtio.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index f68742885..d110965f3 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -234,8 +234,11 @@ class CoredeviceTest(ExperimentCase): def test_time_keeps_running(self): self.execute(TimeKeepsRunning) t1 = self.dataset_mgr.get("time_at_start") + + self.device_mgr.get("comm").close() # start a new session self.execute(TimeKeepsRunning) t2 = self.dataset_mgr.get("time_at_start") + dead_time = mu_to_seconds(t2 - t1, self.device_mgr.get("core")) print(dead_time) self.assertGreater(dead_time, 1*ms) From 71105fd0d79dcdfcb7d6d67370b796e49dd76075 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Mar 2016 15:38:08 +0800 Subject: [PATCH 124/125] rtio: collision_error -> collision --- RELEASE_NOTES.rst | 2 ++ artiq/coredevice/__init__.py | 4 ++-- artiq/coredevice/exceptions.py | 2 +- artiq/gateware/rtio/analyzer.py | 2 +- artiq/gateware/rtio/core.py | 22 +++++++++++----------- artiq/protocols/analyzer.py | 2 +- artiq/runtime/rtio.c | 8 ++++---- artiq/runtime/rtio.h | 2 +- artiq/test/coredevice/test_rtio.py | 8 ++++---- 9 files changed, 27 insertions(+), 25 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 7ea7ce122..4c905ea5a 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -10,3 +10,5 @@ Release notes * Core device flash storage has moved due to increased runtime size. This requires reflashing the runtime and the flash storage filesystem image or erase and rewrite its entries. +* RTIOCollisionError has been renamed to RTIOCollision + \ No newline at end of file diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index 88de8cf7c..da8c72b1d 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,12 +1,12 @@ from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, - RTIOCollisionError, RTIOOverflow, + RTIOCollision, RTIOOverflow, DDSBatchError, CacheError) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) __all__ = [] -__all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollisionError", +__all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision", "RTIOOverflow", "DDSBatchError", "CacheError"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 36314ed1a..aa00ad784 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -86,7 +86,7 @@ class RTIOSequenceError(Exception): """ artiq_builtin = True -class RTIOCollisionError(Exception): +class RTIOCollision(Exception): """Raised when an event is submitted on a given channel with the same coarse timestamp as the previous one but with a different fine timestamp. diff --git a/artiq/gateware/rtio/analyzer.py b/artiq/gateware/rtio/analyzer.py index 22eac2e35..ca66ee7bb 100644 --- a/artiq/gateware/rtio/analyzer.py +++ b/artiq/gateware/rtio/analyzer.py @@ -82,7 +82,7 @@ class MessageEncoder(Module, AutoCSR): rtio_core.counter.value_sys << rtio_core.fine_ts_width), ] for ename in ("o_underflow_reset", "o_sequence_error_reset", - "o_collision_error_reset", "i_overflow_reset"): + "o_collision_reset", "i_overflow_reset"): self.comb += \ If(getattr(kcsrs, ename).re, exception_stb.eq(1), diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index e258b2a04..0f84769e6 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -103,7 +103,7 @@ class _OutputManager(Module): self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() - self.collision_error = Signal() + self.collision = Signal() # # # @@ -126,7 +126,7 @@ class _OutputManager(Module): # Special cases replace = Signal() sequence_error = Signal() - collision_error = Signal() + collision = Signal() any_error = Signal() nop = Signal() self.sync.rsys += [ @@ -140,10 +140,10 @@ class _OutputManager(Module): < buf.timestamp[fine_ts_width:]) ] if fine_ts_width: - self.sync.rsys += collision_error.eq( + self.sync.rsys += collision.eq( (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) & (self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width])) - self.comb += any_error.eq(sequence_error | collision_error) + self.comb += any_error.eq(sequence_error | collision) if interface.suppress_nop: # disable NOP at reset: do not suppress a first write with all 0s nop_en = Signal(reset=0) @@ -163,7 +163,7 @@ class _OutputManager(Module): ] self.comb += [ self.sequence_error.eq(self.we & sequence_error), - self.collision_error.eq(self.we & collision_error) + self.collision.eq(self.we & collision) ] # Buffer read and FIFO write @@ -335,7 +335,7 @@ class _KernelCSRs(AutoCSR): self.o_status = CSRStatus(4) self.o_underflow_reset = CSR() self.o_sequence_error_reset = CSR() - self.o_collision_error_reset = CSR() + self.o_collision_reset = CSR() if data_width: self.i_data = CSRStatus(data_width) @@ -422,22 +422,22 @@ class RTIO(Module): underflow = Signal() sequence_error = Signal() - collision_error = Signal() + collision = Signal() self.sync.rsys += [ If(selected & self.kcsrs.o_underflow_reset.re, underflow.eq(0)), If(selected & self.kcsrs.o_sequence_error_reset.re, sequence_error.eq(0)), - If(selected & self.kcsrs.o_collision_error_reset.re, - collision_error.eq(0)), + If(selected & self.kcsrs.o_collision_reset.re, + collision.eq(0)), If(o_manager.underflow, underflow.eq(1)), If(o_manager.sequence_error, sequence_error.eq(1)), - If(o_manager.collision_error, collision_error.eq(1)) + If(o_manager.collision, collision.eq(1)) ] o_statuses.append(Cat(~o_manager.writable, underflow, sequence_error, - collision_error)) + collision)) if channel.interface.i is not None: i_manager = _InputManager(channel.interface.i, self.counter, diff --git a/artiq/protocols/analyzer.py b/artiq/protocols/analyzer.py index 440c3b593..46ca712ee 100644 --- a/artiq/protocols/analyzer.py +++ b/artiq/protocols/analyzer.py @@ -15,6 +15,6 @@ class ExceptionType(Enum): o_underflow_reset = 0b010000 o_sequence_error_reset = 0b010001 - o_collision_error_reset = 0b010010 + o_collision_reset = 0b010010 i_overflow_reset = 0b100000 diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 34aca2d78..90dd7fb5e 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -33,10 +33,10 @@ static void rtio_process_exceptional_status( "RTIO sequence error at {0} mu, channel {1}", timestamp, channel, 0); } - if(status & RTIO_O_STATUS_COLLISION_ERROR) { - rtio_o_collision_error_reset_write(1); - artiq_raise_from_c("RTIOCollisionError", - "RTIO collision error at {0} mu, channel {1}", + if(status & RTIO_O_STATUS_COLLISION) { + rtio_o_collision_reset_write(1); + artiq_raise_from_c("RTIOCollision", + "RTIO collision at {0} mu, channel {1}", timestamp, channel, 0); } } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index a1ab8a90a..c32dca484 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -6,7 +6,7 @@ #define RTIO_O_STATUS_FULL 1 #define RTIO_O_STATUS_UNDERFLOW 2 #define RTIO_O_STATUS_SEQUENCE_ERROR 4 -#define RTIO_O_STATUS_COLLISION_ERROR 8 +#define RTIO_O_STATUS_COLLISION 8 #define RTIO_I_STATUS_EMPTY 1 #define RTIO_I_STATUS_OVERFLOW 2 diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index d110965f3..9caccc68b 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -150,7 +150,7 @@ class SequenceError(EnvExperiment): self.ttl_out.pulse(25*us) -class CollisionError(EnvExperiment): +class Collision(EnvExperiment): def build(self): self.setattr_device("core") self.setattr_device("ttl_out_serdes") @@ -220,9 +220,9 @@ class CoredeviceTest(ExperimentCase): with self.assertRaises(RTIOSequenceError): self.execute(SequenceError) - def test_collision_error(self): - with self.assertRaises(RTIOCollisionError): - self.execute(CollisionError) + def test_collision(self): + with self.assertRaises(RTIOCollision): + self.execute(Collision) def test_watchdog(self): # watchdog only works on the device From 2953b069dcb42d849891ca5b4cb0b3000ae23470 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Mar 2016 15:58:25 +0800 Subject: [PATCH 125/125] rtio: when rtlink addresses are different, issue collision not replace (fixes #320) --- artiq/gateware/rtio/core.py | 12 ++++++++---- artiq/test/coredevice/test_rtio.py | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 0f84769e6..8c2d1b237 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -130,19 +130,23 @@ class _OutputManager(Module): any_error = Signal() nop = Signal() self.sync.rsys += [ - # Note: replace does not perform any RTLink address checks, - # i.e. a write to a different address will be silently replaced - # as well. + # Note: replace may be asserted at the same time as collision + # when addresses are different. In that case, it is a collision. replace.eq(self.ev.timestamp == buf.timestamp), # Detect sequence errors on coarse timestamps only # so that they are mutually exclusive with collision errors. sequence_error.eq(self.ev.timestamp[fine_ts_width:] < buf.timestamp[fine_ts_width:]) ] + if hasattr(self.ev, "a"): + different_addresses = self.ev.a != buf.a + else: + different_addresses = 0 if fine_ts_width: self.sync.rsys += collision.eq( (self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) - & (self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width])) + & ((self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]) + |different_addresses)) self.comb += any_error.eq(sequence_error | collision) if interface.suppress_nop: # disable NOP at reset: do not suppress a first write with all 0s diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index 9caccc68b..8f5dcb48c 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -163,6 +163,17 @@ class Collision(EnvExperiment): delay_mu(1) +class AddressCollision(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("loop_in") + + @kernel + def run(self): + self.loop_in.input() + self.loop_in.pulse(10*us) + + class TimeKeepsRunning(EnvExperiment): def build(self): self.setattr_device("core") @@ -224,6 +235,10 @@ class CoredeviceTest(ExperimentCase): with self.assertRaises(RTIOCollision): self.execute(Collision) + def test_address_collision(self): + with self.assertRaises(RTIOCollision): + self.execute(AddressCollision) + def test_watchdog(self): # watchdog only works on the device with self.assertRaises(IOError):