mirror of https://github.com/m-labs/artiq.git
doc: various cleanups
This commit is contained in:
parent
cb036a30c7
commit
73d0a84b44
|
@ -6,10 +6,9 @@ from artiq.devices import rtio_core
|
|||
class DDS(AutoContext):
|
||||
"""Core device Direct Digital Synthesis (DDS) driver.
|
||||
|
||||
This driver controls DDS devices managed directly by the core device's
|
||||
runtime. It also uses a RTIO channel (through
|
||||
:class:`artiq.devices.rtio_core.RTIOOut`) to control a RF switch that
|
||||
gates the output of the DDS device.
|
||||
Controls DDS devices managed directly by the core device's runtime. It also
|
||||
uses a RTIO channel (through :class:`artiq.devices.rtio_core.RTIOOut`) to
|
||||
control a RF switch that gates the output of the DDS device.
|
||||
|
||||
:param dds_sysclk: DDS system frequency, used for computing the frequency
|
||||
tuning words.
|
||||
|
|
|
@ -28,8 +28,8 @@ class _RTIOBase(AutoContext):
|
|||
class RTIOOut(_RTIOBase):
|
||||
"""RTIO output driver.
|
||||
|
||||
This driver configures the corresponding RTIO channel as output on the core
|
||||
device and provides functions to set its level.
|
||||
Configures the corresponding RTIO channel as output on the core device and
|
||||
provides functions to set its level.
|
||||
|
||||
This driver supports zero-length transition suppression. For example, if
|
||||
two pulses are emitted back-to-back with no delay between them, they will
|
||||
|
@ -82,9 +82,9 @@ class RTIOOut(_RTIOBase):
|
|||
class RTIOIn(_RTIOBase):
|
||||
"""RTIO input driver.
|
||||
|
||||
This driver configures the corresponding RTIO channel as input on the core
|
||||
device and provides functions to analyze the incoming signal, with
|
||||
real-time gating to prevent overflows.
|
||||
Configures the corresponding RTIO channel as input on the core device and
|
||||
provides functions to analyze the incoming signal, with real-time gating
|
||||
to prevent overflows.
|
||||
|
||||
:param core: core device
|
||||
:param channel: channel number
|
||||
|
|
|
@ -127,7 +127,7 @@ class AutoContext:
|
|||
... self.exp2 = SubExperiment(self, bar=self.bar2)
|
||||
... self.exp3 = SubExperiment(self, bar=self.bar2 + self.offset)
|
||||
...
|
||||
>>> def run():
|
||||
... def run():
|
||||
... self.exp1.run()
|
||||
... self.exp2.run()
|
||||
... self.exp3.run()
|
||||
|
|
|
@ -4,9 +4,9 @@ ARTIQ documentation
|
|||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 2
|
||||
|
||||
installing
|
||||
tutorial
|
||||
core_reference
|
||||
drivers_reference
|
||||
installing
|
||||
tutorial
|
||||
core_reference
|
||||
drivers_reference
|
||||
|
|
|
@ -7,29 +7,30 @@ Preparing the core device FPGA board
|
|||
You may skip those steps if the board is already flashed.
|
||||
|
||||
You will need:
|
||||
* FPGA vendor tools (e.g. Xilinx ISE or Vivado)
|
||||
* OpenRISC GCC/binutils toolchain (or1k-elf-...)
|
||||
* Python 3.3+
|
||||
* Migen and MiSoC (http://m-labs.hk/gateware.html)
|
||||
|
||||
* FPGA vendor tools (e.g. Xilinx ISE or Vivado)
|
||||
* OpenRISC GCC/binutils toolchain (or1k-elf-...)
|
||||
* Python 3.3+
|
||||
* Migen and MiSoC (http://m-labs.hk/gateware.html)
|
||||
|
||||
After these components are installed, build and flash the bitstream and BIOS by running `from the MiSoC top-level directory`: ::
|
||||
|
||||
$ ./make.py -X /path_to/ARTIQ/soc -t artiq all
|
||||
$ ./make.py -X /path_to/ARTIQ/soc -t artiq all
|
||||
|
||||
Then, build and flash the ARTIQ runtime: ::
|
||||
|
||||
$ cd /path_to/ARTIQ/soc/runtime
|
||||
$ make flash
|
||||
$ cd /path_to/ARTIQ/soc/runtime
|
||||
$ make 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 bitstream that was newly written into the flash): ::
|
||||
|
||||
$ flterm --port /dev/ttyUSB1
|
||||
MiSoC BIOS http://m-labs.hk
|
||||
[...]
|
||||
Booting from flash...
|
||||
Loading xxxxx bytes from flash...
|
||||
Executing booted program.
|
||||
ARTIQ runtime built <date/time>
|
||||
$ flterm --port /dev/ttyUSB1
|
||||
MiSoC BIOS http://m-labs.hk
|
||||
[...]
|
||||
Booting from flash...
|
||||
Loading xxxxx bytes from flash...
|
||||
Executing booted program.
|
||||
ARTIQ runtime built <date/time>
|
||||
|
||||
The communication parameters are 115200 8-N-1.
|
||||
|
||||
|
@ -38,40 +39,39 @@ Installing the host-side software
|
|||
|
||||
The main dependency of ARTIQ is LLVM and its Python bindings (http://llvmpy.org). Currently, this installation is tedious because of the OpenRISC support not being merged upstream LLVM and because of incompatibilities between the versions of LLVM that support OpenRISC and the versions of LLVM that support the Python bindings. ::
|
||||
|
||||
git clone https://github.com/openrisc/llvm-or1k
|
||||
cd llvm-or1k
|
||||
git checkout b3a48efb2c05ed6cedc5395ae726c6a6573ef3ba
|
||||
patch -p1 < /path_to/ARTIQ/patches/llvm/*
|
||||
git clone https://github.com/openrisc/llvm-or1k
|
||||
cd llvm-or1k
|
||||
git checkout b3a48efb2c05ed6cedc5395ae726c6a6573ef3ba
|
||||
patch -p1 < /path_to/ARTIQ/patches/llvm/*
|
||||
|
||||
cd tools
|
||||
git clone https://github.com/openrisc/clang-or1k clang
|
||||
cd clang
|
||||
git checkout 02d831c7e7dc1517abed9cc96abdfb937af954eb
|
||||
patch -p1 < /path_to/ARTIQ/patches/clang/*
|
||||
cd tools
|
||||
git clone https://github.com/openrisc/clang-or1k clang
|
||||
cd clang
|
||||
git checkout 02d831c7e7dc1517abed9cc96abdfb937af954eb
|
||||
patch -p1 < /path_to/ARTIQ/patches/clang/*
|
||||
|
||||
cd ../..
|
||||
mkdir build && cd build
|
||||
../configure --prefix=/usr/local/llvm-or1k
|
||||
make ENABLE_OPTIMIZED=1 REQUIRES_RTTI=1
|
||||
sudo -E make install ENABLE_OPTIMIZED=1 REQUIRES_RTTI=1
|
||||
cd ../..
|
||||
mkdir build && cd build
|
||||
../configure --prefix=/usr/local/llvm-or1k
|
||||
make ENABLE_OPTIMIZED=1 REQUIRES_RTTI=1
|
||||
sudo -E make install ENABLE_OPTIMIZED=1 REQUIRES_RTTI=1
|
||||
|
||||
cd ../..
|
||||
git clone https://github.com/llvmpy/llvmpy
|
||||
cd llvmpy
|
||||
git checkout llvm-3.4
|
||||
git checkout 7af2f7140391d4f708adf2721e84f23c1b89e97a
|
||||
patch -p1 < /path_to/ARTIQ/patches/llvmpy/*
|
||||
LLVM_CONFIG_PATH=/usr/local/llvm-or1k/bin/llvm-config sudo -E python setup.py install
|
||||
cd ../..
|
||||
git clone https://github.com/llvmpy/llvmpy
|
||||
cd llvmpy
|
||||
git checkout llvm-3.4
|
||||
git checkout 7af2f7140391d4f708adf2721e84f23c1b89e97a
|
||||
patch -p1 < /path_to/ARTIQ/patches/llvmpy/*
|
||||
LLVM_CONFIG_PATH=/usr/local/llvm-or1k/bin/llvm-config sudo -E python setup.py install
|
||||
|
||||
.. note::
|
||||
``python`` refers to Python 3. You may need to use the ``python3`` command instead of ``python`` on some distributions.
|
||||
``python`` refers to Python 3. You may need to use the ``python3`` command instead of ``python`` on some distributions.
|
||||
|
||||
You may want to use ``checkinstall`` instead of ``make install`` (to register the installation with your package manager) and ``pip3 install --user .`` instead of ``sudo -E python setup.py install``.
|
||||
|
||||
You can then install ARTIQ itself: ::
|
||||
|
||||
cd /path_to/ARTIQ
|
||||
sudo python setup.py
|
||||
cd /path_to/ARTIQ
|
||||
sudo python setup.py
|
||||
|
||||
Alternatively, you can simply add the ARTIQ directory to your ``PYTHONPATH`` environment variable. The advantage of this technique is that you will not need to reinstall ARTIQ when modifying or upgrading it, which is useful during development.
|
||||
|
||||
|
|
|
@ -6,33 +6,33 @@ Connecting to the core device
|
|||
|
||||
As a very first step, we will turn on a LED on the core device. Create a file ``led.py`` containing the following: ::
|
||||
|
||||
from artiq import *
|
||||
from artiq.devices import corecom_serial, core, gpio_core
|
||||
from artiq import *
|
||||
from artiq.devices import corecom_serial, core, gpio_core
|
||||
|
||||
class LED(AutoContext):
|
||||
parameters = "led"
|
||||
class LED(AutoContext):
|
||||
parameters = "led"
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.led.set(1)
|
||||
@kernel
|
||||
def run(self):
|
||||
self.led.set(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
with corecom_serial.CoreCom() as com:
|
||||
core_driver = core.Core(com)
|
||||
led_driver = gpio_core.GPIOOut(core=core_driver, channel=0)
|
||||
exp = LED(core=core_driver, led=led_driver)
|
||||
exp.run()
|
||||
if __name__ == "__main__":
|
||||
with corecom_serial.CoreCom() as com:
|
||||
core_driver = core.Core(com)
|
||||
led_driver = gpio_core.GPIOOut(core=core_driver, channel=0)
|
||||
exp = LED(core=core_driver, led=led_driver)
|
||||
exp.run()
|
||||
|
||||
The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.core.AutoContext`. ``AutoContext`` is part of the mechanism that attaches device drivers and retrieves parameters according to a database. We are not using the database yet; instead, we import and create the device drivers and establish communication with the core device manually. The ``parameters`` string gives the list of devices (and parameters) that our class needs in order to operate. ``AutoContext`` sets them as object attributes, so our ``led`` parameter becomes accessible as ``self.led``. Finally, the ``@kernel`` decorator tells the system that the ``run`` method must be executed on the core device (instead of the host).
|
||||
|
||||
Run this example with: ::
|
||||
|
||||
python led.py
|
||||
python led.py
|
||||
|
||||
The LED of the device should turn on. Congratulations! You have a basic ARTIQ system up and running.
|
||||
|
||||
.. note::
|
||||
ARTIQ requires Python 3, and you may need to use the ``python3`` command instead of ``python`` on some distributions.
|
||||
ARTIQ requires Python 3, and you may need to use the ``python3`` command instead of ``python`` on some distributions.
|
||||
|
||||
Host/core device interaction
|
||||
----------------------------
|
||||
|
@ -41,22 +41,22 @@ A method or function running on the core device (which we call a "kernel") may c
|
|||
|
||||
Modify the code as follows: ::
|
||||
|
||||
def input_led_state():
|
||||
return int(input("Enter desired LED state: "))
|
||||
def input_led_state():
|
||||
return int(input("Enter desired LED state: "))
|
||||
|
||||
class LED(AutoContext):
|
||||
parameters = "led"
|
||||
class LED(AutoContext):
|
||||
parameters = "led"
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.led.set(input_led_state())
|
||||
@kernel
|
||||
def run(self):
|
||||
self.led.set(input_led_state())
|
||||
|
||||
You can then turn the LED off and on by entering 0 or 1 at the prompt that appears: ::
|
||||
|
||||
$ python led.py
|
||||
Enter desired LED state: 1
|
||||
$ python led.py
|
||||
Enter desired LED state: 0
|
||||
$ python led.py
|
||||
Enter desired LED state: 1
|
||||
$ python 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.
|
||||
|
||||
|
@ -81,24 +81,24 @@ The point of running code on the core device is the ability to meet demanding re
|
|||
|
||||
Create a new file ``rtio.py`` containing the following: ::
|
||||
|
||||
from artiq import *
|
||||
from artiq.devices import corecom_serial, core, rtio_core
|
||||
from artiq import *
|
||||
from artiq.devices import corecom_serial, core, rtio_core
|
||||
|
||||
class Tutorial(AutoContext):
|
||||
parameters = "o"
|
||||
class Tutorial(AutoContext):
|
||||
parameters = "o"
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
for i in range(1000000):
|
||||
self.o.pulse(2*us)
|
||||
delay(2*us)
|
||||
@kernel
|
||||
def run(self):
|
||||
for i in range(1000000):
|
||||
self.o.pulse(2*us)
|
||||
delay(2*us)
|
||||
|
||||
if __name__ == "__main__":
|
||||
with corecom_serial.CoreCom() as com:
|
||||
core_driver = core.Core(com)
|
||||
out_driver = rtio_core.RTIOOut(core=core_driver, channel=1)
|
||||
exp = Tutorial(core=core_driver, o=out_driver)
|
||||
exp.run()
|
||||
if __name__ == "__main__":
|
||||
with corecom_serial.CoreCom() as com:
|
||||
core_driver = core.Core(com)
|
||||
out_driver = rtio_core.RTIOOut(core=core_driver, channel=1)
|
||||
exp = Tutorial(core=core_driver, o=out_driver)
|
||||
exp.run()
|
||||
|
||||
Connect an oscilloscope or logic analyzer to the RTIO channel 1 (pin C11 on the Papilio Pro) and run ``python rtio.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.
|
||||
|
||||
|
@ -106,23 +106,23 @@ Instead, inside the core device, output timing is generated by the gateware and
|
|||
|
||||
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: ::
|
||||
|
||||
from artiq.devices.runtime_exceptions import RTIOUnderflow
|
||||
from artiq.devices.runtime_exceptions import RTIOUnderflow
|
||||
|
||||
def print_underflow():
|
||||
print("RTIO underflow occured")
|
||||
def print_underflow():
|
||||
print("RTIO underflow occured")
|
||||
|
||||
class Tutorial(AutoContext):
|
||||
parameters = "led o"
|
||||
class Tutorial(AutoContext):
|
||||
parameters = "led o"
|
||||
|
||||
def run(self):
|
||||
self.led.set(0)
|
||||
try:
|
||||
for i in range(1000000):
|
||||
self.o.pulse(...)
|
||||
delay(...)
|
||||
except RTIOUnderflow:
|
||||
self.led.set(1)
|
||||
print_underflow()
|
||||
def run(self):
|
||||
self.led.set(0)
|
||||
try:
|
||||
for i in range(1000000):
|
||||
self.o.pulse(...)
|
||||
delay(...)
|
||||
except RTIOUnderflow:
|
||||
self.led.set(1)
|
||||
print_underflow()
|
||||
|
||||
Parallel and sequential blocks
|
||||
------------------------------
|
||||
|
@ -131,24 +131,24 @@ It is often necessary that several pulses overlap one another. This can be expre
|
|||
|
||||
Try the following code and observe the generated pulses on a 2-channel oscilloscope or logic analyzer: ::
|
||||
|
||||
for i in range(1000000):
|
||||
with parallel:
|
||||
self.o1.pulse(2*us)
|
||||
self.o2.pulse(4*us)
|
||||
delay(4*us)
|
||||
for i in range(1000000):
|
||||
with parallel:
|
||||
self.o1.pulse(2*us)
|
||||
self.o2.pulse(4*us)
|
||||
delay(4*us)
|
||||
|
||||
If you assign ``o2`` to the RTIO channel 2, the signal will be generated on the pin C10 of the Papilio Pro.
|
||||
|
||||
Within a parallel block, some statements can be made sequential again using a ``with sequential`` construct. Observe the pulses generated by this code: ::
|
||||
|
||||
for i in range(1000000):
|
||||
with parallel:
|
||||
with sequential:
|
||||
self.o1.pulse(2*us)
|
||||
delay(1*us)
|
||||
self.o1.pulse(1*us)
|
||||
self.o2.pulse(4*us)
|
||||
delay(4*us)
|
||||
for i in range(1000000):
|
||||
with parallel:
|
||||
with sequential:
|
||||
self.o1.pulse(2*us)
|
||||
delay(1*us)
|
||||
self.o1.pulse(1*us)
|
||||
self.o2.pulse(4*us)
|
||||
delay(4*us)
|
||||
|
||||
.. warning::
|
||||
In its current implementation, ARTIQ only supports those pulse sequences that can be interleaved at compile time into a sequential series of on/off events. Combinations of ``parallel``/``sequential`` blocks that require multithreading (due to the parallel execution of long loops, complex algorithms, or algorithms that depend on external input) will cause the compiler to return an error.
|
||||
In its current implementation, ARTIQ only supports those pulse sequences that can be interleaved at compile time into a sequential series of on/off events. Combinations of ``parallel``/``sequential`` blocks that require multithreading (due to the parallel execution of long loops, complex algorithms, or algorithms that depend on external input) will cause the compiler to return an error.
|
||||
|
|
Loading…
Reference in New Issue