device and parameter database

This commit is contained in:
Sebastien Bourdeauducq 2014-12-03 18:20:30 +08:00
parent a41009f92a
commit 2a95d27770
23 changed files with 385 additions and 261 deletions

View File

@ -1,5 +1,6 @@
from operator import itemgetter from operator import itemgetter
from artiq.language.context import AutoContext
from artiq.language.units import ms, ns from artiq.language.units import ms, ns
from artiq.coredevice.runtime import LinkInterface from artiq.coredevice.runtime import LinkInterface
@ -13,7 +14,9 @@ class _RuntimeEnvironment(LinkInterface):
return str(self.llvm_module) return str(self.llvm_module)
class Comm: class Comm(AutoContext):
implicit_core = False
def get_runtime_env(self): def get_runtime_env(self):
return _RuntimeEnvironment(1*ns) return _RuntimeEnvironment(1*ns)

View File

@ -8,6 +8,7 @@ import logging
from artiq.language import core as core_language from artiq.language import core as core_language
from artiq.language import units from artiq.language import units
from artiq.language.context import *
from artiq.coredevice.runtime import Environment from artiq.coredevice.runtime import Environment
from artiq.coredevice import runtime_exceptions from artiq.coredevice import runtime_exceptions
@ -59,13 +60,17 @@ def _read_exactly(f, n):
return r return r
class Comm: class Comm(AutoContext):
def __init__(self, dev="/dev/ttyUSB1", baud=115200): serial_dev = Parameter("/dev/ttyUSB1")
self._fd = os.open(dev, os.O_RDWR | os.O_NOCTTY) baud_rate = Parameter(115200)
implicit_core = False
def build(self):
self._fd = os.open(self.serial_dev, os.O_RDWR | os.O_NOCTTY)
self.port = os.fdopen(self._fd, "r+b", buffering=0) self.port = os.fdopen(self._fd, "r+b", buffering=0)
self.set_baud(115200) self.set_baud(115200)
self.set_remote_baud(baud) self.set_remote_baud(self.baud_rate)
self.set_baud(baud) self.set_baud(self.baud_rate)
def set_baud(self, baud): def set_baud(self, baud):
iflag, oflag, cflag, lflag, ispeed, ospeed, cc = \ iflag, oflag, cflag, lflag, ispeed, ospeed, cc = \
@ -109,12 +114,6 @@ class Comm:
self.set_remote_baud(115200) self.set_remote_baud(115200)
self.port.close() self.port.close()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
def _get_device_msg(self): def _get_device_msg(self):
while True: while True:
(reply, ) = struct.unpack("B", _read_exactly(self.port, 1)) (reply, ) = struct.unpack("B", _read_exactly(self.port, 1))

View File

@ -1,5 +1,8 @@
import os import os
from artiq.language.core import *
from artiq.language.context import *
from artiq.transforms.inline import inline from artiq.transforms.inline import inline
from artiq.transforms.lower_units import lower_units from artiq.transforms.lower_units import lower_units
from artiq.transforms.quantize_time import quantize_time from artiq.transforms.quantize_time import quantize_time
@ -10,8 +13,8 @@ from artiq.transforms.unroll_loops import unroll_loops
from artiq.transforms.interleave import interleave from artiq.transforms.interleave import interleave
from artiq.transforms.lower_time import lower_time from artiq.transforms.lower_time import lower_time
from artiq.transforms.unparse import unparse from artiq.transforms.unparse import unparse
from artiq.py2llvm import get_runtime_binary from artiq.py2llvm import get_runtime_binary
from artiq.language.core import *
def _announce_unparse(label, node): def _announce_unparse(label, node):
@ -41,19 +44,20 @@ def _no_debug_unparse(label, node):
pass pass
class Core: class Core(AutoContext):
def __init__(self, comm, external_clock=None, runtime_env=None): comm = Device("comm")
if runtime_env is None: external_clock = Parameter(None)
runtime_env = comm.get_runtime_env() implicit_core = False
self.runtime_env = runtime_env
self.comm = comm def build(self):
self.runtime_env = self.comm.get_runtime_env()
self.core = self self.core = self
if external_clock is None: if self.external_clock is None:
self.ref_period = self.runtime_env.internal_ref_period self.ref_period = self.runtime_env.internal_ref_period
self.comm.switch_clock(False) self.comm.switch_clock(False)
else: else:
self.ref_period = external_clock self.ref_period = self.external_clock
self.comm.switch_clock(True) self.comm.switch_clock(True)
self.initial_time = int64(self.runtime_env.warmup_time/self.ref_period) self.initial_time = int64(self.runtime_env.warmup_time/self.ref_period)

