documentation improvements

Based on PR #1101 by @drewrisinger
This commit is contained in:
Sebastien Bourdeauducq 2018-09-26 12:12:37 +08:00
parent 53c7a5f2c6
commit c71e442929
16 changed files with 97 additions and 86 deletions

View File

@ -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.

View File

@ -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``

View File

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

View File

@ -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.

View File

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

View File

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

View File

@ -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.
@ -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()

View File

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

View File

@ -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:

View File

@ -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:

View File

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

View File

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

View File

@ -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:

View File

@ -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.

View File

@ -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.

View File

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