From 2a95d27770995f56ad0de444525dc26f85b2a110 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 3 Dec 2014 18:20:30 +0800 Subject: [PATCH] device and parameter database --- artiq/coredevice/comm_dummy.py | 5 +- artiq/coredevice/comm_serial.py | 21 ++++----- artiq/coredevice/core.py | 22 +++++---- artiq/coredevice/dds.py | 2 +- artiq/language/context.py | 16 +++++-- artiq/management/dpdb.py | 49 +++++++++++++++++++ artiq/sim/devices.py | 22 ++++++--- doc/manual/getting_started.rst | 49 ++++++++----------- doc/manual/writing_a_driver.rst | 2 +- examples/al_spectroscopy.py | 30 ++---------- examples/ddb.pyon | 76 ++++++++++++++++++++++++++++++ examples/ddb_sim.pyon | 37 +++++++++++++++ examples/dds_test.py | 37 +++------------ examples/mandelbrot.py | 10 ---- examples/pdb.pyon | 1 + examples/photon_histogram.py | 29 ++---------- examples/pulse_performance.py | 12 +---- examples/rtio_skew.py | 19 ++------ examples/transport.py | 59 +++++++---------------- frontend/artiq_run.py | 83 +++++++++++++++++++++++++++++++++ frontend/runelf.py | 28 ----------- test/full_stack.py | 35 ++++++++++---- test/transforms.py | 2 +- 23 files changed, 385 insertions(+), 261 deletions(-) create mode 100644 artiq/management/dpdb.py create mode 100644 examples/ddb.pyon create mode 100644 examples/ddb_sim.pyon create mode 100644 examples/pdb.pyon create mode 100755 frontend/artiq_run.py delete mode 100755 frontend/runelf.py diff --git a/artiq/coredevice/comm_dummy.py b/artiq/coredevice/comm_dummy.py index e6933f086..a23210dbe 100644 --- a/artiq/coredevice/comm_dummy.py +++ b/artiq/coredevice/comm_dummy.py @@ -1,5 +1,6 @@ from operator import itemgetter +from artiq.language.context import AutoContext from artiq.language.units import ms, ns from artiq.coredevice.runtime import LinkInterface @@ -13,7 +14,9 @@ class _RuntimeEnvironment(LinkInterface): return str(self.llvm_module) -class Comm: +class Comm(AutoContext): + implicit_core = False + def get_runtime_env(self): return _RuntimeEnvironment(1*ns) diff --git a/artiq/coredevice/comm_serial.py b/artiq/coredevice/comm_serial.py index 54501f362..dbe53b31c 100644 --- a/artiq/coredevice/comm_serial.py +++ b/artiq/coredevice/comm_serial.py @@ -8,6 +8,7 @@ import logging from artiq.language import core as core_language from artiq.language import units +from artiq.language.context import * from artiq.coredevice.runtime import Environment from artiq.coredevice import runtime_exceptions @@ -59,13 +60,17 @@ def _read_exactly(f, n): return r -class Comm: - def __init__(self, dev="/dev/ttyUSB1", baud=115200): - self._fd = os.open(dev, os.O_RDWR | os.O_NOCTTY) +class Comm(AutoContext): + serial_dev = Parameter("/dev/ttyUSB1") + 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.set_baud(115200) - self.set_remote_baud(baud) - self.set_baud(baud) + self.set_remote_baud(self.baud_rate) + self.set_baud(self.baud_rate) def set_baud(self, baud): iflag, oflag, cflag, lflag, ispeed, ospeed, cc = \ @@ -109,12 +114,6 @@ class Comm: self.set_remote_baud(115200) self.port.close() - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.close() - def _get_device_msg(self): while True: (reply, ) = struct.unpack("B", _read_exactly(self.port, 1)) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index b28dd0967..2f51fd8e6 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -1,5 +1,8 @@ import os +from artiq.language.core import * +from artiq.language.context import * + from artiq.transforms.inline import inline from artiq.transforms.lower_units import lower_units 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.lower_time import lower_time from artiq.transforms.unparse import unparse + from artiq.py2llvm import get_runtime_binary -from artiq.language.core import * def _announce_unparse(label, node): @@ -41,19 +44,20 @@ def _no_debug_unparse(label, node): pass -class Core: - def __init__(self, comm, external_clock=None, runtime_env=None): - if runtime_env is None: - runtime_env = comm.get_runtime_env() - self.runtime_env = runtime_env - self.comm = comm +class Core(AutoContext): + comm = Device("comm") + external_clock = Parameter(None) + implicit_core = False + + def build(self): + self.runtime_env = self.comm.get_runtime_env() self.core = self - if external_clock is None: + if self.external_clock is None: self.ref_period = self.runtime_env.internal_ref_period self.comm.switch_clock(False) else: - self.ref_period = external_clock + self.ref_period = self.external_clock self.comm.switch_clock(True) self.initial_time = int64(self.runtime_env.warmup_time/self.ref_period) diff --git a/artiq/coredevice/dds.py b/artiq/coredevice/dds.py index 7b1a1574a..a1247b6cb 100644 --- a/artiq/coredevice/dds.py +++ b/artiq/coredevice/dds.py @@ -24,7 +24,7 @@ class DDS(AutoContext): the DDS device. """ - dds_sysclk = Parameter() + dds_sysclk = Parameter(1*GHz) reg_channel = Parameter() rtio_switch = Parameter() diff --git a/artiq/language/context.py b/artiq/language/context.py index 2efddf92a..35959e067 100644 --- a/artiq/language/context.py +++ b/artiq/language/context.py @@ -110,13 +110,21 @@ class AutoContext: for k in dir(self): v = getattr(self, k) 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) self.build() - def get_missing_value(self, name, kind): - """Attempts to retrieve ``parameter`` from the object's attributes. + def get_missing_value(self, name, kind, requester): + """Attempts to retrieve ``name`` from the object's attributes. If not present, forwards the request to the parent MVS. The presence of this method makes ``AutoContext`` act as a MVS. @@ -125,7 +133,7 @@ class AutoContext: try: return getattr(self, name) except AttributeError: - return self.mvs.get_missing_value(name, kind) + return self.mvs.get_missing_value(name, kind, requester) def build(self): """This is called by ``__init__`` after the parameter initialization diff --git a/artiq/management/dpdb.py b/artiq/management/dpdb.py new file mode 100644 index 000000000..dc4a3cfd7 --- /dev/null +++ b/artiq/management/dpdb.py @@ -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() diff --git a/artiq/sim/devices.py b/artiq/sim/devices.py index aab83c1cf..7ef00413e 100644 --- a/artiq/sim/devices.py +++ b/artiq/sim/devices.py @@ -1,28 +1,38 @@ 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 import units from artiq.sim import time -class Core: +class Core(AutoContext): + implicit_core = False + + _level = 0 + 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): name = Parameter() - implicit_core = False def build(self): self.prng = Random() + @kernel def wait_edge(self): duration = self.prng.randrange(0, 20)*units.ms time.manager.event(("wait_edge", self.name, duration)) delay(duration) + @kernel def count_gate(self, duration): result = self.prng.randrange(0, 100) time.manager.event(("count_gate", self.name, duration, result)) @@ -32,8 +42,8 @@ class Input(AutoContext): class WaveOutput(AutoContext): name = Parameter() - implicit_core = False + @kernel def pulse(self, frequency, duration): time.manager.event(("pulse", self.name, frequency, duration)) delay(duration) @@ -41,7 +51,7 @@ class WaveOutput(AutoContext): class VoltageOutput(AutoContext): name = Parameter() - implicit_core = False + @kernel def set(self, value): time.manager.event(("set_voltage", self.name, value)) diff --git a/doc/manual/getting_started.rst b/doc/manual/getting_started.rst index aad7ec9c5..a29caa60c 100644 --- a/doc/manual/getting_started.rst +++ b/doc/manual/getting_started.rst @@ -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: :: from artiq import * - from artiq.coredevice import comm_serial, core, gpio + class LED(AutoContext): 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): 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. @@ -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: :: - $ python3 led.py + $ artiq_run.py led Enter desired LED state: 1 - $ python3 led.py + $ artiq_run.py led 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. @@ -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: :: from artiq import * - from artiq.coredevice import comm_serial, core, rtio class Tutorial(AutoContext): - o = Device("ttl_out") + ttl0 = Device("ttl_out") @kernel def run(self): for i in range(1000000): - self.o.pulse(2*us) + self.ttl0.pulse(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. @@ -113,14 +102,14 @@ Try reducing the period of the generated waveform until the CPU cannot keep up w class Tutorial(AutoContext): led = Device("gpio_out") - o = Device("ttl_out") + ttl0 = Device("ttl_out") @kernel def run(self): self.led.off() try: for i in range(1000000): - self.o.pulse(...) + self.ttl0.pulse(...) delay(...) except RTIOUnderflow: 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): with parallel: - self.o1.pulse(2*us) - self.o2.pulse(4*us) + self.ttl0.pulse(2*us) + self.ttl1.pulse(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: :: for i in range(1000000): with parallel: with sequential: - self.o1.pulse(2*us) + self.ttl0.pulse(2*us) delay(1*us) - self.o1.pulse(1*us) - self.o2.pulse(4*us) + self.ttl0.pulse(1*us) + self.ttl1.pulse(4*us) delay(4*us) .. warning:: diff --git a/doc/manual/writing_a_driver.rst b/doc/manual/writing_a_driver.rst index cc750edc0..e02b07988 100644 --- a/doc/manual/writing_a_driver.rst +++ b/doc/manual/writing_a_driver.rst @@ -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. -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 Type: hello diff --git a/examples/al_spectroscopy.py b/examples/al_spectroscopy.py index 000799dff..fadd7d262 100644 --- a/examples/al_spectroscopy.py +++ b/examples/al_spectroscopy.py @@ -8,9 +8,9 @@ class AluminumSpectroscopy(AutoContext): spectroscopy_b = Device("dac") state_detection = Device("dds") pmt = Device("ttl_in") - spectroscopy_freq = Parameter() - photon_limit_low = Parameter() - photon_limit_high = Parameter() + spectroscopy_freq = Parameter(432*MHz) + photon_limit_low = Parameter(10) + photon_limit_high = Parameter(15) @kernel def run(self): @@ -37,27 +37,3 @@ class AluminumSpectroscopy(AutoContext): if photon_count < self.photon_limit_low: state_0_count += 1 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() diff --git a/examples/ddb.pyon b/examples/ddb.pyon new file mode 100644 index 000000000..55e1197d3 --- /dev/null +++ b/examples/ddb.pyon @@ -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" +} diff --git a/examples/ddb_sim.pyon b/examples/ddb_sim.pyon new file mode 100644 index 000000000..a69f07b36 --- /dev/null +++ b/examples/ddb_sim.pyon @@ -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"} + }, +} diff --git a/examples/dds_test.py b/examples/dds_test.py index f79e6a5ad..a84692cc4 100644 --- a/examples/dds_test.py +++ b/examples/dds_test.py @@ -1,12 +1,10 @@ from artiq import * -from artiq.coredevice import comm_serial, core, dds, gpio class DDSTest(AutoContext): - a = Device("dds") - b = Device("dds") - c = Device("dds") - d = Device("dds") + dds0 = Device("dds") + dds1 = Device("dds") + dds2 = Device("dds") led = Device("gpio_out") @kernel @@ -18,30 +16,7 @@ class DDSTest(AutoContext): self.led.off() with parallel: with sequential: - self.a.pulse(100*MHz + 4*i*kHz, 500*us) - self.b.pulse(120*MHz, 500*us) - with sequential: - self.c.pulse(200*MHz, 100*us) - self.d.pulse(250*MHz, 200*us) + self.dds0.pulse(100*MHz + 4*i*kHz, 500*us) + self.dds1.pulse(120*MHz, 500*us) + self.dds2.pulse(200*MHz, 100*us) 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() diff --git a/examples/mandelbrot.py b/examples/mandelbrot.py index 08bd6adc3..529b987c1 100644 --- a/examples/mandelbrot.py +++ b/examples/mandelbrot.py @@ -1,7 +1,6 @@ import sys from artiq import * -from artiq.coredevice import comm_serial, core class Mandelbrot(AutoContext): @@ -36,12 +35,3 @@ class Mandelbrot(AutoContext): z_r = new_z_r self.col(i) self.row() - - -def main(): - with comm_serial.Comm() as comm: - exp = Mandelbrot(core=core.Core(comm)) - exp.run() - -if __name__ == "__main__": - main() diff --git a/examples/pdb.pyon b/examples/pdb.pyon new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/examples/pdb.pyon @@ -0,0 +1 @@ +{} diff --git a/examples/photon_histogram.py b/examples/photon_histogram.py index 7b1005343..566204309 100644 --- a/examples/photon_histogram.py +++ b/examples/photon_histogram.py @@ -1,16 +1,12 @@ from artiq import * -from artiq.coredevice import comm_serial, core, dds, rtio class PhotonHistogram(AutoContext): bd = Device("dds") bdd = Device("dds") pmt = Device("ttl_in") - repeats = Parameter() - nbins = Parameter() - - def report(self, i, n): - print(i, n) + repeats = Parameter(100) + nbins = Parameter(100) @kernel def cool_detect(self): @@ -36,23 +32,4 @@ class PhotonHistogram(AutoContext): hist[n] += 1 for i in range(self.nbins): - self.report(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() + print(i, hist[i]) diff --git a/examples/pulse_performance.py b/examples/pulse_performance.py index 14e9e68de..ef4cc64c5 100644 --- a/examples/pulse_performance.py +++ b/examples/pulse_performance.py @@ -1,5 +1,4 @@ from artiq import * -from artiq.coredevice import comm_serial, core, rtio from artiq.coredevice.runtime_exceptions import RTIOUnderflow @@ -8,7 +7,7 @@ def print_min_period(p): class PulsePerformance(AutoContext): - o = Device("ttl_out") + ttl0 = Device("ttl_out") @kernel def run(self): @@ -16,7 +15,7 @@ class PulsePerformance(AutoContext): while True: try: for i in range(1000): - self.o.pulse(cycles_to_time(T)) + self.ttl0.pulse(cycles_to_time(T)) delay(cycles_to_time(T)) except RTIOUnderflow: T += 1 @@ -25,10 +24,3 @@ class PulsePerformance(AutoContext): print_min_period(int(cycles_to_time(2*T)/(1*ns))) 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() diff --git a/examples/rtio_skew.py b/examples/rtio_skew.py index cef7ff724..7de60c19c 100644 --- a/examples/rtio_skew.py +++ b/examples/rtio_skew.py @@ -1,5 +1,4 @@ from artiq import * -from artiq.coredevice import comm_serial, core, rtio def print_skew(p): @@ -11,27 +10,19 @@ def print_failed(): class RTIOSkew(AutoContext): - i = Device("ttl_in") - o = Device("ttl_out") + pmt0 = Device("ttl_in") + ttl0 = Device("ttl_out") @kernel def run(self): with parallel: - self.i.gate_rising(10*us) + self.pmt0.gate_rising(10*us) with sequential: delay(5*us) out_t = now() - self.o.pulse(5*us) - in_t = self.i.timestamp() + self.ttl0.pulse(5*us) + in_t = self.pmt0.timestamp() if in_t < 0*s: print_failed() else: 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() diff --git a/examples/transport.py b/examples/transport.py index 63a9bf777..804de18d6 100644 --- a/examples/transport.py +++ b/examples/transport.py @@ -1,23 +1,29 @@ import numpy as np 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): bd = Device("dds") + bdd = Device("dds") pmt = Device("ttl_in") - repeats = Parameter() - nbins = Parameter() electrodes = Device("pdq") - transport_data = Parameter() - wait_at_stop = Parameter() - speed = Parameter() + + repeats = Parameter(100) + nbins = Parameter(100) + wait_at_stop = Parameter(100*us) + speed = Parameter(1.5) def prepare(self, stop): - t = self.transport_data["t"][:stop]*self.speed - u = self.transport_data["u"][:stop] + t = transport_data["t"][:stop]*self.speed + u = transport_data["u"][:stop] # start a new frame self.tf = self.electrodes.create_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 # broadcast(s, self.histogram) - -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 - ) + def run(self): # scan transport endpoint - stop = range(10, len(exp.transport_data["t"]), 10) - exp.scan(stop) + stops = range(10, len(transport_data["t"]), 10) + self.scan(stops) diff --git a/frontend/artiq_run.py b/frontend/artiq_run.py new file mode 100755 index 000000000..e10290a8a --- /dev/null +++ b/frontend/artiq_run.py @@ -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() diff --git a/frontend/runelf.py b/frontend/runelf.py deleted file mode 100755 index 0c6e254f7..000000000 --- a/frontend/runelf.py +++ /dev/null @@ -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() diff --git a/test/full_stack.py b/test/full_stack.py index 9c3e0a682..679162cb0 100644 --- a/test/full_stack.py +++ b/test/full_stack.py @@ -13,10 +13,13 @@ no_hardware = bool(os.getenv("ARTIQ_NO_HARDWARE")) def _run_on_device(k_class, **parameters): - with comm_serial.Comm() as comm: - coredev = core.Core(comm) + comm = comm_serial.Comm() + try: + coredev = core.Core(comm=comm) k_inst = k_class(core=coredev, **parameters) k_inst.run() + finally: + comm.close() def _run_on_host(k_class, **parameters): @@ -171,8 +174,9 @@ class ExecutionCase(unittest.TestCase): self.assertEqual(l_device, l_host) def test_misc(self): - with comm_serial.Comm() as comm: - coredev = core.Core(comm) + comm = comm_serial.Comm() + try: + coredev = core.Core(comm=comm) uut = _Misc(core=coredev) uut.run() self.assertEqual(uut.half_input, 42) @@ -189,6 +193,8 @@ class ExecutionCase(unittest.TestCase): uut.dimension_error3() with self.assertRaises(DimensionError): uut.dimension_error4() + finally: + comm.close() def test_pulses(self): l_device, l_host = [], [] @@ -255,8 +261,9 @@ class RTIOCase(unittest.TestCase): # (C11 and C13 on Papilio Pro) def test_loopback(self): npulses = 4 - with comm_serial.Comm() as comm: - coredev = core.Core(comm) + comm = comm_serial.Comm() + try: + coredev = core.Core(comm=comm) uut = _RTIOLoopback( core=coredev, i=rtio.RTIOIn(core=coredev, channel=0), @@ -265,23 +272,31 @@ class RTIOCase(unittest.TestCase): ) uut.run() self.assertEqual(uut.result, npulses) + finally: + comm.close() def test_underflow(self): - with comm_serial.Comm() as comm: - coredev = core.Core(comm) + comm = comm_serial.Comm() + try: + coredev = core.Core(comm=comm) uut = _RTIOUnderflow( core=coredev, o=rtio.RTIOOut(core=coredev, channel=2) ) with self.assertRaises(runtime_exceptions.RTIOUnderflow): uut.run() + finally: + comm.close() def test_sequence_error(self): - with comm_serial.Comm() as comm: - coredev = core.Core(comm) + comm = comm_serial.Comm() + try: + coredev = core.Core(comm=comm) uut = _RTIOSequenceError( core=coredev, o=rtio.RTIOOut(core=coredev, channel=2) ) with self.assertRaises(runtime_exceptions.RTIOSequenceError): uut.run() + finally: + comm.close() diff --git a/test/transforms.py b/test/transforms.py index ea89a7322..c7893f236 100644 --- a/test/transforms.py +++ b/test/transforms.py @@ -39,7 +39,7 @@ def run(): class OptimizeCase(unittest.TestCase): 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] coredev.transform_stack(func_def, dict(), dict()) self.assertEqual(unparse(func_def), optimize_out)