View File

@ -24,7 +24,7 @@ class DDS(AutoContext):
the DDS device. the DDS device.
""" """
dds_sysclk = Parameter() dds_sysclk = Parameter(1*GHz)
reg_channel = Parameter() reg_channel = Parameter()
rtio_switch = Parameter() rtio_switch = Parameter()

View File

@ -110,13 +110,21 @@ class AutoContext:
for k in dir(self): for k in dir(self):
v = getattr(self, k) v = getattr(self, k)
if isinstance(v, _AttributeKind): if isinstance(v, _AttributeKind):
value = self.mvs.get_missing_value(k, v) if self.mvs is None:
if (isinstance(v, Parameter)
and v.default is not NoDefault):
value = v.default
else:
raise AttributeError("Attribute '{}' not specified"
" and no MVS present".format(k))
else:
value = self.mvs.get_missing_value(k, v, self)
setattr(self, k, value) setattr(self, k, value)
self.build() self.build()
def get_missing_value(self, name, kind): def get_missing_value(self, name, kind, requester):
"""Attempts to retrieve ``parameter`` from the object's attributes. """Attempts to retrieve ``name`` from the object's attributes.
If not present, forwards the request to the parent MVS. If not present, forwards the request to the parent MVS.
The presence of this method makes ``AutoContext`` act as a MVS. The presence of this method makes ``AutoContext`` act as a MVS.
@ -125,7 +133,7 @@ class AutoContext:
try: try:
return getattr(self, name) return getattr(self, name)
except AttributeError: except AttributeError:
return self.mvs.get_missing_value(name, kind) return self.mvs.get_missing_value(name, kind, requester)
def build(self): def build(self):
"""This is called by ``__init__`` after the parameter initialization """This is called by ``__init__`` after the parameter initialization

49
artiq/management/dpdb.py Normal file
View File

@ -0,0 +1,49 @@
from collections import OrderedDict
import importlib
from artiq.language.context import *
def create_device(desc, mvs):
module = importlib.import_module(desc["module"])
device_class = getattr(module, desc["class"])
return device_class(mvs, **desc["parameters"])
class DeviceParamDB:
def __init__(self, devices, parameters):
self.devices = devices
self.parameters = parameters
self.active_devices = OrderedDict()
def get_missing_value(self, name, kind, requester):
if isinstance(kind, Device):
if name in self.active_devices:
return self.active_devices[name]
elif name in self.devices:
desc = self.devices[name]
while isinstance(desc, str):
# alias
desc = self.devices[desc]
dev = create_device(desc, self)
self.active_devices[name] = dev
return dev
else:
raise KeyError("Unknown device '{}' of type '{}'"
" requested by {}"
.format(name, kind.type_hint, requester))
elif isinstance(kind, Parameter):
if name in self.parameters:
return self.parameters[name]
elif kind.default is not NoDefault:
return kind.default
else:
raise KeyError("Unknown parameter: " + name)
else:
raise NotImplementedError
def close(self):
for dev in reversed(list(self.active_devices.values())):
if hasattr(dev, "close"):
dev.close()
self.active_devices = OrderedDict()

View File

@ -1,28 +1,38 @@
from random import Random from random import Random
from artiq.language.core import delay from artiq.language.core import delay, kernel
from artiq.language.context import AutoContext, Parameter from artiq.language.context import AutoContext, Parameter
from artiq.language import units from artiq.language import units
from artiq.sim import time from artiq.sim import time
class Core: class Core(AutoContext):
implicit_core = False
_level = 0
def run(self, k_function, k_args, k_kwargs): def run(self, k_function, k_args, k_kwargs):
return k_function(*k_args, **k_kwargs) Core._level += 1
r = k_function(*k_args, **k_kwargs)
Core._level -= 1
if Core._level == 0:
print(time.manager.format_timeline())
return r
class Input(AutoContext): class Input(AutoContext):
name = Parameter() name = Parameter()
implicit_core = False
def build(self): def build(self):
self.prng = Random() self.prng = Random()
@kernel
def wait_edge(self): def wait_edge(self):
duration = self.prng.randrange(0, 20)*units.ms duration = self.prng.randrange(0, 20)*units.ms
time.manager.event(("wait_edge", self.name, duration)) time.manager.event(("wait_edge", self.name, duration))
delay(duration) delay(duration)
@kernel
def count_gate(self, duration): def count_gate(self, duration):
result = self.prng.randrange(0, 100) result = self.prng.randrange(0, 100)
time.manager.event(("count_gate", self.name, duration, result)) time.manager.event(("count_gate", self.name, duration, result))
@ -32,8 +42,8 @@ class Input(AutoContext):
class WaveOutput(AutoContext): class WaveOutput(AutoContext):
name = Parameter() name = Parameter()
implicit_core = False
@kernel
def pulse(self, frequency, duration): def pulse(self, frequency, duration):
time.manager.event(("pulse", self.name, frequency, duration)) time.manager.event(("pulse", self.name, frequency, duration))
delay(duration) delay(duration)
@ -41,7 +51,7 @@ class WaveOutput(AutoContext):
class VoltageOutput(AutoContext): class VoltageOutput(AutoContext):
name = Parameter() name = Parameter()
implicit_core = False
@kernel
def set(self, value): def set(self, value):
time.manager.event(("set_voltage", self.name, value)) time.manager.event(("set_voltage", self.name, value))

