mirror of https://github.com/m-labs/artiq.git
parent
53c7a5f2c6
commit
c71e442929
|
@ -162,16 +162,16 @@ class AD9914:
|
|||
def set_phase_mode(self, phase_mode):
|
||||
"""Sets the phase mode of the DDS channel. Supported phase modes are:
|
||||
|
||||
* ``PHASE_MODE_CONTINUOUS``: the phase accumulator is unchanged when
|
||||
* :const:`PHASE_MODE_CONTINUOUS`: the phase accumulator is unchanged when
|
||||
switching frequencies. The DDS phase is the sum of the phase
|
||||
accumulator and the phase offset. The only discrete jumps in the
|
||||
DDS output phase come from changes to the phase offset.
|
||||
|
||||
* ``PHASE_MODE_ABSOLUTE``: the phase accumulator is reset when
|
||||
* :const:`PHASE_MODE_ABSOLUTE`: the phase accumulator is reset when
|
||||
switching frequencies. Thus, the phase of the DDS at the time of
|
||||
the frequency change is equal to the phase offset.
|
||||
|
||||
* ``PHASE_MODE_TRACKING``: when switching frequencies, the phase
|
||||
* :const:`PHASE_MODE_TRACKING`: when switching frequencies, the phase
|
||||
accumulator is set to the value it would have if the DDS had been
|
||||
running at the specified frequency since the start of the
|
||||
experiment.
|
||||
|
@ -193,7 +193,7 @@ class AD9914:
|
|||
:param ftw: frequency to generate.
|
||||
:param pow: adds an offset to the phase.
|
||||
:param phase_mode: if specified, overrides the default phase mode set
|
||||
by ``set_phase_mode`` for this call.
|
||||
by :meth:`set_phase_mode` for this call.
|
||||
:param ref_time: reference time used to compute phase. Specifying this
|
||||
makes it easier to have a well-defined phase relationship between
|
||||
DDSes on the same bus that are updated at a similar time.
|
||||
|
@ -270,7 +270,7 @@ class AD9914:
|
|||
@kernel
|
||||
def set(self, frequency, phase=0.0, phase_mode=_PHASE_MODE_DEFAULT,
|
||||
amplitude=1.0):
|
||||
"""Like ``set_mu``, but uses Hz and turns."""
|
||||
"""Like :meth:`set_mu`, but uses Hz and turns."""
|
||||
self.set_mu(self.frequency_to_ftw(frequency),
|
||||
self.turns_to_pow(phase), phase_mode,
|
||||
self.amplitude_to_asf(amplitude))
|
||||
|
@ -323,7 +323,7 @@ class AD9914:
|
|||
|
||||
@kernel
|
||||
def set_x(self, frequency, amplitude=1.0):
|
||||
"""Like ``set_x_mu``, but uses Hz and turns.
|
||||
"""Like :meth:`set_x_mu`, but uses Hz and turns.
|
||||
|
||||
Note that the precision of ``float`` is less than the precision
|
||||
of the extended frequency tuning word.
|
||||
|
|
|
@ -34,7 +34,7 @@ def dma_playback(timestamp: TInt64, ptr: TInt32) -> TNone:
|
|||
|
||||
|
||||
class DMARecordContextManager:
|
||||
"""Context manager returned by ``CoreDMA.record()``.
|
||||
"""Context manager returned by :meth:`CoreDMA.record()`.
|
||||
|
||||
Upon entering, starts recording a DMA trace. All RTIO operations are
|
||||
redirected to a newly created DMA buffer after this call, and ``now``
|
||||
|
|
|
@ -184,7 +184,7 @@ class SPIMaster:
|
|||
experiments and are known.
|
||||
|
||||
This method is portable and can also be called from e.g.
|
||||
``__init__``.
|
||||
:meth:`__init__`.
|
||||
|
||||
:param div: SPI clock divider (see: :meth:`set_config_mu`)
|
||||
:param length: SPI transfer length (see: :meth:`set_config_mu`)
|
||||
|
|
|
@ -253,7 +253,7 @@ class Channel:
|
|||
This method does not advance the timeline. Output RF switch setting
|
||||
takes effect immediately and is independent of any other activity
|
||||
(profile settings, other channels). The RF switch behaves like
|
||||
``TTLOut``. RTIO event replacement is supported. IIR updates take place
|
||||
:class:`artiq.coredevice.ttl.TTLOut`. RTIO event replacement is supported. IIR updates take place
|
||||
once the RF switch has been enabled for the configured delay and the
|
||||
profile setting has been stable. Profile changes take between one and
|
||||
two servo cycles to reach the DDS.
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
Drivers for TTL signals on RTIO.
|
||||
|
||||
TTL channels (including the clock generator) all support output event
|
||||
replacement. For example, pulses of "zero" length (e.g. ``on()``
|
||||
immediately followed by ``off()``, without a delay) are suppressed.
|
||||
replacement. For example, pulses of "zero" length (e.g. :meth:`TTLInOut.on`
|
||||
immediately followed by :meth:`TTLInOut.off`, without a delay) are suppressed.
|
||||
"""
|
||||
|
||||
import numpy
|
||||
|
@ -107,8 +107,8 @@ 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
|
||||
signal, you must call :meth:`output`. If the channel is in output mode most of
|
||||
the time in your setup, it is a good idea to call :meth:`output` in the
|
||||
startup kernel.
|
||||
|
||||
There are three input APIs: gating, sampling and watching. When one
|
||||
|
@ -301,10 +301,10 @@ class TTLInOut:
|
|||
@kernel
|
||||
def sample_get(self):
|
||||
"""Returns the value of a sample previously obtained with
|
||||
``sample_input``.
|
||||
:meth:`sample_input`.
|
||||
|
||||
Multiple samples may be queued (using multiple calls to
|
||||
``sample_input``) into the RTIO FIFOs and subsequently read out using
|
||||
:meth:`sample_input`) into the RTIO FIFOs and subsequently read out using
|
||||
multiple calls to this function.
|
||||
|
||||
This function does not interact with the time cursor."""
|
||||
|
@ -324,11 +324,11 @@ class TTLInOut:
|
|||
@kernel
|
||||
def watch_stay_on(self):
|
||||
"""Checks that the input is at a high level at the position
|
||||
of the time cursor and keep checking until ``watch_done``
|
||||
of the time cursor and keep checking until :meth:`watch_done`
|
||||
is called.
|
||||
|
||||
Returns ``True`` if the input is high. A call to this function
|
||||
must always be followed by an eventual call to ``watch_done``
|
||||
must always be followed by an eventual call to :meth:`watch_done`
|
||||
(use e.g. a try/finally construct to ensure this).
|
||||
|
||||
The time cursor is not modified by this function.
|
||||
|
@ -338,7 +338,7 @@ class TTLInOut:
|
|||
|
||||
@kernel
|
||||
def watch_stay_off(self):
|
||||
"""Like ``watch_stay_on``, but for low levels."""
|
||||
"""Like :meth:`watch_stay_on`, but for low levels."""
|
||||
rtio_output(now_mu(), self.channel, 3, 1) # gate rising
|
||||
return rtio_input_data(self.channel) == 0
|
||||
|
||||
|
@ -419,7 +419,7 @@ class TTLClockGen:
|
|||
|
||||
@kernel
|
||||
def set(self, frequency):
|
||||
"""Like ``set_mu``, but using Hz."""
|
||||
"""Like :meth:`set_mu`, but using Hz."""
|
||||
self.set_mu(self.frequency_to_ftw(frequency))
|
||||
|
||||
@kernel
|
||||
|
|
|
@ -28,19 +28,20 @@ def kernel(arg=None, flags={}):
|
|||
This decorator marks an object's method for execution on the core
|
||||
device.
|
||||
|
||||
When a decorated method is called from the Python interpreter, the ``core``
|
||||
When a decorated method is called from the Python interpreter, the :attr:`core`
|
||||
attribute of the object is retrieved and used as core device driver. The
|
||||
core device driver will typically compile, transfer and run the method
|
||||
(kernel) on the device.
|
||||
|
||||
When kernels call another method:
|
||||
- if the method is a kernel for the same core device, is it compiled
|
||||
|
||||
- if the method is a kernel for the same core device, it is compiled
|
||||
and sent in the same binary. Calls between kernels happen entirely on
|
||||
the device.
|
||||
- if the method is a regular Python method (not a kernel), it generates
|
||||
a remote procedure call (RPC) for execution on the host.
|
||||
|
||||
The decorator takes an optional parameter that defaults to ``core`` and
|
||||
The decorator takes an optional parameter that defaults to :attr`core` and
|
||||
specifies the name of the attribute to use as core device driver.
|
||||
|
||||
This decorator must be present in the global namespace of all modules using
|
||||
|
|
|
@ -230,7 +230,7 @@ class HasEnvironment:
|
|||
instead: when the repository is scanned to build the list of
|
||||
available experiments and when the dataset browser ``artiq_browser``
|
||||
is used to open or run the analysis stage of an experiment. Do not
|
||||
rely on being able to operate on devices or arguments in ``build()``.
|
||||
rely on being able to operate on devices or arguments in :meth:`build`.
|
||||
|
||||
Datasets are read-only in this method.
|
||||
|
||||
|
@ -277,7 +277,7 @@ class HasEnvironment:
|
|||
|
||||
def setattr_device(self, key):
|
||||
"""Sets a device driver as attribute. The names of the device driver
|
||||
and of the attribute are the same.
|
||||
and of the attribute are the same.
|
||||
|
||||
The key is added to the instance's kernel invariants."""
|
||||
setattr(self, key, self.get_device(key))
|
||||
|
@ -328,8 +328,10 @@ class HasEnvironment:
|
|||
By default, datasets obtained by this method are archived into the output
|
||||
HDF5 file of the experiment. If an archived dataset is requested more
|
||||
than one time (and therefore its value has potentially changed) or is
|
||||
modified, a warning is emitted. Archival can be turned off by setting
|
||||
the ``archive`` argument to ``False``.
|
||||
modified, a warning is emitted.
|
||||
|
||||
:param archive: Set to ``False`` to prevent archival together with the run's results.
|
||||
Default is ``True``
|
||||
"""
|
||||
try:
|
||||
return self.__dataset_mgr.get(key, archive)
|
||||
|
@ -355,7 +357,7 @@ class Experiment:
|
|||
"""Entry point for pre-computing data necessary for running the
|
||||
experiment.
|
||||
|
||||
Doing such computations outside of ``run`` enables more efficient
|
||||
Doing such computations outside of :meth:`run` enables more efficient
|
||||
scheduling of multiple experiments that need to access the shared
|
||||
hardware during part of their execution.
|
||||
|
||||
|
@ -371,8 +373,8 @@ class Experiment:
|
|||
|
||||
This method may interact with the hardware.
|
||||
|
||||
The experiment may call the scheduler's ``pause`` method while in
|
||||
``run``.
|
||||
The experiment may call the scheduler's :meth:`pause` method while in
|
||||
:meth:`run`.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -382,7 +384,7 @@ class Experiment:
|
|||
This method may be overloaded by the user to implement the analysis
|
||||
phase of the experiment, for example fitting curves.
|
||||
|
||||
Splitting this phase from ``run`` enables tweaking the analysis
|
||||
Splitting this phase from :meth:`run` enables tweaking the analysis
|
||||
algorithm on pre-existing data, and CPU-bound analyses to be run
|
||||
overlapped with the next experiment in a pipelined manner.
|
||||
|
||||
|
@ -392,13 +394,14 @@ class Experiment:
|
|||
|
||||
|
||||
class EnvExperiment(Experiment, HasEnvironment):
|
||||
"""Base class for top-level experiments that use the ``HasEnvironment``
|
||||
environment manager.
|
||||
"""Base class for top-level experiments that use the
|
||||
:class:`~artiq.language.environment.HasEnvironment` environment manager.
|
||||
|
||||
Most experiments should derive from this class."""
|
||||
def prepare(self):
|
||||
"""The default prepare method calls prepare for all children, in the
|
||||
order of instantiation, if the child has a prepare method."""
|
||||
"""This default prepare method calls :meth:`~artiq.language.environment.Experiment.prepare`
|
||||
for all children, in the order of instantiation, if the child has a
|
||||
:meth:`~artiq.language.environment.Experiment.prepare` method."""
|
||||
for child in self.children:
|
||||
if hasattr(child, "prepare"):
|
||||
child.prepare()
|
||||
|
|
|
@ -6,7 +6,8 @@ class AsyncioServer:
|
|||
"""Generic TCP server based on asyncio.
|
||||
|
||||
Users of this class must derive from it and define the
|
||||
``_handle_connection_cr`` method and coroutine.
|
||||
:meth:`~artiq.protocols.asyncio_server.AsyncioServer._handle_connection_cr`
|
||||
method/coroutine.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._client_tasks = set()
|
||||
|
@ -14,10 +15,10 @@ class AsyncioServer:
|
|||
async def start(self, host, port):
|
||||
"""Starts the server.
|
||||
|
||||
The user must call ``stop`` to free resources properly after this
|
||||
method completes successfully.
|
||||
The user must call :meth:`stop`
|
||||
to free resources properly after this method completes successfully.
|
||||
|
||||
This method is a `coroutine`.
|
||||
This method is a *coroutine*.
|
||||
|
||||
:param host: Bind address of the server (see ``asyncio.start_server``
|
||||
from the Python standard library).
|
||||
|
@ -48,3 +49,6 @@ class AsyncioServer:
|
|||
task = asyncio.ensure_future(self._handle_connection_cr(reader, writer))
|
||||
self._client_tasks.add(task)
|
||||
task.add_done_callback(self._client_done)
|
||||
|
||||
async def _handle_connection_cr(self, reader, writer):
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
This module provides a remote procedure call (RPC) mechanism over sockets
|
||||
between conventional computers (PCs) running Python. It strives to be
|
||||
transparent and uses ``artiq.protocols.pyon`` internally so that e.g. Numpy
|
||||
transparent and uses :mod:`artiq.protocols.pyon` internally so that e.g. Numpy
|
||||
arrays can be easily used.
|
||||
|
||||
Note that the server operates on copies of objects provided by the client,
|
||||
|
@ -61,18 +61,18 @@ class Client:
|
|||
can be used as if they were local methods.
|
||||
|
||||
For example, if the server provides method ``foo``, and ``c`` is a local
|
||||
``Client`` object, then the method can be called as: ::
|
||||
:class:`.Client` object, then the method can be called as: ::
|
||||
|
||||
result = c.foo(param1, param2)
|
||||
|
||||
The parameters and the result are automatically transferred with the
|
||||
The parameters and the result are automatically transferred from the
|
||||
server.
|
||||
|
||||
Only methods are supported. Attributes must be accessed by providing and
|
||||
using "get" and/or "set" methods on the server side.
|
||||
|
||||
At object initialization, the connection to the remote server is
|
||||
automatically attempted. The user must call ``close_rpc`` to
|
||||
automatically attempted. The user must call :meth:`~artiq.protocols.pc_rpc.Client.close_rpc` to
|
||||
free resources properly after initialization completes successfully.
|
||||
|
||||
:param host: Identifier of the server. The string can represent a
|
||||
|
@ -81,11 +81,11 @@ class Client:
|
|||
:param port: TCP port to use.
|
||||
:param target_name: Target name to select. ``IncompatibleServer`` is
|
||||
raised if the target does not exist.
|
||||
Use ``AutoTarget`` for automatic selection if the server has only one
|
||||
Use :class:`.AutoTarget` for automatic selection if the server has only one
|
||||
target.
|
||||
Use ``None`` to skip selecting a target. The list of targets can then
|
||||
be retrieved using ``get_rpc_id`` and then one can be selected later
|
||||
using ``select_rpc_target``.
|
||||
be retrieved using :meth:`~artiq.protocols.pc_rpc.Client.get_rpc_id`
|
||||
and then one can be selected later using :meth:`~artiq.protocols.pc_rpc.Client.select_rpc_target`.
|
||||
:param timeout: Socket operation timeout. Use ``None`` for blocking
|
||||
(default), ``0`` for non-blocking, and a finite value to raise
|
||||
``socket.timeout`` if an operation does not complete within the
|
||||
|
@ -198,7 +198,7 @@ class AsyncioClient:
|
|||
|
||||
async def connect_rpc(self, host, port, target_name):
|
||||
"""Connects to the server. This cannot be done in __init__ because
|
||||
this method is a coroutine. See ``Client`` for a description of the
|
||||
this method is a coroutine. See :class:`artiq.protocols.pc_rpc.Client` for a description of the
|
||||
parameters."""
|
||||
self.__reader, self.__writer = \
|
||||
await asyncio.open_connection(host, port, limit=100*1024*1024)
|
||||
|
@ -447,7 +447,8 @@ class _PrettyPrintCall:
|
|||
|
||||
class Server(_AsyncioServer):
|
||||
"""This class creates a TCP server that handles requests coming from
|
||||
``Client`` objects.
|
||||
*Client* objects (whether :class:`.Client`, :class:`.BestEffortClient`,
|
||||
or :class:`.AsyncioClient`).
|
||||
|
||||
The server is designed using ``asyncio`` so that it can easily support
|
||||
multiple connections without the locking issues that arise in
|
||||
|
@ -591,7 +592,7 @@ def simple_server_loop(targets, host, port, description=None):
|
|||
"""Runs a server until an exception is raised (e.g. the user hits Ctrl-C)
|
||||
or termination is requested by a client.
|
||||
|
||||
See ``Server`` for a description of the parameters.
|
||||
See :class:`artiq.protocols.pc_rpc.Server` for a description of the parameters.
|
||||
"""
|
||||
loop = asyncio.get_event_loop()
|
||||
try:
|
||||
|
|
|
@ -7,10 +7,10 @@ large amounts of data with it, and only exchange higher-level, processed data
|
|||
with the experiment (and over the network).
|
||||
|
||||
Controllers with support for remote execution contain an additional target
|
||||
that gives RPC access to instances of ``RemoteExecServer``. One such instance
|
||||
that gives RPC access to instances of :class:`.RemoteExecServer`. One such instance
|
||||
is created per client (experiment) connection and manages one Python namespace
|
||||
in which the experiment can execute arbitrary code by calling the methods of
|
||||
``RemoteExecServer``.
|
||||
:class:`.RemoteExecServer`.
|
||||
|
||||
The namespaces are initialized with the following global values:
|
||||
|
||||
|
|
|
@ -117,10 +117,10 @@ class Subscriber:
|
|||
class Notifier:
|
||||
"""Encapsulates a structure whose changes need to be published.
|
||||
|
||||
All mutations to the structure must be made through the ``Notifier``. The
|
||||
All mutations to the structure must be made through the :class:`.Notifier`. The
|
||||
original structure must only be accessed for reads.
|
||||
|
||||
In addition to the list methods below, the ``Notifier`` supports the index
|
||||
In addition to the list methods below, the :class:`.Notifier` supports the index
|
||||
syntax for modification and deletion of elements. Modification of nested
|
||||
structures can be also done using the index syntax, for example:
|
||||
|
||||
|
@ -131,11 +131,11 @@ class Notifier:
|
|||
[[42]]
|
||||
|
||||
This class does not perform any network I/O and is meant to be used with
|
||||
e.g. the ``Publisher`` for this purpose. Only one publisher at most can be
|
||||
associated with a ``Notifier``.
|
||||
e.g. the :class:`.Publisher` for this purpose. Only one publisher at most can be
|
||||
associated with a :class:`.Notifier`.
|
||||
|
||||
:param backing_struct: Structure to encapsulate. For convenience, it
|
||||
also becomes available as the ``read`` property of the ``Notifier``.
|
||||
also becomes available as the ``read`` property of the :class:`.Notifier`.
|
||||
"""
|
||||
def __init__(self, backing_struct, root=None, path=[]):
|
||||
self.read = backing_struct
|
||||
|
@ -168,7 +168,7 @@ class Notifier:
|
|||
|
||||
def pop(self, i=-1):
|
||||
"""Pop an element from a list. The returned element is not
|
||||
encapsulated in a ``Notifier`` and its mutations are no longer
|
||||
encapsulated in a :class:`.Notifier` and its mutations are no longer
|
||||
tracked."""
|
||||
r = self._backing_struct.pop(i)
|
||||
if self.root.publish is not None:
|
||||
|
@ -199,11 +199,11 @@ class Notifier:
|
|||
|
||||
class Publisher(AsyncioServer):
|
||||
"""A network server that publish changes to structures encapsulated in
|
||||
``Notifiers``.
|
||||
a :class:`.Notifier`.
|
||||
|
||||
:param notifiers: A dictionary containing the notifiers to associate with
|
||||
the ``Publisher``. The keys of the dictionary are the names of the
|
||||
notifiers to be used with ``Subscriber``.
|
||||
the :class:`.Publisher`. The keys of the dictionary are the names of the
|
||||
notifiers to be used with :class:`.Subscriber`.
|
||||
"""
|
||||
def __init__(self, notifiers):
|
||||
AsyncioServer.__init__(self)
|
||||
|
@ -245,7 +245,7 @@ class Publisher(AsyncioServer):
|
|||
finally:
|
||||
self._recipients[notifier_name].remove(queue)
|
||||
except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError):
|
||||
# subscribers disconnecting are a normal occurence
|
||||
# subscribers disconnecting are a normal occurrence
|
||||
pass
|
||||
finally:
|
||||
writer.close()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Core language reference
|
||||
=======================
|
||||
|
||||
The most commonly used features from the ARTIQ language modules and from the core device modules are bundled together in ``artiq.experiment`` and can be imported with ``from artiq.experiment import *``.
|
||||
The most commonly used features from the ARTIQ language modules and from the core device modules are bundled together in :mod:`artiq.experiment` and can be imported with ``from artiq.experiment import *``.
|
||||
|
||||
:mod:`artiq.language.core` module
|
||||
---------------------------------
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Developing a network device support package
|
||||
===========================================
|
||||
Developing a Network Device Support Package (NDSP)
|
||||
==================================================
|
||||
|
||||
Most ARTIQ devices are interfaced through "controllers" that expose RPC interfaces to the network (based on :class:`artiq.protocols.pc_rpc`). The master never does direct I/O to the devices, but issues RPCs to the controllers when needed. As opposed to running everything on the master, this architecture has those main advantages:
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ The device database
|
|||
|
||||
The device database contains information about the devices available in a ARTIQ installation, what drivers to use, what controllers to use and on what machine, and where the devices are connected.
|
||||
|
||||
The master (or ``artiq_run``) instantiates the device drivers (and the RPC clients in the case of controllers) for the experiments based on the contents of the device database.
|
||||
The master (or :mod:`~artiq.frontend.artiq_run`) instantiates the device drivers (and the RPC clients in the case of controllers) for the experiments based on the contents of the device database.
|
||||
|
||||
The device database is stored in the memory of the master and is generated by a Python script typically called ``device_db.py``. That script must define a global variable ``device_db`` with the contents of the database. The device database is a Python dictionary whose keys are the device names, and values can have several types.
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ As a very first step, we will turn on a LED on the core device. Create a file ``
|
|||
self.core.reset()
|
||||
self.led.on()
|
||||
|
||||
The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.environment.EnvExperiment`. Among other features, ``EnvExperiment`` calls our ``build`` method and provides the ``setattr_device`` method that interfaces to the device database to create the appropriate device drivers and make those drivers accessible as ``self.core`` and ``self.led``. The ``@kernel`` decorator tells the system that the ``run`` method must be compiled for and executed on the core device (instead of being interpreted and executed as regular Python code on the host). The decorator uses ``self.core`` internally, which is why we request the core device using ``setattr_device`` like any other.
|
||||
The central part of our code is our ``LED`` class, which derives from :class:`artiq.language.environment.EnvExperiment`. Among other features, :class:`~artiq.language.environment.EnvExperiment` calls our :meth:`~artiq.language.environment.Experiment.build` method and provides the :meth:`~artiq.language.environment.HasEnvironment.setattr_device` method that interfaces to the device database to create the appropriate device drivers and make those drivers accessible as ``self.core`` and ``self.led``. The :func:`~artiq.language.core.kernel` decorator (``@kernel``) tells the system that the :meth:`~artiq.language.environment.Experiment.run` method must be compiled for and executed on the core device (instead of being interpreted and executed as regular Python code on the host). The decorator uses ``self.core`` internally, which is why we request the core device using :meth:`~artiq.language.environment.HasEnvironment.setattr_device` like any other.
|
||||
|
||||
Copy the file ``device_db.py`` (containing the device database) from the ``examples/master`` folder of ARTIQ into the same directory as ``led.py`` (alternatively, you can use the ``--device-db`` option of ``artiq_run``). You will probably want to set the IP address of the core device in ``device_db.py`` so that the computer can connect to it (it is the ``host`` parameter of the ``comm`` entry). See :ref:`device-db` for more information. The example device database is designed for the ``nist_clock`` hardware adapter on the KC705; see :ref:`board-ports` for RTIO channel assignments if you need to adapt the device database to a different hardware platform.
|
||||
|
||||
|
@ -69,18 +69,18 @@ You can then turn the LED off and on by entering 0 or 1 at the prompt that appea
|
|||
$ artiq_run led.py
|
||||
Enter desired LED state: 0
|
||||
|
||||
What happens is the ARTIQ compiler notices that the ``input_led_state`` function does not have a ``@kernel`` decorator and thus must be executed on the host. When the core device calls it, it sends a request to the host to execute it. The host displays the prompt, collects user input, and sends the result back to the core device, which sets the LED state accordingly.
|
||||
What happens is the ARTIQ compiler notices that the :meth:`input_led_state` function does not have a ``@kernel`` decorator (:func:`~artiq.language.core.kernel`) and thus must be executed on the host. When the core device calls it, it sends a request to the host to execute it. The host displays the prompt, collects user input, and sends the result back to the core device, which sets the LED state accordingly.
|
||||
|
||||
RPC functions must always return a value of the same type. When they return a value that is not ``None``, the compiler should be informed in advance of the type of the value, which is what the ``-> TBool`` annotation is for.
|
||||
|
||||
Without the ``break_realtime()`` call, the RTIO events emitted by ``self.led.on()`` or ``self.led.off()`` would be scheduled at a fixed and very short delay after entering ``run()``.
|
||||
These events would fail because the RPC to ``input_led_state()`` can take an arbitrary amount of time and therefore the deadline for submission of RTIO events would have long passed when ``self.led.on()`` or ``self.led.off()`` are called.
|
||||
The ``break_realtime()`` call is necessary to waive the real-time requirements of the LED state change.
|
||||
Without the :meth:`~artiq.coredevice.core.Core.break_realtime` call, the RTIO events emitted by :func:`self.led.on()` or :func:`self.led.off()` would be scheduled at a fixed and very short delay after entering :meth:`~artiq.language.environment.Experiment.run()`.
|
||||
These events would fail because the RPC to :meth:`input_led_state()` can take an arbitrary amount of time and therefore the deadline for submission of RTIO events would have long passed when :func:`self.led.on()` or :func:`self.led.off()` are called.
|
||||
The :meth:`~artiq.coredevice.core.Core.break_realtime` call is necessary to waive the real-time requirements of the LED state change.
|
||||
It advances the timeline far enough to ensure that events can meet the submission deadline.
|
||||
|
||||
|
||||
Real-time I/O (RTIO)
|
||||
--------------------
|
||||
Real-time Input/Output (RTIO)
|
||||
-----------------------------
|
||||
|
||||
The point of running code on the core device is the ability to meet demanding real-time constraints. In particular, the core device can respond to an incoming stimulus or the result of a measurement with a low and predictable latency. We will see how to use inputs later; first, we must familiarize ourselves with how time is managed in kernels.
|
||||
|
||||
|
@ -102,11 +102,11 @@ Create a new file ``rtio.py`` containing the following: ::
|
|||
delay(2*us)
|
||||
self.ttl0.pulse(2*us)
|
||||
|
||||
In its ``build()`` method, the experiment obtains the core device and a TTL device called ``ttl0`` as defined in the device database.
|
||||
In its :meth:`~artiq.language.environment.Experiment.build` method, the experiment obtains the core device and a TTL device called ``ttl0`` as defined in the device database.
|
||||
In ARTIQ, TTL is used roughly synonymous with "a single generic digital signal" and does not refer to a specific signaling standard or voltage/current levels.
|
||||
|
||||
When ``run()``, the experiment first ensures that ``ttl0`` is in output mode and actively driving the device it is connected to.
|
||||
Bidirectional TTL channels (i.e. ``TTLInOut``) are in input (high impedance) mode by default, output-only TTL channels (``TTLOut``) are always in output mode.
|
||||
When :meth:`~artiq.language.environment.Experiment.run`, the experiment first ensures that ``ttl0`` is in output mode and actively driving the device it is connected to.
|
||||
Bidirectional TTL channels (i.e. :class:`~artiq.devices.ttl.TTLInOut`) are in input (high impedance) mode by default, output-only TTL channels (:class:`~artiq.devices.ttl.TTLOut`) are always in output mode.
|
||||
There are no input-only TTL channels.
|
||||
|
||||
The experiment then drives one million 2 µs long pulses separated by 2 µs each.
|
||||
|
@ -118,7 +118,7 @@ Any asymmetry in the overhead would manifest itself in a distorted and variable
|
|||
|
||||
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 smaller than the current RTIO clock timestamp), a :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.
|
||||
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 smaller than the current RTIO clock timestamp), a :exc:`~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.
|
||||
|
||||
Try reducing the period of the generated waveform until the CPU cannot keep up with the generation of switching events and the underflow exception is raised. Then try catching it: ::
|
||||
|
||||
|
@ -161,7 +161,7 @@ ARTIQ can implement ``with parallel`` blocks without having to resort to any of
|
|||
It simply remembers the position on the timeline when entering the ``parallel`` block and then seeks back to that position after submitting the events generated by each statement.
|
||||
In other words, the statements in the ``parallel`` block are actually executed sequentially, only the RTIO events generated by them are scheduled to be executed in parallel.
|
||||
Note that if a statement takes a lot of CPU time to execute (this different from the events scheduled by a statement taking a long time), it may cause a subsequent statement to miss the deadline for timely submission of its events.
|
||||
This then causes a ``RTIOUnderflow`` exception to be raised.
|
||||
This then causes a :exc:`~artiq.coredevice.exceptions.RTIOUnderflow` exception to be raised.
|
||||
|
||||
Within a parallel block, some statements can be made sequential again using a ``with sequential`` construct. Observe the pulses generated by this code: ::
|
||||
|
||||
|
@ -199,8 +199,8 @@ The core device records the real-time I/O waveforms into a circular buffer. It i
|
|||
Afterwards, the recorded data can be extracted and written to a VCD file using ``artiq_coreanalyzer -w rtio.vcd`` (see: :ref:`core-device-rtio-analyzer-tool`). VCD files can be viewed using third-party tools such as GtkWave.
|
||||
|
||||
|
||||
DMA
|
||||
---
|
||||
Direct Memory Access (DMA)
|
||||
--------------------------
|
||||
|
||||
DMA allows you to store fixed sequences of pulses in system memory, and have the DMA core in the FPGA play them back at high speed. Pulse sequences that are too fast for the CPU (i.e. would cause RTIO underflows) can still be generated using DMA. The only modification of the sequence that the DMA core supports is shifting it in time (so it can be played back at any position of the timeline), everything else is fixed at the time of recording the sequence.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Management system
|
||||
=================
|
||||
|
||||
The management system described below is optional: experiments can be run one by one using ``artiq_run``, and the controllers can run stand-alone (without a controller manager). For their very first steps with ARTIQ or in simple or particular cases, users do not need to deploy the management system.
|
||||
The management system described below is optional: experiments can be run one by one using :mod:`~artiq.frontend.artiq_run`, and the controllers can run stand-alone (without a controller manager). For their very first steps with ARTIQ or in simple or particular cases, users do not need to deploy the management system.
|
||||
|
||||
Components
|
||||
**********
|
||||
|
@ -42,13 +42,15 @@ Basics
|
|||
|
||||
To use hardware resources more efficiently, potentially compute-intensive pre-computation and analysis phases of other experiments is executed in parallel with the body of the current experiment that accesses the hardware.
|
||||
|
||||
.. seealso:: These steps are implemented in :class:`~artiq.language.environment.Experiment`. However, user-written experiments should usually derive from (sub-class) :class:`artiq.language.environment.EnvExperiment`.
|
||||
|
||||
Experiments are divided into three phases that are programmed by the user:
|
||||
|
||||
1. The preparation stage, that pre-fetches and pre-computes any data that necessary to run the experiment. Users may implement this stage by overloading the ``prepare`` method. It is not permitted to access hardware in this stage, as doing so may conflict with other experiments using the same devices.
|
||||
2. The running stage, that corresponds to the body of the experiment, and typically accesses hardware. Users must implement this stage and overload the ``run`` method.
|
||||
3. The analysis stage, where raw results collected in the running stage are post-processed and may lead to updates of the parameter database. This stage may be implemented by overloading the ``analyze`` method.
|
||||
1. The **preparation** stage, that pre-fetches and pre-computes any data that necessary to run the experiment. Users may implement this stage by overloading the :meth:`~artiq.language.environment.Experiment.prepare` method. It is not permitted to access hardware in this stage, as doing so may conflict with other experiments using the same devices.
|
||||
2. The **running** stage, that corresponds to the body of the experiment, and typically accesses hardware. Users must implement this stage and overload the :meth:`~artiq.language.environment.Experiment.run` method.
|
||||
3. The **analysis** stage, where raw results collected in the running stage are post-processed and may lead to updates of the parameter database. This stage may be implemented by overloading the :meth:`~artiq.language.environment.Experiment.analyze` method.
|
||||
|
||||
.. note:: Only the ``run`` method implementation is mandatory; if the experiment does not fit into the pipelined scheduling model, it can leave one or both of the other methods empty (which is the default).
|
||||
.. note:: Only the :meth:`~artiq.language.environment.Experiment.run` method implementation is mandatory; if the experiment does not fit into the pipelined scheduling model, it can leave one or both of the other methods empty (which is the default).
|
||||
|
||||
The three phases of several experiments are then executed in a pipelined manner by the scheduler in the ARTIQ master: experiment A executes its preparation stage, then experiment A executes its running stage while experiment B executes its preparation stage, and so on.
|
||||
|
||||
|
@ -70,11 +72,11 @@ If there are other experiments with higher priority (e.g. a high-priority timed
|
|||
Otherwise, ``pause()`` returns immediately.
|
||||
To check whether ``pause()`` would in fact *not* return immediately, use :meth:`artiq.master.scheduler.Scheduler.check_pause`.
|
||||
|
||||
The experiment must place the hardware in a safe state and disconnect from the core device (typically, by using ``self.core.comm.close()``) before calling ``pause``.
|
||||
The experiment must place the hardware in a safe state and disconnect from the core device (typically, by calling ``self.coredevice.comm.close()`` from the kernel, which is equivalent to :meth:`artiq.coredevice.core.Core.close`) before calling ``pause()``.
|
||||
|
||||
Accessing the ``pause`` and ``check_pause`` methods is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using ``get_device`` or ``attr_device``.
|
||||
Accessing the ``pause()`` and :meth:`~artiq.master.scheduler.Scheduler.check_pause` methods is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using :meth:`~artiq.language.environment.HasEnvironment.get_device` (``self.get_device()``) or :meth:`~artiq.language.environment.HasEnvironment.setattr_device` (``self.setattr_device()``).
|
||||
|
||||
``check_pause`` can be called (via RPC) from a kernel, but ``pause`` must not.
|
||||
:meth:`~artiq.master.scheduler.Scheduler.check_pause` can be called (via RPC) from a kernel, but ``pause()`` must not.
|
||||
|
||||
Multiple pipelines
|
||||
------------------
|
||||
|
|
Loading…
Reference in New Issue