View File

@ -7,7 +7,7 @@ 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: :: 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 import *
from artiq.coredevice import comm_serial, core, gpio
class LED(AutoContext): class LED(AutoContext):
led = Device("gpio_out") led = Device("gpio_out")
@ -16,18 +16,14 @@ As a very first step, we will turn on a LED on the core device. Create a file ``
def run(self): def run(self):
self.led.on() self.led.on()
if __name__ == "__main__":
with comm_serial.Comm() as comm:
core_driver = core.Core(comm)
led_driver = gpio.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. Abstract attributes such as ``Device("gpio_out")`` list the devices (and parameters) that our class needs in order to operate. ``AutoContext`` replaces them with the actual device drivers (and parameter values).. Finally, the ``@kernel`` decorator tells the system that the ``run`` method must be executed on the core device (instead of the host). 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. Abstract attributes such as ``Device("gpio_out")`` list the devices (and parameters) that our class needs in order to operate, and the names of the attributes (e.g. ``led``) are used to search the database. ``AutoContext`` replaces them with the actual device drivers (and parameter values). 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: :: Copy the files ``ddb.pyon`` and ``pdb.pyon`` (containing the device and parameter databases) from the ``examples`` folder of ARTIQ into the same directory as ``led.py`` (alternatively, you can use the ``-d`` and ``-p`` options of ``artiq_run.py``). You can open the database files using a text editor - their contents are in a human-readable format.
python3 led.py Run your code using ``artiq_run.py``, which is part of the ARTIQ front-end tools: ::
$ artiq_run.py led
The LED of the device should turn on. Congratulations! You have a basic ARTIQ system up and running. The LED of the device should turn on. Congratulations! You have a basic ARTIQ system up and running.
@ -53,9 +49,9 @@ Modify the code as follows: ::
You can then turn the LED off and on by entering 0 or 1 at the prompt that appears: :: You can then turn the LED off and on by entering 0 or 1 at the prompt that appears: ::
$ python3 led.py $ artiq_run.py led
Enter desired LED state: 1 Enter desired LED state: 1
$ python3 led.py $ artiq_run.py led
Enter desired LED state: 0 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 ``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.
@ -82,25 +78,18 @@ 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: :: Create a new file ``rtio.py`` containing the following: ::
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core, rtio
class Tutorial(AutoContext): class Tutorial(AutoContext):
o = Device("ttl_out") ttl0 = Device("ttl_out")
@kernel @kernel
def run(self): def run(self):
for i in range(1000000): for i in range(1000000):
self.o.pulse(2*us) self.ttl0.pulse(2*us)
delay(2*us) delay(2*us)
if __name__ == "__main__":
with comm_serial.Comm() as comm:
core_driver = core.Core(comm)
out_driver = rtio.RTIOOut(core=core_driver, channel=2)
exp = Tutorial(core=core_driver, o=out_driver)
exp.run()
Connect an oscilloscope or logic analyzer to the RTIO channel 2 (pin C11 on the Papilio Pro, TTL0) and run ``python3 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. Connect an oscilloscope or logic analyzer to TTL0 (pin C11 on the Papilio Pro) and run ``artiq_run.py led``. 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.runtime_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. 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.runtime_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.
@ -113,14 +102,14 @@ Try reducing the period of the generated waveform until the CPU cannot keep up w
class Tutorial(AutoContext): class Tutorial(AutoContext):
led = Device("gpio_out") led = Device("gpio_out")
o = Device("ttl_out") ttl0 = Device("ttl_out")
@kernel @kernel
def run(self): def run(self):
self.led.off() self.led.off()
try: try:
for i in range(1000000): for i in range(1000000):
self.o.pulse(...) self.ttl0.pulse(...)
delay(...) delay(...)
except RTIOUnderflow: except RTIOUnderflow:
self.led.on() self.led.on()
@ -135,21 +124,21 @@ Try the following code and observe the generated pulses on a 2-channel oscillosc
for i in range(1000000): for i in range(1000000):
with parallel: with parallel:
self.o1.pulse(2*us) self.ttl0.pulse(2*us)
self.o2.pulse(4*us) self.ttl1.pulse(4*us)
delay(4*us) delay(4*us)
If you assign ``o2`` to the RTIO channel 3, the signal will be generated on the pin C10 (TTL1) of the Papilio Pro. TTL1 is assigned to the pin C10 of the Papilio Pro. The name of the attributes (``ttl0`` and ``ttl1``) is used to look up hardware in the device database.
Within a parallel block, some statements can be made sequential again using a ``with sequential`` construct. Observe the pulses generated by this code: :: 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): for i in range(1000000):
with parallel: with parallel:
with sequential: with sequential:
self.o1.pulse(2*us) self.ttl0.pulse(2*us)
delay(1*us) delay(1*us)
self.o1.pulse(1*us) self.ttl0.pulse(1*us)
self.o2.pulse(4*us) self.ttl1.pulse(4*us)
delay(4*us) delay(4*us)
.. warning:: .. warning::

View File

@ -49,7 +49,7 @@ and verify that you can connect to the TCP port: ::
:tip: Use the key combination Ctrl-AltGr-9 to get the ``telnet>`` prompt, and enter ``close`` to quit Telnet. Quit the controller with Ctrl-C. :tip: Use the key combination Ctrl-AltGr-9 to get the ``telnet>`` prompt, and enter ``close`` to quit Telnet. Quit the controller with Ctrl-C.
Also verify that you can get the type of the server (the "hello" string passed to ``simple_server_loop``) using the ``identify-controller.py`` program from the ARTIQ front-end tools: :: Also verify that you can get the type of the server (the "hello" string passed to ``simple_server_loop``) using the ``identify_controller.py`` program from the ARTIQ front-end tools: ::
$ identify_controller.py ::1 7777 $ identify_controller.py ::1 7777
Type: hello Type: hello

View File

@ -8,9 +8,9 @@ class AluminumSpectroscopy(AutoContext):
spectroscopy_b = Device("dac") spectroscopy_b = Device("dac")
state_detection = Device("dds") state_detection = Device("dds")
pmt = Device("ttl_in") pmt = Device("ttl_in")
spectroscopy_freq = Parameter() spectroscopy_freq = Parameter(432*MHz)
photon_limit_low = Parameter() photon_limit_low = Parameter(10)
photon_limit_high = Parameter() photon_limit_high = Parameter(15)
@kernel @kernel
def run(self): def run(self):
@ -37,27 +37,3 @@ class AluminumSpectroscopy(AutoContext):
if photon_count < self.photon_limit_low: if photon_count < self.photon_limit_low:
state_0_count += 1 state_0_count += 1
return state_0_count return state_0_count
def main():
from artiq.sim import devices as sd
from artiq.sim import time
exp = AluminumSpectroscopy(
core=sd.Core(),
mains_sync=sd.Input(name="mains_sync"),
laser_cooling=sd.WaveOutput(name="laser_cooling"),
spectroscopy=sd.WaveOutput(name="spectroscopy"),
spectroscopy_b=sd.VoltageOutput(name="spectroscopy_b"),
state_detection=sd.WaveOutput(name="state_detection"),
pmt=sd.Input(name="pmt"),
spectroscopy_freq=432*MHz,
photon_limit_low=10,
photon_limit_high=15
)
exp.run()
print(time.manager.format_timeline())
if __name__ == "__main__":
main()

76
examples/ddb.pyon Normal file
View File

@ -0,0 +1,76 @@
{
"comm": {
"module": "artiq.coredevice.comm_serial",
"class": "Comm",
"parameters": {}
},
"core": {
"module": "artiq.coredevice.core",
"class": "Core",
"parameters": {}
},
"led": {
"module": "artiq.coredevice.gpio",
"class": "GPIOOut",
"parameters": {"channel": 0}
},
"pmt0": {
"module": "artiq.coredevice.rtio",
"class": "RTIOIn",
"parameters": {"channel": 0}
},
"pmt1": {
"module": "artiq.coredevice.rtio",
"class": "RTIOIn",
"parameters": {"channel": 1}
},
"ttl0": {
"module": "artiq.coredevice.rtio",
"class": "RTIOOut",
"parameters": {"channel": 2}
},
"ttl1": {
"module": "artiq.coredevice.rtio",
"class": "RTIOOut",
"parameters": {"channel": 3}
},
"ttl2": {
"module": "artiq.coredevice.rtio",
"class": "RTIOOut",
"parameters": {"channel": 4}
},
"dds0": {
"module": "artiq.coredevice.dds",
"class": "DDS",
"parameters": {"reg_channel": 0, "rtio_switch": 5}
},
"dds1": {
"module": "artiq.coredevice.dds",
"class": "DDS",
"parameters": {"reg_channel": 1, "rtio_switch": 6}
},
"dds2": {
"module": "artiq.coredevice.dds",
"class": "DDS",
"parameters": {"reg_channel": 2, "rtio_switch": 7}
},
"electrodes": {
"module": "artiq.devices.pdq2",
"class": "CompoundPDQ2",
"parameters": {
"ids": ["qc_q1_0", "qc_q1_1", "qc_q1_2", "qc_q1_3"],
"rtio_trigger": 7,
"rtio_frame": (2, 3, 4)
},
"comment": "Conflicts with dds2 and ttl0-2"
},
"pmt": "pmt0",
"bd": "dds0",
"bdd": "dds1"
}

37
examples/ddb_sim.pyon Normal file
View File

@ -0,0 +1,37 @@
{
"core": {
"module": "artiq.sim.devices",
"class": "Core",
"parameters": {}
},
"mains_sync": {
"module": "artiq.sim.devices",
"class": "Input",
"parameters": {"name": "mains_sync"}
},
"pmt": {
"module": "artiq.sim.devices",
"class": "Input",
"parameters": {"name": "pmt"}
},
"laser_cooling": {
"module": "artiq.sim.devices",
"class": "WaveOutput",
"parameters": {"name": "laser_cooling"}
},
"spectroscopy": {
"module": "artiq.sim.devices",
"class": "WaveOutput",
"parameters": {"name": "spectroscopy"}
},
"spectroscopy_b": {
"module": "artiq.sim.devices",
"class": "VoltageOutput",
"parameters": {"name": "spectroscopy_b"}
},
"state_detection": {
"module": "artiq.sim.devices",
"class": "WaveOutput",
"parameters": {"name": "state_detection"}
},
}

View File

@ -1,12 +1,10 @@
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core, dds, gpio
class DDSTest(AutoContext): class DDSTest(AutoContext):
a = Device("dds") dds0 = Device("dds")
b = Device("dds") dds1 = Device("dds")
c = Device("dds") dds2 = Device("dds")
d = Device("dds")
led = Device("gpio_out") led = Device("gpio_out")
@kernel @kernel
@ -18,30 +16,7 @@ class DDSTest(AutoContext):
self.led.off() self.led.off()
with parallel: with parallel:
with sequential: with sequential:
self.a.pulse(100*MHz + 4*i*kHz, 500*us) self.dds0.pulse(100*MHz + 4*i*kHz, 500*us)
self.b.pulse(120*MHz, 500*us) self.dds1.pulse(120*MHz, 500*us)
with sequential: self.dds2.pulse(200*MHz, 100*us)
self.c.pulse(200*MHz, 100*us)
self.d.pulse(250*MHz, 200*us)
self.led.off() self.led.off()
def main():
with comm_serial.Comm() as comm:
coredev = core.Core(comm)
exp = DDSTest(
core=coredev,
a=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=0, rtio_switch=2),
b=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=1, rtio_switch=3),
c=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=2, rtio_switch=4),
d=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=3, rtio_switch=5),
led=gpio.GPIOOut(core=coredev, channel=0)
)
exp.run()
if __name__ == "__main__":
main()

View File

@ -1,7 +1,6 @@
import sys import sys
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core
class Mandelbrot(AutoContext): class Mandelbrot(AutoContext):
@ -36,12 +35,3 @@ class Mandelbrot(AutoContext):
z_r = new_z_r z_r = new_z_r
self.col(i) self.col(i)
self.row() self.row()
def main():
with comm_serial.Comm() as comm:
exp = Mandelbrot(core=core.Core(comm))
exp.run()
if __name__ == "__main__":
main()

1
examples/pdb.pyon Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -1,16 +1,12 @@
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core, dds, rtio
class PhotonHistogram(AutoContext): class PhotonHistogram(AutoContext):
bd = Device("dds") bd = Device("dds")
bdd = Device("dds") bdd = Device("dds")
pmt = Device("ttl_in") pmt = Device("ttl_in")
repeats = Parameter() repeats = Parameter(100)
nbins = Parameter() nbins = Parameter(100)
def report(self, i, n):
print(i, n)
@kernel @kernel
def cool_detect(self): def cool_detect(self):
@ -36,23 +32,4 @@ class PhotonHistogram(AutoContext):
hist[n] += 1 hist[n] += 1
for i in range(self.nbins): for i in range(self.nbins):
self.report(i, hist[i]) print(i, hist[i])
def main():
with comm_serial.Comm() as comm:
coredev = core.Core(comm)
exp = PhotonHistogram(
core=coredev,
bd=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=0, rtio_switch=2),
bdd=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=1, rtio_switch=3),
pmt=rtio.RTIOIn(core=coredev, channel=0),
repeats=100,
nbins=100
)
exp.run()
if __name__ == "__main__":
main()

View File

@ -1,5 +1,4 @@
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core, rtio
from artiq.coredevice.runtime_exceptions import RTIOUnderflow from artiq.coredevice.runtime_exceptions import RTIOUnderflow
@ -8,7 +7,7 @@ def print_min_period(p):
class PulsePerformance(AutoContext): class PulsePerformance(AutoContext):
o = Device("ttl_out") ttl0 = Device("ttl_out")
@kernel @kernel
def run(self): def run(self):
@ -16,7 +15,7 @@ class PulsePerformance(AutoContext):
while True: while True:
try: try:
for i in range(1000): for i in range(1000):
self.o.pulse(cycles_to_time(T)) self.ttl0.pulse(cycles_to_time(T))
delay(cycles_to_time(T)) delay(cycles_to_time(T))
except RTIOUnderflow: except RTIOUnderflow:
T += 1 T += 1
@ -25,10 +24,3 @@ class PulsePerformance(AutoContext):
print_min_period(int(cycles_to_time(2*T)/(1*ns))) print_min_period(int(cycles_to_time(2*T)/(1*ns)))
break break
if __name__ == "__main__":
with comm_serial.Comm() as comm:
coredev = core.Core(comm)
exp = PulsePerformance(core=coredev,
o=rtio.RTIOOut(core=coredev, channel=2))
exp.run()

View File

@ -1,5 +1,4 @@
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core, rtio
def print_skew(p): def print_skew(p):
@ -11,27 +10,19 @@ def print_failed():
class RTIOSkew(AutoContext): class RTIOSkew(AutoContext):
i = Device("ttl_in") pmt0 = Device("ttl_in")
o = Device("ttl_out") ttl0 = Device("ttl_out")
@kernel @kernel
def run(self): def run(self):
with parallel: with parallel:
self.i.gate_rising(10*us) self.pmt0.gate_rising(10*us)
with sequential: with sequential:
delay(5*us) delay(5*us)
out_t = now() out_t = now()
self.o.pulse(5*us) self.ttl0.pulse(5*us)
in_t = self.i.timestamp() in_t = self.pmt0.timestamp()
if in_t < 0*s: if in_t < 0*s:
print_failed() print_failed()
else: else:
print_skew(int((out_t - in_t)/(1*ns))) print_skew(int((out_t - in_t)/(1*ns)))
if __name__ == "__main__":
with comm_serial.Comm() as comm:
coredev = core.Core(comm)
exp = RTIOSkew(core=coredev,
i=rtio.RTIOIn(core=coredev, channel=0),
o=rtio.RTIOOut(core=coredev, channel=2))
exp.run()

View File

@ -1,23 +1,29 @@
import numpy as np import numpy as np
from artiq import * from artiq import *
from artiq.coredevice import comm_serial, core, dds, rtio
from artiq.devices import pdq2
# data is usually precomputed offline
transport_data = dict(
t=np.linspace(0, 10, 101), # waveform time
u=np.random.randn(101, 4*3*3), # waveform data,
# 4 devices, 3 board each, 3 dacs each
)
class Transport(AutoContext): class Transport(AutoContext):
bd = Device("dds") bd = Device("dds")
bdd = Device("dds")
pmt = Device("ttl_in") pmt = Device("ttl_in")
repeats = Parameter()
nbins = Parameter()
electrodes = Device("pdq") electrodes = Device("pdq")
transport_data = Parameter()
wait_at_stop = Parameter() repeats = Parameter(100)
speed = Parameter() nbins = Parameter(100)
wait_at_stop = Parameter(100*us)
speed = Parameter(1.5)
def prepare(self, stop): def prepare(self, stop):
t = self.transport_data["t"][:stop]*self.speed t = transport_data["t"][:stop]*self.speed
u = self.transport_data["u"][:stop] u = transport_data["u"][:stop]
# start a new frame # start a new frame
self.tf = self.electrodes.create_frame() self.tf = self.electrodes.create_frame()
# interpolates t and u and appends the (t, u) segment to the frame # interpolates t and u and appends the (t, u) segment to the frame
@ -110,36 +116,7 @@ class Transport(AutoContext):
# live update 2d plot with current self.histogram # live update 2d plot with current self.histogram
# broadcast(s, self.histogram) # broadcast(s, self.histogram)
def run(self):
if __name__ == "__main__":
# data is usually precomputed offline
data = dict(
t=np.linspace(0, 10, 101), # waveform time
u=np.random.randn(101, 4*3*3), # waveform data,
# 4 devices, 3 board each, 3 dacs each
)
with comm_serial.Comm() as comm:
coredev = core.Core(comm)
exp = Transport(
core=coredev,
bd=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=0, rtio_switch=2),
bdd=dds.DDS(core=coredev, dds_sysclk=1*GHz,
reg_channel=1, rtio_switch=3),
pmt=rtio.RTIOIn(core=coredev, channel=0),
# a compound pdq device that wraps multiple usb devices (looked up
# by usb "serial number"/id) into one
electrodes=pdq2.CompoundPDQ2(
core=coredev,
ids=["qc_q1_{}".format(i) for i in range(4)],
rtio_trigger=4, rtio_frame=(5, 6, 7)),
transport_data=data, # or: json.load
wait_at_stop=100*us,
speed=1.5,
repeats=100,
nbins=100
)
# scan transport endpoint # scan transport endpoint
stop = range(10, len(exp.transport_data["t"]), 10) stops = range(10, len(transport_data["t"]), 10)
exp.scan(stop) self.scan(stops)

83
frontend/artiq_run.py Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env python3
import argparse
import importlib
import sys
from inspect import isclass
from operator import itemgetter
from artiq.language.context import *
from artiq.management import pyon
from artiq.management.dpdb import DeviceParamDB
class ELFRunner(AutoContext):
comm = Device("comm")
implicit_core = False
def run(self, filename, function):
with open(filename, "rb") as f:
binary = f.read()
comm.load(binary)
comm.run(function)
comm.serve(dict(), dict())
def _get_args():
parser = argparse.ArgumentParser(description="Experiment running tool")
parser.add_argument("-d", "--ddb", default="ddb.pyon",
help="device database file")
parser.add_argument("-p", "--pdb", default="pdb.pyon",
help="parameter database file")
parser.add_argument("-e", "--elf", default=False, action="store_true",
help="run ELF binary")
parser.add_argument("-f", "--function", default="run",
help="function to run")
parser.add_argument("-u", "--unit", default=None,
help="unit to run")
parser.add_argument("module",
help="module containing the unit to run")
return parser.parse_args()
def main():
args = _get_args()
devices = pyon.load_file(args.ddb)
parameters = pyon.load_file(args.pdb)
dpdb = DeviceParamDB(devices, parameters)
try:
if args.elf:
unit_inst = ELFRunner(dpdb)
unit_inst.run(args.file, args.function)
else:
sys.path.append(".")
module = importlib.import_module(args.module)
if args.unit is None:
units = [(k, v) for k, v in module.__dict__.items()
if k[0] != "_" and isclass(v) and issubclass(v, AutoContext) and v is not AutoContext]
l = len(units)
if l == 0:
print("No units found in module")
sys.exit(1)
elif l > 1:
print("More than one unit found in module:")
for k, v in sorted(units, key=itemgetter(0)):
print(" " + k)
print("Use -u to specify which unit to use.")
sys.exit(1)
else:
unit = units[0][1]
else:
unit = getattr(module, args.unit)
unit_inst = unit(dpdb)
f = getattr(unit_inst, args.function)
f()
finally:
dpdb.close()
if __name__ == "__main__":
main()

View File

@ -1,28 +0,0 @@
#!/usr/bin/env python3
import argparse
from artiq.coredevice import comm_serial
def main():
parser = argparse.ArgumentParser(description="Core device ELF loading tool")
parser.add_argument("-e", default=False, action="store_true",
help="show environment")
parser.add_argument("-f", default="run",
help="function to run")
parser.add_argument("file",
help="ELF binary to load")
args = parser.parse_args()
with open(args.file, "rb") as f:
binary = f.read()
with comm_serial.Comm() as comm:
runtime_env = comm.get_runtime_env()
if args.e:
print(runtime_env)
comm.load(binary)
comm.run(args.f)
comm.serve(dict(), dict())
if __name__ == "__main__":
main()

View File

@ -13,10 +13,13 @@ no_hardware = bool(os.getenv("ARTIQ_NO_HARDWARE"))
def _run_on_device(k_class, **parameters): def _run_on_device(k_class, **parameters):
with comm_serial.Comm() as comm: comm = comm_serial.Comm()
coredev = core.Core(comm) try:
coredev = core.Core(comm=comm)
k_inst = k_class(core=coredev, **parameters) k_inst = k_class(core=coredev, **parameters)
k_inst.run() k_inst.run()
finally:
comm.close()
def _run_on_host(k_class, **parameters): def _run_on_host(k_class, **parameters):
@ -171,8 +174,9 @@ class ExecutionCase(unittest.TestCase):
self.assertEqual(l_device, l_host) self.assertEqual(l_device, l_host)
def test_misc(self): def test_misc(self):
with comm_serial.Comm() as comm: comm = comm_serial.Comm()
coredev = core.Core(comm) try:
coredev = core.Core(comm=comm)
uut = _Misc(core=coredev) uut = _Misc(core=coredev)
uut.run() uut.run()
self.assertEqual(uut.half_input, 42) self.assertEqual(uut.half_input, 42)
@ -189,6 +193,8 @@ class ExecutionCase(unittest.TestCase):
uut.dimension_error3() uut.dimension_error3()
with self.assertRaises(DimensionError): with self.assertRaises(DimensionError):
uut.dimension_error4() uut.dimension_error4()
finally:
comm.close()
def test_pulses(self): def test_pulses(self):
l_device, l_host = [], [] l_device, l_host = [], []
@ -255,8 +261,9 @@ class RTIOCase(unittest.TestCase):
# (C11 and C13 on Papilio Pro) # (C11 and C13 on Papilio Pro)
def test_loopback(self): def test_loopback(self):
npulses = 4 npulses = 4
with comm_serial.Comm() as comm: comm = comm_serial.Comm()
coredev = core.Core(comm) try:
coredev = core.Core(comm=comm)
uut = _RTIOLoopback( uut = _RTIOLoopback(
core=coredev, core=coredev,
i=rtio.RTIOIn(core=coredev, channel=0), i=rtio.RTIOIn(core=coredev, channel=0),
@ -265,23 +272,31 @@ class RTIOCase(unittest.TestCase):
) )
uut.run() uut.run()
self.assertEqual(uut.result, npulses) self.assertEqual(uut.result, npulses)
finally:
comm.close()
def test_underflow(self): def test_underflow(self):
with comm_serial.Comm() as comm: comm = comm_serial.Comm()
coredev = core.Core(comm) try:
coredev = core.Core(comm=comm)
uut = _RTIOUnderflow( uut = _RTIOUnderflow(
core=coredev, core=coredev,
o=rtio.RTIOOut(core=coredev, channel=2) o=rtio.RTIOOut(core=coredev, channel=2)
) )
with self.assertRaises(runtime_exceptions.RTIOUnderflow): with self.assertRaises(runtime_exceptions.RTIOUnderflow):
uut.run() uut.run()
finally:
comm.close()
def test_sequence_error(self): def test_sequence_error(self):
with comm_serial.Comm() as comm: comm = comm_serial.Comm()
coredev = core.Core(comm) try:
coredev = core.Core(comm=comm)
uut = _RTIOSequenceError( uut = _RTIOSequenceError(
core=coredev, core=coredev,
o=rtio.RTIOOut(core=coredev, channel=2) o=rtio.RTIOOut(core=coredev, channel=2)
) )
with self.assertRaises(runtime_exceptions.RTIOSequenceError): with self.assertRaises(runtime_exceptions.RTIOSequenceError):
uut.run() uut.run()
finally:
comm.close()

View File

@ -39,7 +39,7 @@ def run():
class OptimizeCase(unittest.TestCase): class OptimizeCase(unittest.TestCase):
def test_optimize(self): def test_optimize(self):
coredev = core.Core(comm_dummy.Comm()) coredev = core.Core(comm=comm_dummy.Comm())
func_def = ast.parse(optimize_in).body[0] func_def = ast.parse(optimize_in).body[0]
coredev.transform_stack(func_def, dict(), dict()) coredev.transform_stack(func_def, dict(), dict())
self.assertEqual(unparse(func_def), optimize_out) self.assertEqual(unparse(func_def), optimize_out)