forked from M-Labs/artiq
Compare commits
190 Commits
Author | SHA1 | Date |
---|---|---|
David Nadlinger | 56c5637230 | |
whitequark | 73ebebf6d7 | |
whitequark | faef018b9d | |
David Nadlinger | aa6542fecf | |
Sebastien Bourdeauducq | 87f296c0b5 | |
Sebastien Bourdeauducq | de89288b63 | |
Sebastien Bourdeauducq | af1f87833a | |
Drew | f301ad814d | |
Sebastien Bourdeauducq | c894fe028c | |
Sebastien Bourdeauducq | 7c6eee22a8 | |
Robert Jördens | f2eafa89fb | |
whitequark | dbd1cb9e04 | |
whitequark | ae88c1328b | |
Sebastien Bourdeauducq | ad07274a1b | |
Sebastien Bourdeauducq | f96084e88d | |
Sebastien Bourdeauducq | ed9815da92 | |
Sebastien Bourdeauducq | eadb39c283 | |
Sebastien Bourdeauducq | 5a3d12f07b | |
Sebastien Bourdeauducq | 8c891c43a8 | |
Sebastien Bourdeauducq | eb6bc995cc | |
Marius Weber | c0d89db677 | |
Sebastien Bourdeauducq | 6d7790844d | |
Robert Jördens | 9afe84ab79 | |
whitequark | dbf4e78087 | |
whitequark | da01a03a6e | |
whitequark | 98e61e4d4d | |
whitequark | b91822ffe6 | |
Sebastien Bourdeauducq | fb1dfcf372 | |
Sebastien Bourdeauducq | c83e22c11c | |
Sebastien Bourdeauducq | 12a1a8ee97 | |
whitequark | 77d511dc37 | |
Robert Jördens | df2322422d | |
whitequark | 0982c965b1 | |
whitequark | 10d0c6df00 | |
whitequark | e8ff55791c | |
whitequark | f10980de8d | |
Sebastien Bourdeauducq | c659ae0681 | |
David Nadlinger | a6b61f0c1d | |
Sebastien Bourdeauducq | 17665c7271 | |
Sebastien Bourdeauducq | 8210ee61cf | |
whitequark | 3e7cdaa5d7 | |
whitequark | cccadd0a55 | |
Sebastien Bourdeauducq | e8092f6f11 | |
whitequark | 408734b776 | |
whitequark | 36204a97d8 | |
Sebastien Bourdeauducq | 1afcf8b459 | |
whitequark | 66a7e09937 | |
whitequark | 086b5ff416 | |
whitequark | 2f4064ef0c | |
whitequark | e7b3876eb6 | |
whitequark | 5f3cb1263c | |
whitequark | d0e5aec862 | |
whitequark | 21406a585e | |
whitequark | 02f6727fa2 | |
Sebastien Bourdeauducq | 5667cef667 | |
Sebastien Bourdeauducq | 1c4c5c9d96 | |
Sebastien Bourdeauducq | bb91582acc | |
whitequark | 9c6978be84 | |
whitequark | 9589decd54 | |
whitequark | a4fd9ad3e1 | |
whitequark | 88134db35a | |
Sebastien Bourdeauducq | 0faf781d5f | |
Sebastien Bourdeauducq | 840a401c5e | |
Sebastien Bourdeauducq | 6a0bba1d89 | |
Sebastien Bourdeauducq | b84a2a1eeb | |
Sebastien Bourdeauducq | 5f3e417bb5 | |
whitequark | 843f871fd1 | |
whitequark | 04e8d379b8 | |
whitequark | 44e5d0804e | |
whitequark | 1e8cc731b6 | |
whitequark | 2c12e150f3 | |
whitequark | 84d807a5e4 | |
whitequark | 4c65fb79b9 | |
whitequark | d11f66291c | |
Sebastien Bourdeauducq | 66817e3b82 | |
Sebastien Bourdeauducq | e93a07bc8d | |
Sebastien Bourdeauducq | 5dfd0e4701 | |
Sebastien Bourdeauducq | 6972ba4ee3 | |
Sebastien Bourdeauducq | 3c678ad351 | |
Sebastien Bourdeauducq | 8c654748fa | |
Sebastien Bourdeauducq | 9630833033 | |
Sebastien Bourdeauducq | a844a3350e | |
Sebastien Bourdeauducq | 394b66cd8c | |
Sebastien Bourdeauducq | 41ae1d8e77 | |
Sebastien Bourdeauducq | 35b70b3123 | |
whitequark | 24925f1c9e | |
whitequark | ba017147fc | |
whitequark | 3904138c20 | |
Sebastien Bourdeauducq | ce2b5a97cb | |
Sebastien Bourdeauducq | 595d374f07 | |
whitequark | c09269a323 | |
Sebastien Bourdeauducq | 59fdb32b7b | |
Sebastien Bourdeauducq | 7337842ff9 | |
Sebastien Bourdeauducq | 87b51cbcc2 | |
whitequark | 232940e17f | |
Robert Jördens | 806d583153 | |
whitequark | 015189b2ae | |
whitequark | d0d150d974 | |
whitequark | 81f0efea9b | |
Sebastien Bourdeauducq | cb605cf014 | |
Sebastien Bourdeauducq | 1568e55388 | |
whitequark | fbb58b5c8a | |
whitequark | 92c94c1f62 | |
Robert Jördens | 9db30ce8dc | |
Robert Jördens | 49a265453d | |
Sebastien Bourdeauducq | e1aafcbb4f | |
Sebastien Bourdeauducq | 2548e9d833 | |
whitequark | b92b00a1c8 | |
Sebastien Bourdeauducq | bfb03fdbba | |
Sebastien Bourdeauducq | 59fe69a4b3 | |
Sebastien Bourdeauducq | 8276c6588b | |
whitequark | aa64b8ad7a | |
whitequark | 6f7771cb01 | |
whitequark | d2e9ea8de6 | |
Robert Jördens | a85fd13c21 | |
Sebastien Bourdeauducq | 5f2256cb25 | |
Sebastien Bourdeauducq | 917477f937 | |
whitequark | 4f3e7af8d5 | |
whitequark | 3b82c585d1 | |
Sebastien Bourdeauducq | a433794483 | |
Sebastien Bourdeauducq | 6641f4c1ac | |
whitequark | 24562d232e | |
whitequark | 40b9a84a2b | |
whitequark | 46218c1fff | |
whitequark | 3ba82cf19c | |
Sebastien Bourdeauducq | e14626e432 | |
Sebastien Bourdeauducq | 4ae93d4fd8 | |
Robert Jördens | 66d1647efd | |
whitequark | e6306b712d | |
Sebastien Bourdeauducq | 14a90e5386 | |
Sebastien Bourdeauducq | 00c9b20d1e | |
Sebastien Bourdeauducq | 8c19d90179 | |
whitequark | 135c138ec3 | |
whitequark | d419ccdeca | |
Sebastien Bourdeauducq | 246a2bb3e1 | |
whitequark | 26dbf0841c | |
whitequark | 7af02787e0 | |
whitequark | 4bda29f863 | |
whitequark | c44d08a826 | |
whitequark | fbf7e70ef8 | |
whitequark | ca48c29a8b | |
whitequark | c9be535ba5 | |
whitequark | 3f8dc0233a | |
whitequark | 30b7bcf362 | |
whitequark | 99bc18dcd7 | |
whitequark | 65204a091f | |
whitequark | 2fd3b3324a | |
whitequark | 7f04e75042 | |
whitequark | e364213b62 | |
whitequark | 99bb1b0b70 | |
whitequark | b72178756e | |
whitequark | 6cbf8786d8 | |
whitequark | 0ede5d8638 | |
whitequark | 231bf77b43 | |
Sebastien Bourdeauducq | df2f0ead4a | |
whitequark | 16d49f38c1 | |
whitequark | 3f0277197f | |
Robert Jördens | e02dc834e6 | |
Robert Jördens | 6cb7f2e8e2 | |
Sebastien Bourdeauducq | 4c2f25e85e | |
Robert Jördens | 2c85597daa | |
Sebastien Bourdeauducq | 76a908c8a9 | |
whitequark | 0e5a5441aa | |
Sebastien Bourdeauducq | 45f510bcdc | |
Sebastien Bourdeauducq | 7e5a301a27 | |
Sebastien Bourdeauducq | 14714d3f9d | |
Sebastien Bourdeauducq | 25d3fc1e55 | |
Sebastien Bourdeauducq | f83cf8d1bb | |
Sebastien Bourdeauducq | 8ebca38323 | |
Sebastien Bourdeauducq | 0c47f83634 | |
whitequark | f0937bde16 | |
whitequark | 3ec1850949 | |
whitequark | 0d79b7d292 | |
whitequark | 3e96e0b10d | |
Sebastien Bourdeauducq | 6902868d58 | |
whitequark | 89b7c9e091 | |
Sebastien Bourdeauducq | 52e331204e | |
Robert Jördens | 8edb6a135a | |
Robert Jördens | cd0d73a1a2 | |
Sebastien Bourdeauducq | a6cd42c4aa | |
whitequark | 45c6ca96f8 | |
whitequark | db8300c990 | |
whitequark | ce7e30edfe | |
whitequark | a06f04dfbe | |
whitequark | 1521231b1b | |
whitequark | 67997d8955 | |
Sebastien Bourdeauducq | a49bb2bc50 | |
Sebastien Bourdeauducq | d500e61d89 | |
Sebastien Bourdeauducq | 04a9a0ce95 | |
Sebastien Bourdeauducq | ac28b377c7 |
|
@ -95,4 +95,4 @@ then you just add a line saying
|
||||||
using your legal name (sorry, no pseudonyms or anonymous contributions.)
|
using your legal name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
|
|
||||||
ARTIQ files that do not contain a license header are copyrighted by M-Labs Limited
|
ARTIQ files that do not contain a license header are copyrighted by M-Labs Limited
|
||||||
and are licensed under GNU GPL version 3.
|
and are licensed under GNU LGPL version 3 or later.
|
||||||
|
|
10
README.rst
10
README.rst
|
@ -9,11 +9,9 @@ It is maintained and developed by `M-Labs <https://m-labs.hk>`_ and the initial
|
||||||
|
|
||||||
The system features a high-level programming language that helps describing complex experiments, which is compiled and executed on dedicated hardware with nanosecond timing resolution and sub-microsecond latency. It includes graphical user interfaces to parametrize and schedule experiments and to visualize and explore the results.
|
The system features a high-level programming language that helps describing complex experiments, which is compiled and executed on dedicated hardware with nanosecond timing resolution and sub-microsecond latency. It includes graphical user interfaces to parametrize and schedule experiments and to visualize and explore the results.
|
||||||
|
|
||||||
ARTIQ uses FPGA hardware to perform its time-critical tasks.
|
ARTIQ uses FPGA hardware to perform its time-critical tasks. The `Sinara hardware <https://github.com/sinara-hw>`_, and in particular the Kasli FPGA carrier, is designed to work with ARTIQ (support for Sinara is available in ARTIQ-4 and above).
|
||||||
It is designed to be portable to hardware platforms from different vendors and FPGA manufacturers.
|
ARTIQ is designed to be portable to hardware platforms from different vendors and FPGA manufacturers.
|
||||||
Currently, several different configurations of a `high-end FPGA evaluation kit <http://www.xilinx.com/products/boards-and-kits/ek-k7-kc705-g.html>`_ are used and supported. This FPGA platform can be combined with any number of additional peripherals, either already accessible from ARTIQ or made accessible with little effort.
|
Several different configurations of a `high-end FPGA evaluation kit <http://www.xilinx.com/products/boards-and-kits/ek-k7-kc705-g.html>`_ are also used and supported. FPGA platforms can be combined with any number of additional peripherals, either already accessible from ARTIQ or made accessible with little effort.
|
||||||
|
|
||||||
Custom hardware components with widely extended capabilities and advanced support for scalable and fully distributed real-time control of experiments `are being designed <https://github.com/m-labs/sinara>`_.
|
|
||||||
|
|
||||||
ARTIQ and its dependencies are available in the form of `conda packages <https://conda.anaconda.org/m-labs/label/main>`_ for both Linux and Windows.
|
ARTIQ and its dependencies are available in the form of `conda packages <https://conda.anaconda.org/m-labs/label/main>`_ for both Linux and Windows.
|
||||||
Packages containing pre-compiled binary images to be loaded onto the hardware platforms are supplied for each configuration.
|
Packages containing pre-compiled binary images to be loaded onto the hardware platforms are supplied for each configuration.
|
||||||
|
@ -31,7 +29,7 @@ Website: https://m-labs.hk/artiq
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Copyright (C) 2014-2017 M-Labs Limited.
|
Copyright (C) 2014-2019 M-Labs Limited.
|
||||||
|
|
||||||
ARTIQ is free software: you can redistribute it and/or modify
|
ARTIQ is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
|
|
@ -3,13 +3,57 @@
|
||||||
Release notes
|
Release notes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
3.7
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
|
3.6
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
|
3.5
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
|
3.4
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
|
3.3
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
|
3.2
|
||||||
|
---
|
||||||
|
|
||||||
|
* To accommodate larger runtimes, the flash layout as changed. As a result, the
|
||||||
|
contents of the flash storage will be lost when upgrading. Set the values back
|
||||||
|
(IP, MAC address, startup kernel, etc.) after the upgrade.
|
||||||
|
|
||||||
|
|
||||||
|
3.1
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
3.0
|
3.0
|
||||||
---
|
---
|
||||||
|
|
||||||
* The --embed option of applets is replaced with the environment variable
|
* The ``--embed`` option of applets is replaced with the environment variable
|
||||||
``ARTIQ_APPLET_EMBED``. The GUI sets this enviroment variable itself and the
|
``ARTIQ_APPLET_EMBED``. The GUI sets this enviroment variable itself and the
|
||||||
user simply needs to remove the ``--embed`` argument.
|
user simply needs to remove the ``--embed`` argument.
|
||||||
* EnvExperiment's prepare calls prepare for all its children.
|
* ``EnvExperiment``'s ``prepare`` calls ``prepare`` for all its children.
|
||||||
* Dynamic ``__getattr__``'s returning RPC target methods are not supported anymore.
|
* Dynamic ``__getattr__``'s returning RPC target methods are not supported anymore.
|
||||||
Controller driver classes must define all their methods intended for RPC as
|
Controller driver classes must define all their methods intended for RPC as
|
||||||
members.
|
members.
|
||||||
|
@ -22,7 +66,7 @@ Release notes
|
||||||
* The DDS class names and setup options have changed, this requires an update of
|
* The DDS class names and setup options have changed, this requires an update of
|
||||||
the device database.
|
the device database.
|
||||||
* ``int(a, width=b)`` has been removed. Use ``int32(a)`` and ``int64(a)``.
|
* ``int(a, width=b)`` has been removed. Use ``int32(a)`` and ``int64(a)``.
|
||||||
* The kc705 gateware target has been renamed kc705_dds.
|
* The KC705 gateware target has been renamed ``kc705_dds``.
|
||||||
* ``artiq.coredevice.comm_tcp`` has been renamed ``artiq.coredevice.comm_kernel``,
|
* ``artiq.coredevice.comm_tcp`` has been renamed ``artiq.coredevice.comm_kernel``,
|
||||||
and ``Comm`` has been renamed ``CommKernel``.
|
and ``Comm`` has been renamed ``CommKernel``.
|
||||||
* The "collision" and "busy" RTIO errors are reported through the log instead of
|
* The "collision" and "busy" RTIO errors are reported through the log instead of
|
||||||
|
@ -38,8 +82,8 @@ Release notes
|
||||||
identifiers (``true``, ``null``, ...) with their Python equivalents
|
identifiers (``true``, ``null``, ...) with their Python equivalents
|
||||||
(``True``, ``None`` ...).
|
(``True``, ``None`` ...).
|
||||||
* Controllers are now named ``aqctl_XXX`` instead of ``XXX_controller``.
|
* Controllers are now named ``aqctl_XXX`` instead of ``XXX_controller``.
|
||||||
* In the device database, the "comm" device has been folded into the "core" device.
|
* In the device database, the ``comm`` device has been folded into the ``core`` device.
|
||||||
Move the "host" argument into the "core" device, and remove the "comm" device.
|
Move the "host" argument into the ``core`` device, and remove the ``comm`` device.
|
||||||
* The core device log now contains important information about events such as
|
* The core device log now contains important information about events such as
|
||||||
RTIO collisions. A new controller ``aqctl_corelog`` must be running to forward
|
RTIO collisions. A new controller ``aqctl_corelog`` must be running to forward
|
||||||
those logs to the master. See the example device databases to see how to
|
those logs to the master. See the example device databases to see how to
|
||||||
|
@ -52,8 +96,15 @@ Release notes
|
||||||
at https://github.com/m-labs/pdq. All SPI/USB driver layers, Mediator,
|
at https://github.com/m-labs/pdq. All SPI/USB driver layers, Mediator,
|
||||||
CompoundPDQ and examples/documentation has been moved.
|
CompoundPDQ and examples/documentation has been moved.
|
||||||
* The master now rotates log files at midnight, rather than based on log size.
|
* The master now rotates log files at midnight, rather than based on log size.
|
||||||
* The results keys start_time and run_time are now stored as doubles of UNIX time,
|
* The results keys ``start_time`` and ``run_time`` are now stored as doubles of UNIX time,
|
||||||
rather than ints. The file names are still based on local time.
|
rather than ints. The file names are still based on local time.
|
||||||
|
* Packages are no longer available for 32-bit Windows.
|
||||||
|
|
||||||
|
|
||||||
|
2.5
|
||||||
|
---
|
||||||
|
|
||||||
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
2.4
|
2.4
|
||||||
|
@ -62,7 +113,6 @@ Release notes
|
||||||
No further notes.
|
No further notes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2.3
|
2.3
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -258,8 +258,9 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
||||||
def dropEvent(self, ev):
|
def dropEvent(self, ev):
|
||||||
for uri in ev.mimeData().urls():
|
for uri in ev.mimeData().urls():
|
||||||
if uri.scheme() == "file":
|
if uri.scheme() == "file":
|
||||||
logger.debug("Loading HDF5 arguments from %s", uri.path())
|
filename = QtCore.QDir.toNativeSeparators(uri.toLocalFile())
|
||||||
asyncio.ensure_future(self.load_hdf5_task(uri.path()))
|
logger.debug("Loading HDF5 arguments from %s", filename)
|
||||||
|
asyncio.ensure_future(self.load_hdf5_task(filename))
|
||||||
break
|
break
|
||||||
|
|
||||||
async def compute_arginfo(self):
|
async def compute_arginfo(self):
|
||||||
|
|
|
@ -169,6 +169,9 @@ def fn_ValueError():
|
||||||
def fn_ZeroDivisionError():
|
def fn_ZeroDivisionError():
|
||||||
return types.TExceptionConstructor(TException("ZeroDivisionError"))
|
return types.TExceptionConstructor(TException("ZeroDivisionError"))
|
||||||
|
|
||||||
|
def fn_RuntimeError():
|
||||||
|
return types.TExceptionConstructor(TException("RuntimeError"))
|
||||||
|
|
||||||
def fn_range():
|
def fn_range():
|
||||||
return types.TBuiltinFunction("range")
|
return types.TBuiltinFunction("range")
|
||||||
|
|
||||||
|
|
|
@ -749,6 +749,11 @@ class Stitcher:
|
||||||
quote_function=self._quote_function)
|
quote_function=self._quote_function)
|
||||||
|
|
||||||
def _function_loc(self, function):
|
def _function_loc(self, function):
|
||||||
|
if isinstance(function, SpecializedFunction):
|
||||||
|
function = function.host_function
|
||||||
|
if hasattr(function, 'artiq_embedded') and function.artiq_embedded.function:
|
||||||
|
function = function.artiq_embedded.function
|
||||||
|
|
||||||
filename = function.__code__.co_filename
|
filename = function.__code__.co_filename
|
||||||
line = function.__code__.co_firstlineno
|
line = function.__code__.co_firstlineno
|
||||||
name = function.__code__.co_name
|
name = function.__code__.co_name
|
||||||
|
|
|
@ -64,8 +64,8 @@ class Module:
|
||||||
interleaver = transforms.Interleaver(engine=self.engine)
|
interleaver = transforms.Interleaver(engine=self.engine)
|
||||||
invariant_detection = analyses.InvariantDetection(engine=self.engine)
|
invariant_detection = analyses.InvariantDetection(engine=self.engine)
|
||||||
|
|
||||||
cast_monomorphizer.visit(src.typedtree)
|
|
||||||
int_monomorphizer.visit(src.typedtree)
|
int_monomorphizer.visit(src.typedtree)
|
||||||
|
cast_monomorphizer.visit(src.typedtree)
|
||||||
inferencer.visit(src.typedtree)
|
inferencer.visit(src.typedtree)
|
||||||
monomorphism_validator.visit(src.typedtree)
|
monomorphism_validator.visit(src.typedtree)
|
||||||
escape_validator.visit(src.typedtree)
|
escape_validator.visit(src.typedtree)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import os, sys, tempfile, subprocess
|
import os, sys, tempfile, subprocess, io
|
||||||
from artiq.compiler import types
|
from artiq.compiler import types
|
||||||
from llvmlite_artiq import ir as ll, binding as llvm
|
from llvmlite_artiq import ir as ll, binding as llvm
|
||||||
|
|
||||||
|
@ -8,40 +8,44 @@ llvm.initialize_all_asmprinters()
|
||||||
|
|
||||||
class RunTool:
|
class RunTool:
|
||||||
def __init__(self, pattern, **tempdata):
|
def __init__(self, pattern, **tempdata):
|
||||||
self.files = []
|
self._pattern = pattern
|
||||||
self.pattern = pattern
|
self._tempdata = tempdata
|
||||||
self.tempdata = tempdata
|
self._tempnames = {}
|
||||||
|
self._tempfiles = {}
|
||||||
def maketemp(self, data):
|
|
||||||
f = tempfile.NamedTemporaryFile()
|
|
||||||
f.write(data)
|
|
||||||
f.flush()
|
|
||||||
self.files.append(f)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
tempfiles = {}
|
for key, data in self._tempdata.items():
|
||||||
tempnames = {}
|
if data is None:
|
||||||
for key in self.tempdata:
|
fd, filename = tempfile.mkstemp()
|
||||||
tempfiles[key] = self.maketemp(self.tempdata[key])
|
os.close(fd)
|
||||||
tempnames[key] = tempfiles[key].name
|
self._tempnames[key] = filename
|
||||||
|
else:
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
f.write(data)
|
||||||
|
self._tempnames[key] = f.name
|
||||||
|
|
||||||
cmdline = []
|
cmdline = []
|
||||||
for argument in self.pattern:
|
for argument in self._pattern:
|
||||||
cmdline.append(argument.format(**tempnames))
|
cmdline.append(argument.format(**self._tempnames))
|
||||||
|
|
||||||
process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
stdout, stderr = process.communicate()
|
stdout, stderr = process.communicate()
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
raise Exception("{} invocation failed: {}".
|
raise Exception("{} invocation failed: {}".
|
||||||
format(cmdline[0], stderr.decode('utf-8')))
|
format(cmdline[0], stderr))
|
||||||
|
|
||||||
tempfiles["__stdout__"] = stdout.decode('utf-8')
|
self._tempfiles["__stdout__"] = io.StringIO(stdout)
|
||||||
return tempfiles
|
for key in self._tempdata:
|
||||||
|
if self._tempdata[key] is None:
|
||||||
|
self._tempfiles[key] = open(self._tempnames[key], "rb")
|
||||||
|
return self._tempfiles
|
||||||
|
|
||||||
def __exit__(self, exc_typ, exc_value, exc_trace):
|
def __exit__(self, exc_typ, exc_value, exc_trace):
|
||||||
for f in self.files:
|
for file in self._tempfiles.values():
|
||||||
f.close()
|
file.close()
|
||||||
|
for filename in self._tempnames.values():
|
||||||
|
os.unlink(filename)
|
||||||
|
|
||||||
def _dump(target, kind, suffix, content):
|
def _dump(target, kind, suffix, content):
|
||||||
if target is not None:
|
if target is not None:
|
||||||
|
@ -166,7 +170,7 @@ class Target:
|
||||||
with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] +
|
with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] +
|
||||||
["{{obj{}}}".format(index) for index in range(len(objects))] +
|
["{{obj{}}}".format(index) for index in range(len(objects))] +
|
||||||
["-o", "{output}"],
|
["-o", "{output}"],
|
||||||
output=b"",
|
output=None,
|
||||||
**{"obj{}".format(index): obj for index, obj in enumerate(objects)}) \
|
**{"obj{}".format(index): obj for index, obj in enumerate(objects)}) \
|
||||||
as results:
|
as results:
|
||||||
library = results["output"].read()
|
library = results["output"].read()
|
||||||
|
@ -181,7 +185,7 @@ class Target:
|
||||||
|
|
||||||
def strip(self, library):
|
def strip(self, library):
|
||||||
with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"],
|
with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"],
|
||||||
library=library, output=b"") \
|
library=library, output=None) \
|
||||||
as results:
|
as results:
|
||||||
return results["output"].read()
|
return results["output"].read()
|
||||||
|
|
||||||
|
@ -198,7 +202,7 @@ class Target:
|
||||||
"--demangle", "--exe={library}"] + offset_addresses,
|
"--demangle", "--exe={library}"] + offset_addresses,
|
||||||
library=library) \
|
library=library) \
|
||||||
as results:
|
as results:
|
||||||
lines = iter(results["__stdout__"].rstrip().split("\n"))
|
lines = iter(results["__stdout__"].read().rstrip().split("\n"))
|
||||||
backtrace = []
|
backtrace = []
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -216,13 +220,17 @@ class Target:
|
||||||
filename, line = location.rsplit(":", 1)
|
filename, line = location.rsplit(":", 1)
|
||||||
if filename == "??" or filename == "<synthesized>":
|
if filename == "??" or filename == "<synthesized>":
|
||||||
continue
|
continue
|
||||||
|
if line == "?":
|
||||||
|
line = -1
|
||||||
|
else:
|
||||||
|
line = int(line)
|
||||||
# can't get column out of addr2line D:
|
# can't get column out of addr2line D:
|
||||||
backtrace.append((filename, int(line), -1, function, address))
|
backtrace.append((filename, line, -1, function, address))
|
||||||
return backtrace
|
return backtrace
|
||||||
|
|
||||||
def demangle(self, names):
|
def demangle(self, names):
|
||||||
with RunTool([self.triple + "-c++filt"] + names) as results:
|
with RunTool([self.triple + "-c++filt"] + names) as results:
|
||||||
return results["__stdout__"].rstrip().split("\n")
|
return results["__stdout__"].read().rstrip().split("\n")
|
||||||
|
|
||||||
class NativeTarget(Target):
|
class NativeTarget(Target):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import sys, os
|
import sys, os, tokenize
|
||||||
|
|
||||||
from artiq.master.databases import DeviceDB
|
from artiq.master.databases import DeviceDB
|
||||||
from artiq.master.worker_db import DeviceManager
|
from artiq.master.worker_db import DeviceManager
|
||||||
|
@ -27,7 +27,7 @@ def main():
|
||||||
ddb_path = os.path.join(os.path.dirname(sys.argv[1]), "device_db.py")
|
ddb_path = os.path.join(os.path.dirname(sys.argv[1]), "device_db.py")
|
||||||
dmgr = DeviceManager(DeviceDB(ddb_path))
|
dmgr = DeviceManager(DeviceDB(ddb_path))
|
||||||
|
|
||||||
with open(sys.argv[1]) as f:
|
with tokenize.open(sys.argv[1]) as f:
|
||||||
testcase_code = compile(f.read(), f.name, "exec")
|
testcase_code = compile(f.read(), f.name, "exec")
|
||||||
testcase_vars = {'__name__': 'testbench', 'dmgr': dmgr}
|
testcase_vars = {'__name__': 'testbench', 'dmgr': dmgr}
|
||||||
exec(testcase_code, testcase_vars)
|
exec(testcase_code, testcase_vars)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import sys, os
|
import sys, os, tokenize
|
||||||
from pythonparser import diagnostic
|
from pythonparser import diagnostic
|
||||||
from ...language.environment import ProcessArgumentManager
|
from ...language.environment import ProcessArgumentManager
|
||||||
from ...master.databases import DeviceDB, DatasetDB
|
from ...master.databases import DeviceDB, DatasetDB
|
||||||
|
@ -22,7 +22,7 @@ def main():
|
||||||
engine = diagnostic.Engine()
|
engine = diagnostic.Engine()
|
||||||
engine.process = process_diagnostic
|
engine.process = process_diagnostic
|
||||||
|
|
||||||
with open(sys.argv[1]) as f:
|
with tokenize.open(sys.argv[1]) as f:
|
||||||
testcase_code = compile(f.read(), f.name, "exec")
|
testcase_code = compile(f.read(), f.name, "exec")
|
||||||
testcase_vars = {'__name__': 'testbench'}
|
testcase_vars = {'__name__': 'testbench'}
|
||||||
exec(testcase_code, testcase_vars)
|
exec(testcase_code, testcase_vars)
|
||||||
|
|
|
@ -5,6 +5,6 @@ from .cast_monomorphizer import CastMonomorphizer
|
||||||
from .iodelay_estimator import IODelayEstimator
|
from .iodelay_estimator import IODelayEstimator
|
||||||
from .artiq_ir_generator import ARTIQIRGenerator
|
from .artiq_ir_generator import ARTIQIRGenerator
|
||||||
from .dead_code_eliminator import DeadCodeEliminator
|
from .dead_code_eliminator import DeadCodeEliminator
|
||||||
from .llvm_ir_generator import LLVMIRGenerator
|
|
||||||
from .interleaver import Interleaver
|
from .interleaver import Interleaver
|
||||||
from .typedtree_printer import TypedtreePrinter
|
from .typedtree_printer import TypedtreePrinter
|
||||||
|
from .llvm_ir_generator import LLVMIRGenerator
|
||||||
|
|
|
@ -1289,6 +1289,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
return self.append(ir.Select(cond,
|
return self.append(ir.Select(cond,
|
||||||
ir.Constant(False, builtins.TBool()),
|
ir.Constant(False, builtins.TBool()),
|
||||||
ir.Constant(True, builtins.TBool())))
|
ir.Constant(True, builtins.TBool())))
|
||||||
|
elif isinstance(node.op, ast.Invert):
|
||||||
|
operand = self.visit(node.operand)
|
||||||
|
return self.append(ir.Arith(ast.BitXor(loc=None),
|
||||||
|
ir.Constant(-1, operand.type), operand))
|
||||||
elif isinstance(node.op, ast.USub):
|
elif isinstance(node.op, ast.USub):
|
||||||
operand = self.visit(node.operand)
|
operand = self.visit(node.operand)
|
||||||
return self.append(ir.Arith(ast.Sub(loc=None),
|
return self.append(ir.Arith(ast.Sub(loc=None),
|
||||||
|
@ -1319,7 +1323,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
ir.Constant("shift amount must be nonnegative", builtins.TStr())),
|
ir.Constant("shift amount must be nonnegative", builtins.TStr())),
|
||||||
loc=node.right.loc)
|
loc=node.right.loc)
|
||||||
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
elif isinstance(node.op, (ast.Div, ast.FloorDiv, ast.Mod)):
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||||
lambda: self.alloc_exn(builtins.TException("ZeroDivisionError"),
|
lambda: self.alloc_exn(builtins.TException("ZeroDivisionError"),
|
||||||
|
@ -1474,7 +1478,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def polymorphic_compare_pair_inclusion(self, op, needle, haystack):
|
def polymorphic_compare_pair_inclusion(self, needle, haystack):
|
||||||
if builtins.is_range(haystack.type):
|
if builtins.is_range(haystack.type):
|
||||||
# Optimized range `in` operator
|
# Optimized range `in` operator
|
||||||
start = self.append(ir.GetAttr(haystack, "start"))
|
start = self.append(ir.GetAttr(haystack, "start"))
|
||||||
|
@ -1518,21 +1522,29 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
if isinstance(op, ast.NotIn):
|
return result
|
||||||
result = self.append(ir.Select(result,
|
|
||||||
|
def invert(self, value):
|
||||||
|
return self.append(ir.Select(value,
|
||||||
ir.Constant(False, builtins.TBool()),
|
ir.Constant(False, builtins.TBool()),
|
||||||
ir.Constant(True, builtins.TBool())))
|
ir.Constant(True, builtins.TBool())))
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def polymorphic_compare_pair(self, op, lhs, rhs):
|
def polymorphic_compare_pair(self, op, lhs, rhs):
|
||||||
if isinstance(op, (ast.Is, ast.IsNot)):
|
if isinstance(op, (ast.Is, ast.IsNot)):
|
||||||
# The backend will handle equality of aggregates.
|
# The backend will handle equality of aggregates.
|
||||||
return self.append(ir.Compare(op, lhs, rhs))
|
return self.append(ir.Compare(op, lhs, rhs))
|
||||||
elif isinstance(op, (ast.In, ast.NotIn)):
|
elif isinstance(op, ast.In):
|
||||||
return self.polymorphic_compare_pair_inclusion(op, lhs, rhs)
|
return self.polymorphic_compare_pair_inclusion(lhs, rhs)
|
||||||
else: # Eq, NotEq, Lt, LtE, Gt, GtE
|
elif isinstance(op, ast.NotIn):
|
||||||
|
result = self.polymorphic_compare_pair_inclusion(lhs, rhs)
|
||||||
|
return self.invert(result)
|
||||||
|
elif isinstance(op, (ast.Eq, ast.Lt, ast.LtE, ast.Gt, ast.GtE)):
|
||||||
return self.polymorphic_compare_pair_order(op, lhs, rhs)
|
return self.polymorphic_compare_pair_order(op, lhs, rhs)
|
||||||
|
elif isinstance(op, ast.NotEq):
|
||||||
|
result = self.polymorphic_compare_pair_order(ast.Eq(loc=op.loc), lhs, rhs)
|
||||||
|
return self.invert(result)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
def visit_CompareT(self, node):
|
def visit_CompareT(self, node):
|
||||||
# Essentially a sequence of `and`s performed over results
|
# Essentially a sequence of `and`s performed over results
|
||||||
|
@ -1622,7 +1634,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
elif (types.is_builtin(typ, "list") or types.is_builtin(typ, "array") or
|
elif (types.is_builtin(typ, "list") or types.is_builtin(typ, "array") or
|
||||||
types.is_builtin(typ, "bytearray")):
|
types.is_builtin(typ, "bytearray") or types.is_builtin(typ, "bytes")):
|
||||||
if len(node.args) == 0 and len(node.keywords) == 0:
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||||
length = ir.Constant(0, builtins.TInt32())
|
length = ir.Constant(0, builtins.TInt32())
|
||||||
return self.append(ir.Alloc([length], node.type))
|
return self.append(ir.Alloc([length], node.type))
|
||||||
|
@ -1633,6 +1645,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
def body_gen(index):
|
def body_gen(index):
|
||||||
elt = self.iterable_get(arg, index)
|
elt = self.iterable_get(arg, index)
|
||||||
|
elt = self.append(ir.Coerce(elt, builtins.get_iterable_elt(node.type)))
|
||||||
self.append(ir.SetElem(result, index, elt))
|
self.append(ir.SetElem(result, index, elt))
|
||||||
return self.append(ir.Arith(ast.Add(loc=None), index,
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
||||||
ir.Constant(1, length.type)))
|
ir.Constant(1, length.type)))
|
||||||
|
@ -1721,7 +1734,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.polymorphic_print([self.visit(prefix)],
|
self.polymorphic_print([self.visit(prefix)],
|
||||||
separator=" ", suffix="\x1E", as_rtio=True)
|
separator=" ", suffix="\x1E", as_rtio=True)
|
||||||
self.polymorphic_print([self.visit(arg) for arg in args],
|
self.polymorphic_print([self.visit(arg) for arg in args],
|
||||||
separator=" ", suffix="\n\x1D", as_rtio=True)
|
separator=" ", suffix="\x1D", as_rtio=True)
|
||||||
return ir.Constant(None, builtins.TNone())
|
return ir.Constant(None, builtins.TNone())
|
||||||
elif types.is_builtin(typ, "delay"):
|
elif types.is_builtin(typ, "delay"):
|
||||||
if len(node.args) == 1 and len(node.keywords) == 0:
|
if len(node.args) == 1 and len(node.keywords) == 0:
|
||||||
|
@ -1808,7 +1821,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
assert None not in args
|
assert None not in args
|
||||||
|
|
||||||
if self.unwind_target is None:
|
if self.unwind_target is None or \
|
||||||
|
types.is_c_function(callee.type) and "nounwind" in callee.type.flags:
|
||||||
insn = self.append(ir.Call(func, args, arg_exprs))
|
insn = self.append(ir.Call(func, args, arg_exprs))
|
||||||
else:
|
else:
|
||||||
after_invoke = self.add_block("invoke")
|
after_invoke = self.add_block("invoke")
|
||||||
|
|
|
@ -11,13 +11,12 @@ class CastMonomorphizer(algorithm.Visitor):
|
||||||
self.engine = engine
|
self.engine = engine
|
||||||
|
|
||||||
def visit_CallT(self, node):
|
def visit_CallT(self, node):
|
||||||
self.generic_visit(node)
|
|
||||||
|
|
||||||
if (types.is_builtin(node.func.type, "int") or
|
if (types.is_builtin(node.func.type, "int") or
|
||||||
types.is_builtin(node.func.type, "int32") or
|
types.is_builtin(node.func.type, "int32") or
|
||||||
types.is_builtin(node.func.type, "int64")):
|
types.is_builtin(node.func.type, "int64")):
|
||||||
typ = node.type.find()
|
typ = node.type.find()
|
||||||
if (not types.is_var(typ["width"]) and
|
if (not types.is_var(typ["width"]) and
|
||||||
|
len(node.args) == 1 and
|
||||||
builtins.is_int(node.args[0].type) and
|
builtins.is_int(node.args[0].type) and
|
||||||
types.is_var(node.args[0].type.find()["width"])):
|
types.is_var(node.args[0].type.find()["width"])):
|
||||||
if isinstance(node.args[0], asttyped.BinOpT):
|
if isinstance(node.args[0], asttyped.BinOpT):
|
||||||
|
@ -29,3 +28,20 @@ class CastMonomorphizer(algorithm.Visitor):
|
||||||
|
|
||||||
node.args[0].type.unify(typ)
|
node.args[0].type.unify(typ)
|
||||||
|
|
||||||
|
if types.is_builtin(node.func.type, "int") or \
|
||||||
|
types.is_builtin(node.func.type, "round"):
|
||||||
|
typ = node.type.find()
|
||||||
|
if types.is_var(typ["width"]):
|
||||||
|
typ["width"].unify(types.TValue(32))
|
||||||
|
|
||||||
|
self.generic_visit(node)
|
||||||
|
|
||||||
|
def visit_CoerceT(self, node):
|
||||||
|
if isinstance(node.value, asttyped.NumT) and \
|
||||||
|
builtins.is_int(node.type) and \
|
||||||
|
builtins.is_int(node.value.type) and \
|
||||||
|
not types.is_var(node.type["width"]) and \
|
||||||
|
types.is_var(node.value.type["width"]):
|
||||||
|
node.value.type.unify(node.type)
|
||||||
|
|
||||||
|
self.generic_visit(node)
|
||||||
|
|
|
@ -310,9 +310,17 @@ class Inferencer(algorithm.Visitor):
|
||||||
node_types = []
|
node_types = []
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
if isinstance(node, asttyped.CoerceT):
|
if isinstance(node, asttyped.CoerceT):
|
||||||
|
# If we already know exactly what we coerce this value to, use that type,
|
||||||
|
# or we'll get an unification error in case the coerced type is not the same
|
||||||
|
# as the type of the coerced value.
|
||||||
|
# Otherwise, use the potentially more specific subtype when considering possible
|
||||||
|
# coercions, or we may get stuck.
|
||||||
|
if node.type.fold(False, lambda acc, ty: acc or types.is_var(ty)):
|
||||||
node_types.append(node.value.type)
|
node_types.append(node.value.type)
|
||||||
else:
|
else:
|
||||||
node_types.append(node.type)
|
node_types.append(node.type)
|
||||||
|
else:
|
||||||
|
node_types.append(node.type)
|
||||||
if any(map(types.is_var, node_types)): # not enough info yet
|
if any(map(types.is_var, node_types)): # not enough info yet
|
||||||
return
|
return
|
||||||
elif not all(map(builtins.is_numeric, node_types)):
|
elif not all(map(builtins.is_numeric, node_types)):
|
||||||
|
@ -682,6 +690,11 @@ class Inferencer(algorithm.Visitor):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
diagnose(valid_forms())
|
diagnose(valid_forms())
|
||||||
|
elif types.is_builtin(typ, "str"):
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"strings currently cannot be constructed", {},
|
||||||
|
node.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
elif types.is_builtin(typ, "list") or types.is_builtin(typ, "array"):
|
elif types.is_builtin(typ, "list") or types.is_builtin(typ, "array"):
|
||||||
if types.is_builtin(typ, "list"):
|
if types.is_builtin(typ, "list"):
|
||||||
valid_forms = lambda: [
|
valid_forms = lambda: [
|
||||||
|
@ -999,6 +1012,17 @@ class Inferencer(algorithm.Visitor):
|
||||||
elif keyword.arg in typ_optargs:
|
elif keyword.arg in typ_optargs:
|
||||||
self._unify(keyword.value.type, typ_optargs[keyword.arg],
|
self._unify(keyword.value.type, typ_optargs[keyword.arg],
|
||||||
keyword.value.loc, None)
|
keyword.value.loc, None)
|
||||||
|
else:
|
||||||
|
note = diagnostic.Diagnostic("note",
|
||||||
|
"extraneous argument", {},
|
||||||
|
keyword.loc)
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"this function of type {type} does not accept argument '{name}'",
|
||||||
|
{"type": types.TypePrinter().name(node.func.type),
|
||||||
|
"name": keyword.arg},
|
||||||
|
node.func.loc, [], [note])
|
||||||
|
self.engine.process(diag)
|
||||||
|
return
|
||||||
passed_args[keyword.arg] = keyword.arg_loc
|
passed_args[keyword.arg] = keyword.arg_loc
|
||||||
|
|
||||||
for formalname in typ_args:
|
for formalname in typ_args:
|
||||||
|
|
|
@ -26,22 +26,3 @@ class IntMonomorphizer(algorithm.Visitor):
|
||||||
return
|
return
|
||||||
|
|
||||||
node.type["width"].unify(types.TValue(width))
|
node.type["width"].unify(types.TValue(width))
|
||||||
|
|
||||||
def visit_CallT(self, node):
|
|
||||||
self.generic_visit(node)
|
|
||||||
|
|
||||||
if types.is_builtin(node.func.type, "int") or \
|
|
||||||
types.is_builtin(node.func.type, "round"):
|
|
||||||
typ = node.type.find()
|
|
||||||
if types.is_var(typ["width"]):
|
|
||||||
typ["width"].unify(types.TValue(32))
|
|
||||||
|
|
||||||
def visit_CoerceT(self, node):
|
|
||||||
if isinstance(node.value, asttyped.NumT) and \
|
|
||||||
builtins.is_int(node.type) and \
|
|
||||||
builtins.is_int(node.value.type) and \
|
|
||||||
not types.is_var(node.type["width"]) and \
|
|
||||||
types.is_var(node.value.type["width"]):
|
|
||||||
node.value.type.unify(node.type)
|
|
||||||
|
|
||||||
self.generic_visit(node)
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from pythonparser import ast, diagnostic
|
||||||
from llvmlite_artiq import ir as ll, binding as llvm
|
from llvmlite_artiq import ir as ll, binding as llvm
|
||||||
from ...language import core as language_core
|
from ...language import core as language_core
|
||||||
from .. import types, builtins, ir
|
from .. import types, builtins, ir
|
||||||
|
from ..embedding import SpecializedFunction
|
||||||
|
|
||||||
|
|
||||||
llvoid = ll.VoidType()
|
llvoid = ll.VoidType()
|
||||||
|
@ -22,7 +23,7 @@ llptr = ll.IntType(8).as_pointer()
|
||||||
llptrptr = ll.IntType(8).as_pointer().as_pointer()
|
llptrptr = ll.IntType(8).as_pointer().as_pointer()
|
||||||
llslice = ll.LiteralStructType([llptr, lli32])
|
llslice = ll.LiteralStructType([llptr, lli32])
|
||||||
llsliceptr = ll.LiteralStructType([llptr, lli32]).as_pointer()
|
llsliceptr = ll.LiteralStructType([llptr, lli32]).as_pointer()
|
||||||
llmetadata = ll.MetaData()
|
llmetadata = ll.MetaDataType()
|
||||||
|
|
||||||
|
|
||||||
def memoize(generator):
|
def memoize(generator):
|
||||||
|
@ -270,7 +271,7 @@ class LLVMIRGenerator:
|
||||||
sanitized_str = re.sub(rb"[^a-zA-Z0-9_.]", b"", as_bytes[:20]).decode('ascii')
|
sanitized_str = re.sub(rb"[^a-zA-Z0-9_.]", b"", as_bytes[:20]).decode('ascii')
|
||||||
name = self.llmodule.get_unique_name("S.{}".format(sanitized_str))
|
name = self.llmodule.get_unique_name("S.{}".format(sanitized_str))
|
||||||
|
|
||||||
llstr = self.llmodule.get_global(name)
|
llstr = self.llmodule.globals.get(name)
|
||||||
if llstr is None:
|
if llstr is None:
|
||||||
llstrty = ll.ArrayType(lli8, len(as_bytes))
|
llstrty = ll.ArrayType(lli8, len(as_bytes))
|
||||||
llstr = ll.GlobalVariable(self.llmodule, llstrty, name)
|
llstr = ll.GlobalVariable(self.llmodule, llstrty, name)
|
||||||
|
@ -306,7 +307,7 @@ class LLVMIRGenerator:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def llbuiltin(self, name):
|
def llbuiltin(self, name):
|
||||||
llglobal = self.llmodule.get_global(name)
|
llglobal = self.llmodule.globals.get(name)
|
||||||
if llglobal is not None:
|
if llglobal is not None:
|
||||||
return llglobal
|
return llglobal
|
||||||
|
|
||||||
|
@ -326,6 +327,12 @@ class LLVMIRGenerator:
|
||||||
llty = ll.FunctionType(llptr, [])
|
llty = ll.FunctionType(llptr, [])
|
||||||
elif name == "llvm.stackrestore":
|
elif name == "llvm.stackrestore":
|
||||||
llty = ll.FunctionType(llvoid, [llptr])
|
llty = ll.FunctionType(llvoid, [llptr])
|
||||||
|
elif name == "__py_modsi3":
|
||||||
|
llty = ll.FunctionType(lli32, [lli32, lli32])
|
||||||
|
elif name == "__py_moddi3":
|
||||||
|
llty = ll.FunctionType(lli64, [lli64, lli64])
|
||||||
|
elif name == "__py_moddf3":
|
||||||
|
llty = ll.FunctionType(lldouble, [lldouble, lldouble])
|
||||||
elif name == self.target.print_function:
|
elif name == self.target.print_function:
|
||||||
llty = ll.FunctionType(llvoid, [llptr], var_arg=True)
|
llty = ll.FunctionType(llvoid, [llptr], var_arg=True)
|
||||||
elif name == "rtio_log":
|
elif name == "rtio_log":
|
||||||
|
@ -363,13 +370,96 @@ class LLVMIRGenerator:
|
||||||
"watchdog_set", "watchdog_clear",
|
"watchdog_set", "watchdog_clear",
|
||||||
self.target.print_function):
|
self.target.print_function):
|
||||||
llglobal.attributes.add("nounwind")
|
llglobal.attributes.add("nounwind")
|
||||||
|
if name.find("__py_") == 0:
|
||||||
|
llglobal.linkage = 'linkonce_odr'
|
||||||
|
self.emit_intrinsic(name, llglobal)
|
||||||
else:
|
else:
|
||||||
llglobal = ll.GlobalVariable(self.llmodule, llty, name)
|
llglobal = ll.GlobalVariable(self.llmodule, llty, name)
|
||||||
|
|
||||||
return llglobal
|
return llglobal
|
||||||
|
|
||||||
|
def emit_intrinsic(self, name, llfun):
|
||||||
|
llbuilder = ll.IRBuilder()
|
||||||
|
llbuilder.position_at_end(llfun.append_basic_block("entry"))
|
||||||
|
|
||||||
|
if name == "__py_modsi3" or name == "__py_moddi3":
|
||||||
|
if name == "__py_modsi3":
|
||||||
|
llty = lli32
|
||||||
|
elif name == "__py_moddi3":
|
||||||
|
llty = lli64
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
"""
|
||||||
|
Reference Objects/intobject.c
|
||||||
|
xdivy = x / y;
|
||||||
|
xmody = (long)(x - (unsigned long)xdivy * y);
|
||||||
|
/* If the signs of x and y differ, and the remainder is non-0,
|
||||||
|
* C89 doesn't define whether xdivy is now the floor or the
|
||||||
|
* ceiling of the infinitely precise quotient. We want the floor,
|
||||||
|
* and we have it iff the remainder's sign matches y's.
|
||||||
|
*/
|
||||||
|
if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) {
|
||||||
|
xmody += y;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
llx, lly = llfun.args
|
||||||
|
llxdivy = llbuilder.sdiv(llx, lly)
|
||||||
|
llxremy = llbuilder.srem(llx, lly)
|
||||||
|
|
||||||
|
llxmodynonzero = llbuilder.icmp_signed('!=', llxremy, ll.Constant(llty, 0))
|
||||||
|
lldiffsign = llbuilder.icmp_signed('<', llbuilder.xor(llx, lly), ll.Constant(llty, 0))
|
||||||
|
|
||||||
|
llcond = llbuilder.and_(llxmodynonzero, lldiffsign)
|
||||||
|
with llbuilder.if_then(llcond):
|
||||||
|
llbuilder.ret(llbuilder.add(llxremy, lly))
|
||||||
|
llbuilder.ret(llxremy)
|
||||||
|
elif name == "__py_moddf3":
|
||||||
|
"""
|
||||||
|
Reference Objects/floatobject.c
|
||||||
|
mod = fmod(vx, wx);
|
||||||
|
/* fmod is typically exact, so vx-mod is *mathematically* an
|
||||||
|
exact multiple of wx. But this is fp arithmetic, and fp
|
||||||
|
vx - mod is an approximation; the result is that div may
|
||||||
|
not be an exact integral value after the division, although
|
||||||
|
it will always be very close to one.
|
||||||
|
*/
|
||||||
|
// ...
|
||||||
|
if (mod) {
|
||||||
|
/* ensure the remainder has the same sign as the denominator */
|
||||||
|
if ((wx < 0) != (mod < 0)) {
|
||||||
|
mod += wx;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* the remainder is zero, and in the presence of signed zeroes
|
||||||
|
fmod returns different results across platforms; ensure
|
||||||
|
it has the same sign as the denominator; we'd like to do
|
||||||
|
"mod = wx * 0.0", but that may get optimized away */
|
||||||
|
mod *= mod; /* hide "mod = +0" from optimizer */
|
||||||
|
if (wx < 0.0)
|
||||||
|
mod = -mod;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
llv, llw = llfun.args
|
||||||
|
llrem = llbuilder.frem(llv, llw)
|
||||||
|
|
||||||
|
llremnonzero = llbuilder.fcmp_unordered('!=', llrem, ll.Constant(lldouble, 0.0))
|
||||||
|
llwltzero = llbuilder.fcmp_ordered('<', llw, ll.Constant(lldouble, 0.0))
|
||||||
|
llremltzero = llbuilder.fcmp_ordered('<', llrem, ll.Constant(lldouble, 0.0))
|
||||||
|
lldiffsign = llbuilder.icmp_unsigned('!=', llwltzero, llremltzero)
|
||||||
|
|
||||||
|
llcond = llbuilder.and_(llremnonzero, lldiffsign)
|
||||||
|
with llbuilder.if_then(llcond):
|
||||||
|
llbuilder.ret(llbuilder.fadd(llrem, llw))
|
||||||
|
llbuilder.ret(llrem)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
def get_function(self, typ, name):
|
def get_function(self, typ, name):
|
||||||
llfun = self.llmodule.get_global(name)
|
llfun = self.llmodule.globals.get(name)
|
||||||
if llfun is None:
|
if llfun is None:
|
||||||
llfunty = self.llty_of_type(typ, bare=True)
|
llfunty = self.llty_of_type(typ, bare=True)
|
||||||
llfun = ll.Function(self.llmodule, llfunty, name)
|
llfun = ll.Function(self.llmodule, llfunty, name)
|
||||||
|
@ -410,7 +500,7 @@ class LLVMIRGenerator:
|
||||||
llobjects = defaultdict(lambda: [])
|
llobjects = defaultdict(lambda: [])
|
||||||
|
|
||||||
for obj_id, obj_ref, obj_typ in self.embedding_map.iter_objects():
|
for obj_id, obj_ref, obj_typ in self.embedding_map.iter_objects():
|
||||||
llobject = self.llmodule.get_global("O.{}".format(obj_id))
|
llobject = self.llmodule.globals.get("O.{}".format(obj_id))
|
||||||
if llobject is not None:
|
if llobject is not None:
|
||||||
llobjects[obj_typ].append(llobject.bitcast(llptr))
|
llobjects[obj_typ].append(llobject.bitcast(llptr))
|
||||||
|
|
||||||
|
@ -435,11 +525,10 @@ class LLVMIRGenerator:
|
||||||
print(typ)
|
print(typ)
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
if not (types.is_function(typ) or types.is_method(typ) or types.is_rpc(typ) or
|
if name == "__objectid__":
|
||||||
name == "__objectid__"):
|
|
||||||
rpctag = b"Os" + self._rpc_tag(typ, error_handler=rpc_tag_error) + b":n"
|
|
||||||
else:
|
|
||||||
rpctag = b""
|
rpctag = b""
|
||||||
|
else:
|
||||||
|
rpctag = b"Os" + self._rpc_tag(typ, error_handler=rpc_tag_error) + b":n"
|
||||||
|
|
||||||
llrpcattrinit = ll.Constant(llrpcattrty, [
|
llrpcattrinit = ll.Constant(llrpcattrty, [
|
||||||
ll.Constant(lli32, offset),
|
ll.Constant(lli32, offset),
|
||||||
|
@ -472,7 +561,10 @@ class LLVMIRGenerator:
|
||||||
offset += alignment - (offset % alignment)
|
offset += alignment - (offset % alignment)
|
||||||
|
|
||||||
if types.is_instance(typ) and attr not in typ.constant_attributes:
|
if types.is_instance(typ) and attr not in typ.constant_attributes:
|
||||||
|
try:
|
||||||
llrpcattrs.append(llrpcattr_of_attr(offset, attr, attrtyp))
|
llrpcattrs.append(llrpcattr_of_attr(offset, attr, attrtyp))
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
offset += size
|
offset += size
|
||||||
|
|
||||||
|
@ -905,22 +997,15 @@ class LLVMIRGenerator:
|
||||||
return self.llbuilder.sdiv(self.map(insn.lhs()), self.map(insn.rhs()),
|
return self.llbuilder.sdiv(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
elif isinstance(insn.op, ast.Mod):
|
elif isinstance(insn.op, ast.Mod):
|
||||||
# Python only has the modulo operator, LLVM only has the remainder
|
|
||||||
if builtins.is_float(insn.type):
|
|
||||||
llvalue = self.llbuilder.frem(self.map(insn.lhs()), self.map(insn.rhs()))
|
|
||||||
self.add_fast_math_flags(llvalue)
|
|
||||||
return self.llbuilder.call(self.llbuiltin("llvm.copysign.f64"),
|
|
||||||
[llvalue, self.map(insn.rhs())],
|
|
||||||
name=insn.name)
|
|
||||||
else:
|
|
||||||
lllhs, llrhs = map(self.map, (insn.lhs(), insn.rhs()))
|
lllhs, llrhs = map(self.map, (insn.lhs(), insn.rhs()))
|
||||||
llxorsign = self.llbuilder.and_(self.llbuilder.xor(lllhs, llrhs),
|
if builtins.is_float(insn.type):
|
||||||
ll.Constant(lllhs.type, 1 << lllhs.type.width - 1))
|
intrinsic = "__py_moddf3"
|
||||||
llnegate = self.llbuilder.icmp_unsigned('!=',
|
elif builtins.is_int32(insn.type):
|
||||||
llxorsign, ll.Constant(llxorsign.type, 0))
|
intrinsic = "__py_modsi3"
|
||||||
llvalue = self.llbuilder.srem(lllhs, llrhs)
|
elif builtins.is_int64(insn.type):
|
||||||
llnegvalue = self.llbuilder.sub(ll.Constant(llvalue.type, 0), llvalue)
|
intrinsic = "__py_moddi3"
|
||||||
return self.llbuilder.select(llnegate, llnegvalue, llvalue)
|
return self.llbuilder.call(self.llbuiltin(intrinsic), [lllhs, llrhs],
|
||||||
|
name=insn.name)
|
||||||
elif isinstance(insn.op, ast.Pow):
|
elif isinstance(insn.op, ast.Pow):
|
||||||
if builtins.is_float(insn.type):
|
if builtins.is_float(insn.type):
|
||||||
return self.llbuilder.call(self.llbuiltin("llvm.pow.f64"),
|
return self.llbuilder.call(self.llbuiltin("llvm.pow.f64"),
|
||||||
|
@ -1127,7 +1212,7 @@ class LLVMIRGenerator:
|
||||||
llargs.append(llarg)
|
llargs.append(llarg)
|
||||||
|
|
||||||
llfunname = insn.target_function().type.name
|
llfunname = insn.target_function().type.name
|
||||||
llfun = self.llmodule.get_global(llfunname)
|
llfun = self.llmodule.globals.get(llfunname)
|
||||||
if llfun is None:
|
if llfun is None:
|
||||||
llretty = self.llty_of_type(insn.type, for_return=True)
|
llretty = self.llty_of_type(insn.type, for_return=True)
|
||||||
if self.needs_sret(llretty):
|
if self.needs_sret(llretty):
|
||||||
|
@ -1184,6 +1269,8 @@ class LLVMIRGenerator:
|
||||||
elif ir.is_keyword(typ):
|
elif ir.is_keyword(typ):
|
||||||
return b"k" + self._rpc_tag(typ.params["value"],
|
return b"k" + self._rpc_tag(typ.params["value"],
|
||||||
error_handler)
|
error_handler)
|
||||||
|
elif types.is_function(typ) or types.is_method(typ) or types.is_rpc(typ):
|
||||||
|
raise ValueError("RPC tag for functional value")
|
||||||
elif '__objectid__' in typ.attributes:
|
elif '__objectid__' in typ.attributes:
|
||||||
return b"O"
|
return b"O"
|
||||||
else:
|
else:
|
||||||
|
@ -1256,6 +1343,12 @@ class LLVMIRGenerator:
|
||||||
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
|
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
|
||||||
|
|
||||||
if fun_type.async:
|
if fun_type.async:
|
||||||
|
# If this RPC is called using an `invoke` ARTIQ IR instruction, there will be
|
||||||
|
# no other instructions in this basic block. Since this RPC is async, it cannot
|
||||||
|
# possibly raise an exception, so add an explicit jump to the normal successor.
|
||||||
|
if llunwindblock:
|
||||||
|
self.llbuilder.branch(llnormalblock)
|
||||||
|
|
||||||
return ll.Undefined
|
return ll.Undefined
|
||||||
|
|
||||||
# T result = {
|
# T result = {
|
||||||
|
@ -1366,9 +1459,8 @@ class LLVMIRGenerator:
|
||||||
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||||
name=insn.name)
|
name=insn.name)
|
||||||
|
|
||||||
# See the comment in process_Call.
|
# The !tbaa metadata is not legal to use with the invoke instruction,
|
||||||
if types.is_c_function(functiontyp) and 'nowrite' in functiontyp.flags:
|
# so unlike process_Call, we do not set it here.
|
||||||
llcall.set_metadata('tbaa', self.tbaa_nowrite_call)
|
|
||||||
|
|
||||||
return llcall
|
return llcall
|
||||||
|
|
||||||
|
@ -1464,12 +1556,20 @@ class LLVMIRGenerator:
|
||||||
lleltsptr = llglobal.bitcast(lleltsary.type.element.as_pointer())
|
lleltsptr = llglobal.bitcast(lleltsary.type.element.as_pointer())
|
||||||
llconst = ll.Constant(llty, [lleltsptr, ll.Constant(lli32, len(llelts))])
|
llconst = ll.Constant(llty, [lleltsptr, ll.Constant(lli32, len(llelts))])
|
||||||
return llconst
|
return llconst
|
||||||
elif types.is_rpc(typ) or types.is_c_function(typ):
|
elif types.is_rpc(typ) or types.is_c_function(typ) or types.is_builtin_function(typ):
|
||||||
# RPC and C functions have no runtime representation.
|
# RPC, C and builtin functions have no runtime representation.
|
||||||
return ll.Constant(llty, ll.Undefined)
|
return ll.Constant(llty, ll.Undefined)
|
||||||
elif types.is_function(typ):
|
elif types.is_function(typ):
|
||||||
return self.get_function_with_undef_env(typ.find(),
|
try:
|
||||||
self.embedding_map.retrieve_function(value))
|
func = self.embedding_map.retrieve_function(value)
|
||||||
|
except KeyError:
|
||||||
|
# If a class function was embedded directly (e.g. by a `C.f(...)` call),
|
||||||
|
# but it also appears in a class hierarchy, we might need to fall back
|
||||||
|
# to the non-specialized one, since direct invocations do not cause
|
||||||
|
# monomorphization.
|
||||||
|
assert isinstance(value, SpecializedFunction)
|
||||||
|
func = self.embedding_map.retrieve_function(value.host_function)
|
||||||
|
return self.get_function_with_undef_env(typ.find(), func)
|
||||||
elif types.is_method(typ):
|
elif types.is_method(typ):
|
||||||
llclosure = self._quote(value.__func__, types.get_method_function(typ),
|
llclosure = self._quote(value.__func__, types.get_method_function(typ),
|
||||||
lambda: path() + ['__func__'])
|
lambda: path() + ['__func__'])
|
||||||
|
@ -1561,7 +1661,7 @@ class LLVMIRGenerator:
|
||||||
|
|
||||||
llclauseexnname = self.llconst_of_const(
|
llclauseexnname = self.llconst_of_const(
|
||||||
ir.Constant(exnname, builtins.TStr()))
|
ir.Constant(exnname, builtins.TStr()))
|
||||||
llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname))
|
llclauseexnnameptr = self.llmodule.globals.get("exn.{}".format(exnname))
|
||||||
if llclauseexnnameptr is None:
|
if llclauseexnnameptr is None:
|
||||||
llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type,
|
llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type,
|
||||||
name="exn.{}".format(exnname))
|
name="exn.{}".format(exnname))
|
||||||
|
|
|
@ -605,6 +605,14 @@ def is_builtin(typ, name=None):
|
||||||
return isinstance(typ, TBuiltin) and \
|
return isinstance(typ, TBuiltin) and \
|
||||||
typ.name == name
|
typ.name == name
|
||||||
|
|
||||||
|
def is_builtin_function(typ, name=None):
|
||||||
|
typ = typ.find()
|
||||||
|
if name is None:
|
||||||
|
return isinstance(typ, TBuiltinFunction)
|
||||||
|
else:
|
||||||
|
return isinstance(typ, TBuiltinFunction) and \
|
||||||
|
typ.name == name
|
||||||
|
|
||||||
def is_constructor(typ, name=None):
|
def is_constructor(typ, name=None):
|
||||||
typ = typ.find()
|
typ = typ.find()
|
||||||
if name is not None:
|
if name is not None:
|
||||||
|
|
|
@ -298,8 +298,8 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
# and exceptions can only refer to strings, so we don't actually check
|
# and exceptions can only refer to strings, so we don't actually check
|
||||||
# this property. But we will need to, if string operations are ever added.
|
# this property. But we will need to, if string operations are ever added.
|
||||||
|
|
||||||
def visit_assignment(self, target, value, is_aug_assign=False):
|
def visit_assignment(self, target, value):
|
||||||
value_region = self._region_of(value) if not is_aug_assign else self.youngest_region
|
value_region = self._region_of(value)
|
||||||
|
|
||||||
# If this is a variable, we might need to contract the live range.
|
# If this is a variable, we might need to contract the live range.
|
||||||
if isinstance(value_region, Region):
|
if isinstance(value_region, Region):
|
||||||
|
@ -316,19 +316,11 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
target_regions = [self._region_of(name) for name in target_names]
|
target_regions = [self._region_of(name) for name in target_names]
|
||||||
for target_region in target_regions:
|
for target_region in target_regions:
|
||||||
if not Region.outlives(value_region, target_region):
|
if not Region.outlives(value_region, target_region):
|
||||||
if is_aug_assign:
|
|
||||||
target_desc = "the assignment target, allocated here,"
|
|
||||||
else:
|
|
||||||
target_desc = "the assignment target"
|
|
||||||
note = diagnostic.Diagnostic("note",
|
|
||||||
"this expression has type {type}",
|
|
||||||
{"type": types.TypePrinter().name(value.type)},
|
|
||||||
value.loc)
|
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"the assigned value does not outlive the assignment target", {},
|
"the assigned value does not outlive the assignment target", {},
|
||||||
value.loc, [target.loc],
|
value.loc, [target.loc],
|
||||||
notes=self._diagnostics_for(target_region, target.loc,
|
notes=self._diagnostics_for(target_region, target.loc,
|
||||||
target_desc) +
|
"the assignment target") +
|
||||||
self._diagnostics_for(value_region, value.loc,
|
self._diagnostics_for(value_region, value.loc,
|
||||||
"the assigned value"))
|
"the assigned value"))
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
@ -339,9 +331,19 @@ class EscapeValidator(algorithm.Visitor):
|
||||||
|
|
||||||
def visit_AugAssign(self, node):
|
def visit_AugAssign(self, node):
|
||||||
if builtins.is_allocated(node.target.type):
|
if builtins.is_allocated(node.target.type):
|
||||||
# If the target is mutable, op-assignment will allocate
|
note = diagnostic.Diagnostic("note",
|
||||||
# in the youngest region.
|
"try using `{lhs} = {lhs} {op} {rhs}` instead",
|
||||||
self.visit_assignment(node.target, node.value, is_aug_assign=True)
|
{"lhs": node.target.loc.source(),
|
||||||
|
"rhs": node.value.loc.source(),
|
||||||
|
"op": node.op.loc.source()[:-1]},
|
||||||
|
node.loc)
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"values cannot be mutated in-place", {},
|
||||||
|
node.op.loc, [node.target.loc],
|
||||||
|
notes=[note])
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
self.visit_assignment(node.target, node.value)
|
||||||
|
|
||||||
def visit_Return(self, node):
|
def visit_Return(self, node):
|
||||||
region = self._region_of(node.value)
|
region = self._region_of(node.value)
|
||||||
|
|
|
@ -545,7 +545,7 @@ class CommKernel:
|
||||||
self._write_string(function)
|
self._write_string(function)
|
||||||
else:
|
else:
|
||||||
exn_type = type(exn)
|
exn_type = type(exn)
|
||||||
if exn_type in (ZeroDivisionError, ValueError, IndexError) or \
|
if exn_type in (ZeroDivisionError, ValueError, IndexError, RuntimeError) or \
|
||||||
hasattr(exn, "artiq_builtin"):
|
hasattr(exn, "artiq_builtin"):
|
||||||
self._write_string("0:{}".format(exn_type.__name__))
|
self._write_string("0:{}".format(exn_type.__name__))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -172,8 +172,8 @@ class DDSChannel:
|
||||||
|
|
||||||
The time cursor is not modified by any function in this class.
|
The time cursor is not modified by any function in this class.
|
||||||
|
|
||||||
:param bus: name of the DDS bus device that this DDS is connected to.
|
:param bus_channel: RTIO channel number of the DDS bus.
|
||||||
:param channel: channel number of the DDS device to control.
|
:param channel: channel number (on the bus) of the DDS device to control.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kernel_invariants = {
|
kernel_invariants = {
|
||||||
|
@ -291,8 +291,8 @@ class DDSGroupAD9914(DDSGroup):
|
||||||
|
|
||||||
self.write_duration_mu = 5 * self.rtio_period_mu
|
self.write_duration_mu = 5 * self.rtio_period_mu
|
||||||
self.dac_cal_duration_mu = 147000 * self.rtio_period_mu
|
self.dac_cal_duration_mu = 147000 * self.rtio_period_mu
|
||||||
self.init_duration_mu = 8 * self.write_duration_mu + self.dac_cal_duration_mu
|
self.init_duration_mu = 9 * self.write_duration_mu + self.dac_cal_duration_mu
|
||||||
self.init_sync_duration_mu = 16 * self.write_duration_mu + 2 * self.dac_cal_duration_mu
|
self.init_sync_duration_mu = 17 * self.write_duration_mu + 2 * self.dac_cal_duration_mu
|
||||||
self.program_duration_mu = 6 * self.write_duration_mu
|
self.program_duration_mu = 6 * self.write_duration_mu
|
||||||
|
|
||||||
self.continuous_phase_comp = [0] * (self.dds_bus_count * self.dds_channel_count)
|
self.continuous_phase_comp = [0] * (self.dds_bus_count * self.dds_channel_count)
|
||||||
|
|
|
@ -10,6 +10,7 @@ from artiq.coredevice.runtime import source_loader
|
||||||
ZeroDivisionError = builtins.ZeroDivisionError
|
ZeroDivisionError = builtins.ZeroDivisionError
|
||||||
ValueError = builtins.ValueError
|
ValueError = builtins.ValueError
|
||||||
IndexError = builtins.IndexError
|
IndexError = builtins.IndexError
|
||||||
|
RuntimeError = builtins.RuntimeError
|
||||||
|
|
||||||
|
|
||||||
class CoreException:
|
class CoreException:
|
||||||
|
@ -71,8 +72,8 @@ class CacheError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class RTIOUnderflow(Exception):
|
class RTIOUnderflow(Exception):
|
||||||
"""Raised when the CPU fails to submit a RTIO event early enough
|
"""Raised when the CPU or DMA core fails to submit a RTIO event early
|
||||||
(with respect to the event's timestamp).
|
enough (with respect to the event's timestamp).
|
||||||
|
|
||||||
The offending event is discarded and the RTIO core keeps operating.
|
The offending event is discarded and the RTIO core keeps operating.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Config:
|
||||||
Exposes the configurable quantities of a single SAWG channel.
|
Exposes the configurable quantities of a single SAWG channel.
|
||||||
|
|
||||||
Access to the configuration registers for a SAWG channel can not
|
Access to the configuration registers for a SAWG channel can not
|
||||||
be concurrent. There must be at least :attr:_rtio_interval` machine
|
be concurrent. There must be at least :attr:`_rtio_interval` machine
|
||||||
units of delay between accesses. Replacement is not supported and will be
|
units of delay between accesses. Replacement is not supported and will be
|
||||||
lead to an ``RTIOCollision`` as this is likely a programming error.
|
lead to an ``RTIOCollision`` as this is likely a programming error.
|
||||||
All methods therefore advance the timeline by the duration of one
|
All methods therefore advance the timeline by the duration of one
|
||||||
|
|
|
@ -178,6 +178,7 @@ class Controllers:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def __setitem__(self, k, v):
|
def __setitem__(self, k, v):
|
||||||
|
try:
|
||||||
if (isinstance(v, dict) and v["type"] == "controller" and
|
if (isinstance(v, dict) and v["type"] == "controller" and
|
||||||
self.host_filter in get_ip_addresses(v["host"])):
|
self.host_filter in get_ip_addresses(v["host"])):
|
||||||
v["command"] = v["command"].format(name=k,
|
v["command"] = v["command"].format(name=k,
|
||||||
|
@ -185,6 +186,9 @@ class Controllers:
|
||||||
port=v["port"])
|
port=v["port"])
|
||||||
self.queue.put_nowait(("set", (k, v)))
|
self.queue.put_nowait(("set", (k, v)))
|
||||||
self.active_or_queued.add(k)
|
self.active_or_queued.add(k)
|
||||||
|
except:
|
||||||
|
logger.error("Failed to process device database entry %s", k,
|
||||||
|
exc_info=True)
|
||||||
|
|
||||||
def __delitem__(self, k):
|
def __delitem__(self, k):
|
||||||
if k in self.active_or_queued:
|
if k in self.active_or_queued:
|
||||||
|
|
|
@ -207,7 +207,8 @@ class _Tcube:
|
||||||
# derived classes must implement this
|
# derived classes must implement this
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
async def send_request(self, msgreq_id, wait_for_msgs, param1=0, param2=0, data=None):
|
async def send_request(self, msgreq_id, wait_for_msgs, param1=0, param2=0,
|
||||||
|
data=None):
|
||||||
await self.send(Message(msgreq_id, param1, param2, data=data))
|
await self.send(Message(msgreq_id, param1, param2, data=data))
|
||||||
msg = None
|
msg = None
|
||||||
while msg is None or msg.id not in wait_for_msgs:
|
while msg is None or msg.id not in wait_for_msgs:
|
||||||
|
@ -218,7 +219,7 @@ class _Tcube:
|
||||||
async def set_channel_enable_state(self, activated):
|
async def set_channel_enable_state(self, activated):
|
||||||
"""Enable or Disable channel 1.
|
"""Enable or Disable channel 1.
|
||||||
|
|
||||||
:param activated: 1 to enable channel, 2 to disable it.
|
:param activated: 1 to enable channel, 0 to disable it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if activated:
|
if activated:
|
||||||
|
@ -283,9 +284,13 @@ class _Tcube:
|
||||||
|
|
||||||
|
|
||||||
class Tpz(_Tcube):
|
class Tpz(_Tcube):
|
||||||
|
"""Either :py:meth:`set_tpz_io_settings()<Tpz.set_tpz_io_settings>`
|
||||||
|
or :py:meth:`get_tpz_io_settings()<Tpz.get_tpz_io_settings>` must
|
||||||
|
be completed to finish initialising the driver.
|
||||||
|
"""
|
||||||
def __init__(self, serial_dev):
|
def __init__(self, serial_dev):
|
||||||
_Tcube.__init__(self, serial_dev)
|
_Tcube.__init__(self, serial_dev)
|
||||||
self.voltage_limit = self.get_tpz_io_settings()[0]
|
self.voltage_limit = None
|
||||||
|
|
||||||
async def handle_message(self, msg):
|
async def handle_message(self, msg):
|
||||||
msg_id = msg.id
|
msg_id = msg.id
|
||||||
|
@ -348,9 +353,8 @@ class Tpz(_Tcube):
|
||||||
:param voltage: The output voltage applied to the piezo when operating
|
:param voltage: The output voltage applied to the piezo when operating
|
||||||
in open loop mode. The voltage value must be in range
|
in open loop mode. The voltage value must be in range
|
||||||
[0; voltage_limit]. Voltage_limit being set by the
|
[0; voltage_limit]. Voltage_limit being set by the
|
||||||
:py:meth:`set_tpz_io_settings()
|
:py:meth:`set_tpz_io_settings()<Tpz.set_tpz_io_settings>`
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tpz.set_tpz_io_settings>` method
|
method between the three values 75 V, 100 V and 150 V.
|
||||||
between the three values 75 V, 100 V and 150 V.
|
|
||||||
"""
|
"""
|
||||||
if voltage < 0 or voltage > self.voltage_limit:
|
if voltage < 0 or voltage > self.voltage_limit:
|
||||||
raise ValueError("Voltage must be in range [0;{}]"
|
raise ValueError("Voltage must be in range [0;{}]"
|
||||||
|
@ -404,7 +408,7 @@ class Tpz(_Tcube):
|
||||||
|
|
||||||
0x00 Software Only: Unit responds only to software inputs and the
|
0x00 Software Only: Unit responds only to software inputs and the
|
||||||
HV amp output is that set using the :py:meth:`set_output_volts()
|
HV amp output is that set using the :py:meth:`set_output_volts()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tpz.set_output_volts>` method.
|
<Tpz.set_output_volts>` method.
|
||||||
|
|
||||||
0x01 External Signal: Unit sums the differential signal on the rear
|
0x01 External Signal: Unit sums the differential signal on the rear
|
||||||
panel EXT IN(+) and EXT IN(-) connectors with the voltage set
|
panel EXT IN(+) and EXT IN(-) connectors with the voltage set
|
||||||
|
@ -426,9 +430,8 @@ class Tpz(_Tcube):
|
||||||
amplifier circuit.
|
amplifier circuit.
|
||||||
|
|
||||||
:return: Value which selects the various analog sources, cf.
|
:return: Value which selects the various analog sources, cf.
|
||||||
:py:meth:`set_input_volts_source()
|
:py:meth:`set_input_volts_source()<Tpz.set_input_volts_source>`
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tpz.set_input_volts_source>` method
|
method docstring for meaning of bits.
|
||||||
docstring for meaning of bits.
|
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
get_msg = await self.send_request(MGMSG.PZ_REQ_INPUTVOLTSSRC,
|
get_msg = await self.send_request(MGMSG.PZ_REQ_INPUTVOLTSSRC,
|
||||||
|
@ -488,7 +491,7 @@ class Tpz(_Tcube):
|
||||||
applicable channel is specified by the Chan Ident parameter If only a
|
applicable channel is specified by the Chan Ident parameter If only a
|
||||||
sub set of the array is being used (as specified by the cyclelength
|
sub set of the array is being used (as specified by the cyclelength
|
||||||
parameter of the :py:meth:`set_output_lut_parameters()
|
parameter of the :py:meth:`set_output_lut_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tpz.set_output_lut_parameters>`
|
<Tpz.set_output_lut_parameters>`
|
||||||
function), then only the first cyclelength values need to be set. In
|
function), then only the first cyclelength values need to be set. In
|
||||||
this manner, any arbitrary voltage waveform can be programmed into the
|
this manner, any arbitrary voltage waveform can be programmed into the
|
||||||
LUT. Note. The LUT values are output by the system at a maximum
|
LUT. Note. The LUT values are output by the system at a maximum
|
||||||
|
@ -499,8 +502,8 @@ class Tpz(_Tcube):
|
||||||
to 512 for TPZ).
|
to 512 for TPZ).
|
||||||
:param output: The voltage value to be set. Values are in the range
|
:param output: The voltage value to be set. Values are in the range
|
||||||
[0; voltage_limit]. Voltage_limit being set with the
|
[0; voltage_limit]. Voltage_limit being set with the
|
||||||
:py:meth:`set_tpz_io_settings
|
:py:meth:`set_tpz_io_settings<Tpz.set_tpz_io_settings>`
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tpz.set_tpz_io_settings>` method.
|
method.
|
||||||
"""
|
"""
|
||||||
volt = round(output*32767/self.voltage_limit)
|
volt = round(output*32767/self.voltage_limit)
|
||||||
payload = st.pack("<HHH", 1, lut_index, volt)
|
payload = st.pack("<HHH", 1, lut_index, volt)
|
||||||
|
@ -519,7 +522,8 @@ class Tpz(_Tcube):
|
||||||
return index, output*self.voltage_limit/32767
|
return index, output*self.voltage_limit/32767
|
||||||
|
|
||||||
async def set_output_lut_parameters(self, mode, cycle_length, num_cycles,
|
async def set_output_lut_parameters(self, mode, cycle_length, num_cycles,
|
||||||
delay_time, precycle_rest, postcycle_rest):
|
delay_time, precycle_rest,
|
||||||
|
postcycle_rest):
|
||||||
"""Set Waveform Generator Mode parameters.
|
"""Set Waveform Generator Mode parameters.
|
||||||
|
|
||||||
It is possible to use the controller in an arbitrary Waveform
|
It is possible to use the controller in an arbitrary Waveform
|
||||||
|
@ -680,8 +684,8 @@ class Tpz(_Tcube):
|
||||||
|
|
||||||
:return: Returns a tuple whose elements are the voltage limit and the
|
:return: Returns a tuple whose elements are the voltage limit and the
|
||||||
Hub analog input. Refer to :py:meth:`set_tpz_io_settings()
|
Hub analog input. Refer to :py:meth:`set_tpz_io_settings()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tpz.set_tpz_io_settings>` for the
|
<Tpz.set_tpz_io_settings>` for
|
||||||
meaning of those parameters.
|
the meaning of those parameters.
|
||||||
:rtype: a 2 elements tuple (int, int)
|
:rtype: a 2 elements tuple (int, int)
|
||||||
"""
|
"""
|
||||||
get_msg = await self.send_request(MGMSG.PZ_REQ_TPZ_IOSETTINGS,
|
get_msg = await self.send_request(MGMSG.PZ_REQ_TPZ_IOSETTINGS,
|
||||||
|
@ -760,8 +764,7 @@ class Tdc(_Tcube):
|
||||||
|
|
||||||
:return: An 8 int tuple containing the following values: zero_wnd,
|
:return: An 8 int tuple containing the following values: zero_wnd,
|
||||||
vel1, wnd1, vel2, wnd2, vel3, wnd3, vel4. See
|
vel1, wnd1, vel2, wnd2, vel3, wnd3, vel4. See
|
||||||
:py:meth:`set_pot_parameters()
|
:py:meth:`set_pot_parameters()<Tdc.set_pot_parameters>` for a
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_pot_parameters>` for a
|
|
||||||
description of each tuple element meaning.
|
description of each tuple element meaning.
|
||||||
:rtype: An 8 int tuple
|
:rtype: An 8 int tuple
|
||||||
"""
|
"""
|
||||||
|
@ -950,7 +953,8 @@ class Tdc(_Tcube):
|
||||||
This call is blocking until device is homed or move is stopped.
|
This call is blocking until device is homed or move is stopped.
|
||||||
"""
|
"""
|
||||||
await self.send_request(MGMSG.MOT_MOVE_HOME,
|
await self.send_request(MGMSG.MOT_MOVE_HOME,
|
||||||
[MGMSG.MOT_MOVE_HOMED, MGMSG.MOT_MOVE_STOPPED], 1)
|
[MGMSG.MOT_MOVE_HOMED, MGMSG.MOT_MOVE_STOPPED],
|
||||||
|
1)
|
||||||
|
|
||||||
async def set_limit_switch_parameters(self, cw_hw_limit, ccw_hw_limit):
|
async def set_limit_switch_parameters(self, cw_hw_limit, ccw_hw_limit):
|
||||||
"""Set the limit switch parameters.
|
"""Set the limit switch parameters.
|
||||||
|
@ -988,7 +992,7 @@ class Tdc(_Tcube):
|
||||||
:return: A 2 int tuple containing the following in order: cw_hw_limit,
|
:return: A 2 int tuple containing the following in order: cw_hw_limit,
|
||||||
ccw_hw_limit. Cf. description in
|
ccw_hw_limit. Cf. description in
|
||||||
:py:meth:`set_limit_switch_parameters()
|
:py:meth:`set_limit_switch_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_limit_switch_parameters>`
|
<Tdc.set_limit_switch_parameters>`
|
||||||
method.
|
method.
|
||||||
:rtype: A 2 int tuple.
|
:rtype: A 2 int tuple.
|
||||||
"""
|
"""
|
||||||
|
@ -1001,11 +1005,13 @@ class Tdc(_Tcube):
|
||||||
|
|
||||||
The relative distance parameter used for the move will be the parameter
|
The relative distance parameter used for the move will be the parameter
|
||||||
sent previously by a :py:meth:`set_move_relative_parameters()
|
sent previously by a :py:meth:`set_move_relative_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_move_relative_parameters>`
|
<Tdc.set_move_relative_parameters>`
|
||||||
command.
|
command.
|
||||||
"""
|
"""
|
||||||
await self.send_request(MGMSG.MOT_MOVE_RELATIVE,
|
await self.send_request(MGMSG.MOT_MOVE_RELATIVE,
|
||||||
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED], 1)
|
[MGMSG.MOT_MOVE_COMPLETED,
|
||||||
|
MGMSG.MOT_MOVE_STOPPED],
|
||||||
|
1)
|
||||||
|
|
||||||
async def move_relative(self, relative_distance):
|
async def move_relative(self, relative_distance):
|
||||||
"""Start a relative move
|
"""Start a relative move
|
||||||
|
@ -1015,7 +1021,8 @@ class Tdc(_Tcube):
|
||||||
"""
|
"""
|
||||||
payload = st.pack("<Hl", 1, relative_distance)
|
payload = st.pack("<Hl", 1, relative_distance)
|
||||||
await self.send_request(MGMSG.MOT_MOVE_RELATIVE,
|
await self.send_request(MGMSG.MOT_MOVE_RELATIVE,
|
||||||
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED],
|
[MGMSG.MOT_MOVE_COMPLETED,
|
||||||
|
MGMSG.MOT_MOVE_STOPPED],
|
||||||
data=payload)
|
data=payload)
|
||||||
|
|
||||||
async def move_absolute_memory(self):
|
async def move_absolute_memory(self):
|
||||||
|
@ -1023,11 +1030,12 @@ class Tdc(_Tcube):
|
||||||
|
|
||||||
The absolute move position parameter used for the move will be the
|
The absolute move position parameter used for the move will be the
|
||||||
parameter sent previously by a :py:meth:`set_move_absolute_parameters()
|
parameter sent previously by a :py:meth:`set_move_absolute_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_move_absolute_parameters>`
|
<Tdc.set_move_absolute_parameters>`
|
||||||
command.
|
command.
|
||||||
"""
|
"""
|
||||||
await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE,
|
await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE,
|
||||||
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED],
|
[MGMSG.MOT_MOVE_COMPLETED,
|
||||||
|
MGMSG.MOT_MOVE_STOPPED],
|
||||||
param1=1)
|
param1=1)
|
||||||
|
|
||||||
async def move_absolute(self, absolute_distance):
|
async def move_absolute(self, absolute_distance):
|
||||||
|
@ -1039,7 +1047,8 @@ class Tdc(_Tcube):
|
||||||
"""
|
"""
|
||||||
payload = st.pack("<Hl", 1, absolute_distance)
|
payload = st.pack("<Hl", 1, absolute_distance)
|
||||||
await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE,
|
await self.send_request(MGMSG.MOT_MOVE_ABSOLUTE,
|
||||||
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED],
|
[MGMSG.MOT_MOVE_COMPLETED,
|
||||||
|
MGMSG.MOT_MOVE_STOPPED],
|
||||||
data=payload)
|
data=payload)
|
||||||
|
|
||||||
async def move_jog(self, direction):
|
async def move_jog(self, direction):
|
||||||
|
@ -1048,7 +1057,8 @@ class Tdc(_Tcube):
|
||||||
:param direction: The direction to jog. 1 is forward, 2 is backward.
|
:param direction: The direction to jog. 1 is forward, 2 is backward.
|
||||||
"""
|
"""
|
||||||
await self.send_request(MGMSG.MOT_MOVE_JOG,
|
await self.send_request(MGMSG.MOT_MOVE_JOG,
|
||||||
[MGMSG.MOT_MOVE_COMPLETED, MGMSG.MOT_MOVE_STOPPED],
|
[MGMSG.MOT_MOVE_COMPLETED,
|
||||||
|
MGMSG.MOT_MOVE_STOPPED],
|
||||||
param1=1, param2=direction)
|
param1=1, param2=direction)
|
||||||
|
|
||||||
async def move_velocity(self, direction):
|
async def move_velocity(self, direction):
|
||||||
|
@ -1057,15 +1067,15 @@ class Tdc(_Tcube):
|
||||||
When this method is called, the motor will move continuously in the
|
When this method is called, the motor will move continuously in the
|
||||||
specified direction using the velocity parameter set by the
|
specified direction using the velocity parameter set by the
|
||||||
:py:meth:`set_move_relative_parameters()
|
:py:meth:`set_move_relative_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_move_relative_parameters>`
|
<Tdc.set_move_relative_parameters>`
|
||||||
command until a :py:meth:`move_stop()
|
command until a :py:meth:`move_stop()<Tdc.move_stop>` command (either
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.move_stop>` command (either
|
|
||||||
StopImmediate or StopProfiled) is called, or a limit switch is reached.
|
StopImmediate or StopProfiled) is called, or a limit switch is reached.
|
||||||
|
|
||||||
:param direction: The direction to jog: 1 to move forward, 2 to move
|
:param direction: The direction to jog: 1 to move forward, 2 to move
|
||||||
backward.
|
backward.
|
||||||
"""
|
"""
|
||||||
await self.send(Message(MGMSG.MOT_MOVE_VELOCITY, param1=1, param2=direction))
|
await self.send(Message(MGMSG.MOT_MOVE_VELOCITY, param1=1,
|
||||||
|
param2=direction))
|
||||||
|
|
||||||
async def move_stop(self, stop_mode):
|
async def move_stop(self, stop_mode):
|
||||||
"""Stop any type of motor move.
|
"""Stop any type of motor move.
|
||||||
|
@ -1108,8 +1118,8 @@ class Tdc(_Tcube):
|
||||||
:return: A 5 int tuple containing in this order:
|
:return: A 5 int tuple containing in this order:
|
||||||
proportional gain, integral gain, differential gain, integral limit
|
proportional gain, integral gain, differential gain, integral limit
|
||||||
and filter control. Cf. :py:meth:`set_dc_pid_parameters()
|
and filter control. Cf. :py:meth:`set_dc_pid_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_dc_pid_parameters>` for
|
<Tdc.set_dc_pid_parameters>`
|
||||||
precise description.
|
for precise description.
|
||||||
:rtype: A 5 int tuple.
|
:rtype: A 5 int tuple.
|
||||||
"""
|
"""
|
||||||
get_msg = await self.send_request(MGMSG.MOT_REQ_DCPIDPARAMS,
|
get_msg = await self.send_request(MGMSG.MOT_REQ_DCPIDPARAMS,
|
||||||
|
@ -1151,10 +1161,10 @@ class Tdc(_Tcube):
|
||||||
:param mode: If set to 1, the buttons are used to jog the motor. Once
|
:param mode: If set to 1, the buttons are used to jog the motor. Once
|
||||||
set to this mode, the move parameters for the buttons are taken
|
set to this mode, the move parameters for the buttons are taken
|
||||||
from the arguments of the :py:meth:`set_jog_parameters()
|
from the arguments of the :py:meth:`set_jog_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_jog_parameters>` method.
|
<Tdc.set_jog_parameters>`
|
||||||
If set to 2, each button can be programmed with a differente
|
method. If set to 2, each button can be programmed with a
|
||||||
position value such that the controller will move the motor to that
|
differente position value such that the controller will move the
|
||||||
position when the specific button is pressed.
|
motor to that position when the specific button is pressed.
|
||||||
:param position1: The position (in encoder counts) to which the motor
|
:param position1: The position (in encoder counts) to which the motor
|
||||||
will move when the top button is pressed.
|
will move when the top button is pressed.
|
||||||
:param position2: The position (in encoder counts) to which the motor
|
:param position2: The position (in encoder counts) to which the motor
|
||||||
|
@ -1169,8 +1179,8 @@ class Tdc(_Tcube):
|
||||||
|
|
||||||
:return: A 3 int tuple containing in this order: button mode,
|
:return: A 3 int tuple containing in this order: button mode,
|
||||||
position1 and position2. Cf. :py:meth:`set_button_parameters()
|
position1 and position2. Cf. :py:meth:`set_button_parameters()
|
||||||
<artiq.devices.thorlabs_tcube.driver.Tdc.set_button_parameters>` for
|
<Tdc.set_button_parameters>`
|
||||||
description.
|
for description.
|
||||||
:rtype: A 3 int tuple
|
:rtype: A 3 int tuple
|
||||||
"""
|
"""
|
||||||
get_msg = await self.send_request(MGMSG.MOT_REQ_BUTTONPARAMS,
|
get_msg = await self.send_request(MGMSG.MOT_REQ_BUTTONPARAMS,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# The RTIO channel numbers here are for NIST CLOCK on KC705.
|
# The RTIO channel numbers here are for NIST CLOCK on KC705.
|
||||||
# The list of devices here is not exhaustive.
|
# The list of devices here is not exhaustive.
|
||||||
|
|
||||||
core_addr = "kc705.lab.m-labs.hk"
|
core_addr = "kc705-1.lab.m-labs.hk"
|
||||||
|
|
||||||
device_db = {
|
device_db = {
|
||||||
"core": {
|
"core": {
|
||||||
|
@ -33,7 +33,7 @@ device_db = {
|
||||||
"class": "DDSGroupAD9914",
|
"class": "DDSGroupAD9914",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"sysclk": 3e9,
|
"sysclk": 3e9,
|
||||||
"first_dds_bus_channel": 26,
|
"first_dds_bus_channel": 27,
|
||||||
"dds_bus_count": 2,
|
"dds_bus_count": 2,
|
||||||
"dds_channel_count": 3
|
"dds_channel_count": 3
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class TDR(EnvExperiment):
|
||||||
try:
|
try:
|
||||||
self.many(n, self.core.seconds_to_mu(pulse))
|
self.many(n, self.core.seconds_to_mu(pulse))
|
||||||
except PulseNotReceivedError:
|
except PulseNotReceivedError:
|
||||||
print("to few edges: cable too long or wiring bad")
|
print("too few edges: cable too long or wiring bad")
|
||||||
else:
|
else:
|
||||||
print(self.t)
|
print(self.t)
|
||||||
t_rise = mu_to_seconds(self.t[0], self.core)/n - latency
|
t_rise = mu_to_seconds(self.t[0], self.core)/n - latency
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
[root]
|
|
||||||
name = "std_artiq"
|
|
||||||
version = "0.0.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloc_list"
|
name = "alloc_list"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -17,23 +13,38 @@ dependencies = [
|
||||||
"board 0.0.0",
|
"board 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace_artiq"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "board"
|
name = "board"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "build_artiq"
|
name = "build_artiq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.0.0"
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -41,7 +52,7 @@ name = "compiler_builtins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568#631b5687b24af413fdbffa4c2644484e60660b00"
|
source = "git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568#631b5687b24af413fdbffa4c2644484e60660b00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -55,8 +66,8 @@ name = "drtioaux"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"board 0.0.0",
|
"board 0.0.0",
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -64,17 +75,24 @@ dependencies = [
|
||||||
name = "dyld"
|
name = "dyld"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eh"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fringe"
|
name = "fringe"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "git+https://github.com/m-labs/libfringe?rev=bd23494#bd2349467157969324ca7da5d2ae033c7ffac0c0"
|
source = "git+https://github.com/m-labs/libfringe?rev=b8a6d8f#b8a6d8f68df0edaa3d67d9f3b7b62af9d3bb64a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcc"
|
name = "gcc"
|
||||||
version = "0.3.42"
|
version = "0.3.54"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -94,26 +112,35 @@ dependencies = [
|
||||||
"amp 0.0.0",
|
"amp 0.0.0",
|
||||||
"board 0.0.0",
|
"board 0.0.0",
|
||||||
"build_artiq 0.0.0",
|
"build_artiq 0.0.0",
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dyld 0.0.0",
|
"dyld 0.0.0",
|
||||||
|
"eh 0.0.0",
|
||||||
"proto 0.0.0",
|
"proto 0.0.0",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.18"
|
version = "0.2.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.3.6"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log_buffer"
|
name = "log_buffer"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -121,23 +148,23 @@ name = "logger_artiq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"board 0.0.0",
|
"board 0.0.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "managed"
|
name = "managed"
|
||||||
version = "0.4.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proto"
|
name = "proto"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dyld 0.0.0",
|
"dyld 0.0.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -147,17 +174,18 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloc_list 0.0.0",
|
"alloc_list 0.0.0",
|
||||||
"amp 0.0.0",
|
"amp 0.0.0",
|
||||||
|
"backtrace_artiq 0.0.0",
|
||||||
"board 0.0.0",
|
"board 0.0.0",
|
||||||
"build_artiq 0.0.0",
|
"build_artiq 0.0.0",
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)",
|
|
||||||
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"drtioaux 0.0.0",
|
"drtioaux 0.0.0",
|
||||||
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)",
|
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"logger_artiq 0.0.0",
|
"logger_artiq 0.0.0",
|
||||||
|
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proto 0.0.0",
|
"proto 0.0.0",
|
||||||
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=6f5ae33)",
|
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=92e970b)",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -166,6 +194,15 @@ name = "rustc-cfg"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "satman"
|
name = "satman"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -175,7 +212,7 @@ dependencies = [
|
||||||
"build_artiq 0.0.0",
|
"build_artiq 0.0.0",
|
||||||
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)",
|
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)",
|
||||||
"drtioaux 0.0.0",
|
"drtioaux 0.0.0",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"logger_artiq 0.0.0",
|
"logger_artiq 0.0.0",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
@ -183,19 +220,25 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/m-labs/smoltcp?rev=6f5ae33#6f5ae33501827d57926469c6f1a860205a24f7ae"
|
source = "git+https://github.com/m-labs/smoltcp?rev=92e970b#92e970b3798737cfaa2a829d3f1bb7a7353696ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"managed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "std_artiq"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "1.0.3"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -210,18 +253,22 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
|
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||||
|
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
||||||
|
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
|
||||||
"checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)" = "<none>"
|
"checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)" = "<none>"
|
||||||
"checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
|
"checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
|
||||||
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)" = "<none>"
|
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)" = "<none>"
|
||||||
"checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0"
|
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70"
|
"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
|
||||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
|
||||||
"checksum log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec57723b84bbe7bdf76aa93169c9b59e67473317c6de3a83cb2a0f8ccb2aa493"
|
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
|
||||||
"checksum managed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d48e8c30a4363e2981fe4db20527f6ab0f32a243bbc75379dea5a64f60dae4"
|
"checksum log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f033173c9486b7fe97a79c895c0a3483ae395ab6744c985d10078950e2492419"
|
||||||
|
"checksum managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6713e624266d7600e9feae51b1926c6a6a6bebb18ec5a8e11a5f1d5661baba"
|
||||||
"checksum rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a596b5718bf5e059d59a30af12f7f462a152de147aa462b70892849ee18704"
|
"checksum rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a596b5718bf5e059d59a30af12f7f462a152de147aa462b70892849ee18704"
|
||||||
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=6f5ae33)" = "<none>"
|
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
|
||||||
"checksum walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7c16466ecc507c7cb5988db03e6eab4aaeab89a5c37a29251fcfd3ac9b7afe"
|
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=92e970b)" = "<none>"
|
||||||
|
"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff"
|
||||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["runtime", "ksupport", "satman"]
|
members = ["runtime", "ksupport", "satman"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
incremental = false # incompatible with LTO
|
||||||
|
lto = true
|
||||||
|
debug = 2
|
||||||
|
|
|
@ -17,6 +17,7 @@ byteorder = { version = "1.0", default-features = false }
|
||||||
cslice = { version = "0.3" }
|
cslice = { version = "0.3" }
|
||||||
alloc_stub = { path = "../liballoc_stub" }
|
alloc_stub = { path = "../liballoc_stub" }
|
||||||
std_artiq = { path = "../libstd_artiq" }
|
std_artiq = { path = "../libstd_artiq" }
|
||||||
|
eh = { path = "../libeh" }
|
||||||
dyld = { path = "../libdyld" }
|
dyld = { path = "../libdyld" }
|
||||||
board = { path = "../libboard" }
|
board = { path = "../libboard" }
|
||||||
proto = { path = "../libproto" }
|
proto = { path = "../libproto" }
|
||||||
|
|
|
@ -5,32 +5,25 @@ CFLAGS += \
|
||||||
-I$(LIBUNWIND_DIRECTORY) \
|
-I$(LIBUNWIND_DIRECTORY) \
|
||||||
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include \
|
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include \
|
||||||
-I$(MISOC_DIRECTORY)/software/include/dyld
|
-I$(MISOC_DIRECTORY)/software/include/dyld
|
||||||
CFLAGS += -DNDEBUG
|
|
||||||
|
|
||||||
LDFLAGS += --eh-frame-hdr \
|
LDFLAGS += --eh-frame-hdr \
|
||||||
-L../libcompiler-rt \
|
-L../libcompiler-rt \
|
||||||
-L../libbase \
|
-L../libbase \
|
||||||
|
-L../libprintf \
|
||||||
-L../libm \
|
-L../libm \
|
||||||
-L../libunwind
|
-L../libunwind
|
||||||
|
|
||||||
RUSTFLAGS += -Cpanic=unwind
|
RUSTFLAGS += -Cpanic=unwind
|
||||||
|
|
||||||
all: ksupport.elf
|
all:: ksupport.elf
|
||||||
|
|
||||||
.PHONY: $(RUSTOUT)/libksupport.a
|
.PHONY: $(RUSTOUT)/libksupport.a
|
||||||
$(RUSTOUT)/libksupport.a:
|
$(RUSTOUT)/libksupport.a:
|
||||||
$(cargo) --manifest-path $(KSUPPORT_DIRECTORY)/Cargo.toml
|
$(cargo) --manifest-path $(KSUPPORT_DIRECTORY)/Cargo.toml
|
||||||
|
|
||||||
ksupport.elf: $(RUSTOUT)/libksupport.a glue.o
|
ksupport.elf: $(RUSTOUT)/libksupport.a glue.o
|
||||||
$(LD) $(LDFLAGS) -T $(KSUPPORT_DIRECTORY)/ksupport.ld -o $@ $^ \
|
$(link) -T $(KSUPPORT_DIRECTORY)/ksupport.ld \
|
||||||
-lunwind -lcompiler-rt -lbase -lm
|
-lunwind-elf -lcompiler-rt -lprintf-float -lm
|
||||||
@chmod -x $@
|
|
||||||
|
|
||||||
%.o: $(KSUPPORT_DIRECTORY)/%.c
|
%.o: $(KSUPPORT_DIRECTORY)/%.c
|
||||||
$(compile)
|
$(compile)
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) *.o ksupport.elf
|
|
||||||
$(RM) -rf cargo
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
|
@ -75,9 +75,9 @@ static mut API: &'static [(&'static str, *const ())] = &[
|
||||||
|
|
||||||
/* exceptions */
|
/* exceptions */
|
||||||
api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
|
api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
|
||||||
api!(__artiq_personality = ::eh::personality),
|
api!(__artiq_personality = ::eh_artiq::personality),
|
||||||
api!(__artiq_raise = ::eh::raise),
|
api!(__artiq_raise = ::eh_artiq::raise),
|
||||||
api!(__artiq_reraise = ::eh::reraise),
|
api!(__artiq_reraise = ::eh_artiq::reraise),
|
||||||
|
|
||||||
/* proxified syscalls */
|
/* proxified syscalls */
|
||||||
api!(core_log),
|
api!(core_log),
|
||||||
|
|
|
@ -1,460 +0,0 @@
|
||||||
// Portions of the code in this file are derived from code by:
|
|
||||||
//
|
|
||||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
#![allow(non_upper_case_globals, non_camel_case_types, dead_code)]
|
|
||||||
|
|
||||||
use core::{ptr, mem};
|
|
||||||
use cslice::CSlice;
|
|
||||||
use unwind as uw;
|
|
||||||
use libc::{c_int, c_void};
|
|
||||||
|
|
||||||
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
|
||||||
actions: uw::_Unwind_Action,
|
|
||||||
exception_class: uw::_Unwind_Exception_Class,
|
|
||||||
exception_object: *mut uw::_Unwind_Exception,
|
|
||||||
context: *mut uw::_Unwind_Context,
|
|
||||||
stop_parameter: *mut c_void)
|
|
||||||
-> uw::_Unwind_Reason_Code;
|
|
||||||
extern {
|
|
||||||
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
|
|
||||||
stop_fn: _Unwind_Stop_Fn,
|
|
||||||
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DW_EH_PE_omit: u8 = 0xFF;
|
|
||||||
const DW_EH_PE_absptr: u8 = 0x00;
|
|
||||||
|
|
||||||
const DW_EH_PE_uleb128: u8 = 0x01;
|
|
||||||
const DW_EH_PE_udata2: u8 = 0x02;
|
|
||||||
const DW_EH_PE_udata4: u8 = 0x03;
|
|
||||||
const DW_EH_PE_udata8: u8 = 0x04;
|
|
||||||
const DW_EH_PE_sleb128: u8 = 0x09;
|
|
||||||
const DW_EH_PE_sdata2: u8 = 0x0A;
|
|
||||||
const DW_EH_PE_sdata4: u8 = 0x0B;
|
|
||||||
const DW_EH_PE_sdata8: u8 = 0x0C;
|
|
||||||
|
|
||||||
const DW_EH_PE_pcrel: u8 = 0x10;
|
|
||||||
const DW_EH_PE_textrel: u8 = 0x20;
|
|
||||||
const DW_EH_PE_datarel: u8 = 0x30;
|
|
||||||
const DW_EH_PE_funcrel: u8 = 0x40;
|
|
||||||
const DW_EH_PE_aligned: u8 = 0x50;
|
|
||||||
|
|
||||||
const DW_EH_PE_indirect: u8 = 0x80;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct DwarfReader {
|
|
||||||
pub ptr: *const u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C,packed)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct Unaligned<T>(T);
|
|
||||||
|
|
||||||
// This contortion is required due to https://github.com/rust-lang/rust/issues/27060.
|
|
||||||
impl<T> Unaligned<T> {
|
|
||||||
fn get(self) -> T { self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DwarfReader {
|
|
||||||
fn new(ptr: *const u8) -> DwarfReader {
|
|
||||||
DwarfReader { ptr: ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
|
|
||||||
// on a 4-byte boundary. This may cause problems on platforms with strict
|
|
||||||
// alignment requirements. By wrapping data in a "packed" struct, we are
|
|
||||||
// telling the backend to generate "misalignment-safe" code.
|
|
||||||
unsafe fn read<T: Copy>(&mut self) -> T {
|
|
||||||
let result = *(self.ptr as *const Unaligned<T>);
|
|
||||||
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
|
|
||||||
result.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
|
|
||||||
// Length Data".
|
|
||||||
unsafe fn read_uleb128(&mut self) -> u64 {
|
|
||||||
let mut shift: usize = 0;
|
|
||||||
let mut result: u64 = 0;
|
|
||||||
let mut byte: u8;
|
|
||||||
loop {
|
|
||||||
byte = self.read::<u8>();
|
|
||||||
result |= ((byte & 0x7F) as u64) << shift;
|
|
||||||
shift += 7;
|
|
||||||
if byte & 0x80 == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn read_sleb128(&mut self) -> i64 {
|
|
||||||
let mut shift: usize = 0;
|
|
||||||
let mut result: u64 = 0;
|
|
||||||
let mut byte: u8;
|
|
||||||
loop {
|
|
||||||
byte = self.read::<u8>();
|
|
||||||
result |= ((byte & 0x7F) as u64) << shift;
|
|
||||||
shift += 7;
|
|
||||||
if byte & 0x80 == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// sign-extend
|
|
||||||
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
|
|
||||||
result |= (!0 as u64) << shift;
|
|
||||||
}
|
|
||||||
result as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
|
|
||||||
fn round_up(unrounded: usize, align: usize) -> usize {
|
|
||||||
debug_assert!(align.is_power_of_two());
|
|
||||||
(unrounded + align - 1) & !(align - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert!(encoding != DW_EH_PE_omit);
|
|
||||||
|
|
||||||
// DW_EH_PE_aligned implies it's an absolute pointer value
|
|
||||||
if encoding == DW_EH_PE_aligned {
|
|
||||||
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
|
|
||||||
return self.read::<usize>()
|
|
||||||
}
|
|
||||||
|
|
||||||
let value_ptr = self.ptr;
|
|
||||||
let mut result = match encoding & 0x0F {
|
|
||||||
DW_EH_PE_absptr => self.read::<usize>(),
|
|
||||||
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
|
|
||||||
DW_EH_PE_udata2 => self.read::<u16>() as usize,
|
|
||||||
DW_EH_PE_udata4 => self.read::<u32>() as usize,
|
|
||||||
DW_EH_PE_udata8 => self.read::<u64>() as usize,
|
|
||||||
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
|
|
||||||
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
|
|
||||||
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
|
|
||||||
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
result += match encoding & 0x70 {
|
|
||||||
DW_EH_PE_absptr => 0,
|
|
||||||
// relative to address of the encoded value, despite the name
|
|
||||||
DW_EH_PE_pcrel => value_ptr as usize,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if encoding & DW_EH_PE_indirect != 0 {
|
|
||||||
result = *(result as *const usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encoding_size(encoding: u8) -> usize {
|
|
||||||
if encoding == DW_EH_PE_omit {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
match encoding & 0x0F {
|
|
||||||
DW_EH_PE_absptr => mem::size_of::<usize>(),
|
|
||||||
DW_EH_PE_udata2 => 2,
|
|
||||||
DW_EH_PE_udata4 => 4,
|
|
||||||
DW_EH_PE_udata8 => 8,
|
|
||||||
DW_EH_PE_sdata2 => 2,
|
|
||||||
DW_EH_PE_sdata4 => 4,
|
|
||||||
DW_EH_PE_sdata8 => 8,
|
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum EHAction {
|
|
||||||
None,
|
|
||||||
Cleanup(usize),
|
|
||||||
Catch(usize),
|
|
||||||
Terminate,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
|
|
||||||
exn_name: CSlice<u8>) -> EHAction {
|
|
||||||
if lsda.is_null() {
|
|
||||||
return EHAction::None
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut reader = DwarfReader::new(lsda);
|
|
||||||
|
|
||||||
let start_encoding = reader.read::<u8>();
|
|
||||||
// base address for landing pad offsets
|
|
||||||
let lpad_base = if start_encoding != DW_EH_PE_omit {
|
|
||||||
reader.read_encoded_pointer(start_encoding)
|
|
||||||
} else {
|
|
||||||
func_start
|
|
||||||
};
|
|
||||||
|
|
||||||
let ttype_encoding = reader.read::<u8>();
|
|
||||||
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
|
|
||||||
|
|
||||||
let class_info;
|
|
||||||
if ttype_encoding != DW_EH_PE_omit {
|
|
||||||
let class_info_offset = reader.read_uleb128();
|
|
||||||
class_info = reader.ptr.offset(class_info_offset as isize);
|
|
||||||
} else {
|
|
||||||
class_info = ptr::null();
|
|
||||||
}
|
|
||||||
assert!(!class_info.is_null());
|
|
||||||
|
|
||||||
let call_site_encoding = reader.read::<u8>();
|
|
||||||
let call_site_table_length = reader.read_uleb128();
|
|
||||||
let action_table = reader.ptr.offset(call_site_table_length as isize);
|
|
||||||
|
|
||||||
while reader.ptr < action_table {
|
|
||||||
let cs_start = reader.read_encoded_pointer(call_site_encoding);
|
|
||||||
let cs_len = reader.read_encoded_pointer(call_site_encoding);
|
|
||||||
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
|
|
||||||
let cs_action = reader.read_uleb128();
|
|
||||||
|
|
||||||
if ip < func_start + cs_start {
|
|
||||||
// Callsite table is sorted by cs_start, so if we've passed the ip, we
|
|
||||||
// may stop searching.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if ip > func_start + cs_start + cs_len {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if cs_lpad == 0 {
|
|
||||||
return EHAction::None
|
|
||||||
}
|
|
||||||
|
|
||||||
let lpad = lpad_base + cs_lpad;
|
|
||||||
if cs_action == 0 {
|
|
||||||
return EHAction::Cleanup(lpad)
|
|
||||||
}
|
|
||||||
|
|
||||||
let action_entry = action_table.offset((cs_action - 1) as isize);
|
|
||||||
let mut action_reader = DwarfReader::new(action_entry);
|
|
||||||
loop {
|
|
||||||
let type_info_offset = action_reader.read_sleb128() as isize;
|
|
||||||
let action_offset = action_reader.clone().read_sleb128() as isize;
|
|
||||||
assert!(type_info_offset >= 0);
|
|
||||||
|
|
||||||
if type_info_offset > 0 {
|
|
||||||
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
|
|
||||||
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
|
|
||||||
.read_encoded_pointer(ttype_encoding);
|
|
||||||
let type_info = *(type_info_ptr as *const CSlice<u8>);
|
|
||||||
|
|
||||||
if type_info.as_ref() == exn_name.as_ref() {
|
|
||||||
return EHAction::Catch(lpad)
|
|
||||||
}
|
|
||||||
|
|
||||||
if type_info.len() == 0 {
|
|
||||||
// This is a catch-all clause. We don't compare type_info_ptr with null here
|
|
||||||
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
|
|
||||||
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
|
|
||||||
// a proper null pointer.
|
|
||||||
return EHAction::Catch(lpad)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if action_offset == 0 {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
action_reader.ptr = action_reader.ptr.offset(action_offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EHAction::None
|
|
||||||
}
|
|
||||||
|
|
||||||
// the function has a personality but no landing pads; this is fine
|
|
||||||
EHAction::None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Exception<'a> {
|
|
||||||
pub name: CSlice<'a, u8>,
|
|
||||||
pub file: CSlice<'a, u8>,
|
|
||||||
pub line: u32,
|
|
||||||
pub column: u32,
|
|
||||||
pub function: CSlice<'a, u8>,
|
|
||||||
pub message: CSlice<'a, u8>,
|
|
||||||
pub param: [i64; 3]
|
|
||||||
}
|
|
||||||
|
|
||||||
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
|
|
||||||
|
|
||||||
const MAX_BACKTRACE_SIZE: usize = 128;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct ExceptionInfo {
|
|
||||||
uw_exception: uw::_Unwind_Exception,
|
|
||||||
exception: Option<Exception<'static>>,
|
|
||||||
handled: bool,
|
|
||||||
backtrace: [usize; MAX_BACKTRACE_SIZE],
|
|
||||||
backtrace_size: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
|
|
||||||
|
|
||||||
#[cfg(any(target_arch = "or1k"))]
|
|
||||||
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
|
|
||||||
|
|
||||||
#[export_name="__artiq_personality"]
|
|
||||||
pub extern fn personality(version: c_int,
|
|
||||||
actions: uw::_Unwind_Action,
|
|
||||||
uw_exception_class: uw::_Unwind_Exception_Class,
|
|
||||||
uw_exception: *mut uw::_Unwind_Exception,
|
|
||||||
context: *mut uw::_Unwind_Context)
|
|
||||||
-> uw::_Unwind_Reason_Code {
|
|
||||||
unsafe {
|
|
||||||
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
|
|
||||||
return uw::_URC_FATAL_PHASE1_ERROR
|
|
||||||
}
|
|
||||||
|
|
||||||
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
|
||||||
let ip = uw::_Unwind_GetIP(context) - 1;
|
|
||||||
let func_start = uw::_Unwind_GetRegionStart(context);
|
|
||||||
|
|
||||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
||||||
let exception = &exception_info.exception.unwrap();
|
|
||||||
|
|
||||||
let eh_action = find_eh_action(lsda, func_start, ip, exception.name);
|
|
||||||
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
|
|
||||||
match eh_action {
|
|
||||||
EHAction::None |
|
|
||||||
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
|
|
||||||
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
|
|
||||||
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match eh_action {
|
|
||||||
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
|
|
||||||
EHAction::Cleanup(lpad) |
|
|
||||||
EHAction::Catch(lpad) => {
|
|
||||||
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
|
|
||||||
exception_info.handled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass a pair of the unwinder exception and ARTIQ exception
|
|
||||||
// (which immediately follows).
|
|
||||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
|
||||||
uw_exception as uw::_Unwind_Word);
|
|
||||||
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
|
|
||||||
exception as *const _ as uw::_Unwind_Word);
|
|
||||||
uw::_Unwind_SetIP(context, lpad);
|
|
||||||
return uw::_URC_INSTALL_CONTEXT;
|
|
||||||
}
|
|
||||||
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
|
||||||
uw_exception: *mut uw::_Unwind_Exception) {
|
|
||||||
unsafe {
|
|
||||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
||||||
|
|
||||||
exception_info.exception = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern fn uncaught_exception(_version: c_int,
|
|
||||||
actions: uw::_Unwind_Action,
|
|
||||||
_uw_exception_class: uw::_Unwind_Exception_Class,
|
|
||||||
uw_exception: *mut uw::_Unwind_Exception,
|
|
||||||
context: *mut uw::_Unwind_Context,
|
|
||||||
_stop_parameter: *mut c_void)
|
|
||||||
-> uw::_Unwind_Reason_Code {
|
|
||||||
unsafe {
|
|
||||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
||||||
|
|
||||||
if exception_info.backtrace_size < exception_info.backtrace.len() {
|
|
||||||
let ip = uw::_Unwind_GetIP(context);
|
|
||||||
exception_info.backtrace[exception_info.backtrace_size] = ip;
|
|
||||||
exception_info.backtrace_size += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
|
|
||||||
::terminate(&exception_info.exception.unwrap(),
|
|
||||||
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
|
|
||||||
} else {
|
|
||||||
uw::_URC_NO_REASON
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
|
|
||||||
// See https://github.com/rust-lang/rust/issues/39498.
|
|
||||||
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
|
|
||||||
uw_exception: uw::_Unwind_Exception {
|
|
||||||
exception_class: EXCEPTION_CLASS,
|
|
||||||
exception_cleanup: cleanup,
|
|
||||||
private: [0; uw::unwinder_private_data_size],
|
|
||||||
},
|
|
||||||
exception: None,
|
|
||||||
handled: false,
|
|
||||||
backtrace: [0; MAX_BACKTRACE_SIZE],
|
|
||||||
backtrace_size: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
#[export_name="__artiq_raise"]
|
|
||||||
#[unwind]
|
|
||||||
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
|
||||||
// Zing! The Exception<'a> as Exception<'static> cast is not really sound in case
|
|
||||||
// the exception is ever captured. Fortunately, they currently aren't, and we save
|
|
||||||
// on the hassle of having to allocate exceptions somewhere except on stack.
|
|
||||||
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
|
||||||
INFLIGHT.handled = false;
|
|
||||||
|
|
||||||
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
|
|
||||||
assert!(result == uw::_URC_END_OF_STACK);
|
|
||||||
|
|
||||||
INFLIGHT.backtrace_size = 0;
|
|
||||||
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
|
||||||
uncaught_exception, ptr::null_mut());
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_name="__artiq_reraise"]
|
|
||||||
#[unwind]
|
|
||||||
pub unsafe extern fn reraise() -> ! {
|
|
||||||
if INFLIGHT.handled {
|
|
||||||
raise(&INFLIGHT.exception.unwrap())
|
|
||||||
} else {
|
|
||||||
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stub implementations for the functions the panic_unwind crate expects to be provided.
|
|
||||||
// These all do nothing in libunwind, but aren't built for OR1K.
|
|
||||||
pub mod stubs {
|
|
||||||
#![allow(bad_style, unused_variables)]
|
|
||||||
|
|
||||||
use super::{uw, c_int};
|
|
||||||
|
|
||||||
#[export_name="_Unwind_GetIPInfo"]
|
|
||||||
pub unsafe extern fn _Unwind_GetIPInfo(ctx: *mut uw::_Unwind_Context,
|
|
||||||
ip_before_insn: *mut c_int) -> uw::_Unwind_Word {
|
|
||||||
*ip_before_insn = 0;
|
|
||||||
uw::_Unwind_GetIP(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_name="_Unwind_GetTextRelBase"]
|
|
||||||
pub unsafe extern fn _Unwind_GetTextRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_name="_Unwind_GetDataRelBase"]
|
|
||||||
pub unsafe extern fn _Unwind_GetDataRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
// Portions of the code in this file are derived from code by:
|
||||||
|
//
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
#![allow(private_no_mangle_fns, non_camel_case_types)]
|
||||||
|
|
||||||
|
use core::{ptr, mem};
|
||||||
|
use cslice::CSlice;
|
||||||
|
use unwind as uw;
|
||||||
|
use libc::{c_int, c_void};
|
||||||
|
|
||||||
|
use eh::dwarf::{self, EHAction};
|
||||||
|
|
||||||
|
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
||||||
|
actions: uw::_Unwind_Action,
|
||||||
|
exception_class: uw::_Unwind_Exception_Class,
|
||||||
|
exception_object: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context,
|
||||||
|
stop_parameter: *mut c_void)
|
||||||
|
-> uw::_Unwind_Reason_Code;
|
||||||
|
extern {
|
||||||
|
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
|
||||||
|
stop_fn: _Unwind_Stop_Fn,
|
||||||
|
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Exception<'a> {
|
||||||
|
pub name: CSlice<'a, u8>,
|
||||||
|
pub file: CSlice<'a, u8>,
|
||||||
|
pub line: u32,
|
||||||
|
pub column: u32,
|
||||||
|
pub function: CSlice<'a, u8>,
|
||||||
|
pub message: CSlice<'a, u8>,
|
||||||
|
pub param: [i64; 3]
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
|
||||||
|
|
||||||
|
const MAX_BACKTRACE_SIZE: usize = 128;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct ExceptionInfo {
|
||||||
|
uw_exception: uw::_Unwind_Exception,
|
||||||
|
exception: Option<Exception<'static>>,
|
||||||
|
handled: bool,
|
||||||
|
backtrace: [usize; MAX_BACKTRACE_SIZE],
|
||||||
|
backtrace_size: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "or1k"))]
|
||||||
|
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
|
||||||
|
|
||||||
|
#[export_name="__artiq_personality"]
|
||||||
|
pub extern fn personality(version: c_int,
|
||||||
|
actions: uw::_Unwind_Action,
|
||||||
|
uw_exception_class: uw::_Unwind_Exception_Class,
|
||||||
|
uw_exception: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context)
|
||||||
|
-> uw::_Unwind_Reason_Code {
|
||||||
|
unsafe {
|
||||||
|
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
|
||||||
|
return uw::_URC_FATAL_PHASE1_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
||||||
|
let ip = uw::_Unwind_GetIP(context) - 1;
|
||||||
|
let func_start = uw::_Unwind_GetRegionStart(context);
|
||||||
|
|
||||||
|
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||||
|
let exception = &exception_info.exception.unwrap();
|
||||||
|
|
||||||
|
let eh_action = dwarf::find_eh_action(lsda, func_start, ip, exception.name);
|
||||||
|
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None |
|
||||||
|
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
|
||||||
|
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
|
||||||
|
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
|
||||||
|
EHAction::Cleanup(lpad) |
|
||||||
|
EHAction::Catch(lpad) => {
|
||||||
|
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
|
||||||
|
exception_info.handled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass a pair of the unwinder exception and ARTIQ exception
|
||||||
|
// (which immediately follows).
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
||||||
|
uw_exception as uw::_Unwind_Word);
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
|
||||||
|
exception as *const _ as uw::_Unwind_Word);
|
||||||
|
uw::_Unwind_SetIP(context, lpad);
|
||||||
|
return uw::_URC_INSTALL_CONTEXT;
|
||||||
|
}
|
||||||
|
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
||||||
|
uw_exception: *mut uw::_Unwind_Exception) {
|
||||||
|
unsafe {
|
||||||
|
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||||
|
|
||||||
|
exception_info.exception = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn uncaught_exception(_version: c_int,
|
||||||
|
actions: uw::_Unwind_Action,
|
||||||
|
_uw_exception_class: uw::_Unwind_Exception_Class,
|
||||||
|
uw_exception: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context,
|
||||||
|
_stop_parameter: *mut c_void)
|
||||||
|
-> uw::_Unwind_Reason_Code {
|
||||||
|
unsafe {
|
||||||
|
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||||
|
|
||||||
|
if exception_info.backtrace_size < exception_info.backtrace.len() {
|
||||||
|
let ip = uw::_Unwind_GetIP(context);
|
||||||
|
exception_info.backtrace[exception_info.backtrace_size] = ip;
|
||||||
|
exception_info.backtrace_size += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
|
||||||
|
::terminate(&exception_info.exception.unwrap(),
|
||||||
|
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
|
||||||
|
} else {
|
||||||
|
uw::_URC_NO_REASON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
|
||||||
|
// See https://github.com/rust-lang/rust/issues/39498.
|
||||||
|
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
|
||||||
|
uw_exception: uw::_Unwind_Exception {
|
||||||
|
exception_class: EXCEPTION_CLASS,
|
||||||
|
exception_cleanup: cleanup,
|
||||||
|
private: [0; uw::unwinder_private_data_size],
|
||||||
|
},
|
||||||
|
exception: None,
|
||||||
|
handled: true,
|
||||||
|
backtrace: [0; MAX_BACKTRACE_SIZE],
|
||||||
|
backtrace_size: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
#[export_name="__artiq_raise"]
|
||||||
|
#[unwind(allowed)]
|
||||||
|
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
||||||
|
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
|
||||||
|
// the exception is ever captured. Fortunately, they currently aren't, and we save
|
||||||
|
// on the hassle of having to allocate exceptions somewhere except on stack.
|
||||||
|
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
||||||
|
INFLIGHT.handled = false;
|
||||||
|
|
||||||
|
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
|
||||||
|
assert!(result == uw::_URC_END_OF_STACK);
|
||||||
|
|
||||||
|
INFLIGHT.backtrace_size = 0;
|
||||||
|
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
||||||
|
uncaught_exception, ptr::null_mut());
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_name="__artiq_reraise"]
|
||||||
|
#[unwind(allowed)]
|
||||||
|
pub unsafe extern fn reraise() -> ! {
|
||||||
|
use cslice::AsCSlice;
|
||||||
|
|
||||||
|
if INFLIGHT.handled {
|
||||||
|
match INFLIGHT.exception {
|
||||||
|
Some(ref exception) => raise(exception),
|
||||||
|
None => raise(&Exception {
|
||||||
|
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
|
||||||
|
file: file!().as_c_slice(),
|
||||||
|
line: line!(),
|
||||||
|
column: column!(),
|
||||||
|
// https://github.com/rust-lang/rfcs/pull/1719
|
||||||
|
function: "__artiq_reraise".as_c_slice(),
|
||||||
|
message: "No active exception to reraise".as_c_slice(),
|
||||||
|
param: [0, 0, 0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,8 @@ void send_to_rtio_log(long long int timestamp, struct slice data);
|
||||||
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
|
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
|
||||||
#define KSUPPORT_HEADER_SIZE 0x80
|
#define KSUPPORT_HEADER_SIZE 0x80
|
||||||
|
|
||||||
|
FILE *stderr;
|
||||||
|
|
||||||
/* called by libunwind */
|
/* called by libunwind */
|
||||||
int fprintf(FILE *stream, const char *fmt, ...)
|
int fprintf(FILE *stream, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#![feature(lang_items, asm, libc, panic_unwind, unwind_attributes, global_allocator)]
|
#![feature(lang_items, asm, panic_unwind, libc, unwind_attributes,
|
||||||
|
panic_implementation, panic_info_message)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate unwind;
|
extern crate unwind;
|
||||||
|
@ -10,6 +11,7 @@ extern crate alloc_stub;
|
||||||
extern crate std_artiq as std;
|
extern crate std_artiq as std;
|
||||||
|
|
||||||
extern crate board;
|
extern crate board;
|
||||||
|
extern crate eh;
|
||||||
extern crate dyld;
|
extern crate dyld;
|
||||||
extern crate proto;
|
extern crate proto;
|
||||||
extern crate amp;
|
extern crate amp;
|
||||||
|
@ -52,10 +54,26 @@ macro_rules! recv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
#[lang = "panic_fmt"]
|
#[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
|
||||||
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
pub fn oom(_layout: core::alloc::Layout) -> ! {
|
||||||
send(&Log(format_args!("panic at {}:{}: {}\n", file, line, args)));
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
|
#[panic_implementation]
|
||||||
|
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
if let Some(location) = info.location() {
|
||||||
|
send(&Log(format_args!("panic at {}:{}:{}",
|
||||||
|
location.file(), location.line(), location.column())));
|
||||||
|
} else {
|
||||||
|
send(&Log(format_args!("panic at unknown location")));
|
||||||
|
}
|
||||||
|
if let Some(message) = info.message() {
|
||||||
|
send(&Log(format_args!("{}\n", message)));
|
||||||
|
} else {
|
||||||
|
send(&Log(format_args!("\n")));
|
||||||
|
}
|
||||||
send(&RunAborted);
|
send(&RunAborted);
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
@ -72,25 +90,25 @@ macro_rules! println {
|
||||||
macro_rules! raise {
|
macro_rules! raise {
|
||||||
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
|
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
|
||||||
use cslice::AsCSlice;
|
use cslice::AsCSlice;
|
||||||
let exn = $crate::eh::Exception {
|
let exn = $crate::eh_artiq::Exception {
|
||||||
name: concat!("0:artiq.coredevice.exceptions.", $name).as_bytes().as_c_slice(),
|
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
|
||||||
file: file!().as_bytes().as_c_slice(),
|
file: file!().as_c_slice(),
|
||||||
line: line!(),
|
line: line!(),
|
||||||
column: column!(),
|
column: column!(),
|
||||||
// https://github.com/rust-lang/rfcs/pull/1719
|
// https://github.com/rust-lang/rfcs/pull/1719
|
||||||
function: "(Rust function)".as_bytes().as_c_slice(),
|
function: "(Rust function)".as_c_slice(),
|
||||||
message: $message.as_bytes().as_c_slice(),
|
message: $message.as_c_slice(),
|
||||||
param: [$param0, $param1, $param2]
|
param: [$param0, $param1, $param2]
|
||||||
};
|
};
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
unsafe { $crate::eh::raise(&exn) }
|
unsafe { $crate::eh_artiq::raise(&exn) }
|
||||||
});
|
});
|
||||||
($name:expr, $message:expr) => ({
|
($name:expr, $message:expr) => ({
|
||||||
raise!($name, $message, 0, 0, 0)
|
raise!($name, $message, 0, 0, 0)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod eh;
|
mod eh_artiq;
|
||||||
mod api;
|
mod api;
|
||||||
mod rtio;
|
mod rtio;
|
||||||
mod nrt_bus;
|
mod nrt_bus;
|
||||||
|
@ -114,6 +132,7 @@ pub extern fn send_to_rtio_log(timestamp: i64, text: CSlice<u8>) {
|
||||||
rtio::log(timestamp, text.as_ref())
|
rtio::log(timestamp, text.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
|
extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
|
||||||
while !rpc_queue::empty() {}
|
while !rpc_queue::empty() {}
|
||||||
send(&RpcSend {
|
send(&RpcSend {
|
||||||
|
@ -124,6 +143,7 @@ extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ()) {
|
extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ()) {
|
||||||
while rpc_queue::full() {}
|
while rpc_queue::full() {}
|
||||||
rpc_queue::enqueue(|mut slice| {
|
rpc_queue::enqueue(|mut slice| {
|
||||||
|
@ -146,6 +166,7 @@ extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn rpc_recv(slot: *mut ()) -> usize {
|
extern fn rpc_recv(slot: *mut ()) -> usize {
|
||||||
send(&RpcRecvRequest(slot));
|
send(&RpcRecvRequest(slot));
|
||||||
recv!(&RpcRecvReply(ref result) => {
|
recv!(&RpcRecvReply(ref result) => {
|
||||||
|
@ -153,7 +174,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
|
||||||
&Ok(alloc_size) => alloc_size,
|
&Ok(alloc_size) => alloc_size,
|
||||||
&Err(ref exception) =>
|
&Err(ref exception) =>
|
||||||
unsafe {
|
unsafe {
|
||||||
eh::raise(&eh::Exception {
|
eh_artiq::raise(&eh_artiq::Exception {
|
||||||
name: exception.name.as_bytes().as_c_slice(),
|
name: exception.name.as_bytes().as_c_slice(),
|
||||||
file: exception.file.as_bytes().as_c_slice(),
|
file: exception.file.as_bytes().as_c_slice(),
|
||||||
line: exception.line,
|
line: exception.line,
|
||||||
|
@ -167,7 +188,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminate(exception: &eh::Exception, mut backtrace: &mut [usize]) -> ! {
|
fn terminate(exception: &eh_artiq::Exception, backtrace: &mut [usize]) -> ! {
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
for index in 0..backtrace.len() {
|
for index in 0..backtrace.len() {
|
||||||
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
|
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
|
||||||
|
@ -193,6 +214,7 @@ fn terminate(exception: &eh::Exception, mut backtrace: &mut [usize]) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn watchdog_set(ms: i64) -> i32 {
|
extern fn watchdog_set(ms: i64) -> i32 {
|
||||||
if ms < 0 {
|
if ms < 0 {
|
||||||
raise!("ValueError", "cannot set a watchdog with a negative timeout")
|
raise!("ValueError", "cannot set a watchdog with a negative timeout")
|
||||||
|
@ -202,10 +224,12 @@ extern fn watchdog_set(ms: i64) -> i32 {
|
||||||
recv!(&WatchdogSetReply { id } => id) as i32
|
recv!(&WatchdogSetReply { id } => id) as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn watchdog_clear(id: i32) {
|
extern fn watchdog_clear(id: i32) {
|
||||||
send(&WatchdogClear { id: id as usize })
|
send(&WatchdogClear { id: id as usize })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
|
extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
|
||||||
send(&CacheGetRequest {
|
send(&CacheGetRequest {
|
||||||
key: str::from_utf8(key.as_ref()).unwrap()
|
key: str::from_utf8(key.as_ref()).unwrap()
|
||||||
|
@ -213,6 +237,7 @@ extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
|
||||||
recv!(&CacheGetReply { value } => value.as_c_slice())
|
recv!(&CacheGetReply { value } => value.as_c_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
|
extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
|
||||||
send(&CachePutRequest {
|
send(&CachePutRequest {
|
||||||
key: str::from_utf8(key.as_ref()).unwrap(),
|
key: str::from_utf8(key.as_ref()).unwrap(),
|
||||||
|
@ -229,15 +254,12 @@ const DMA_BUFFER_SIZE: usize = 64 * 1024;
|
||||||
|
|
||||||
struct DmaRecorder {
|
struct DmaRecorder {
|
||||||
active: bool,
|
active: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
padding: [u8; 3], //https://github.com/rust-lang/rust/issues/41315
|
|
||||||
data_len: usize,
|
data_len: usize,
|
||||||
buffer: [u8; DMA_BUFFER_SIZE],
|
buffer: [u8; DMA_BUFFER_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut DMA_RECORDER: DmaRecorder = DmaRecorder {
|
static mut DMA_RECORDER: DmaRecorder = DmaRecorder {
|
||||||
active: false,
|
active: false,
|
||||||
padding: [0; 3],
|
|
||||||
data_len: 0,
|
data_len: 0,
|
||||||
buffer: [0; DMA_BUFFER_SIZE],
|
buffer: [0; DMA_BUFFER_SIZE],
|
||||||
};
|
};
|
||||||
|
@ -249,6 +271,7 @@ fn dma_record_flush() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn dma_record_start(name: CSlice<u8>) {
|
extern fn dma_record_start(name: CSlice<u8>) {
|
||||||
let name = str::from_utf8(name.as_ref()).unwrap();
|
let name = str::from_utf8(name.as_ref()).unwrap();
|
||||||
|
|
||||||
|
@ -268,6 +291,7 @@ extern fn dma_record_start(name: CSlice<u8>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn dma_record_stop(duration: i64) {
|
extern fn dma_record_stop(duration: i64) {
|
||||||
unsafe {
|
unsafe {
|
||||||
dma_record_flush();
|
dma_record_flush();
|
||||||
|
@ -289,10 +313,12 @@ extern fn dma_record_stop(duration: i64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn dma_record_output(timestamp: i64, channel: i32, address: i32, word: i32) {
|
extern fn dma_record_output(timestamp: i64, channel: i32, address: i32, word: i32) {
|
||||||
dma_record_output_wide(timestamp, channel, address, [word].as_c_slice())
|
dma_record_output_wide(timestamp, channel, address, [word].as_c_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, words: CSlice<i32>) {
|
extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, words: CSlice<i32>) {
|
||||||
assert!(words.len() <= 16); // enforce the hardware limit
|
assert!(words.len() <= 16); // enforce the hardware limit
|
||||||
|
|
||||||
|
@ -333,7 +359,7 @@ extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, wor
|
||||||
if DMA_RECORDER.buffer.len() - DMA_RECORDER.data_len < length {
|
if DMA_RECORDER.buffer.len() - DMA_RECORDER.data_len < length {
|
||||||
dma_record_flush()
|
dma_record_flush()
|
||||||
}
|
}
|
||||||
let mut dst = &mut DMA_RECORDER.buffer[DMA_RECORDER.data_len..
|
let dst = &mut DMA_RECORDER.buffer[DMA_RECORDER.data_len..
|
||||||
DMA_RECORDER.data_len + length];
|
DMA_RECORDER.data_len + length];
|
||||||
dst[..header_length].copy_from_slice(&header[..]);
|
dst[..header_length].copy_from_slice(&header[..]);
|
||||||
dst[header_length..].copy_from_slice(&data[..]);
|
dst[header_length..].copy_from_slice(&data[..]);
|
||||||
|
@ -341,6 +367,7 @@ extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, wor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(aborts)]
|
||||||
extern fn dma_erase(name: CSlice<u8>) {
|
extern fn dma_erase(name: CSlice<u8>) {
|
||||||
let name = str::from_utf8(name.as_ref()).unwrap();
|
let name = str::from_utf8(name.as_ref()).unwrap();
|
||||||
|
|
||||||
|
@ -353,6 +380,7 @@ struct DmaTrace {
|
||||||
address: i32,
|
address: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||||
let name = str::from_utf8(name.as_ref()).unwrap();
|
let name = str::from_utf8(name.as_ref()).unwrap();
|
||||||
|
|
||||||
|
@ -372,6 +400,7 @@ extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unwind(allowed)]
|
||||||
extern fn dma_playback(timestamp: i64, ptr: i32) {
|
extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||||
assert!(ptr % 64 == 0);
|
assert!(ptr % 64 == 0);
|
||||||
|
|
||||||
|
@ -480,18 +509,32 @@ pub unsafe fn main() {
|
||||||
attribute_writeback(typeinfo as *const ());
|
attribute_writeback(typeinfo as *const ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all async RPCs are processed before exiting.
|
||||||
|
// Otherwise, if the comms and kernel CPU run in the following sequence:
|
||||||
|
//
|
||||||
|
// comms kernel
|
||||||
|
// ----------------------- -----------------------
|
||||||
|
// check for async RPC
|
||||||
|
// post async RPC
|
||||||
|
// post RunFinished
|
||||||
|
// check for mailbox
|
||||||
|
//
|
||||||
|
// the async RPC would be missed.
|
||||||
|
send(&RpcFlush);
|
||||||
|
|
||||||
send(&RunFinished);
|
send(&RunFinished);
|
||||||
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
#[unwind(allowed)]
|
||||||
pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
|
pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
|
||||||
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
|
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't export this because libbase does.
|
#[no_mangle]
|
||||||
// #[no_mangle]
|
#[unwind(allowed)]
|
||||||
pub extern fn abort() {
|
pub extern fn abort() {
|
||||||
panic!("aborted")
|
panic!("aborted")
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||||
|
csr::rtio::i_overflow_reset_write(1);
|
||||||
raise!("RTIOOverflow",
|
raise!("RTIOOverflow",
|
||||||
"RTIO input overflow on channel {0}",
|
"RTIO input overflow on channel {0}",
|
||||||
channel as i64, 0, 0);
|
channel as i64, 0, 0);
|
||||||
|
@ -140,18 +141,19 @@ pub fn log(timestamp: i64, data: &[u8]) {
|
||||||
for i in 0..data.len() {
|
for i in 0..data.len() {
|
||||||
word <<= 8;
|
word <<= 8;
|
||||||
word |= data[i] as u32;
|
word |= data[i] as u32;
|
||||||
if i % 4 == 0 {
|
if i % 4 == 3 {
|
||||||
rtio_o_data_write(0, word);
|
rtio_o_data_write(0, word);
|
||||||
csr::rtio::o_we_write(1);
|
csr::rtio::o_we_write(1);
|
||||||
word = 0;
|
word = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
word <<= 8;
|
if word != 0 {
|
||||||
rtio_o_data_write(0, word);
|
rtio_o_data_write(0, word);
|
||||||
csr::rtio::o_we_write(1);
|
csr::rtio::o_we_write(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(has_rtio_log))]
|
#[cfg(not(has_rtio_log))]
|
||||||
pub fn log(_timestamp: i64, _data: &[u8]) {}
|
pub fn log(_timestamp: i64, _data: &[u8]) {}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
#![feature(alloc, allocator_api)]
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate alloc;
|
use core::{ptr, mem, fmt};
|
||||||
|
use core::alloc::{GlobalAlloc, Layout};
|
||||||
use core::{mem, fmt};
|
|
||||||
use alloc::allocator::{Layout, AllocErr, Alloc};
|
|
||||||
|
|
||||||
// The minimum alignment guaranteed by the architecture.
|
// The minimum alignment guaranteed by the architecture.
|
||||||
const MIN_ALIGN: usize = 4;
|
const MIN_ALIGN: usize = 4;
|
||||||
|
@ -42,10 +39,10 @@ impl ListAlloc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a> Alloc for &'a ListAlloc {
|
unsafe impl GlobalAlloc for ListAlloc {
|
||||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
if layout.align() > MIN_ALIGN {
|
if layout.align() > MIN_ALIGN {
|
||||||
return Err(AllocErr::Unsupported { details: "alignment too large" })
|
panic!("cannot allocate with alignment {}", layout.align())
|
||||||
}
|
}
|
||||||
|
|
||||||
let header_size = mem::size_of::<Header>();
|
let header_size = mem::size_of::<Header>();
|
||||||
|
@ -83,7 +80,7 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
|
||||||
|
|
||||||
if (*curr).size >= size {
|
if (*curr).size >= size {
|
||||||
(*curr).magic = MAGIC_BUSY;
|
(*curr).magic = MAGIC_BUSY;
|
||||||
return Ok(curr.offset(1) as *mut u8)
|
return curr.offset(1) as *mut u8
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => panic!("heap corruption detected at {:p}", curr)
|
_ => panic!("heap corruption detected at {:p}", curr)
|
||||||
|
@ -92,31 +89,25 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
|
||||||
curr = (*curr).next;
|
curr = (*curr).next;
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(AllocErr::Exhausted { request: layout })
|
ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) {
|
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||||
let curr = (ptr as *mut Header).offset(-1);
|
let curr = (ptr as *mut Header).offset(-1);
|
||||||
if (*curr).magic != MAGIC_BUSY {
|
if (*curr).magic != MAGIC_BUSY {
|
||||||
panic!("heap corruption detected at {:p}", curr)
|
panic!("heap corruption detected at {:p}", curr)
|
||||||
}
|
}
|
||||||
(*curr).magic = MAGIC_FREE;
|
(*curr).magic = MAGIC_FREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn oom(&mut self, err: AllocErr) -> ! {
|
|
||||||
panic!("cannot allocate: {:?}", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListAlloc {
|
impl fmt::Display for ListAlloc {
|
||||||
pub fn debug_dump(&self, f: &mut fmt::Write) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut total_busy = 0;
|
let mut total_busy = 0;
|
||||||
let mut total_idle = 0;
|
let mut total_idle = 0;
|
||||||
let mut total_meta = 0;
|
let mut total_meta = 0;
|
||||||
|
|
||||||
write!(f, "Heap view:\n")?;
|
|
||||||
|
|
||||||
let mut curr = self.root;
|
let mut curr = self.root;
|
||||||
while !curr.is_null() {
|
while !curr.is_null() {
|
||||||
total_meta += mem::size_of::<Header>();
|
total_meta += mem::size_of::<Header>();
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use alloc::allocator::{Layout, AllocErr, Alloc};
|
use core::alloc::{Layout, GlobalAlloc};
|
||||||
|
|
||||||
pub struct StubAlloc;
|
pub struct StubAlloc;
|
||||||
|
|
||||||
unsafe impl<'a> Alloc for &'a StubAlloc {
|
unsafe impl GlobalAlloc for StubAlloc {
|
||||||
unsafe fn alloc(&mut self, _layout: Layout) -> Result<*mut u8, AllocErr> {
|
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) {
|
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "backtrace_artiq"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "backtrace_artiq"
|
||||||
|
path = "lib.rs"
|
|
@ -0,0 +1,30 @@
|
||||||
|
#![feature(libc, panic_unwind)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate unwind;
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
use unwind as uw;
|
||||||
|
use libc::c_void;
|
||||||
|
|
||||||
|
pub fn backtrace<F>(mut f: F) -> Result<(), uw::_Unwind_Reason_Code>
|
||||||
|
where F: FnMut(usize) -> ()
|
||||||
|
{
|
||||||
|
extern fn trace<F>(context: *mut uw::_Unwind_Context, arg: *mut c_void)
|
||||||
|
-> uw::_Unwind_Reason_Code
|
||||||
|
where F: FnMut(usize) -> ()
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let step_fn = &mut *(arg as *mut F);
|
||||||
|
step_fn(uw::_Unwind_GetIP(context));
|
||||||
|
uw::_URC_NO_REASON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
match uw::_Unwind_Backtrace(trace::<F>, &mut f as *mut _ as *mut c_void) {
|
||||||
|
uw::_URC_NO_REASON => Ok(()),
|
||||||
|
err => Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ name = "board"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
byteorder = { version = "1.0", default-features = false }
|
||||||
log = { version = "0.3", default-features = false }
|
log = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
#[cfg(has_spiflash)]
|
||||||
|
mod imp {
|
||||||
|
use core::str;
|
||||||
|
use byteorder::{ByteOrder, BigEndian};
|
||||||
|
use cache;
|
||||||
|
use spiflash;
|
||||||
|
|
||||||
|
// One flash sector immediately before the firmware.
|
||||||
|
const ADDR: usize = ::mem::FLASH_BOOT_ADDRESS - spiflash::SECTOR_SIZE;
|
||||||
|
const SIZE: usize = spiflash::SECTOR_SIZE;
|
||||||
|
|
||||||
|
mod lock {
|
||||||
|
use core::slice;
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||||
|
|
||||||
|
static LOCKED: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
|
|
||||||
|
pub struct Lock;
|
||||||
|
|
||||||
|
impl Lock {
|
||||||
|
pub fn take() -> Result<Lock, ()> {
|
||||||
|
if LOCKED.swap(1, Ordering::SeqCst) != 0 {
|
||||||
|
Err(()) // already locked
|
||||||
|
} else {
|
||||||
|
Ok(Lock) // locked now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &'static [u8] {
|
||||||
|
unsafe { slice::from_raw_parts(super::ADDR as *const u8, super::SIZE) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Lock {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
LOCKED.store(0, Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use self::lock::Lock;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Iter<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
offset: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iter<'a> {
|
||||||
|
fn new(data: &'a [u8]) -> Iter<'a> {
|
||||||
|
Iter { data: data, offset: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Iter<'a> {
|
||||||
|
type Item = Result<(&'a [u8], &'a [u8]), ()>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let data = &self.data[self.offset..];
|
||||||
|
|
||||||
|
if data.len() < 4 {
|
||||||
|
error!("offset {}: truncated record", self.offset);
|
||||||
|
return Some(Err(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let record_size = BigEndian::read_u32(data) as usize;
|
||||||
|
if record_size == !0 /* all ones; erased flash */ {
|
||||||
|
return None
|
||||||
|
} else if record_size < 4 || record_size > data.len() {
|
||||||
|
error!("offset {}: invalid record size {}", self.offset, record_size);
|
||||||
|
return Some(Err(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let record_body = &data[4..record_size];
|
||||||
|
match record_body.iter().position(|&x| x == 0) {
|
||||||
|
None => {
|
||||||
|
error!("offset {}: missing separator", self.offset);
|
||||||
|
Some(Err(()))
|
||||||
|
}
|
||||||
|
Some(pos) => {
|
||||||
|
self.offset += record_size;
|
||||||
|
|
||||||
|
let (key, zero_and_value) = record_body.split_at(pos);
|
||||||
|
Some(Ok((key, &zero_and_value[1..])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(key: &str, f: F) -> R {
|
||||||
|
f(Lock::take().and_then(|lock| {
|
||||||
|
let mut iter = Iter::new(lock.data());
|
||||||
|
let mut value = &[][..];
|
||||||
|
while let Some(result) = iter.next() {
|
||||||
|
let (record_key, record_value) = result?;
|
||||||
|
if key.as_bytes() == record_key {
|
||||||
|
// last write wins
|
||||||
|
value = record_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(value)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(key: &str, f: F) -> R {
|
||||||
|
read(key, |result| {
|
||||||
|
f(result.and_then(|value| str::from_utf8(value).map_err(|_| ())))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn append_at<'a>(mut data: &'a [u8], key: &[u8], value: &[u8]) -> Result<&'a [u8], ()> {
|
||||||
|
let record_size = 4 + key.len() + 1 + value.len();
|
||||||
|
if data.len() < record_size {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut record_size_bytes = [0u8; 4];
|
||||||
|
BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32);
|
||||||
|
|
||||||
|
spiflash::write(data.as_ptr() as usize, &record_size_bytes[..]);
|
||||||
|
data = &data[record_size_bytes.len()..];
|
||||||
|
|
||||||
|
spiflash::write(data.as_ptr() as usize, key);
|
||||||
|
data = &data[key.len()..];
|
||||||
|
|
||||||
|
spiflash::write(data.as_ptr() as usize, &[0]);
|
||||||
|
data = &data[1..];
|
||||||
|
|
||||||
|
spiflash::write(data.as_ptr() as usize, value);
|
||||||
|
data = &data[value.len()..];
|
||||||
|
|
||||||
|
cache::flush_l2_cache();
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compact() -> Result<(), ()> {
|
||||||
|
let lock = Lock::take()?;
|
||||||
|
|
||||||
|
static mut OLD_DATA: [u8; SIZE] = [0; SIZE];
|
||||||
|
let old_data = unsafe {
|
||||||
|
OLD_DATA.copy_from_slice(lock.data());
|
||||||
|
&OLD_DATA[..]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data = lock.data();
|
||||||
|
unsafe { spiflash::erase_sector(data.as_ptr() as usize) };
|
||||||
|
|
||||||
|
// This is worst-case quadratic, but we're limited by a small SPI flash sector size,
|
||||||
|
// so it does not really matter.
|
||||||
|
let mut iter = Iter::new(old_data);
|
||||||
|
'iter: while let Some(result) = iter.next() {
|
||||||
|
let (key, mut value) = result?;
|
||||||
|
if value.is_empty() {
|
||||||
|
// This is a removed entry, ignore it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut next_iter = iter.clone();
|
||||||
|
while let Some(next_result) = next_iter.next() {
|
||||||
|
let (next_key, _) = next_result?;
|
||||||
|
if key == next_key {
|
||||||
|
// There's another entry that overwrites this one, ignore this one.
|
||||||
|
continue 'iter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = unsafe { append_at(data, key, value)? };
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append(key: &str, value: &[u8]) -> Result<(), ()> {
|
||||||
|
let lock = Lock::take()?;
|
||||||
|
|
||||||
|
let free = {
|
||||||
|
let mut iter = Iter::new(lock.data());
|
||||||
|
while let Some(result) = iter.next() {
|
||||||
|
let _ = result?;
|
||||||
|
}
|
||||||
|
&iter.data[iter.offset..]
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { append_at(free, key.as_bytes(), value)? };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(key: &str, value: &[u8]) -> Result<(), ()> {
|
||||||
|
match append(key, value) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(()) => {
|
||||||
|
compact()?;
|
||||||
|
append(key, value)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(key: &str) -> Result<(), ()> {
|
||||||
|
write(key, &[])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn erase() -> Result<(), ()> {
|
||||||
|
let lock = Lock::take()?;
|
||||||
|
|
||||||
|
unsafe { spiflash::erase_sector(lock.data().as_ptr() as usize) };
|
||||||
|
cache::flush_l2_cache();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_spiflash))]
|
||||||
|
mod imp {
|
||||||
|
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(_key: &str, f: F) -> R {
|
||||||
|
f(Err(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(_key: &str, f: F) -> R {
|
||||||
|
f(Err(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(_key: &str, _value: &[u8]) -> Result<(), ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(_key: &str) -> Result<(), ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn erase() -> Result<(), ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::imp::*;
|
|
@ -1,10 +1,11 @@
|
||||||
#![feature(asm, lang_items)]
|
#![feature(asm, lang_items)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate byteorder;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use core::{cmp, ptr, str};
|
use core::{cmp, str};
|
||||||
|
|
||||||
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs"));
|
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs"));
|
||||||
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));
|
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));
|
||||||
|
@ -18,6 +19,7 @@ pub mod uart_console;
|
||||||
|
|
||||||
#[cfg(has_spiflash)]
|
#[cfg(has_spiflash)]
|
||||||
pub mod spiflash;
|
pub mod spiflash;
|
||||||
|
pub mod config;
|
||||||
|
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
|
@ -43,11 +45,13 @@ pub use uart_console::Console;
|
||||||
|
|
||||||
pub fn ident(buf: &mut [u8]) -> &str {
|
pub fn ident(buf: &mut [u8]) -> &str {
|
||||||
unsafe {
|
unsafe {
|
||||||
let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE);
|
csr::identifier::address_write(0);
|
||||||
let len = cmp::min(len as usize, buf.len());
|
let len = csr::identifier::data_read();
|
||||||
|
let len = cmp::min(len, buf.len() as u8);
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
buf[i] = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE.offset(1 + i as isize)) as u8
|
csr::identifier::address_write(1 + i);
|
||||||
|
buf[i as usize] = csr::identifier::data_read();
|
||||||
}
|
}
|
||||||
str::from_utf8_unchecked(&buf[..len])
|
str::from_utf8_unchecked(&buf[..len as usize])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use csr;
|
use csr;
|
||||||
|
|
||||||
|
pub const SECTOR_SIZE: usize = csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize;
|
||||||
|
pub const PAGE_SIZE: usize = csr::CONFIG_SPIFLASH_PAGE_SIZE as usize;
|
||||||
|
|
||||||
|
const PAGE_MASK: usize = PAGE_SIZE - 1;
|
||||||
|
|
||||||
const CMD_PP: u8 = 0x02;
|
const CMD_PP: u8 = 0x02;
|
||||||
const CMD_WRDI: u8 = 0x04;
|
const CMD_WRDI: u8 = 0x04;
|
||||||
const CMD_RDSR: u8 = 0x05;
|
const CMD_RDSR: u8 = 0x05;
|
||||||
|
@ -15,8 +20,7 @@ const PIN_DQ_I: u8 = 1 << 3;
|
||||||
|
|
||||||
const SR_WIP: u8 = 1;
|
const SR_WIP: u8 = 1;
|
||||||
|
|
||||||
fn write_byte(mut byte: u8) {
|
unsafe fn write_byte(mut byte: u8) {
|
||||||
unsafe {
|
|
||||||
csr::spiflash::bitbang_write(0);
|
csr::spiflash::bitbang_write(0);
|
||||||
for _ in 0..8 {
|
for _ in 0..8 {
|
||||||
csr::spiflash::bitbang_write((byte & 0x80) >> 7);
|
csr::spiflash::bitbang_write((byte & 0x80) >> 7);
|
||||||
|
@ -25,10 +29,8 @@ fn write_byte(mut byte: u8) {
|
||||||
}
|
}
|
||||||
csr::spiflash::bitbang_write(0);
|
csr::spiflash::bitbang_write(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn write_addr(mut addr: usize) {
|
unsafe fn write_addr(mut addr: usize) {
|
||||||
unsafe {
|
|
||||||
csr::spiflash::bitbang_write(0);
|
csr::spiflash::bitbang_write(0);
|
||||||
for _ in 0..24 {
|
for _ in 0..24 {
|
||||||
csr::spiflash::bitbang_write(((addr & 0x800000) >> 23) as u8);
|
csr::spiflash::bitbang_write(((addr & 0x800000) >> 23) as u8);
|
||||||
|
@ -37,7 +39,6 @@ fn write_addr(mut addr: usize) {
|
||||||
}
|
}
|
||||||
csr::spiflash::bitbang_write(0);
|
csr::spiflash::bitbang_write(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_until_ready() {
|
fn wait_until_ready() {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -59,8 +60,7 @@ fn wait_until_ready() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn erase_sector(addr: usize) {
|
pub unsafe fn erase_sector(addr: usize) {
|
||||||
unsafe {
|
|
||||||
let sector_addr = addr & !(csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize - 1);
|
let sector_addr = addr & !(csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize - 1);
|
||||||
|
|
||||||
csr::spiflash::bitbang_en_write(1);
|
csr::spiflash::bitbang_en_write(1);
|
||||||
|
@ -78,10 +78,8 @@ pub fn erase_sector(addr: usize) {
|
||||||
|
|
||||||
csr::spiflash::bitbang_en_write(0);
|
csr::spiflash::bitbang_en_write(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn write_page(addr: usize, data: &[u8]) {
|
unsafe fn write_page(addr: usize, data: &[u8]) {
|
||||||
unsafe {
|
|
||||||
csr::spiflash::bitbang_en_write(1);
|
csr::spiflash::bitbang_en_write(1);
|
||||||
|
|
||||||
wait_until_ready();
|
wait_until_ready();
|
||||||
|
@ -101,12 +99,8 @@ fn write_page(addr: usize, data: &[u8]) {
|
||||||
|
|
||||||
csr::spiflash::bitbang_en_write(0);
|
csr::spiflash::bitbang_en_write(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const PAGE_SIZE: usize = csr::CONFIG_SPIFLASH_PAGE_SIZE as usize;
|
pub unsafe fn write(mut addr: usize, mut data: &[u8]) {
|
||||||
const PAGE_MASK: usize = PAGE_SIZE - 1;
|
|
||||||
|
|
||||||
pub fn write(mut addr: usize, mut data: &[u8]) {
|
|
||||||
if addr & PAGE_MASK != 0 {
|
if addr & PAGE_MASK != 0 {
|
||||||
let size = cmp::min((PAGE_SIZE - (addr & PAGE_MASK)) as usize, data.len());
|
let size = cmp::min((PAGE_SIZE - (addr & PAGE_MASK)) as usize, data.len());
|
||||||
write_page(addr, &data[..size]);
|
write_page(addr, &data[..size]);
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "eh"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "eh"
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cslice = { version = "0.3" }
|
|
@ -0,0 +1,243 @@
|
||||||
|
#![allow(non_upper_case_globals, dead_code)]
|
||||||
|
|
||||||
|
use core::{ptr, mem};
|
||||||
|
use cslice::CSlice;
|
||||||
|
|
||||||
|
const DW_EH_PE_omit: u8 = 0xFF;
|
||||||
|
const DW_EH_PE_absptr: u8 = 0x00;
|
||||||
|
|
||||||
|
const DW_EH_PE_uleb128: u8 = 0x01;
|
||||||
|
const DW_EH_PE_udata2: u8 = 0x02;
|
||||||
|
const DW_EH_PE_udata4: u8 = 0x03;
|
||||||
|
const DW_EH_PE_udata8: u8 = 0x04;
|
||||||
|
const DW_EH_PE_sleb128: u8 = 0x09;
|
||||||
|
const DW_EH_PE_sdata2: u8 = 0x0A;
|
||||||
|
const DW_EH_PE_sdata4: u8 = 0x0B;
|
||||||
|
const DW_EH_PE_sdata8: u8 = 0x0C;
|
||||||
|
|
||||||
|
const DW_EH_PE_pcrel: u8 = 0x10;
|
||||||
|
const DW_EH_PE_textrel: u8 = 0x20;
|
||||||
|
const DW_EH_PE_datarel: u8 = 0x30;
|
||||||
|
const DW_EH_PE_funcrel: u8 = 0x40;
|
||||||
|
const DW_EH_PE_aligned: u8 = 0x50;
|
||||||
|
|
||||||
|
const DW_EH_PE_indirect: u8 = 0x80;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DwarfReader {
|
||||||
|
pub ptr: *const u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DwarfReader {
|
||||||
|
fn new(ptr: *const u8) -> DwarfReader {
|
||||||
|
DwarfReader { ptr: ptr }
|
||||||
|
}
|
||||||
|
|
||||||
|
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
|
||||||
|
// on a 4-byte boundary. This may cause problems on platforms with strict
|
||||||
|
// alignment requirements. By wrapping data in a "packed" struct, we are
|
||||||
|
// telling the backend to generate "misalignment-safe" code.
|
||||||
|
unsafe fn read<T: Copy>(&mut self) -> T {
|
||||||
|
let result = ptr::read_unaligned(self.ptr as *const T);
|
||||||
|
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
|
||||||
|
// Length Data".
|
||||||
|
unsafe fn read_uleb128(&mut self) -> u64 {
|
||||||
|
let mut shift: usize = 0;
|
||||||
|
let mut result: u64 = 0;
|
||||||
|
let mut byte: u8;
|
||||||
|
loop {
|
||||||
|
byte = self.read::<u8>();
|
||||||
|
result |= ((byte & 0x7F) as u64) << shift;
|
||||||
|
shift += 7;
|
||||||
|
if byte & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_sleb128(&mut self) -> i64 {
|
||||||
|
let mut shift: usize = 0;
|
||||||
|
let mut result: u64 = 0;
|
||||||
|
let mut byte: u8;
|
||||||
|
loop {
|
||||||
|
byte = self.read::<u8>();
|
||||||
|
result |= ((byte & 0x7F) as u64) << shift;
|
||||||
|
shift += 7;
|
||||||
|
if byte & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sign-extend
|
||||||
|
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
|
||||||
|
result |= (!0 as u64) << shift;
|
||||||
|
}
|
||||||
|
result as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
|
||||||
|
fn round_up(unrounded: usize, align: usize) -> usize {
|
||||||
|
debug_assert!(align.is_power_of_two());
|
||||||
|
(unrounded + align - 1) & !(align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(encoding != DW_EH_PE_omit);
|
||||||
|
|
||||||
|
// DW_EH_PE_aligned implies it's an absolute pointer value
|
||||||
|
if encoding == DW_EH_PE_aligned {
|
||||||
|
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
|
||||||
|
return self.read::<usize>()
|
||||||
|
}
|
||||||
|
|
||||||
|
let value_ptr = self.ptr;
|
||||||
|
let mut result = match encoding & 0x0F {
|
||||||
|
DW_EH_PE_absptr => self.read::<usize>(),
|
||||||
|
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
|
||||||
|
DW_EH_PE_udata2 => self.read::<u16>() as usize,
|
||||||
|
DW_EH_PE_udata4 => self.read::<u32>() as usize,
|
||||||
|
DW_EH_PE_udata8 => self.read::<u64>() as usize,
|
||||||
|
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
|
||||||
|
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
|
||||||
|
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
|
||||||
|
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
result += match encoding & 0x70 {
|
||||||
|
DW_EH_PE_absptr => 0,
|
||||||
|
// relative to address of the encoded value, despite the name
|
||||||
|
DW_EH_PE_pcrel => value_ptr as usize,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if encoding & DW_EH_PE_indirect != 0 {
|
||||||
|
result = *(result as *const usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encoding_size(encoding: u8) -> usize {
|
||||||
|
if encoding == DW_EH_PE_omit {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
match encoding & 0x0F {
|
||||||
|
DW_EH_PE_absptr => mem::size_of::<usize>(),
|
||||||
|
DW_EH_PE_udata2 => 2,
|
||||||
|
DW_EH_PE_udata4 => 4,
|
||||||
|
DW_EH_PE_udata8 => 8,
|
||||||
|
DW_EH_PE_sdata2 => 2,
|
||||||
|
DW_EH_PE_sdata4 => 4,
|
||||||
|
DW_EH_PE_sdata8 => 8,
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum EHAction {
|
||||||
|
None,
|
||||||
|
Cleanup(usize),
|
||||||
|
Catch(usize),
|
||||||
|
Terminate,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
|
||||||
|
exn_name: CSlice<u8>) -> EHAction {
|
||||||
|
if lsda.is_null() {
|
||||||
|
return EHAction::None
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = DwarfReader::new(lsda);
|
||||||
|
|
||||||
|
let start_encoding = reader.read::<u8>();
|
||||||
|
// base address for landing pad offsets
|
||||||
|
let lpad_base = if start_encoding != DW_EH_PE_omit {
|
||||||
|
reader.read_encoded_pointer(start_encoding)
|
||||||
|
} else {
|
||||||
|
func_start
|
||||||
|
};
|
||||||
|
|
||||||
|
let ttype_encoding = reader.read::<u8>();
|
||||||
|
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
|
||||||
|
|
||||||
|
let class_info;
|
||||||
|
if ttype_encoding != DW_EH_PE_omit {
|
||||||
|
let class_info_offset = reader.read_uleb128();
|
||||||
|
class_info = reader.ptr.offset(class_info_offset as isize);
|
||||||
|
} else {
|
||||||
|
class_info = ptr::null();
|
||||||
|
}
|
||||||
|
assert!(!class_info.is_null());
|
||||||
|
|
||||||
|
let call_site_encoding = reader.read::<u8>();
|
||||||
|
let call_site_table_length = reader.read_uleb128();
|
||||||
|
let action_table = reader.ptr.offset(call_site_table_length as isize);
|
||||||
|
|
||||||
|
while reader.ptr < action_table {
|
||||||
|
let cs_start = reader.read_encoded_pointer(call_site_encoding);
|
||||||
|
let cs_len = reader.read_encoded_pointer(call_site_encoding);
|
||||||
|
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
|
||||||
|
let cs_action = reader.read_uleb128();
|
||||||
|
|
||||||
|
if ip < func_start + cs_start {
|
||||||
|
// Callsite table is sorted by cs_start, so if we've passed the ip, we
|
||||||
|
// may stop searching.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ip > func_start + cs_start + cs_len {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs_lpad == 0 {
|
||||||
|
return EHAction::None
|
||||||
|
}
|
||||||
|
|
||||||
|
let lpad = lpad_base + cs_lpad;
|
||||||
|
if cs_action == 0 {
|
||||||
|
return EHAction::Cleanup(lpad)
|
||||||
|
}
|
||||||
|
|
||||||
|
let action_entry = action_table.offset((cs_action - 1) as isize);
|
||||||
|
let mut action_reader = DwarfReader::new(action_entry);
|
||||||
|
loop {
|
||||||
|
let type_info_offset = action_reader.read_sleb128() as isize;
|
||||||
|
let action_offset = action_reader.clone().read_sleb128() as isize;
|
||||||
|
assert!(type_info_offset >= 0);
|
||||||
|
|
||||||
|
if type_info_offset > 0 {
|
||||||
|
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
|
||||||
|
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
|
||||||
|
.read_encoded_pointer(ttype_encoding);
|
||||||
|
let type_info = *(type_info_ptr as *const CSlice<u8>);
|
||||||
|
|
||||||
|
if type_info.as_ref() == exn_name.as_ref() {
|
||||||
|
return EHAction::Catch(lpad)
|
||||||
|
}
|
||||||
|
|
||||||
|
if type_info.len() == 0 {
|
||||||
|
// This is a catch-all clause. We don't compare type_info_ptr with null here
|
||||||
|
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
|
||||||
|
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
|
||||||
|
// a proper null pointer.
|
||||||
|
return EHAction::Catch(lpad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if action_offset == 0 {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
action_reader.ptr = action_reader.ptr.offset(action_offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EHAction::None
|
||||||
|
}
|
||||||
|
|
||||||
|
// the function has a personality but no landing pads; this is fine
|
||||||
|
EHAction::None
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// This is the Rust personality function, adapted for use in ARTIQ. We never actually panic
|
||||||
|
// from Rust or recover from Rust exceptions (there's nothing to catch the panics), but we
|
||||||
|
// need a personality function to step back through Rust frames in order to make a backtrace.
|
||||||
|
//
|
||||||
|
// By design, this personality function is only ever called in the search phase, although
|
||||||
|
// to keep things simple and close to upstream, it is not modified
|
||||||
|
#![allow(private_no_mangle_fns)]
|
||||||
|
|
||||||
|
use unwind as uw;
|
||||||
|
use libc::{c_int, uintptr_t};
|
||||||
|
use cslice::AsCSlice;
|
||||||
|
|
||||||
|
use dwarf::{self, EHAction};
|
||||||
|
|
||||||
|
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
|
||||||
|
// and TargetLowering::getExceptionSelectorRegister() for each architecture,
|
||||||
|
// then mapped to DWARF register numbers via register definition tables
|
||||||
|
// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
|
||||||
|
// See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86")]
|
||||||
|
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "or1k"))]
|
||||||
|
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
|
||||||
|
|
||||||
|
// The following code is based on GCC's C and C++ personality routines. For reference, see:
|
||||||
|
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
|
||||||
|
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
|
||||||
|
#[lang = "eh_personality"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[allow(unused)]
|
||||||
|
unsafe extern "C" fn rust_eh_personality(version: c_int,
|
||||||
|
actions: uw::_Unwind_Action,
|
||||||
|
exception_class: uw::_Unwind_Exception_Class,
|
||||||
|
exception_object: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context)
|
||||||
|
-> uw::_Unwind_Reason_Code {
|
||||||
|
if version != 1 {
|
||||||
|
return uw::_URC_FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
let eh_action = match find_eh_action(context) {
|
||||||
|
Ok(action) => action,
|
||||||
|
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
|
||||||
|
};
|
||||||
|
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None |
|
||||||
|
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
|
||||||
|
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
|
||||||
|
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
|
||||||
|
EHAction::Cleanup(lpad) |
|
||||||
|
EHAction::Catch(lpad) => {
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t);
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
|
||||||
|
uw::_Unwind_SetIP(context, lpad);
|
||||||
|
return uw::_URC_INSTALL_CONTEXT;
|
||||||
|
}
|
||||||
|
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
|
||||||
|
-> Result<EHAction, ()>
|
||||||
|
{
|
||||||
|
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
||||||
|
let func = uw::_Unwind_GetRegionStart(context);
|
||||||
|
let ip = uw::_Unwind_GetIP(context);
|
||||||
|
Ok(dwarf::find_eh_action(lsda, func, ip, [].as_c_slice()))
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#![feature(lang_items, panic_unwind, libc, unwind_attributes)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate cslice;
|
||||||
|
extern crate unwind;
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
pub mod dwarf;
|
||||||
|
// pub mod eh_rust;
|
|
@ -8,6 +8,6 @@ name = "logger_artiq"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = { version = "0.3", default-features = false, features = [] }
|
log = { version = "0.4", default-features = false }
|
||||||
log_buffer = { version = "1.0" }
|
log_buffer = { version = "1.2" }
|
||||||
board = { path = "../libboard" }
|
board = { path = "../libboard" }
|
||||||
|
|
|
@ -2,19 +2,49 @@
|
||||||
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate log_buffer;
|
extern crate log_buffer;
|
||||||
|
#[macro_use]
|
||||||
extern crate board;
|
extern crate board;
|
||||||
|
|
||||||
use core::{mem, ptr};
|
use core::cell::{Cell, RefCell, RefMut};
|
||||||
use core::cell::{Cell, RefCell};
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use log::{Log, LogMetadata, LogRecord, LogLevelFilter, MaxLogLevelFilter};
|
use log::{Log, LevelFilter};
|
||||||
use log_buffer::LogBuffer;
|
use log_buffer::LogBuffer;
|
||||||
use board::{Console, clock};
|
use board::clock;
|
||||||
|
|
||||||
|
pub struct LogBufferRef<'a> {
|
||||||
|
buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>,
|
||||||
|
old_log_level: LevelFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LogBufferRef<'a> {
|
||||||
|
fn new(buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>) -> LogBufferRef<'a> {
|
||||||
|
let old_log_level = log::max_level();
|
||||||
|
log::set_max_level(LevelFilter::Off);
|
||||||
|
LogBufferRef { buffer, old_log_level }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.buffer.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.buffer.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract(&mut self) -> &str {
|
||||||
|
self.buffer.extract()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for LogBufferRef<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
log::set_max_level(self.old_log_level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct BufferLogger {
|
pub struct BufferLogger {
|
||||||
buffer: RefCell<LogBuffer<&'static mut [u8]>>,
|
buffer: RefCell<LogBuffer<&'static mut [u8]>>,
|
||||||
filter: RefCell<Option<MaxLogLevelFilter>>,
|
uart_filter: Cell<LevelFilter>
|
||||||
uart_filter: Cell<LogLevelFilter>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut LOGGER: *const BufferLogger = 0 as *const _;
|
static mut LOGGER: *const BufferLogger = 0 as *const _;
|
||||||
|
@ -23,71 +53,36 @@ impl BufferLogger {
|
||||||
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
|
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
|
||||||
BufferLogger {
|
BufferLogger {
|
||||||
buffer: RefCell::new(LogBuffer::new(buffer)),
|
buffer: RefCell::new(LogBuffer::new(buffer)),
|
||||||
filter: RefCell::new(None),
|
uart_filter: Cell::new(LevelFilter::Info),
|
||||||
uart_filter: Cell::new(LogLevelFilter::Info),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register<F: FnOnce()>(&self, f: F) {
|
pub fn register<F: FnOnce()>(&self, f: F) {
|
||||||
// log::set_logger_raw captures a pointer to ourselves, so we must prevent
|
|
||||||
// ourselves from being moved or dropped after that function is called (and
|
|
||||||
// before log::shutdown_logger_raw is called).
|
|
||||||
unsafe {
|
unsafe {
|
||||||
log::set_logger_raw(|max_log_level| {
|
|
||||||
max_log_level.set(LogLevelFilter::Info);
|
|
||||||
*self.filter.borrow_mut() = Some(max_log_level);
|
|
||||||
self as *const Log
|
|
||||||
}).expect("global logger can only be initialized once");
|
|
||||||
LOGGER = self;
|
LOGGER = self;
|
||||||
|
log::set_logger(&*LOGGER)
|
||||||
|
.expect("global logger can only be initialized once");
|
||||||
}
|
}
|
||||||
|
log::set_max_level(LevelFilter::Info);
|
||||||
f();
|
f();
|
||||||
log::shutdown_logger_raw().unwrap();
|
|
||||||
unsafe {
|
|
||||||
LOGGER = ptr::null();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with<R, F: FnOnce(&BufferLogger) -> R>(f: F) -> R {
|
pub fn with<R, F: FnOnce(&BufferLogger) -> R>(f: F) -> R {
|
||||||
f(unsafe { mem::transmute::<*const BufferLogger, &BufferLogger>(LOGGER) })
|
f(unsafe { &*LOGGER })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn buffer<'a>(&'a self) -> Result<LogBufferRef<'a>, ()> {
|
||||||
self.buffer.borrow_mut().clear()
|
self.buffer
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map(LogBufferRef::new)
|
||||||
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn uart_log_level(&self) -> LevelFilter {
|
||||||
self.buffer.borrow_mut().extract().len() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extract<R, F: FnOnce(&str) -> R>(&self, f: F) -> R {
|
|
||||||
let old_log_level = self.max_log_level();
|
|
||||||
self.set_max_log_level(LogLevelFilter::Off);
|
|
||||||
let result = f(self.buffer.borrow_mut().extract());
|
|
||||||
self.set_max_log_level(old_log_level);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_log_level(&self) -> LogLevelFilter {
|
|
||||||
self.filter
|
|
||||||
.borrow()
|
|
||||||
.as_ref()
|
|
||||||
.expect("register the logger before touching maximum log level")
|
|
||||||
.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_max_log_level(&self, max_level: LogLevelFilter) {
|
|
||||||
self.filter
|
|
||||||
.borrow()
|
|
||||||
.as_ref()
|
|
||||||
.expect("register the logger before touching maximum log level")
|
|
||||||
.set(max_level)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uart_log_level(&self) -> LogLevelFilter {
|
|
||||||
self.uart_filter.get()
|
self.uart_filter.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_uart_log_level(&self, max_level: LogLevelFilter) {
|
pub fn set_uart_log_level(&self, max_level: LevelFilter) {
|
||||||
self.uart_filter.set(max_level)
|
self.uart_filter.set(max_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,25 +91,28 @@ impl BufferLogger {
|
||||||
unsafe impl Sync for BufferLogger {}
|
unsafe impl Sync for BufferLogger {}
|
||||||
|
|
||||||
impl Log for BufferLogger {
|
impl Log for BufferLogger {
|
||||||
fn enabled(&self, _metadata: &LogMetadata) -> bool {
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&self, record: &LogRecord) {
|
fn log(&self, record: &log::Record) {
|
||||||
if self.enabled(record.metadata()) {
|
if self.enabled(record.metadata()) {
|
||||||
let timestamp = clock::get_us();
|
let timestamp = clock::get_us();
|
||||||
let seconds = timestamp / 1_000_000;
|
let seconds = timestamp / 1_000_000;
|
||||||
let micros = timestamp % 1_000_000;
|
let micros = timestamp % 1_000_000;
|
||||||
|
|
||||||
writeln!(self.buffer.borrow_mut(),
|
if let Ok(mut buffer) = self.buffer.try_borrow_mut() {
|
||||||
"[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
|
writeln!(buffer, "[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
|
||||||
record.level(), record.target(), record.args()).unwrap();
|
record.level(), record.target(), record.args()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if record.level() <= self.uart_filter.get() {
|
if record.level() <= self.uart_filter.get() {
|
||||||
writeln!(Console,
|
println!("[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
|
||||||
"[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
|
record.level(), record.target(), record.args());
|
||||||
record.level(), record.target(), record.args()).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,6 @@ path = "lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "1.0", default-features = false }
|
byteorder = { version = "1.0", default-features = false }
|
||||||
cslice = { version = "0.3" }
|
cslice = { version = "0.3" }
|
||||||
log = { version = "0.3", default-features = false, optional = true }
|
log = { version = "0.4", default-features = false, optional = true }
|
||||||
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
|
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
|
||||||
dyld = { path = "../libdyld" }
|
dyld = { path = "../libdyld" }
|
||||||
|
|
|
@ -74,6 +74,7 @@ pub enum Message<'a> {
|
||||||
},
|
},
|
||||||
RpcRecvRequest(*mut ()),
|
RpcRecvRequest(*mut ()),
|
||||||
RpcRecvReply(Result<usize, Exception<'a>>),
|
RpcRecvReply(Result<usize, Exception<'a>>),
|
||||||
|
RpcFlush,
|
||||||
|
|
||||||
CacheGetRequest { key: &'a str },
|
CacheGetRequest { key: &'a str },
|
||||||
CacheGetReply { value: &'static [i32] },
|
CacheGetReply { value: &'static [i32] },
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::vec::Vec;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use {ReadExt, WriteExt};
|
use {ReadExt, WriteExt};
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
use log::LogLevelFilter;
|
use log;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
|
@ -10,9 +10,9 @@ pub enum Request {
|
||||||
ClearLog,
|
ClearLog,
|
||||||
PullLog,
|
PullLog,
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
SetLogFilter(LogLevelFilter),
|
SetLogFilter(log::LevelFilter),
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
SetUartLogFilter(LogLevelFilter),
|
SetUartLogFilter(log::LevelFilter),
|
||||||
|
|
||||||
Hotswap(Vec<u8>),
|
Hotswap(Vec<u8>),
|
||||||
Reboot,
|
Reboot,
|
||||||
|
@ -29,14 +29,14 @@ pub enum Reply<'a> {
|
||||||
impl Request {
|
impl Request {
|
||||||
pub fn read_from(reader: &mut Read) -> io::Result<Request> {
|
pub fn read_from(reader: &mut Read) -> io::Result<Request> {
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
fn read_log_level_filter(reader: &mut Read) -> io::Result<LogLevelFilter> {
|
fn read_log_level_filter(reader: &mut Read) -> io::Result<log::LevelFilter> {
|
||||||
Ok(match reader.read_u8()? {
|
Ok(match reader.read_u8()? {
|
||||||
0 => LogLevelFilter::Off,
|
0 => log::LevelFilter::Off,
|
||||||
1 => LogLevelFilter::Error,
|
1 => log::LevelFilter::Error,
|
||||||
2 => LogLevelFilter::Warn,
|
2 => log::LevelFilter::Warn,
|
||||||
3 => LogLevelFilter::Info,
|
3 => log::LevelFilter::Info,
|
||||||
4 => LogLevelFilter::Debug,
|
4 => log::LevelFilter::Debug,
|
||||||
5 => LogLevelFilter::Trace,
|
5 => log::LevelFilter::Trace,
|
||||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData,
|
_ => return Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||||
"invalid log level"))
|
"invalid log level"))
|
||||||
})
|
})
|
||||||
|
|
|
@ -140,11 +140,11 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Tag::Keyword(it) => {
|
Tag::Keyword(it) => {
|
||||||
struct Keyword<'a> { name: CSlice<'a, u8>, contents: () };
|
struct Keyword<'a> { name: CSlice<'a, u8> };
|
||||||
consume_value!(Keyword, |ptr| {
|
consume_value!(Keyword, |ptr| {
|
||||||
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
||||||
let tag = it.clone().next().expect("truncated tag");
|
let tag = it.clone().next().expect("truncated tag");
|
||||||
let mut data = &(*ptr).contents as *const ();
|
let mut data = ptr.offset(1) as *const ();
|
||||||
send_value(writer, tag, &mut data)
|
send_value(writer, tag, &mut data)
|
||||||
})
|
})
|
||||||
// Tag::Keyword never appears in composite types, so we don't have
|
// Tag::Keyword never appears in composite types, so we don't have
|
||||||
|
@ -243,8 +243,8 @@ mod tag {
|
||||||
Tag::Int64 => 8,
|
Tag::Int64 => 8,
|
||||||
Tag::Float64 => 8,
|
Tag::Float64 => 8,
|
||||||
Tag::String => 4,
|
Tag::String => 4,
|
||||||
Tag::Bytes => 4,
|
Tag::Bytes => 8,
|
||||||
Tag::ByteArray => 4,
|
Tag::ByteArray => 8,
|
||||||
Tag::Tuple(it, arity) => {
|
Tag::Tuple(it, arity) => {
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
for _ in 0..arity {
|
for _ in 0..arity {
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
#![feature(lang_items, asm, alloc, needs_panic_runtime,
|
#![feature(lang_items, asm, alloc, needs_panic_runtime, use_extern_macros,
|
||||||
unicode, raw, int_error_internals, try_from, macro_reexport,
|
unicode, raw, int_error_internals, try_from,
|
||||||
allow_internal_unstable, stmt_expr_attributes, str_internals)]
|
allow_internal_unstable, stmt_expr_attributes, str_internals)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![needs_panic_runtime]
|
#![needs_panic_runtime]
|
||||||
|
|
||||||
extern crate std_unicode;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
#[macro_reexport(vec, format)]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num,
|
pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num,
|
||||||
ops, option, ptr, result, sync,
|
ops, option, ptr, result, sync,
|
||||||
char, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize, f32, f64};
|
char, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize, f32, f64};
|
||||||
pub use alloc::{arc, rc, raw_vec};
|
pub use alloc::{arc, rc, raw_vec};
|
||||||
pub use alloc::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, linked_list, slice,
|
pub use alloc::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, format, linked_list, slice,
|
||||||
str, string, vec, vec_deque};
|
str, string, vec, vec_deque};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|
|
@ -15,28 +15,25 @@ build_artiq = { path = "../libbuild_artiq" }
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "1.0", default-features = false }
|
byteorder = { version = "1.0", default-features = false }
|
||||||
cslice = { version = "0.3" }
|
cslice = { version = "0.3" }
|
||||||
log = { version = "0.3", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
|
managed = { version = "= 0.7.0", default-features = false, features = ["alloc", "map"] }
|
||||||
alloc_list = { path = "../liballoc_list" }
|
alloc_list = { path = "../liballoc_list" }
|
||||||
std_artiq = { path = "../libstd_artiq", features = ["alloc", "io_error_alloc"] }
|
std_artiq = { path = "../libstd_artiq", features = ["alloc", "io_error_alloc"] }
|
||||||
logger_artiq = { path = "../liblogger_artiq" }
|
logger_artiq = { path = "../liblogger_artiq" }
|
||||||
|
backtrace_artiq = { path = "../libbacktrace_artiq" }
|
||||||
board = { path = "../libboard", features = ["uart_console"] }
|
board = { path = "../libboard", features = ["uart_console"] }
|
||||||
proto = { path = "../libproto", features = ["log"] }
|
proto = { path = "../libproto", features = ["log"] }
|
||||||
amp = { path = "../libamp" }
|
amp = { path = "../libamp" }
|
||||||
drtioaux = { path = "../libdrtioaux" }
|
drtioaux = { path = "../libdrtioaux" }
|
||||||
|
|
||||||
[dependencies.compiler_builtins]
|
|
||||||
git = "https://github.com/rust-lang-nursery/compiler-builtins"
|
|
||||||
rev = "631b568"
|
|
||||||
features = ["mem"]
|
|
||||||
|
|
||||||
[dependencies.fringe]
|
[dependencies.fringe]
|
||||||
git = "https://github.com/m-labs/libfringe"
|
git = "https://github.com/m-labs/libfringe"
|
||||||
rev = "bd23494"
|
rev = "b8a6d8f"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["alloc"]
|
features = ["alloc"]
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
git = "https://github.com/m-labs/smoltcp"
|
git = "https://github.com/m-labs/smoltcp"
|
||||||
rev = "6f5ae33"
|
rev = "92e970b" # NB: also change in libboard_misoc/Cargo.toml
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["alloc", "log"]
|
features = ["rust-1.28", "alloc", "log", "proto-ipv4", "socket-tcp"]
|
||||||
|
|
|
@ -1,32 +1,30 @@
|
||||||
include ../include/generated/variables.mak
|
include ../include/generated/variables.mak
|
||||||
include $(MISOC_DIRECTORY)/software/common.mak
|
include $(MISOC_DIRECTORY)/software/common.mak
|
||||||
|
|
||||||
LDFLAGS += -L../libbase
|
CFLAGS += \
|
||||||
|
-I$(LIBUNWIND_DIRECTORY) \
|
||||||
|
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include
|
||||||
|
|
||||||
RUSTFLAGS += -Cpanic=abort
|
LDFLAGS += -L../libbase \
|
||||||
|
-L../libunwind
|
||||||
|
|
||||||
all: runtime.bin runtime.fbi
|
RUSTFLAGS += -Cpanic=unwind
|
||||||
|
|
||||||
|
all:: runtime.bin runtime.fbi
|
||||||
|
|
||||||
.PHONY: $(RUSTOUT)/libruntime.a
|
.PHONY: $(RUSTOUT)/libruntime.a
|
||||||
$(RUSTOUT)/libruntime.a:
|
$(RUSTOUT)/libruntime.a:
|
||||||
$(cargo) --manifest-path $(RUNTIME_DIRECTORY)/Cargo.toml
|
$(cargo) --manifest-path $(RUNTIME_DIRECTORY)/Cargo.toml
|
||||||
|
|
||||||
runtime.elf: $(RUSTOUT)/libruntime.a ksupport_data.o
|
runtime.elf: $(RUSTOUT)/libruntime.a ksupport_data.o
|
||||||
$(LD) $(LDFLAGS) -T $(RUNTIME_DIRECTORY)/runtime.ld -o $@ $^
|
$(link) -T $(RUNTIME_DIRECTORY)/runtime.ld \
|
||||||
@chmod -x $@
|
-lunwind-bare
|
||||||
|
|
||||||
ksupport_data.o: ../ksupport/ksupport.elf
|
ksupport_data.o: ../ksupport/ksupport.elf
|
||||||
$(LD) -r -b binary -o $@ $<
|
$(LD) -r -b binary -o $@ $<
|
||||||
|
|
||||||
%.bin: %.elf
|
%.bin: %.elf
|
||||||
$(OBJCOPY) -O binary $< $@
|
$(objcopy) -O binary
|
||||||
@chmod -x $@
|
|
||||||
|
|
||||||
%.fbi: %.bin
|
%.fbi: %.bin
|
||||||
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $<
|
$(mscimg) -f
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) *.o runtime.elf runtime.bin runtime.fbi
|
|
||||||
$(RM) -rf cargo
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
|
@ -5,18 +5,13 @@ use analyzer_proto::*;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 512 * 1024;
|
const BUFFER_SIZE: usize = 512 * 1024;
|
||||||
|
|
||||||
// hack until https://github.com/rust-lang/rust/issues/33626 is fixed
|
#[repr(align(64))]
|
||||||
#[repr(simd)]
|
|
||||||
struct Align64(u64, u64, u64, u64, u64, u64, u64, u64);
|
|
||||||
|
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
data: [u8; BUFFER_SIZE],
|
data: [u8; BUFFER_SIZE]
|
||||||
__alignment: [Align64; 0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut BUFFER: Buffer = Buffer {
|
static mut BUFFER: Buffer = Buffer {
|
||||||
data: [0; BUFFER_SIZE],
|
data: [0; BUFFER_SIZE]
|
||||||
__alignment: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn arm() {
|
fn arm() {
|
||||||
|
@ -68,9 +63,6 @@ fn worker(stream: &mut TcpStream) -> io::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread(io: Io) {
|
pub fn thread(io: Io) {
|
||||||
// verify that the hack above works
|
|
||||||
assert!(::core::mem::align_of::<Buffer>() == 64);
|
|
||||||
|
|
||||||
let listener = TcpListener::new(&io, 65535);
|
let listener = TcpListener::new(&io, 65535);
|
||||||
listener.listen(1382).expect("analyzer: cannot listen");
|
listener.listen(1382).expect("analyzer: cannot listen");
|
||||||
|
|
||||||
|
|
|
@ -1,192 +0,0 @@
|
||||||
use core::str;
|
|
||||||
use std::btree_map::BTreeMap;
|
|
||||||
use byteorder::{ByteOrder, BigEndian};
|
|
||||||
use board::{mem, csr, cache, spiflash};
|
|
||||||
|
|
||||||
const ADDR: usize = mem::FLASH_BOOT_ADDRESS + 0x80000 /* max runtime size */;
|
|
||||||
const SIZE: usize = csr::CONFIG_SPIFLASH_SECTOR_SIZE as usize;
|
|
||||||
|
|
||||||
mod lock {
|
|
||||||
use core::slice;
|
|
||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
static LOCKED: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
pub struct Lock;
|
|
||||||
|
|
||||||
impl Lock {
|
|
||||||
pub fn take() -> Result<Lock, ()> {
|
|
||||||
if LOCKED.swap(1, Ordering::SeqCst) != 0 {
|
|
||||||
Err(()) // already locked
|
|
||||||
} else {
|
|
||||||
Ok(Lock) // locked now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data(&self) -> &'static [u8] {
|
|
||||||
unsafe { slice::from_raw_parts(super::ADDR as *const u8, super::SIZE) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Lock {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
LOCKED.store(0, Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::lock::Lock;
|
|
||||||
|
|
||||||
struct Iter<'a> {
|
|
||||||
data: &'a [u8],
|
|
||||||
offset: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iter<'a> {
|
|
||||||
fn new(data: &'a [u8]) -> Iter<'a> {
|
|
||||||
Iter { data: data, offset: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
|
||||||
type Item = Result<(&'a [u8], &'a [u8]), ()>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let data = &self.data[self.offset..];
|
|
||||||
|
|
||||||
if data.len() < 4 {
|
|
||||||
error!("offset {}: truncated record", self.offset);
|
|
||||||
return Some(Err(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
let record_size = BigEndian::read_u32(data) as usize;
|
|
||||||
if record_size < 4 {
|
|
||||||
error!("offset {}: invalid record size", self.offset);
|
|
||||||
return Some(Err(()))
|
|
||||||
}
|
|
||||||
if record_size == !0 /* all ones; erased flash */ {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let record_body = &data[4..record_size];
|
|
||||||
match record_body.iter().position(|&x| x == 0) {
|
|
||||||
None => {
|
|
||||||
error!("offset {}: missing separator", self.offset);
|
|
||||||
Some(Err(()))
|
|
||||||
}
|
|
||||||
Some(pos) => {
|
|
||||||
self.offset += record_size;
|
|
||||||
|
|
||||||
let (key, zero_and_value) = record_body.split_at(pos);
|
|
||||||
Some(Ok((key, &zero_and_value[1..])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(key: &str, f: F) -> R {
|
|
||||||
f(Lock::take().and_then(|lock| {
|
|
||||||
let mut iter = Iter::new(lock.data());
|
|
||||||
let mut value = &[][..];
|
|
||||||
while let Some(result) = iter.next() {
|
|
||||||
let (record_key, record_value) = result?;
|
|
||||||
if key.as_bytes() == record_key {
|
|
||||||
// last write wins
|
|
||||||
value = record_value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(value)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(key: &str, f: F) -> R {
|
|
||||||
read(key, |result| {
|
|
||||||
f(result.and_then(|value| str::from_utf8(value).map_err(|_| ())))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_at(mut offset: usize, key: &[u8], value: &[u8]) -> Result<usize, ()> {
|
|
||||||
let record_size = 4 + key.len() + 1 + value.len();
|
|
||||||
if offset + record_size > SIZE {
|
|
||||||
return Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut record_size_bytes = [0u8; 4];
|
|
||||||
BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32);
|
|
||||||
|
|
||||||
spiflash::write(ADDR + offset, &record_size_bytes[..]);
|
|
||||||
offset += record_size_bytes.len();
|
|
||||||
|
|
||||||
spiflash::write(ADDR + offset, key);
|
|
||||||
offset += key.len();
|
|
||||||
|
|
||||||
spiflash::write(ADDR + offset, &[0]);
|
|
||||||
offset += 1;
|
|
||||||
|
|
||||||
spiflash::write(ADDR + offset, value);
|
|
||||||
offset += value.len();
|
|
||||||
|
|
||||||
cache::flush_l2_cache();
|
|
||||||
Ok(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compact() -> Result<(), ()> {
|
|
||||||
let lock = Lock::take()?;
|
|
||||||
|
|
||||||
let mut items = BTreeMap::new();
|
|
||||||
{
|
|
||||||
let mut iter = Iter::new(lock.data());
|
|
||||||
while let Some(result) = iter.next() {
|
|
||||||
let (key, value) = result?;
|
|
||||||
items.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spiflash::erase_sector(ADDR);
|
|
||||||
cache::flush_l2_cache();
|
|
||||||
|
|
||||||
let mut offset = 0;
|
|
||||||
for (key, value) in items {
|
|
||||||
offset = append_at(offset, key, value)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append(key: &str, value: &[u8]) -> Result<(), ()> {
|
|
||||||
let lock = Lock::take()?;
|
|
||||||
|
|
||||||
let free_offset = {
|
|
||||||
let mut iter = Iter::new(lock.data());
|
|
||||||
while let Some(result) = iter.next() {
|
|
||||||
let _ = result?;
|
|
||||||
}
|
|
||||||
iter.offset
|
|
||||||
};
|
|
||||||
|
|
||||||
append_at(free_offset, key.as_bytes(), value)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(key: &str, value: &[u8]) -> Result<(), ()> {
|
|
||||||
match append(key, value) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(()) => {
|
|
||||||
compact()?;
|
|
||||||
append(key, value)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(key: &str) -> Result<(), ()> {
|
|
||||||
write(key, &[])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn erase() -> Result<(), ()> {
|
|
||||||
let _lock = Lock::take()?;
|
|
||||||
|
|
||||||
spiflash::erase_sector(ADDR);
|
|
||||||
cache::flush_l2_cache();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
pub fn read<F: FnOnce(Result<&[u8], ()>) -> R, R>(_key: &str, f: F) -> R {
|
|
||||||
f(Err(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_str<F: FnOnce(Result<&str, ()>) -> R, R>(_key: &str, f: F) -> R {
|
|
||||||
f(Err(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(_key: &str, _value: &[u8]) -> Result<(), ()> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(_key: &str) -> Result<(), ()> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn erase() -> Result<(), ()> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
|
@ -1,88 +1,159 @@
|
||||||
use core::slice;
|
use core::{slice, fmt};
|
||||||
|
use smoltcp::Result;
|
||||||
|
use smoltcp::time::Instant;
|
||||||
|
use smoltcp::phy::{self, DeviceCapabilities, Device};
|
||||||
|
|
||||||
use board::{csr, mem};
|
use board::{csr, mem};
|
||||||
use smoltcp::Error;
|
|
||||||
use smoltcp::phy::{DeviceLimits, Device};
|
|
||||||
|
|
||||||
const RX0_BASE: usize = mem::ETHMAC_BASE + 0x0000;
|
const RX_SLOTS: usize = csr::ETHMAC_RX_SLOTS as usize;
|
||||||
const RX1_BASE: usize = mem::ETHMAC_BASE + 0x0800;
|
const TX_SLOTS: usize = csr::ETHMAC_TX_SLOTS as usize;
|
||||||
const RX2_BASE: usize = mem::ETHMAC_BASE + 0x1000;
|
const SLOT_SIZE: usize = csr::ETHMAC_SLOT_SIZE as usize;
|
||||||
const RX3_BASE: usize = mem::ETHMAC_BASE + 0x1800;
|
|
||||||
const TX0_BASE: usize = mem::ETHMAC_BASE + 0x2000;
|
|
||||||
const TX1_BASE: usize = mem::ETHMAC_BASE + 0x2800;
|
|
||||||
const TX2_BASE: usize = mem::ETHMAC_BASE + 0x3000;
|
|
||||||
const TX3_BASE: usize = mem::ETHMAC_BASE + 0x3800;
|
|
||||||
|
|
||||||
const RX_BUFFERS: [*mut u8; 4] = [RX0_BASE as *mut u8, RX1_BASE as *mut u8,
|
fn next_rx_slot() -> Option<usize> {
|
||||||
RX2_BASE as *mut u8, RX3_BASE as *mut u8];
|
|
||||||
const TX_BUFFERS: [*mut u8; 4] = [TX0_BASE as *mut u8, TX1_BASE as *mut u8,
|
|
||||||
TX2_BASE as *mut u8, TX3_BASE as *mut u8];
|
|
||||||
|
|
||||||
pub struct EthernetDevice;
|
|
||||||
|
|
||||||
impl Device for EthernetDevice {
|
|
||||||
type RxBuffer = RxBuffer;
|
|
||||||
type TxBuffer = TxBuffer;
|
|
||||||
|
|
||||||
fn limits(&self) -> DeviceLimits {
|
|
||||||
let mut limits = DeviceLimits::default();
|
|
||||||
limits.max_transmission_unit = 1514;
|
|
||||||
limits.max_burst_size = Some(RX_BUFFERS.len());
|
|
||||||
limits
|
|
||||||
}
|
|
||||||
|
|
||||||
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer, Error> {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if csr::ethmac::sram_writer_ev_pending_read() != 0 {
|
if csr::ethmac::sram_writer_ev_pending_read() == 0 {
|
||||||
let slot = csr::ethmac::sram_writer_slot_read();
|
None
|
||||||
let length = csr::ethmac::sram_writer_length_read();
|
|
||||||
Ok(RxBuffer(slice::from_raw_parts(RX_BUFFERS[slot as usize],
|
|
||||||
length as usize)))
|
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Exhausted)
|
Some(csr::ethmac::sram_writer_slot_read() as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transmit(&mut self, _timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
|
fn next_tx_slot() -> Option<usize> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if csr::ethmac::sram_reader_ready_read() != 0 {
|
if csr::ethmac::sram_reader_ready_read() == 0 {
|
||||||
let slot = csr::ethmac::sram_reader_slot_read();
|
None
|
||||||
let slot = (slot + 1) % (TX_BUFFERS.len() as u8);
|
} else {
|
||||||
csr::ethmac::sram_reader_slot_write(slot);
|
Some((csr::ethmac::sram_reader_slot_read() as usize + 1) % TX_SLOTS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rx_buffer(slot: usize) -> *const u8 {
|
||||||
|
debug_assert!(slot < RX_SLOTS);
|
||||||
|
(mem::ETHMAC_BASE + SLOT_SIZE * slot) as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tx_buffer(slot: usize) -> *mut u8 {
|
||||||
|
debug_assert!(slot < TX_SLOTS);
|
||||||
|
(mem::ETHMAC_BASE + SLOT_SIZE * (RX_SLOTS + slot)) as _
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EthernetDevice(());
|
||||||
|
|
||||||
|
impl EthernetDevice {
|
||||||
|
pub unsafe fn new() -> EthernetDevice {
|
||||||
|
EthernetDevice(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Device<'a> for EthernetDevice {
|
||||||
|
type RxToken = EthernetRxSlot;
|
||||||
|
type TxToken = EthernetTxSlot;
|
||||||
|
|
||||||
|
fn capabilities(&self) -> DeviceCapabilities {
|
||||||
|
let mut caps = DeviceCapabilities::default();
|
||||||
|
caps.max_transmission_unit = 1514;
|
||||||
|
caps.max_burst_size = Some(RX_SLOTS);
|
||||||
|
caps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||||
|
if let (Some(rx_slot), Some(tx_slot)) = (next_rx_slot(), next_tx_slot()) {
|
||||||
|
Some((EthernetRxSlot(rx_slot), EthernetTxSlot(tx_slot)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transmit(&mut self) -> Option<Self::TxToken> {
|
||||||
|
if let Some(tx_slot) = next_tx_slot() {
|
||||||
|
Some(EthernetTxSlot(tx_slot))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EthernetRxSlot(usize);
|
||||||
|
|
||||||
|
impl phy::RxToken for EthernetRxSlot {
|
||||||
|
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
|
||||||
|
where F: FnOnce(&[u8]) -> Result<R>
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let length = csr::ethmac::sram_writer_length_read() as usize;
|
||||||
|
let result = f(slice::from_raw_parts(rx_buffer(self.0), length));
|
||||||
|
csr::ethmac::sram_writer_ev_pending_write(1);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EthernetTxSlot(usize);
|
||||||
|
|
||||||
|
impl phy::TxToken for EthernetTxSlot {
|
||||||
|
fn consume<R, F>(self, _timestamp: Instant, length: usize, f: F) -> Result<R>
|
||||||
|
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||||
|
{
|
||||||
|
debug_assert!(length < SLOT_SIZE);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let result = f(slice::from_raw_parts_mut(tx_buffer(self.0), length))?;
|
||||||
|
csr::ethmac::sram_reader_slot_write(self.0 as u8);
|
||||||
csr::ethmac::sram_reader_length_write(length as u16);
|
csr::ethmac::sram_reader_length_write(length as u16);
|
||||||
Ok(TxBuffer(slice::from_raw_parts_mut(TX_BUFFERS[slot as usize],
|
csr::ethmac::sram_reader_start_write(1);
|
||||||
length as usize)))
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub struct EthernetStatistics {
|
||||||
|
rx_preamble_errors: u32,
|
||||||
|
rx_crc_errors: u32,
|
||||||
|
rx_dropped: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthernetStatistics {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
EthernetStatistics {
|
||||||
|
rx_preamble_errors: csr::ethmac::preamble_errors_read(),
|
||||||
|
rx_crc_errors: csr::ethmac::crc_errors_read(),
|
||||||
|
rx_dropped: csr::ethmac::sram_writer_errors_read(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) -> Option<Self> {
|
||||||
|
let old = self.clone();
|
||||||
|
*self = Self::new();
|
||||||
|
|
||||||
|
let diff = EthernetStatistics {
|
||||||
|
rx_preamble_errors: self.rx_preamble_errors.wrapping_sub(old.rx_preamble_errors),
|
||||||
|
rx_crc_errors: self.rx_crc_errors.wrapping_sub(old.rx_crc_errors),
|
||||||
|
rx_dropped: self.rx_dropped.wrapping_sub(old.rx_dropped),
|
||||||
|
};
|
||||||
|
if diff == EthernetStatistics::default() {
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Exhausted)
|
Some(diff)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RxBuffer(&'static [u8]);
|
impl fmt::Display for EthernetStatistics {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
impl AsRef<[u8]> for RxBuffer {
|
if self.rx_preamble_errors > 0 {
|
||||||
fn as_ref(&self) -> &[u8] { self.0 }
|
write!(f, " rx preamble errors: {}", self.rx_preamble_errors)?
|
||||||
}
|
}
|
||||||
|
if self.rx_crc_errors > 0 {
|
||||||
impl Drop for RxBuffer {
|
write!(f, " rx crc errors: {}", self.rx_crc_errors)?
|
||||||
fn drop(&mut self) {
|
}
|
||||||
unsafe { csr::ethmac::sram_writer_ev_pending_write(1) }
|
if self.rx_dropped > 0 {
|
||||||
}
|
write!(f, " rx dropped: {}", self.rx_dropped)?
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
pub struct TxBuffer(&'static mut [u8]);
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for TxBuffer {
|
|
||||||
fn as_ref(&self) -> &[u8] { self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[u8]> for TxBuffer {
|
|
||||||
fn as_mut(&mut self) -> &mut [u8] { self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TxBuffer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { csr::ethmac::sram_reader_start_write(1) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,7 +319,7 @@ pub fn process_kern_hwreq(io: &Io, request: &kern::Message) -> io::Result<bool>
|
||||||
match request {
|
match request {
|
||||||
&kern::RtioInitRequest => {
|
&kern::RtioInitRequest => {
|
||||||
info!("resetting RTIO");
|
info!("resetting RTIO");
|
||||||
rtio_mgt::init_core();
|
rtio_mgt::init_core(false);
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(compiler_builtins_lib, alloc, repr_simd, lang_items, const_fn, global_allocator)]
|
#![feature(lang_items, alloc, panic_implementation, panic_info_message)]
|
||||||
|
|
||||||
extern crate compiler_builtins;
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate cslice;
|
extern crate cslice;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -14,6 +13,7 @@ extern crate alloc_list;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std_artiq as std;
|
extern crate std_artiq as std;
|
||||||
extern crate logger_artiq;
|
extern crate logger_artiq;
|
||||||
|
extern crate backtrace_artiq;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate board;
|
extern crate board;
|
||||||
extern crate proto;
|
extern crate proto;
|
||||||
|
@ -21,24 +21,12 @@ extern crate amp;
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
extern crate drtioaux;
|
extern crate drtioaux;
|
||||||
|
|
||||||
use std::boxed::Box;
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress};
|
|
||||||
|
use board::{config, irq, boot, clock};
|
||||||
use proto::{mgmt_proto, analyzer_proto, moninj_proto, rpc_proto, session_proto, kernel_proto};
|
use proto::{mgmt_proto, analyzer_proto, moninj_proto, rpc_proto, session_proto, kernel_proto};
|
||||||
use amp::{mailbox, rpc_queue};
|
use amp::{mailbox, rpc_queue};
|
||||||
|
|
||||||
macro_rules! borrow_mut {
|
|
||||||
($x:expr) => ({
|
|
||||||
match $x.try_borrow_mut() {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(_) => panic!("cannot borrow mutably at {}:{}", file!(), line!())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(has_spiflash)]
|
|
||||||
mod config;
|
|
||||||
#[cfg(not(has_spiflash))]
|
|
||||||
#[path="config_dummy.rs"] mod config;
|
|
||||||
mod ethmac;
|
mod ethmac;
|
||||||
mod rtio_mgt;
|
mod rtio_mgt;
|
||||||
|
|
||||||
|
@ -95,7 +83,7 @@ fn startup() {
|
||||||
|
|
||||||
let protocol_addr;
|
let protocol_addr;
|
||||||
match config::read_str("ip", |r| r?.parse()) {
|
match config::read_str("ip", |r| r?.parse()) {
|
||||||
Err(()) | Ok(IpAddress::Unspecified) => {
|
Err(()) => {
|
||||||
protocol_addr = IpAddress::v4(192, 168, 1, 50);
|
protocol_addr = IpAddress::v4(192, 168, 1, 50);
|
||||||
info!("using default IP address {}", protocol_addr);
|
info!("using default IP address {}", protocol_addr);
|
||||||
}
|
}
|
||||||
|
@ -105,19 +93,36 @@ fn startup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn _net_trace_writer<U>(timestamp: u64, printer: smoltcp::wire::PrettyPrinter<U>)
|
let net_device = unsafe { ethmac::EthernetDevice::new() };
|
||||||
// where U: smoltcp::wire::pretty_print::PrettyPrint {
|
|
||||||
// let seconds = timestamp / 1000;
|
|
||||||
// let micros = timestamp % 1000 * 1000;
|
|
||||||
// print!("\x1b[37m[{:6}.{:06}s]\n{}\x1b[0m", seconds, micros, printer)
|
|
||||||
// }
|
|
||||||
|
|
||||||
let net_device = ethmac::EthernetDevice;
|
let net_device = {
|
||||||
// let net_device = smoltcp::phy::EthernetTracer::new(net_device, _net_trace_writer);
|
use smoltcp::time::Instant;
|
||||||
let arp_cache = smoltcp::iface::SliceArpCache::new([Default::default(); 8]);
|
use smoltcp::wire::PrettyPrinter;
|
||||||
let mut interface = smoltcp::iface::EthernetInterface::new(
|
use smoltcp::wire::EthernetFrame;
|
||||||
Box::new(net_device), Box::new(arp_cache) as Box<smoltcp::iface::ArpCache>,
|
|
||||||
hardware_addr, [protocol_addr]);
|
fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
|
||||||
|
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n",
|
||||||
|
timestamp.secs(), timestamp.millis(), printer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
|
||||||
|
|
||||||
|
let net_trace_fn: fn(Instant, PrettyPrinter<EthernetFrame<&[u8]>>);
|
||||||
|
match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
|
||||||
|
Ok(true) => net_trace_fn = net_trace_writer,
|
||||||
|
_ => net_trace_fn = net_trace_silent
|
||||||
|
}
|
||||||
|
smoltcp::phy::EthernetTracer::new(net_device, net_trace_fn)
|
||||||
|
};
|
||||||
|
|
||||||
|
let neighbor_cache =
|
||||||
|
smoltcp::iface::NeighborCache::new(alloc::btree_map::BTreeMap::new());
|
||||||
|
let mut interface =
|
||||||
|
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
|
||||||
|
.neighbor_cache(neighbor_cache)
|
||||||
|
.ethernet_addr(hardware_addr)
|
||||||
|
.ip_addrs([IpCidr::new(protocol_addr, 0)])
|
||||||
|
.finalize();
|
||||||
|
|
||||||
let mut scheduler = sched::Scheduler::new();
|
let mut scheduler = sched::Scheduler::new();
|
||||||
let io = scheduler.io();
|
let io = scheduler.io();
|
||||||
|
@ -129,39 +134,41 @@ fn startup() {
|
||||||
#[cfg(has_rtio_analyzer)]
|
#[cfg(has_rtio_analyzer)]
|
||||||
io.spawn(4096, analyzer::thread);
|
io.spawn(4096, analyzer::thread);
|
||||||
|
|
||||||
match config::read_str("log_level", |r| r?.parse()) {
|
match config::read_str("log_level", |r| r.map(|s| s.parse())) {
|
||||||
Err(()) => (),
|
Ok(Ok(log_level_filter)) => {
|
||||||
Ok(log_level_filter) => {
|
|
||||||
info!("log level set to {} by `log_level` config key",
|
info!("log level set to {} by `log_level` config key",
|
||||||
log_level_filter);
|
log_level_filter);
|
||||||
logger_artiq::BufferLogger::with(|logger|
|
log::set_max_level(log_level_filter);
|
||||||
logger.set_max_log_level(log_level_filter));
|
|
||||||
}
|
}
|
||||||
|
_ => info!("log level set to INFO by default")
|
||||||
}
|
}
|
||||||
|
match config::read_str("uart_log_level", |r| r.map(|s| s.parse())) {
|
||||||
match config::read_str("uart_log_level", |r| r?.parse()) {
|
Ok(Ok(uart_log_level_filter)) => {
|
||||||
Err(()) => {
|
|
||||||
info!("UART log level set to INFO by default");
|
|
||||||
},
|
|
||||||
Ok(uart_log_level_filter) => {
|
|
||||||
info!("UART log level set to {} by `uart_log_level` config key",
|
info!("UART log level set to {} by `uart_log_level` config key",
|
||||||
uart_log_level_filter);
|
uart_log_level_filter);
|
||||||
logger_artiq::BufferLogger::with(|logger|
|
logger_artiq::BufferLogger::with(|logger|
|
||||||
logger.set_uart_log_level(uart_log_level_filter));
|
logger.set_uart_log_level(uart_log_level_filter));
|
||||||
}
|
}
|
||||||
|
_ => info!("UART log level set to INFO by default")
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
scheduler.run();
|
scheduler.run();
|
||||||
|
|
||||||
match interface.poll(&mut *borrow_mut!(scheduler.sockets()),
|
{
|
||||||
board::clock::get_ms()) {
|
let sockets = &mut *scheduler.sockets().borrow_mut();
|
||||||
Ok(_poll_at) => (),
|
loop {
|
||||||
|
let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
|
||||||
|
match interface.poll(sockets, timestamp) {
|
||||||
|
Ok(true) => (),
|
||||||
|
Ok(false) => break,
|
||||||
Err(smoltcp::Error::Unrecognized) => (),
|
Err(smoltcp::Error::Unrecognized) => (),
|
||||||
Err(err) => warn!("network error: {}", err)
|
Err(err) => warn!("network error: {}", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static mut ALLOC: alloc_list::ListAlloc = alloc_list::EMPTY;
|
static mut ALLOC: alloc_list::ListAlloc = alloc_list::EMPTY;
|
||||||
|
@ -189,28 +196,47 @@ pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32)
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern fn abort() {
|
pub extern fn abort() {
|
||||||
panic!("aborted")
|
println!("aborted");
|
||||||
|
|
||||||
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
#[lang = "panic_fmt"]
|
#[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
|
||||||
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
pub fn oom(layout: core::alloc::Layout) -> ! {
|
||||||
println!("panic at {}:{}: {}", file, line, args);
|
panic!("heap view: {}\ncannot allocate layout: {:?}", unsafe { &ALLOC }, layout)
|
||||||
|
}
|
||||||
|
|
||||||
if config::read_str("panic_reboot", |r| r == Ok("1")) {
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
println!("rebooting...");
|
#[panic_implementation]
|
||||||
unsafe { board::boot::reboot() }
|
pub fn panic_impl(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
irq::set_ie(false);
|
||||||
|
|
||||||
|
if let Some(location) = info.location() {
|
||||||
|
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
|
||||||
|
} else {
|
||||||
|
print!("panic at unknown location");
|
||||||
|
}
|
||||||
|
if let Some(message) = info.message() {
|
||||||
|
println!("{}", message);
|
||||||
|
} else {
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("backtrace for software version {}:",
|
||||||
|
include_str!(concat!(env!("OUT_DIR"), "/git-describe")));
|
||||||
|
let _ = backtrace_artiq::backtrace(|ip| {
|
||||||
|
// Backtrace gives us the return address, i.e. the address after the delay slot,
|
||||||
|
// but we're interested in the call instruction.
|
||||||
|
println!("{:#08x}", ip - 2 * 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
if config::read_str("panic_reset", |r| r == Ok("1")) {
|
||||||
|
println!("restarting...");
|
||||||
|
unsafe { boot::reboot() }
|
||||||
} else {
|
} else {
|
||||||
println!("halting.");
|
println!("halting.");
|
||||||
println!("use `artiq_coreconfig write -s panic_reboot 1` to reboot instead");
|
println!("use `artiq_coreconfig write -s panic_reset 1` to restart instead");
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort.
|
|
||||||
// This is never called.
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern fn _Unwind_Resume() -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use log::{self, LevelFilter};
|
||||||
|
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use log::LogLevelFilter;
|
|
||||||
use logger_artiq::BufferLogger;
|
use logger_artiq::BufferLogger;
|
||||||
use sched::Io;
|
use sched::Io;
|
||||||
use sched::{TcpListener, TcpStream};
|
use sched::{TcpListener, TcpStream};
|
||||||
|
@ -27,28 +28,31 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
|
||||||
match Request::read_from(stream)? {
|
match Request::read_from(stream)? {
|
||||||
Request::GetLog => {
|
Request::GetLog => {
|
||||||
BufferLogger::with(|logger| {
|
BufferLogger::with(|logger| {
|
||||||
logger.extract(|log| {
|
let mut buffer = io.until_ok(|| logger.buffer())?;
|
||||||
Reply::LogContent(log).write_to(stream)
|
Reply::LogContent(buffer.extract()).write_to(stream)
|
||||||
})
|
|
||||||
})?;
|
})?;
|
||||||
},
|
}
|
||||||
|
|
||||||
Request::ClearLog => {
|
Request::ClearLog => {
|
||||||
BufferLogger::with(|logger|
|
BufferLogger::with(|logger| -> io::Result<()> {
|
||||||
logger.clear());
|
let mut buffer = io.until_ok(|| logger.buffer())?;
|
||||||
|
Ok(buffer.clear())
|
||||||
|
})?;
|
||||||
|
|
||||||
Reply::Success.write_to(stream)?;
|
Reply::Success.write_to(stream)?;
|
||||||
},
|
}
|
||||||
|
|
||||||
Request::PullLog => {
|
Request::PullLog => {
|
||||||
|
BufferLogger::with(|logger| -> io::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
io.until(|| BufferLogger::with(|logger| !logger.is_empty()))?;
|
// Do this *before* acquiring the buffer, since that sets the log level
|
||||||
|
// to OFF.
|
||||||
|
let log_level = log::max_level();
|
||||||
|
|
||||||
BufferLogger::with(|logger| {
|
let mut buffer = io.until_ok(|| logger.buffer())?;
|
||||||
let log_level = logger.max_log_level();
|
if buffer.is_empty() { continue }
|
||||||
logger.extract(|log| {
|
|
||||||
stream.write_string(log)?;
|
|
||||||
|
|
||||||
if log_level == LogLevelFilter::Trace {
|
stream.write_string(buffer.extract())?;
|
||||||
|
|
||||||
|
if log_level == LevelFilter::Trace {
|
||||||
// Hold exclusive access over the logger until we get positive
|
// Hold exclusive access over the logger until we get positive
|
||||||
// acknowledgement; otherwise we get an infinite loop of network
|
// acknowledgement; otherwise we get an infinite loop of network
|
||||||
// trace messages being transmitted and causing more network
|
// trace messages being transmitted and causing more network
|
||||||
|
@ -56,30 +60,26 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
|
||||||
//
|
//
|
||||||
// Any messages unrelated to this management socket that arrive
|
// Any messages unrelated to this management socket that arrive
|
||||||
// while it is flushed are lost, but such is life.
|
// while it is flushed are lost, but such is life.
|
||||||
stream.flush()
|
stream.flush()?;
|
||||||
} else {
|
}
|
||||||
Ok(())
|
|
||||||
|
// Clear the log *after* flushing the network buffers, or we're just
|
||||||
|
// going to resend all the trace messages on the next iteration.
|
||||||
|
buffer.clear();
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(logger.clear()) as io::Result<()>
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
Request::SetLogFilter(level) => {
|
Request::SetLogFilter(level) => {
|
||||||
info!("changing log level to {}", level);
|
info!("changing log level to {}", level);
|
||||||
BufferLogger::with(|logger|
|
log::set_max_level(level);
|
||||||
logger.set_max_log_level(level));
|
|
||||||
Reply::Success.write_to(stream)?;
|
Reply::Success.write_to(stream)?;
|
||||||
},
|
}
|
||||||
|
|
||||||
Request::SetUartLogFilter(level) => {
|
Request::SetUartLogFilter(level) => {
|
||||||
info!("changing UART log level to {}", level);
|
info!("changing UART log level to {}", level);
|
||||||
BufferLogger::with(|logger|
|
BufferLogger::with(|logger|
|
||||||
logger.set_uart_log_level(level));
|
logger.set_uart_log_level(level));
|
||||||
Reply::Success.write_to(stream)?;
|
Reply::Success.write_to(stream)?;
|
||||||
},
|
}
|
||||||
|
|
||||||
Request::Hotswap(firmware) => {
|
Request::Hotswap(firmware) => {
|
||||||
Reply::RebootImminent.write_to(stream)?;
|
Reply::RebootImminent.write_to(stream)?;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use config;
|
use board::{csr, config};
|
||||||
use board::csr;
|
|
||||||
use sched::Io;
|
use sched::Io;
|
||||||
|
|
||||||
#[cfg(has_rtio_crg)]
|
#[cfg(has_rtio_crg)]
|
||||||
|
@ -219,16 +218,25 @@ pub fn startup(io: &Io) {
|
||||||
|
|
||||||
let clk = config::read("startup_clock", |result| {
|
let clk = config::read("startup_clock", |result| {
|
||||||
match result {
|
match result {
|
||||||
Ok(b"i") => RtioClock::Internal,
|
Ok(b"i") => {
|
||||||
Ok(b"e") => RtioClock::External,
|
info!("using internal startup RTIO clock");
|
||||||
_ => {
|
RtioClock::Internal
|
||||||
error!("unrecognized startup_clock configuration entry");
|
},
|
||||||
|
Ok(b"e") => {
|
||||||
|
info!("using external startup RTIO clock");
|
||||||
|
RtioClock::External
|
||||||
|
},
|
||||||
|
Err(()) => {
|
||||||
|
info!("using internal startup RTIO clock (by default)");
|
||||||
|
RtioClock::Internal
|
||||||
|
},
|
||||||
|
Ok(_) => {
|
||||||
|
error!("unrecognized startup_clock configuration entry, using internal RTIO clock");
|
||||||
RtioClock::Internal
|
RtioClock::Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
info!("startup RTIO clock: {:?}", clk);
|
|
||||||
if !crg::switch_clock(clk as u8) {
|
if !crg::switch_clock(clk as u8) {
|
||||||
error!("startup RTIO clock failed");
|
error!("startup RTIO clock failed");
|
||||||
warn!("this may cause the system initialization to fail");
|
warn!("this may cause the system initialization to fail");
|
||||||
|
@ -236,13 +244,16 @@ pub fn startup(io: &Io) {
|
||||||
}
|
}
|
||||||
|
|
||||||
drtio::startup(io);
|
drtio::startup(io);
|
||||||
init_core();
|
init_core(true);
|
||||||
io.spawn(4096, async_error_thread);
|
io.spawn(4096, async_error_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_core() {
|
pub fn init_core(phy: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::rtio_core::reset_write(1);
|
csr::rtio_core::reset_write(1);
|
||||||
|
if phy {
|
||||||
|
csr::rtio_core::reset_phy_write(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
drtio::init()
|
drtio::init()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,21 @@ SECTIONS
|
||||||
_etext = .;
|
_etext = .;
|
||||||
} > runtime
|
} > runtime
|
||||||
|
|
||||||
|
.eh_frame :
|
||||||
|
{
|
||||||
|
__eh_frame_start = .;
|
||||||
|
KEEP(*(.eh_frame))
|
||||||
|
__eh_frame_end = .;
|
||||||
|
} > runtime
|
||||||
|
|
||||||
|
.eh_frame_hdr :
|
||||||
|
{
|
||||||
|
KEEP(*(.eh_frame_hdr))
|
||||||
|
} > runtime
|
||||||
|
|
||||||
|
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
|
||||||
|
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
|
||||||
|
|
||||||
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
||||||
.got :
|
.got :
|
||||||
{
|
{
|
||||||
|
@ -79,10 +94,4 @@ SECTIONS
|
||||||
. = ORIGIN(runtime) + LENGTH(runtime);
|
. = ORIGIN(runtime) + LENGTH(runtime);
|
||||||
_eheap = .;
|
_eheap = .;
|
||||||
} > runtime
|
} > runtime
|
||||||
|
|
||||||
/DISCARD/ :
|
|
||||||
{
|
|
||||||
*(.eh_frame)
|
|
||||||
*(.gcc_except_table)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::cell::{Cell, RefCell, RefMut};
|
use std::result;
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::io::{Read, Write, Result, Error, ErrorKind};
|
use std::io::{Read, Write, Result, Error, ErrorKind};
|
||||||
use fringe::OwnedStack;
|
use fringe::OwnedStack;
|
||||||
use fringe::generator::{Generator, Yielder, State as GeneratorState};
|
use fringe::generator::{Generator, Yielder, State as GeneratorState};
|
||||||
|
|
||||||
|
use smoltcp::time::Duration;
|
||||||
|
use smoltcp::Error as NetworkError;
|
||||||
use smoltcp::wire::IpEndpoint;
|
use smoltcp::wire::IpEndpoint;
|
||||||
use smoltcp::socket::{AsSocket, SocketHandle};
|
use smoltcp::socket::{SocketHandle, SocketRef};
|
||||||
type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>;
|
|
||||||
|
|
||||||
use board;
|
use board;
|
||||||
use urc::Urc;
|
use urc::Urc;
|
||||||
|
|
||||||
|
type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WaitRequest {
|
struct WaitRequest {
|
||||||
event: Option<*const (Fn() -> bool + 'static)>,
|
event: Option<*mut FnMut() -> bool>,
|
||||||
timeout: Option<u64>
|
timeout: Option<u64>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +127,7 @@ impl Scheduler {
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
self.sockets.borrow_mut().prune();
|
self.sockets.borrow_mut().prune();
|
||||||
|
|
||||||
self.threads.append(&mut *borrow_mut!(self.spawned));
|
self.threads.append(&mut *self.spawned.borrow_mut());
|
||||||
if self.threads.len() == 0 { return }
|
if self.threads.len() == 0 { return }
|
||||||
|
|
||||||
let now = board::clock::get_ms();
|
let now = board::clock::get_ms();
|
||||||
|
@ -132,27 +136,23 @@ impl Scheduler {
|
||||||
self.run_idx = (self.run_idx + 1) % self.threads.len();
|
self.run_idx = (self.run_idx + 1) % self.threads.len();
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let mut thread = borrow_mut!(self.threads[self.run_idx].0);
|
let &mut Thread { ref mut generator, ref mut interrupted, ref waiting_for } =
|
||||||
match thread.waiting_for {
|
&mut *self.threads[self.run_idx].0.borrow_mut();
|
||||||
_ if thread.interrupted => {
|
if *interrupted {
|
||||||
thread.interrupted = false;
|
*interrupted = false;
|
||||||
thread.generator.resume(WaitResult::Interrupted)
|
generator.resume(WaitResult::Interrupted)
|
||||||
}
|
} else if waiting_for.event.is_none() && waiting_for.timeout.is_none() {
|
||||||
WaitRequest { event: None, timeout: None } =>
|
generator.resume(WaitResult::Completed)
|
||||||
thread.generator.resume(WaitResult::Completed),
|
} else if waiting_for.timeout.map(|instant| now >= instant).unwrap_or(false) {
|
||||||
WaitRequest { timeout: Some(instant), .. } if now >= instant =>
|
generator.resume(WaitResult::TimedOut)
|
||||||
thread.generator.resume(WaitResult::TimedOut),
|
} else if waiting_for.event.map(|event| unsafe { (*event)() }).unwrap_or(false) {
|
||||||
WaitRequest { event: Some(event), .. } if unsafe { (*event)() } =>
|
generator.resume(WaitResult::Completed)
|
||||||
thread.generator.resume(WaitResult::Completed),
|
} else if self.run_idx == start_idx {
|
||||||
_ => {
|
|
||||||
if self.run_idx == start_idx {
|
|
||||||
// We've checked every thread and none of them are runnable.
|
// We've checked every thread and none of them are runnable.
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
@ -163,7 +163,7 @@ impl Scheduler {
|
||||||
},
|
},
|
||||||
Some(wait_request) => {
|
Some(wait_request) => {
|
||||||
// The thread has suspended itself.
|
// The thread has suspended itself.
|
||||||
let mut thread = borrow_mut!(self.threads[self.run_idx].0);
|
let mut thread = self.threads[self.run_idx].0.borrow_mut();
|
||||||
thread.waiting_for = wait_request
|
thread.waiting_for = wait_request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ impl<'a> Io<'a> {
|
||||||
pub fn spawn<F>(&self, stack_size: usize, f: F) -> ThreadHandle
|
pub fn spawn<F>(&self, stack_size: usize, f: F) -> ThreadHandle
|
||||||
where F: 'static + FnOnce(Io) + Send {
|
where F: 'static + FnOnce(Io) + Send {
|
||||||
let handle = unsafe { Thread::new(self, stack_size, f) };
|
let handle = unsafe { Thread::new(self, stack_size, f) };
|
||||||
borrow_mut!(self.spawned).push(handle.clone());
|
self.spawned.borrow_mut().push(handle.clone());
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,13 +224,25 @@ impl<'a> Io<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn until<F: Fn() -> bool + 'static>(&self, f: F) -> Result<()> {
|
pub fn until<F: FnMut() -> bool>(&self, mut f: F) -> Result<()> {
|
||||||
|
let f = unsafe { mem::transmute::<&mut FnMut() -> bool, *mut FnMut() -> bool>(&mut f) };
|
||||||
self.suspend(WaitRequest {
|
self.suspend(WaitRequest {
|
||||||
timeout: None,
|
timeout: None,
|
||||||
event: Some(&f as *const _)
|
event: Some(f)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn until_ok<T, E, F: FnMut() -> result::Result<T, E>>(&self, mut f: F) -> Result<T> {
|
||||||
|
let mut value = None;
|
||||||
|
self.until(|| {
|
||||||
|
if let Ok(result) = f() {
|
||||||
|
value = Some(result)
|
||||||
|
}
|
||||||
|
value.is_some()
|
||||||
|
})?;
|
||||||
|
Ok(value.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn join(&self, handle: ThreadHandle) -> Result<()> {
|
pub fn join(&self, handle: ThreadHandle) -> Result<()> {
|
||||||
self.until(move || handle.terminated())
|
self.until(move || handle.terminated())
|
||||||
}
|
}
|
||||||
|
@ -240,87 +252,18 @@ macro_rules! until {
|
||||||
($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({
|
($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({
|
||||||
let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle);
|
let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle);
|
||||||
$socket.io.until(move || {
|
$socket.io.until(move || {
|
||||||
let mut sockets = borrow_mut!(sockets);
|
let mut sockets = sockets.borrow_mut();
|
||||||
let $var: &mut $ty = sockets.get_mut(handle).as_socket();
|
let $var = sockets.get::<$ty>(handle);
|
||||||
$cond
|
$cond
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
use ::smoltcp::Error as ErrorLower;
|
use ::smoltcp::Error as ErrorLower;
|
||||||
// https://github.com/rust-lang/rust/issues/44057
|
// https://github.com/rust-lang/rust/issues/26264
|
||||||
// type ErrorLower = ::smoltcp::Error;
|
// type ErrorLower = ::smoltcp::Error;
|
||||||
|
|
||||||
type UdpPacketBuffer = ::smoltcp::socket::UdpPacketBuffer<'static>;
|
|
||||||
type UdpSocketBuffer = ::smoltcp::socket::UdpSocketBuffer<'static, 'static>;
|
|
||||||
type UdpSocketLower = ::smoltcp::socket::UdpSocket<'static, 'static>;
|
|
||||||
|
|
||||||
pub struct UdpSocket<'a> {
|
|
||||||
io: &'a Io<'a>,
|
|
||||||
handle: SocketHandle
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> UdpSocket<'a> {
|
|
||||||
pub fn new(io: &'a Io<'a>, buffer_depth: usize, buffer_width: usize) -> UdpSocket<'a> {
|
|
||||||
let mut rx_buffer = vec![];
|
|
||||||
let mut tx_buffer = vec![];
|
|
||||||
for _ in 0..buffer_depth {
|
|
||||||
rx_buffer.push(UdpPacketBuffer::new(vec![0; buffer_width]));
|
|
||||||
tx_buffer.push(UdpPacketBuffer::new(vec![0; buffer_width]));
|
|
||||||
}
|
|
||||||
let handle = borrow_mut!(io.sockets)
|
|
||||||
.add(UdpSocketLower::new(
|
|
||||||
UdpSocketBuffer::new(rx_buffer),
|
|
||||||
UdpSocketBuffer::new(tx_buffer)));
|
|
||||||
UdpSocket {
|
|
||||||
io: io,
|
|
||||||
handle: handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_lower<'b>(&'b self) -> RefMut<'b, UdpSocketLower> {
|
|
||||||
RefMut::map(borrow_mut!(self.io.sockets),
|
|
||||||
|sockets| sockets.get_mut(self.handle).as_socket())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bind<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<()> {
|
|
||||||
match self.as_lower().bind(endpoint) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(ErrorLower::Illegal) =>
|
|
||||||
Err(Error::new(ErrorKind::Other, "already listening")),
|
|
||||||
Err(ErrorLower::Unaddressable) =>
|
|
||||||
Err(Error::new(ErrorKind::AddrNotAvailable, "port cannot be zero")),
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint)> {
|
|
||||||
until!(self, UdpSocketLower, |s| s.can_recv())?;
|
|
||||||
match self.as_lower().recv_slice(buf) {
|
|
||||||
Ok(result) => Ok(result),
|
|
||||||
Err(_) => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_to(&self, buf: &[u8], addr: IpEndpoint) -> Result<()> {
|
|
||||||
until!(self, UdpSocketLower, |s| s.can_send())?;
|
|
||||||
match self.as_lower().send_slice(buf, addr) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(ErrorLower::Unaddressable) =>
|
|
||||||
Err(Error::new(ErrorKind::AddrNotAvailable, "unaddressable destination")),
|
|
||||||
Err(ErrorLower::Truncated) =>
|
|
||||||
Err(Error::new(ErrorKind::Other, "packet does not fit in buffer")),
|
|
||||||
Err(_) => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for UdpSocket<'a> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
borrow_mut!(self.io.sockets).release(self.handle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>;
|
type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>;
|
||||||
type TcpSocketLower = ::smoltcp::socket::TcpSocket<'static>;
|
type TcpSocketLower = ::smoltcp::socket::TcpSocket<'static>;
|
||||||
|
|
||||||
|
@ -337,7 +280,8 @@ impl<'a> TcpListener<'a> {
|
||||||
fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle {
|
fn new_lower(io: &'a Io<'a>, buffer_size: usize) -> SocketHandle {
|
||||||
let rx_buffer = vec![0; buffer_size];
|
let rx_buffer = vec![0; buffer_size];
|
||||||
let tx_buffer = vec![0; buffer_size];
|
let tx_buffer = vec![0; buffer_size];
|
||||||
borrow_mut!(io.sockets)
|
io.sockets
|
||||||
|
.borrow_mut()
|
||||||
.add(TcpSocketLower::new(
|
.add(TcpSocketLower::new(
|
||||||
TcpSocketBuffer::new(rx_buffer),
|
TcpSocketBuffer::new(rx_buffer),
|
||||||
TcpSocketBuffer::new(tx_buffer)))
|
TcpSocketBuffer::new(tx_buffer)))
|
||||||
|
@ -352,35 +296,41 @@ impl<'a> TcpListener<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_lower<'b>(&'b self) -> RefMut<'b, TcpSocketLower> {
|
fn with_lower<F, R>(&self, f: F) -> R
|
||||||
RefMut::map(borrow_mut!(self.io.sockets),
|
where F: FnOnce(SocketRef<TcpSocketLower>) -> R {
|
||||||
|sockets| sockets.get_mut(self.handle.get()).as_socket())
|
let mut sockets = self.io.sockets.borrow_mut();
|
||||||
|
let result = f(sockets.get(self.handle.get()));
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.as_lower().is_open()
|
self.with_lower(|s| s.is_open())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_accept(&self) -> bool {
|
pub fn can_accept(&self) -> bool {
|
||||||
self.as_lower().is_active()
|
self.with_lower(|s| s.is_active())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_endpoint(&self) -> IpEndpoint {
|
pub fn local_endpoint(&self) -> IpEndpoint {
|
||||||
self.as_lower().local_endpoint()
|
self.with_lower(|s| s.local_endpoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<()> {
|
pub fn listen<T: Into<IpEndpoint>>(&self, endpoint: T) -> Result<()> {
|
||||||
let endpoint = endpoint.into();
|
let endpoint = endpoint.into();
|
||||||
match self.as_lower().listen(endpoint) {
|
self.with_lower(|mut s| s.listen(endpoint))
|
||||||
Ok(()) => Ok(()),
|
.map(|()| {
|
||||||
Err(ErrorLower::Illegal) =>
|
|
||||||
Err(Error::new(ErrorKind::Other, "already listening")),
|
|
||||||
Err(ErrorLower::Unaddressable) =>
|
|
||||||
Err(Error::new(ErrorKind::InvalidInput, "port cannot be zero")),
|
|
||||||
_ => unreachable!()
|
|
||||||
}?;
|
|
||||||
self.endpoint.set(endpoint);
|
self.endpoint.set(endpoint);
|
||||||
Ok(())
|
()
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
match err {
|
||||||
|
ErrorLower::Illegal =>
|
||||||
|
Error::new(ErrorKind::Other, "already listening"),
|
||||||
|
ErrorLower::Unaddressable =>
|
||||||
|
Error::new(ErrorKind::InvalidInput, "port cannot be zero"),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accept(&self) -> Result<TcpStream<'a>> {
|
pub fn accept(&self) -> Result<TcpStream<'a>> {
|
||||||
|
@ -389,8 +339,8 @@ impl<'a> TcpListener<'a> {
|
||||||
// that still counts as accepting even though nothing may be sent.
|
// that still counts as accepting even though nothing may be sent.
|
||||||
let (sockets, handle) = (self.io.sockets.clone(), self.handle.get());
|
let (sockets, handle) = (self.io.sockets.clone(), self.handle.get());
|
||||||
self.io.until(move || {
|
self.io.until(move || {
|
||||||
let mut sockets = borrow_mut!(sockets);
|
let mut sockets = sockets.borrow_mut();
|
||||||
let socket: &mut TcpSocketLower = sockets.get_mut(handle).as_socket();
|
let socket = sockets.get::<TcpSocketLower>(handle);
|
||||||
socket.may_send() || socket.may_recv()
|
socket.may_send() || socket.may_recv()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -407,14 +357,14 @@ impl<'a> TcpListener<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
self.as_lower().close()
|
self.with_lower(|mut s| s.close())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for TcpListener<'a> {
|
impl<'a> Drop for TcpListener<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.as_lower().close();
|
self.with_lower(|mut s| s.close());
|
||||||
borrow_mut!(self.io.sockets).release(self.handle.get())
|
self.io.sockets.borrow_mut().release(self.handle.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,57 +387,59 @@ impl<'a> TcpStream<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_lower<'b>(&'b self) -> RefMut<'b, TcpSocketLower> {
|
fn with_lower<F, R>(&self, f: F) -> R
|
||||||
RefMut::map(borrow_mut!(self.io.sockets),
|
where F: FnOnce(SocketRef<TcpSocketLower>) -> R {
|
||||||
|sockets| sockets.get_mut(self.handle).as_socket())
|
let mut sockets = self.io.sockets.borrow_mut();
|
||||||
|
let result = f(sockets.get(self.handle));
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.as_lower().is_open()
|
self.with_lower(|s| s.is_open())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn may_send(&self) -> bool {
|
pub fn may_send(&self) -> bool {
|
||||||
self.as_lower().may_send()
|
self.with_lower(|s| s.may_send())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn may_recv(&self) -> bool {
|
pub fn may_recv(&self) -> bool {
|
||||||
self.as_lower().may_recv()
|
self.with_lower(|s| s.may_recv())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_send(&self) -> bool {
|
pub fn can_send(&self) -> bool {
|
||||||
self.as_lower().can_send()
|
self.with_lower(|s| s.can_send())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_recv(&self) -> bool {
|
pub fn can_recv(&self) -> bool {
|
||||||
self.as_lower().can_recv()
|
self.with_lower(|s| s.can_recv())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_endpoint(&self) -> IpEndpoint {
|
pub fn local_endpoint(&self) -> IpEndpoint {
|
||||||
self.as_lower().local_endpoint()
|
self.with_lower(|s| s.local_endpoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote_endpoint(&self) -> IpEndpoint {
|
pub fn remote_endpoint(&self) -> IpEndpoint {
|
||||||
self.as_lower().remote_endpoint()
|
self.with_lower(|s| s.remote_endpoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn timeout(&self) -> Option<u64> {
|
pub fn timeout(&self) -> Option<u64> {
|
||||||
self.as_lower().timeout()
|
self.with_lower(|s| s.timeout().as_ref().map(Duration::millis))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_timeout(&self, value: Option<u64>) {
|
pub fn set_timeout(&self, value: Option<u64>) {
|
||||||
self.as_lower().set_timeout(value)
|
self.with_lower(|mut s| s.set_timeout(value.map(Duration::from_millis)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keep_alive(&self) -> Option<u64> {
|
pub fn keep_alive(&self) -> Option<u64> {
|
||||||
self.as_lower().keep_alive()
|
self.with_lower(|s| s.keep_alive().as_ref().map(Duration::millis))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_keep_alive(&self, value: Option<u64>) {
|
pub fn set_keep_alive(&self, value: Option<u64>) {
|
||||||
self.as_lower().set_keep_alive(value)
|
self.with_lower(|mut s| s.set_keep_alive(value.map(Duration::from_millis)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) -> Result<()> {
|
pub fn close(&self) -> Result<()> {
|
||||||
self.as_lower().close();
|
self.with_lower(|mut s| s.close());
|
||||||
until!(self, TcpSocketLower, |s| !s.is_open())?;
|
until!(self, TcpSocketLower, |s| !s.is_open())?;
|
||||||
// right now the socket may be in TIME-WAIT state. if we don't give it a chance to send
|
// right now the socket may be in TIME-WAIT state. if we don't give it a chance to send
|
||||||
// a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); }
|
// a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); }
|
||||||
|
@ -499,12 +451,12 @@ impl<'a> TcpStream<'a> {
|
||||||
impl<'a> Read for TcpStream<'a> {
|
impl<'a> Read for TcpStream<'a> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
// Only borrow the underlying socket for the span of the next statement.
|
// Only borrow the underlying socket for the span of the next statement.
|
||||||
let result = self.as_lower().recv_slice(buf);
|
let result = self.with_lower(|mut s| s.recv_slice(buf));
|
||||||
match result {
|
match result {
|
||||||
// Slow path: we need to block until buffer is non-empty.
|
// Slow path: we need to block until buffer is non-empty.
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?;
|
until!(self, TcpSocketLower, |s| s.can_recv() || !s.may_recv())?;
|
||||||
match self.as_lower().recv_slice(buf) {
|
match self.with_lower(|mut s| s.recv_slice(buf)) {
|
||||||
Ok(length) => Ok(length),
|
Ok(length) => Ok(length),
|
||||||
Err(ErrorLower::Illegal) => Ok(0),
|
Err(ErrorLower::Illegal) => Ok(0),
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
|
@ -523,12 +475,12 @@ impl<'a> Read for TcpStream<'a> {
|
||||||
impl<'a> Write for TcpStream<'a> {
|
impl<'a> Write for TcpStream<'a> {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||||
// Only borrow the underlying socket for the span of the next statement.
|
// Only borrow the underlying socket for the span of the next statement.
|
||||||
let result = self.as_lower().send_slice(buf);
|
let result = self.with_lower(|mut s| s.send_slice(buf));
|
||||||
match result {
|
match result {
|
||||||
// Slow path: we need to block until buffer is non-full.
|
// Slow path: we need to block until buffer is non-full.
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?;
|
until!(self, TcpSocketLower, |s| s.can_send() || !s.may_send())?;
|
||||||
match self.as_lower().send_slice(buf) {
|
match self.with_lower(|mut s| s.send_slice(buf)) {
|
||||||
Ok(length) => Ok(length),
|
Ok(length) => Ok(length),
|
||||||
Err(ErrorLower::Illegal) => Ok(0),
|
Err(ErrorLower::Illegal) => Ok(0),
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
|
@ -545,7 +497,7 @@ impl<'a> Write for TcpStream<'a> {
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<()> {
|
fn flush(&mut self) -> Result<()> {
|
||||||
until!(self, TcpSocketLower, |s| s.send_queue() == 0 || !s.may_send())?;
|
until!(self, TcpSocketLower, |s| s.send_queue() == 0 || !s.may_send())?;
|
||||||
if self.as_lower().send_queue() == 0 {
|
if self.with_lower(|s| s.send_queue()) == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(ErrorKind::ConnectionAborted, "connection aborted"))
|
Err(Error::new(ErrorKind::ConnectionAborted, "connection aborted"))
|
||||||
|
@ -555,7 +507,7 @@ impl<'a> Write for TcpStream<'a> {
|
||||||
|
|
||||||
impl<'a> Drop for TcpStream<'a> {
|
impl<'a> Drop for TcpStream<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.as_lower().close();
|
self.with_lower(|mut s| s.close());
|
||||||
borrow_mut!(self.io.sockets).release(self.handle)
|
self.io.sockets.borrow_mut().release(self.handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,14 @@ use std::{mem, str};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use {config, rtio_mgt, mailbox, rpc_queue, kernel};
|
use {rtio_mgt, mailbox, rpc_queue, kernel};
|
||||||
use cache::Cache;
|
use cache::Cache;
|
||||||
use rtio_dma::Manager as DmaManager;
|
use rtio_dma::Manager as DmaManager;
|
||||||
use urc::Urc;
|
use urc::Urc;
|
||||||
use sched::{ThreadHandle, Io};
|
use sched::{ThreadHandle, Io};
|
||||||
use sched::{TcpListener, TcpStream};
|
use sched::{TcpListener, TcpStream};
|
||||||
use byteorder::{ByteOrder, NetworkEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
use board;
|
use board::{self, config};
|
||||||
|
|
||||||
use rpc_proto as rpc;
|
use rpc_proto as rpc;
|
||||||
use session_proto as host;
|
use session_proto as host;
|
||||||
|
@ -440,7 +440,13 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>,
|
||||||
kern_acknowledge()
|
kern_acknowledge()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
&kern::RpcFlush => {
|
||||||
|
// See ksupport/lib.rs for the reason this request exists.
|
||||||
|
// We do not need to do anything here because of how the main loop is
|
||||||
|
// structured.
|
||||||
|
kern_acknowledge()
|
||||||
|
},
|
||||||
|
|
||||||
&kern::CacheGetRequest { key } => {
|
&kern::CacheGetRequest { key } => {
|
||||||
let value = session.congress.cache.get(key);
|
let value = session.congress.cache.get(key);
|
||||||
|
@ -508,7 +514,7 @@ fn process_kern_queued_rpc(stream: &mut TcpStream,
|
||||||
let length = NetworkEndian::read_u32(slice) as usize;
|
let length = NetworkEndian::read_u32(slice) as usize;
|
||||||
host_write(stream, host::Reply::RpcRequest { async: true })?;
|
host_write(stream, host::Reply::RpcRequest { async: true })?;
|
||||||
debug!("{:?}", &slice[4..][..length]);
|
debug!("{:?}", &slice[4..][..length]);
|
||||||
stream.write(&slice[4..][..length])?;
|
stream.write_all(&slice[4..][..length])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -519,16 +525,16 @@ fn host_kernel_worker(io: &Io,
|
||||||
let mut session = Session::new(congress);
|
let mut session = Session::new(congress);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
while !rpc_queue::empty() {
|
|
||||||
process_kern_queued_rpc(stream, &mut session)?
|
|
||||||
}
|
|
||||||
|
|
||||||
if stream.can_recv() {
|
if stream.can_recv() {
|
||||||
process_host_message(io, stream, &mut session)?
|
process_host_message(io, stream, &mut session)?
|
||||||
} else if !stream.may_recv() {
|
} else if !stream.may_recv() {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while !rpc_queue::empty() {
|
||||||
|
process_kern_queued_rpc(stream, &mut session)?
|
||||||
|
}
|
||||||
|
|
||||||
if mailbox::receive() != 0 {
|
if mailbox::receive() != 0 {
|
||||||
process_kern_message(io, Some(stream), &mut session)?;
|
process_kern_message(io, Some(stream), &mut session)?;
|
||||||
}
|
}
|
||||||
|
@ -615,7 +621,7 @@ pub fn thread(io: Io) {
|
||||||
{
|
{
|
||||||
let congress = congress.clone();
|
let congress = congress.clone();
|
||||||
respawn(&io, &mut kernel_thread, move |io| {
|
respawn(&io, &mut kernel_thread, move |io| {
|
||||||
let mut congress = borrow_mut!(congress);
|
let mut congress = congress.borrow_mut();
|
||||||
info!("running startup kernel");
|
info!("running startup kernel");
|
||||||
match flash_kernel_worker(&io, &mut congress, "startup_kernel") {
|
match flash_kernel_worker(&io, &mut congress, "startup_kernel") {
|
||||||
Ok(()) => info!("startup kernel finished"),
|
Ok(()) => info!("startup kernel finished"),
|
||||||
|
@ -650,7 +656,7 @@ pub fn thread(io: Io) {
|
||||||
let congress = congress.clone();
|
let congress = congress.clone();
|
||||||
let stream = stream.into_handle();
|
let stream = stream.into_handle();
|
||||||
respawn(&io, &mut kernel_thread, move |io| {
|
respawn(&io, &mut kernel_thread, move |io| {
|
||||||
let mut congress = borrow_mut!(congress);
|
let mut congress = congress.borrow_mut();
|
||||||
let mut stream = TcpStream::from_handle(&io, stream);
|
let mut stream = TcpStream::from_handle(&io, stream);
|
||||||
match host_kernel_worker(&io, &mut stream, &mut *congress) {
|
match host_kernel_worker(&io, &mut stream, &mut *congress) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
|
@ -673,7 +679,7 @@ pub fn thread(io: Io) {
|
||||||
|
|
||||||
let congress = congress.clone();
|
let congress = congress.clone();
|
||||||
respawn(&io, &mut kernel_thread, move |io| {
|
respawn(&io, &mut kernel_thread, move |io| {
|
||||||
let mut congress = borrow_mut!(congress);
|
let mut congress = congress.borrow_mut();
|
||||||
match flash_kernel_worker(&io, &mut *congress, "idle_kernel") {
|
match flash_kernel_worker(&io, &mut *congress, "idle_kernel") {
|
||||||
Ok(()) =>
|
Ok(()) =>
|
||||||
info!("idle kernel finished, standing by"),
|
info!("idle kernel finished, standing by"),
|
||||||
|
|
|
@ -5,25 +5,17 @@ LDFLAGS += -L../libbase
|
||||||
|
|
||||||
RUSTFLAGS += -Cpanic=abort
|
RUSTFLAGS += -Cpanic=abort
|
||||||
|
|
||||||
all: satman.bin satman.fbi
|
all:: satman.bin satman.fbi
|
||||||
|
|
||||||
.PHONY: $(RUSTOUT)/libsatman.a
|
.PHONY: $(RUSTOUT)/libsatman.a
|
||||||
$(RUSTOUT)/libsatman.a:
|
$(RUSTOUT)/libsatman.a:
|
||||||
$(cargo) --manifest-path $(SATMAN_DIRECTORY)/Cargo.toml
|
$(cargo) --manifest-path $(SATMAN_DIRECTORY)/Cargo.toml
|
||||||
|
|
||||||
satman.elf: $(RUSTOUT)/libsatman.a
|
satman.elf: $(RUSTOUT)/libsatman.a
|
||||||
$(LD) $(LDFLAGS) -T $(SATMAN_DIRECTORY)/satman.ld -o $@ $^
|
$(link) -T $(SATMAN_DIRECTORY)/satman.ld
|
||||||
@chmod -x $@
|
|
||||||
|
|
||||||
%.bin: %.elf
|
%.bin: %.elf
|
||||||
$(OBJCOPY) -O binary $< $@
|
$(objcopy) -O binary
|
||||||
@chmod -x $@
|
|
||||||
|
|
||||||
%.fbi: %.bin
|
%.fbi: %.bin
|
||||||
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $<
|
$(mscimg) -f
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) satman.elf satman.bin satman.fbi
|
|
||||||
$(RM) -rf cargo
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#![feature(compiler_builtins_lib, lang_items)]
|
#![feature(never_type, panic_implementation, panic_info_message)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate compiler_builtins;
|
extern crate compiler_builtins;
|
||||||
|
@ -241,10 +241,19 @@ pub extern fn abort() {
|
||||||
panic!("aborted")
|
panic!("aborted")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
#[lang = "panic_fmt"]
|
#[panic_implementation]
|
||||||
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
|
||||||
println!("panic at {}:{}: {}", file, line, args);
|
if let Some(location) = info.location() {
|
||||||
|
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
|
||||||
|
} else {
|
||||||
|
print!("panic at unknown location");
|
||||||
|
}
|
||||||
|
if let Some(message) = info.message() {
|
||||||
|
println!(": {}", message);
|
||||||
|
} else {
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,345 @@
|
||||||
|
#![feature(never_type, panic_implementation, panic_info_message)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate board_misoc;
|
||||||
|
extern crate board_artiq;
|
||||||
|
|
||||||
|
use board_misoc::{csr, ident, clock, uart_logger};
|
||||||
|
use board_artiq::{i2c, spi, si5324, drtioaux};
|
||||||
|
#[cfg(has_serwb_phy_amc)]
|
||||||
|
use board_artiq::serwb;
|
||||||
|
#[cfg(has_hmc830_7043)]
|
||||||
|
use board_artiq::hmc830_7043;
|
||||||
|
|
||||||
|
fn drtio_reset(reset: bool) {
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[0].reset_write)(if reset { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drtio_reset_phy(reset: bool) {
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[0].reset_phy_write)(if reset { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drtio_tsc_loaded() -> bool {
|
||||||
|
unsafe {
|
||||||
|
let tsc_loaded = (csr::DRTIO[0].tsc_loaded_read)() == 1;
|
||||||
|
if tsc_loaded {
|
||||||
|
(csr::DRTIO[0].tsc_loaded_write)(1);
|
||||||
|
}
|
||||||
|
tsc_loaded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_aux_packet(packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
||||||
|
// and u16 otherwise; hence the `as _` conversion.
|
||||||
|
match packet {
|
||||||
|
drtioaux::Packet::EchoRequest =>
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::EchoReply),
|
||||||
|
drtioaux::Packet::ResetRequest { phy } => {
|
||||||
|
if phy {
|
||||||
|
drtio_reset_phy(true);
|
||||||
|
drtio_reset_phy(false);
|
||||||
|
} else {
|
||||||
|
drtio_reset(true);
|
||||||
|
drtio_reset(false);
|
||||||
|
}
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::ResetAck)
|
||||||
|
},
|
||||||
|
|
||||||
|
drtioaux::Packet::RtioErrorRequest => {
|
||||||
|
let errors;
|
||||||
|
unsafe {
|
||||||
|
errors = (csr::DRTIO[0].rtio_error_read)();
|
||||||
|
}
|
||||||
|
if errors & 1 != 0 {
|
||||||
|
let channel;
|
||||||
|
unsafe {
|
||||||
|
channel = (csr::DRTIO[0].sequence_error_channel_read)();
|
||||||
|
(csr::DRTIO[0].rtio_error_write)(1);
|
||||||
|
}
|
||||||
|
drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::RtioErrorSequenceErrorReply { channel })
|
||||||
|
} else if errors & 2 != 0 {
|
||||||
|
let channel;
|
||||||
|
unsafe {
|
||||||
|
channel = (csr::DRTIO[0].collision_channel_read)();
|
||||||
|
(csr::DRTIO[0].rtio_error_write)(2);
|
||||||
|
}
|
||||||
|
drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::RtioErrorCollisionReply { channel })
|
||||||
|
} else if errors & 4 != 0 {
|
||||||
|
let channel;
|
||||||
|
unsafe {
|
||||||
|
channel = (csr::DRTIO[0].busy_channel_read)();
|
||||||
|
(csr::DRTIO[0].rtio_error_write)(4);
|
||||||
|
}
|
||||||
|
drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::RtioErrorBusyReply { channel })
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::RtioNoErrorReply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::Packet::MonitorRequest { channel, probe } => {
|
||||||
|
let value;
|
||||||
|
#[cfg(has_rtio_moninj)]
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::mon_probe_sel_write(probe);
|
||||||
|
csr::rtio_moninj::mon_value_update_write(1);
|
||||||
|
value = csr::rtio_moninj::mon_value_read();
|
||||||
|
}
|
||||||
|
#[cfg(not(has_rtio_moninj))]
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
let reply = drtioaux::Packet::MonitorReply { value: value as u32 };
|
||||||
|
drtioaux::send_link(0, &reply)
|
||||||
|
},
|
||||||
|
drtioaux::Packet::InjectionRequest { channel, overrd, value } => {
|
||||||
|
#[cfg(has_rtio_moninj)]
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::inj_override_sel_write(overrd);
|
||||||
|
csr::rtio_moninj::inj_value_write(value);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
drtioaux::Packet::InjectionStatusRequest { channel, overrd } => {
|
||||||
|
let value;
|
||||||
|
#[cfg(has_rtio_moninj)]
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::inj_override_sel_write(overrd);
|
||||||
|
value = csr::rtio_moninj::inj_value_read();
|
||||||
|
}
|
||||||
|
#[cfg(not(has_rtio_moninj))]
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::InjectionStatusReply { value: value })
|
||||||
|
},
|
||||||
|
|
||||||
|
drtioaux::Packet::I2cStartRequest { busno } => {
|
||||||
|
let succeeded = i2c::start(busno).is_ok();
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cRestartRequest { busno } => {
|
||||||
|
let succeeded = i2c::restart(busno).is_ok();
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cStopRequest { busno } => {
|
||||||
|
let succeeded = i2c::stop(busno).is_ok();
|
||||||
|
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cWriteRequest { busno, data } => {
|
||||||
|
match i2c::write(busno, data) {
|
||||||
|
Ok(ack) => drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
|
||||||
|
Err(_) => drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::I2cWriteReply { succeeded: false, ack: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cReadRequest { busno, ack } => {
|
||||||
|
match i2c::read(busno, ack) {
|
||||||
|
Ok(data) => drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
|
||||||
|
Err(_) => drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::Packet::SpiSetConfigRequest { busno, flags, length, div, cs } => {
|
||||||
|
let succeeded = spi::set_config(busno, flags, length, div, cs).is_ok();
|
||||||
|
drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
|
||||||
|
},
|
||||||
|
drtioaux::Packet::SpiWriteRequest { busno, data } => {
|
||||||
|
let succeeded = spi::write(busno, data).is_ok();
|
||||||
|
drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::SpiReadRequest { busno } => {
|
||||||
|
match spi::read(busno) {
|
||||||
|
Ok(data) => drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::SpiReadReply { succeeded: true, data: data }),
|
||||||
|
Err(_) => drtioaux::send_link(0,
|
||||||
|
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
warn!("received unexpected aux packet");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_aux_packets() {
|
||||||
|
let result =
|
||||||
|
drtioaux::recv_link(0).and_then(|packet| {
|
||||||
|
if let Some(packet) = packet {
|
||||||
|
process_aux_packet(packet)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
match result {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(e) => warn!("aux packet error ({})", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_errors() {
|
||||||
|
let errors;
|
||||||
|
unsafe {
|
||||||
|
errors = (csr::DRTIO[0].protocol_error_read)();
|
||||||
|
}
|
||||||
|
if errors & 1 != 0 {
|
||||||
|
error!("received packet of an unknown type");
|
||||||
|
}
|
||||||
|
if errors & 2 != 0 {
|
||||||
|
error!("received truncated packet");
|
||||||
|
}
|
||||||
|
if errors & 4 != 0 {
|
||||||
|
let channel;
|
||||||
|
let timestamp_event;
|
||||||
|
let timestamp_counter;
|
||||||
|
unsafe {
|
||||||
|
channel = (csr::DRTIO[0].underflow_channel_read)();
|
||||||
|
timestamp_event = (csr::DRTIO[0].underflow_timestamp_event_read)() as i64;
|
||||||
|
timestamp_counter = (csr::DRTIO[0].underflow_timestamp_counter_read)() as i64;
|
||||||
|
}
|
||||||
|
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
|
||||||
|
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
|
||||||
|
}
|
||||||
|
if errors & 8 != 0 {
|
||||||
|
error!("write overflow");
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[0].protocol_error_write)(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rtio_frequency = "150.0")]
|
||||||
|
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||||
|
= si5324::FrequencySettings {
|
||||||
|
n1_hs : 6,
|
||||||
|
nc1_ls : 6,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 270,
|
||||||
|
n31 : 75,
|
||||||
|
n32 : 75,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
};
|
||||||
|
|
||||||
|
fn drtio_link_rx_up() -> bool {
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[0].rx_up_read)() == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIPHASER_PHASE: u16 = 32;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn main() -> i32 {
|
||||||
|
clock::init();
|
||||||
|
uart_logger::ConsoleLogger::register();
|
||||||
|
|
||||||
|
info!("ARTIQ satellite manager starting...");
|
||||||
|
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
|
||||||
|
info!("gateware ident {}", ident::read(&mut [0; 64]));
|
||||||
|
|
||||||
|
#[cfg(has_slave_fpga_cfg)]
|
||||||
|
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
|
||||||
|
#[cfg(has_serwb_phy_amc)]
|
||||||
|
serwb::wait_init();
|
||||||
|
|
||||||
|
i2c::init();
|
||||||
|
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
||||||
|
#[cfg(has_hmc830_7043)]
|
||||||
|
/* must be the first SPI init because of HMC830 SPI mode selection */
|
||||||
|
hmc830_7043::init().expect("cannot initialize HMC830/7043");
|
||||||
|
unsafe {
|
||||||
|
csr::drtio_transceiver::stable_clkin_write(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_ad9154)]
|
||||||
|
{
|
||||||
|
board_artiq::ad9154::jesd_unreset();
|
||||||
|
board_artiq::ad9154::init();
|
||||||
|
}
|
||||||
|
#[cfg(has_allaki_atts)]
|
||||||
|
board_artiq::hmc542::program_all(8/*=4dB*/);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
while !drtio_link_rx_up() {
|
||||||
|
process_errors();
|
||||||
|
}
|
||||||
|
info!("link is up, switching to recovered clock");
|
||||||
|
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
|
||||||
|
si5324::siphaser::calibrate_skew(SIPHASER_PHASE).expect("failed to calibrate skew");
|
||||||
|
drtioaux::reset(0);
|
||||||
|
drtio_reset(false);
|
||||||
|
drtio_reset_phy(false);
|
||||||
|
while drtio_link_rx_up() {
|
||||||
|
process_errors();
|
||||||
|
process_aux_packets();
|
||||||
|
if drtio_tsc_loaded() {
|
||||||
|
#[cfg(has_ad9154)]
|
||||||
|
{
|
||||||
|
if let Err(e) = board_artiq::jesd204sync::sysref_auto_rtio_align() {
|
||||||
|
error!("failed to align SYSREF at FPGA: {}", e);
|
||||||
|
}
|
||||||
|
if let Err(e) = board_artiq::jesd204sync::sysref_auto_dac_align() {
|
||||||
|
error!("failed to align SYSREF at DAC: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = drtioaux::send_link(0, &drtioaux::Packet::TSCAck) {
|
||||||
|
error!("aux packet error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drtio_reset_phy(true);
|
||||||
|
drtio_reset(true);
|
||||||
|
drtio_tsc_loaded();
|
||||||
|
info!("link is down, switching to local crystal clock");
|
||||||
|
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn exception(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
|
||||||
|
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn abort() {
|
||||||
|
println!("aborted");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
|
#[panic_implementation]
|
||||||
|
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
if let Some(location) = info.location() {
|
||||||
|
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
|
||||||
|
} else {
|
||||||
|
print!("panic at unknown location");
|
||||||
|
}
|
||||||
|
if let Some(message) = info.message() {
|
||||||
|
println!(": {}", message);
|
||||||
|
} else {
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
loop {}
|
||||||
|
}
|
|
@ -53,6 +53,8 @@ def main():
|
||||||
dev = Tdc(args.device)
|
dev = Tdc(args.device)
|
||||||
elif product == "tpz001":
|
elif product == "tpz001":
|
||||||
dev = Tpz(args.device)
|
dev = Tpz(args.device)
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(dev.get_tpz_io_settings())
|
||||||
else:
|
else:
|
||||||
print("Invalid product string (-P/--product), "
|
print("Invalid product string (-P/--product), "
|
||||||
"choose from tdc001 or tpz001")
|
"choose from tdc001 or tpz001")
|
||||||
|
|
|
@ -51,6 +51,10 @@ Prerequisites:
|
||||||
help="target adapter, default: %(default)s")
|
help="target adapter, default: %(default)s")
|
||||||
parser.add_argument("--target-file", default=None,
|
parser.add_argument("--target-file", default=None,
|
||||||
help="use alternative OpenOCD target file")
|
help="use alternative OpenOCD target file")
|
||||||
|
parser.add_argument("-I", "--preinit-command", default=[], action="append",
|
||||||
|
help="add a pre-initialization OpenOCD command. "
|
||||||
|
"Useful for selecting a development board "
|
||||||
|
"when several are connected.")
|
||||||
parser.add_argument("-f", "--storage", help="write file to storage area")
|
parser.add_argument("-f", "--storage", help="write file to storage area")
|
||||||
parser.add_argument("-d", "--dir", help="look for files in this directory")
|
parser.add_argument("-d", "--dir", help="look for files in this directory")
|
||||||
parser.add_argument("action", metavar="ACTION", nargs="*",
|
parser.add_argument("action", metavar="ACTION", nargs="*",
|
||||||
|
@ -69,8 +73,8 @@ def main():
|
||||||
"start": "xc7_program xc7.tap",
|
"start": "xc7_program xc7.tap",
|
||||||
"gateware": 0x000000,
|
"gateware": 0x000000,
|
||||||
"bios": 0xaf0000,
|
"bios": 0xaf0000,
|
||||||
"runtime": 0xb00000,
|
"storage": 0xb30000,
|
||||||
"storage": 0xb80000,
|
"runtime": 0xb40000,
|
||||||
},
|
},
|
||||||
}[opts.target]
|
}[opts.target]
|
||||||
|
|
||||||
|
@ -84,6 +88,7 @@ def main():
|
||||||
conv = False
|
conv = False
|
||||||
|
|
||||||
prog = []
|
prog = []
|
||||||
|
prog.extend(opts.preinit_command)
|
||||||
prog.append("init")
|
prog.append("init")
|
||||||
for action in opts.action:
|
for action in opts.action:
|
||||||
if action == "proxy":
|
if action == "proxy":
|
||||||
|
|
|
@ -94,6 +94,7 @@ class DBWriter(TaskObject):
|
||||||
"too many pending updates", k)
|
"too many pending updates", k)
|
||||||
|
|
||||||
async def _do(self):
|
async def _do(self):
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
while True:
|
while True:
|
||||||
k, v, t = await self._queue.get()
|
k, v, t = await self._queue.get()
|
||||||
url = self.base_url + "/write"
|
url = self.base_url + "/write"
|
||||||
|
@ -102,8 +103,7 @@ class DBWriter(TaskObject):
|
||||||
data = "{},dataset={} {} {}".format(
|
data = "{},dataset={} {} {}".format(
|
||||||
self.table, k, format_influxdb(v), round(t*1e3))
|
self.table, k, format_influxdb(v), round(t*1e3))
|
||||||
try:
|
try:
|
||||||
response = await aiohttp.request(
|
response = await session.post(url, params=params, data=data)
|
||||||
"POST", url, params=params, data=data)
|
|
||||||
except:
|
except:
|
||||||
logger.warning("got exception trying to update '%s'",
|
logger.warning("got exception trying to update '%s'",
|
||||||
k, exc_info=True)
|
k, exc_info=True)
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
# * tcpdump has CAP_NET_RAW capabilities set
|
# * tcpdump has CAP_NET_RAW capabilities set
|
||||||
# use # setcap cap_net_raw+eip /usr/sbin/tcpdump
|
# use # setcap cap_net_raw+eip /usr/sbin/tcpdump
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
import os
|
||||||
|
import argparse
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from artiq.tools import verbosity_args, init_logger, logger, SSHClient
|
from artiq.tools import verbosity_args, init_logger, logger, SSHClient
|
||||||
|
@ -49,7 +49,6 @@ def main():
|
||||||
subprocess.check_call(args.command)
|
subprocess.check_call(args.command)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logger.error("Command failed")
|
logger.error("Command failed")
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
tcpdump.close()
|
tcpdump.close()
|
||||||
sftp.get("{tmp}/trace.pcap".format(tmp=client.tmp),
|
sftp.get("{tmp}/trace.pcap".format(tmp=client.tmp),
|
||||||
|
|
|
@ -6,6 +6,7 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
import numpy as np # Needed to use numpy in RPC call arguments on cmd line
|
import numpy as np # Needed to use numpy in RPC call arguments on cmd line
|
||||||
import pprint
|
import pprint
|
||||||
|
import inspect
|
||||||
|
|
||||||
from artiq.protocols.pc_rpc import AutoTarget, Client
|
from artiq.protocols.pc_rpc import AutoTarget, Client
|
||||||
|
|
||||||
|
@ -46,33 +47,7 @@ def list_methods(remote):
|
||||||
print(doc["docstring"])
|
print(doc["docstring"])
|
||||||
print()
|
print()
|
||||||
for name, (argspec, docstring) in sorted(doc["methods"].items()):
|
for name, (argspec, docstring) in sorted(doc["methods"].items()):
|
||||||
args = ""
|
print(name + inspect.formatargspec(**argspec))
|
||||||
for arg in argspec["args"]:
|
|
||||||
args += arg
|
|
||||||
if argspec["defaults"] is not None:
|
|
||||||
kword_index = len(argspec["defaults"]) - len(argspec["args"])\
|
|
||||||
+ argspec["args"].index(arg)
|
|
||||||
if kword_index >= 0:
|
|
||||||
if argspec["defaults"][kword_index] == Ellipsis:
|
|
||||||
args += "=..."
|
|
||||||
else:
|
|
||||||
args += "={}".format(argspec["defaults"][kword_index])
|
|
||||||
if argspec["args"].index(arg) < len(argspec["args"]) - 1:
|
|
||||||
args += ", "
|
|
||||||
if argspec["varargs"] is not None:
|
|
||||||
args += ", *{}".format(argspec["varargs"])
|
|
||||||
elif len(argspec["kwonlyargs"]) > 0:
|
|
||||||
args += ", *"
|
|
||||||
for kwonlyarg in argspec["kwonlyargs"]:
|
|
||||||
args += ", {}".format(kwonlyarg)
|
|
||||||
if kwonlyarg in argspec["kwonlydefaults"]:
|
|
||||||
if argspec["kwonlydefaults"][kwonlyarg] == Ellipsis:
|
|
||||||
args += "=..."
|
|
||||||
else:
|
|
||||||
args += "={}".format(argspec["kwonlydefaults"][kwonlyarg])
|
|
||||||
if argspec["varkw"] is not None:
|
|
||||||
args += ", **{}".format(argspec["varkw"])
|
|
||||||
print("{}({})".format(name, args))
|
|
||||||
if docstring is not None:
|
if docstring is not None:
|
||||||
print(textwrap.indent(docstring, " "))
|
print(textwrap.indent(docstring, " "))
|
||||||
print()
|
print()
|
||||||
|
|
|
@ -17,11 +17,12 @@ class _OSERDESE2_8X(Module):
|
||||||
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
|
||||||
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
|
||||||
o_OQ=pad_o, o_TQ=self.t_out,
|
o_OQ=pad_o, o_TQ=self.t_out,
|
||||||
|
i_RST=ResetSignal("rio_phy"),
|
||||||
i_CLK=ClockSignal("rtiox4"),
|
i_CLK=ClockSignal("rtiox4"),
|
||||||
i_CLKDIV=ClockSignal("rio_phy"),
|
i_CLKDIV=ClockSignal("rio_phy"),
|
||||||
i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3],
|
i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3],
|
||||||
i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7],
|
i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7],
|
||||||
i_TCE=1, i_OCE=1, i_RST=0,
|
i_TCE=1, i_OCE=1,
|
||||||
i_T1=self.t_in)
|
i_T1=self.t_in)
|
||||||
if pad_n is None:
|
if pad_n is None:
|
||||||
self.comb += pad.eq(pad_o)
|
self.comb += pad.eq(pad_o)
|
||||||
|
@ -49,7 +50,8 @@ class _ISERDESE2_8X(Module):
|
||||||
i_D=pad_i,
|
i_D=pad_i,
|
||||||
i_CLK=ClockSignal("rtiox4"),
|
i_CLK=ClockSignal("rtiox4"),
|
||||||
i_CLKB=~ClockSignal("rtiox4"),
|
i_CLKB=~ClockSignal("rtiox4"),
|
||||||
i_CE1=1, i_RST=0,
|
i_CE1=1,
|
||||||
|
i_RST=ResetSignal("rio_phy"),
|
||||||
i_CLKDIV=ClockSignal("rio_phy"))
|
i_CLKDIV=ClockSignal("rio_phy"))
|
||||||
if pad_n is None:
|
if pad_n is None:
|
||||||
self.comb += pad_i.eq(pad)
|
self.comb += pad_i.eq(pad)
|
||||||
|
|
|
@ -115,166 +115,3 @@ class InOut(Module):
|
||||||
self.submodules += pe
|
self.submodules += pe
|
||||||
self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width))
|
self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width))
|
||||||
self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o)
|
self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o)
|
||||||
|
|
||||||
|
|
||||||
class _FakeSerdes(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.o = Signal(8)
|
|
||||||
self.i = Signal(8)
|
|
||||||
self.oe = Signal()
|
|
||||||
|
|
||||||
|
|
||||||
class _OutputTB(Module):
|
|
||||||
def __init__(self):
|
|
||||||
serdes = _FakeSerdes()
|
|
||||||
self.submodules.dut = RenameClockDomains(Output(serdes),
|
|
||||||
{"rio_phy": "sys"})
|
|
||||||
|
|
||||||
def gen_simulation(self, selfp):
|
|
||||||
selfp.dut.rtlink.o.data = 1
|
|
||||||
selfp.dut.rtlink.o.fine_ts = 1
|
|
||||||
selfp.dut.rtlink.o.stb = 1
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.data = 0
|
|
||||||
selfp.dut.rtlink.o.fine_ts = 2
|
|
||||||
selfp.dut.rtlink.o.stb = 1
|
|
||||||
yield
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.data = 1
|
|
||||||
selfp.dut.rtlink.o.fine_ts = 7
|
|
||||||
selfp.dut.rtlink.o.stb = 1
|
|
||||||
for _ in range(6):
|
|
||||||
# note that stb stays active; output should not change
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
class _InOutTB(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.serdes = _FakeSerdes()
|
|
||||||
self.submodules.dut = RenameClockDomains(InOut(self.serdes),
|
|
||||||
{"rio_phy": "sys",
|
|
||||||
"rio": "sys"})
|
|
||||||
|
|
||||||
def check_input(self, selfp, stb, fine_ts=None):
|
|
||||||
if stb != selfp.dut.rtlink.i.stb:
|
|
||||||
print("KO rtlink.i.stb should be {} but is {}"
|
|
||||||
.format(stb, selfp.dut.rtlink.i.stb))
|
|
||||||
elif fine_ts is not None and fine_ts != selfp.dut.rtlink.i.fine_ts:
|
|
||||||
print("KO rtlink.i.fine_ts should be {} but is {}"
|
|
||||||
.format(fine_ts, selfp.dut.rtlink.i.fine_ts))
|
|
||||||
else:
|
|
||||||
print("OK")
|
|
||||||
|
|
||||||
def check_output(self, selfp, data):
|
|
||||||
if selfp.serdes.o != data:
|
|
||||||
print("KO io.o should be {} but is {}".format(data, selfp.serdes.o))
|
|
||||||
else:
|
|
||||||
print("OK")
|
|
||||||
|
|
||||||
def check_output_enable(self, selfp, oe):
|
|
||||||
if selfp.serdes.oe != oe:
|
|
||||||
print("KO io.oe should be {} but is {}".format(oe, selfp.serdes.oe))
|
|
||||||
else:
|
|
||||||
print("OK")
|
|
||||||
|
|
||||||
def gen_simulation(self, selfp):
|
|
||||||
selfp.dut.rtlink.o.address = 2
|
|
||||||
selfp.dut.rtlink.o.data = 0b11
|
|
||||||
selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising + falling
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
|
|
||||||
self.check_output_enable(selfp, 0)
|
|
||||||
yield
|
|
||||||
|
|
||||||
selfp.serdes.i = 0b11111110 # rising edge at fine_ts = 1
|
|
||||||
yield
|
|
||||||
selfp.serdes.i = 0b11111111
|
|
||||||
yield
|
|
||||||
self.check_input(selfp, stb=1, fine_ts=1)
|
|
||||||
|
|
||||||
selfp.serdes.i = 0b01111111 # falling edge at fine_ts = 7
|
|
||||||
yield
|
|
||||||
selfp.serdes.i = 0b00000000
|
|
||||||
yield
|
|
||||||
self.check_input(selfp, stb=1, fine_ts=7)
|
|
||||||
|
|
||||||
selfp.serdes.i = 0b11000000 # rising edge at fine_ts = 6
|
|
||||||
yield
|
|
||||||
selfp.serdes.i = 0b11111111
|
|
||||||
yield
|
|
||||||
self.check_input(selfp, stb=1, fine_ts=6)
|
|
||||||
|
|
||||||
selfp.dut.rtlink.o.address = 2
|
|
||||||
selfp.dut.rtlink.o.data = 0b11
|
|
||||||
selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising only
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
yield
|
|
||||||
|
|
||||||
selfp.serdes.i = 0b00001111 # falling edge at fine_ts = 4
|
|
||||||
yield
|
|
||||||
self.check_input(selfp, stb=0) # no strobe, sensitivity is rising edge
|
|
||||||
|
|
||||||
selfp.serdes.i = 0b11110000 # rising edge at fine_ts = 4
|
|
||||||
yield
|
|
||||||
self.check_input(selfp, stb=1, fine_ts=4)
|
|
||||||
|
|
||||||
selfp.dut.rtlink.o.address = 1
|
|
||||||
selfp.dut.rtlink.o.data = 1
|
|
||||||
selfp.dut.rtlink.o.stb = 1 # set Output Enable to 1
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
yield
|
|
||||||
yield
|
|
||||||
self.check_output_enable(selfp, 1)
|
|
||||||
|
|
||||||
selfp.dut.rtlink.o.address = 0
|
|
||||||
selfp.dut.rtlink.o.data = 1
|
|
||||||
selfp.dut.rtlink.o.fine_ts = 3
|
|
||||||
selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 3
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
yield
|
|
||||||
self.check_output(selfp, data=0b11111000)
|
|
||||||
|
|
||||||
yield
|
|
||||||
self.check_output(selfp, data=0xFF) # stays at 1
|
|
||||||
|
|
||||||
selfp.dut.rtlink.o.data = 0
|
|
||||||
selfp.dut.rtlink.o.fine_ts = 0
|
|
||||||
selfp.dut.rtlink.o.stb = 1 # falling edge at fine_ts = 0
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
yield
|
|
||||||
self.check_output(selfp, data=0)
|
|
||||||
|
|
||||||
yield
|
|
||||||
self.check_output(selfp, data=0)
|
|
||||||
|
|
||||||
selfp.dut.rtlink.o.data = 1
|
|
||||||
selfp.dut.rtlink.o.fine_ts = 7
|
|
||||||
selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 7
|
|
||||||
yield
|
|
||||||
selfp.dut.rtlink.o.stb = 0
|
|
||||||
yield
|
|
||||||
self.check_output(selfp, data=0b10000000)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import sys
|
|
||||||
from migen.sim.generic import Simulator, TopLevel
|
|
||||||
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print("Incorrect command line")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
cls = {
|
|
||||||
"output": _OutputTB,
|
|
||||||
"inout": _InOutTB
|
|
||||||
}[sys.argv[1]]
|
|
||||||
|
|
||||||
with Simulator(cls(), TopLevel("top.vcd", clk_period=int(1/0.125))) as s:
|
|
||||||
s.run()
|
|
||||||
|
|
|
@ -208,9 +208,11 @@ class SPIMaster(Module):
|
||||||
|
|
||||||
clk_t = TSTriple()
|
clk_t = TSTriple()
|
||||||
self.specials += clk_t.get_tristate(pads.clk)
|
self.specials += clk_t.get_tristate(pads.clk)
|
||||||
self.comb += [
|
self.comb += clk_t.oe.eq(~config.offline),
|
||||||
clk_t.oe.eq(~config.offline),
|
self.sync += [
|
||||||
clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity),
|
If(spi.cg.ce & spi.cg.edge,
|
||||||
|
clk_t.o.eq((~spi.cg.clk & spi.cs_next) ^ config.clk_polarity)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
mosi_t = TSTriple()
|
mosi_t = TSTriple()
|
||||||
|
|
|
@ -157,7 +157,6 @@ class Phaser(MiniSoC, AMPSoC):
|
||||||
"rtio": 0x20000000,
|
"rtio": 0x20000000,
|
||||||
"rtio_dma": 0x30000000,
|
"rtio_dma": 0x30000000,
|
||||||
"mailbox": 0x70000000,
|
"mailbox": 0x70000000,
|
||||||
"ad9154": 0x50000000,
|
|
||||||
}
|
}
|
||||||
mem_map.update(MiniSoC.mem_map)
|
mem_map.update(MiniSoC.mem_map)
|
||||||
|
|
|
@ -127,6 +127,7 @@ class SMA_SPI(_NIST_Ions):
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
[self.rtio.cri, self.rtio_dma.cri],
|
[self.rtio.cri, self.rtio_dma.cri],
|
||||||
[self.rtio_core.cri])
|
[self.rtio_core.cri])
|
||||||
|
self.register_kernel_cpu_csrdevice("cri_con")
|
||||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||||
self.csr_devices.append("rtio_moninj")
|
self.csr_devices.append("rtio_moninj")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from migen import *
|
||||||
|
|
||||||
|
from artiq.gateware.rtio.phy.ttl_serdes_generic import *
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeSerdes:
|
||||||
|
def __init__(self):
|
||||||
|
self.o = Signal(8)
|
||||||
|
self.i = Signal(8)
|
||||||
|
self.oe = Signal()
|
||||||
|
|
||||||
|
|
||||||
|
class _TB(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.serdes = _FakeSerdes()
|
||||||
|
self.submodules.dut = ClockDomainsRenamer({"rio_phy": "sys", "rio": "sys"})(
|
||||||
|
InOut(self.serdes))
|
||||||
|
|
||||||
|
|
||||||
|
class TestTTLSerdes(unittest.TestCase):
|
||||||
|
def test_input(self):
|
||||||
|
tb = _TB()
|
||||||
|
|
||||||
|
def gen():
|
||||||
|
yield tb.dut.rtlink.o.address.eq(2)
|
||||||
|
yield tb.dut.rtlink.o.data.eq(0b11)
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(1) # set sensitivity to rising + falling
|
||||||
|
yield
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(0)
|
||||||
|
yield
|
||||||
|
|
||||||
|
self.assertEqual((yield tb.serdes.oe), 0)
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.stb), 0)
|
||||||
|
|
||||||
|
yield tb.serdes.i.eq(0b11111110) # rising edge at fine_ts = 1
|
||||||
|
yield
|
||||||
|
yield tb.serdes.i.eq(0b11111111)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 1)
|
||||||
|
|
||||||
|
yield tb.serdes.i.eq(0b01111111) # falling edge at fine_ts = 7
|
||||||
|
yield
|
||||||
|
yield tb.serdes.i.eq(0b00000000)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 7)
|
||||||
|
|
||||||
|
yield tb.serdes.i.eq(0b11000000) # rising edge at fine_ts = 6
|
||||||
|
yield
|
||||||
|
yield tb.serdes.i.eq(0b11111111)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 6)
|
||||||
|
|
||||||
|
yield tb.dut.rtlink.o.address.eq(2)
|
||||||
|
yield tb.dut.rtlink.o.data.eq(0b01)
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(1) # set sensitivity to rising only
|
||||||
|
yield
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(0)
|
||||||
|
yield
|
||||||
|
|
||||||
|
yield tb.serdes.i.eq(0b00001111) # falling edge at fine_ts = 4
|
||||||
|
yield
|
||||||
|
yield tb.serdes.i.eq(0b00000000)
|
||||||
|
yield
|
||||||
|
# no strobe, sensitivity is rising edge
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.stb), 0)
|
||||||
|
|
||||||
|
yield tb.serdes.i.eq(0b11110000) # rising edge at fine_ts = 4
|
||||||
|
yield
|
||||||
|
yield tb.serdes.i.eq(0b11111111)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.stb), 1)
|
||||||
|
self.assertEqual((yield tb.dut.rtlink.i.fine_ts), 4)
|
||||||
|
|
||||||
|
run_simulation(tb, gen())
|
||||||
|
|
||||||
|
def test_output(self):
|
||||||
|
tb = _TB()
|
||||||
|
|
||||||
|
def gen():
|
||||||
|
yield tb.dut.rtlink.o.address.eq(1)
|
||||||
|
yield tb.dut.rtlink.o.data.eq(1)
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(1) # set Output Enable to 1
|
||||||
|
yield
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(0)
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.serdes.oe), 1)
|
||||||
|
|
||||||
|
yield tb.dut.rtlink.o.address.eq(0)
|
||||||
|
yield tb.dut.rtlink.o.data.eq(1)
|
||||||
|
yield tb.dut.rtlink.o.fine_ts.eq(3)
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(1) # rising edge at fine_ts = 3
|
||||||
|
yield
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(0)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.serdes.o), 0b11111000)
|
||||||
|
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.serdes.o), 0b11111111) # stays at 1
|
||||||
|
|
||||||
|
yield tb.dut.rtlink.o.data.eq(0)
|
||||||
|
yield tb.dut.rtlink.o.fine_ts.eq(0)
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(1) # falling edge at fine_ts = 0
|
||||||
|
yield
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(0)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.serdes.o), 0b00000000)
|
||||||
|
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.serdes.o), 0b00000000)
|
||||||
|
|
||||||
|
yield tb.dut.rtlink.o.data.eq(1)
|
||||||
|
yield tb.dut.rtlink.o.fine_ts.eq(7)
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(1) # rising edge at fine_ts = 7
|
||||||
|
yield
|
||||||
|
yield tb.dut.rtlink.o.stb.eq(0)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield tb.serdes.o), 0b10000000)
|
||||||
|
|
||||||
|
run_simulation(tb, gen())
|
|
@ -321,7 +321,6 @@ class AppletsDock(QtWidgets.QDockWidget):
|
||||||
|
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.datasets_sub = datasets_sub
|
self.datasets_sub = datasets_sub
|
||||||
self.dock_to_item = dict()
|
|
||||||
self.applet_uids = set()
|
self.applet_uids = set()
|
||||||
|
|
||||||
self.table = QtWidgets.QTreeWidget()
|
self.table = QtWidgets.QTreeWidget()
|
||||||
|
@ -414,12 +413,12 @@ class AppletsDock(QtWidgets.QDockWidget):
|
||||||
finally:
|
finally:
|
||||||
self.table.itemChanged.connect(self.item_changed)
|
self.table.itemChanged.connect(self.item_changed)
|
||||||
|
|
||||||
def create(self, uid, name, spec):
|
def create(self, item, name, spec):
|
||||||
dock = _AppletDock(self.datasets_sub, uid, name, spec)
|
dock = _AppletDock(self.datasets_sub, item.applet_uid, name, spec)
|
||||||
self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
|
self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
|
||||||
dock.setFloating(True)
|
dock.setFloating(True)
|
||||||
asyncio.ensure_future(dock.start())
|
asyncio.ensure_future(dock.start())
|
||||||
dock.sigClosed.connect(partial(self.on_dock_closed, dock))
|
dock.sigClosed.connect(partial(self.on_dock_closed, item, dock))
|
||||||
return dock
|
return dock
|
||||||
|
|
||||||
def item_changed(self, item, column):
|
def item_changed(self, item, column):
|
||||||
|
@ -437,15 +436,15 @@ class AppletsDock(QtWidgets.QDockWidget):
|
||||||
if item.applet_dock is None:
|
if item.applet_dock is None:
|
||||||
name = item.text(0)
|
name = item.text(0)
|
||||||
spec = self.get_spec(item)
|
spec = self.get_spec(item)
|
||||||
dock = self.create(item.applet_uid, name, spec)
|
dock = self.create(item, name, spec)
|
||||||
item.applet_dock = dock
|
item.applet_dock = dock
|
||||||
if item.applet_geometry is not None:
|
if item.applet_geometry is not None:
|
||||||
dock.restoreGeometry(item.applet_geometry)
|
dock.restoreGeometry(item.applet_geometry)
|
||||||
# geometry is now handled by main window state
|
# geometry is now handled by main window state
|
||||||
item.applet_geometry = None
|
item.applet_geometry = None
|
||||||
self.dock_to_item[dock] = item
|
|
||||||
else:
|
else:
|
||||||
dock = item.applet_dock
|
dock = item.applet_dock
|
||||||
|
item.applet_dock = None
|
||||||
if dock is not None:
|
if dock is not None:
|
||||||
# This calls self.on_dock_closed
|
# This calls self.on_dock_closed
|
||||||
dock.close()
|
dock.close()
|
||||||
|
@ -455,12 +454,9 @@ class AppletsDock(QtWidgets.QDockWidget):
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def on_dock_closed(self, dock):
|
def on_dock_closed(self, item, dock):
|
||||||
item = self.dock_to_item[dock]
|
|
||||||
item.applet_dock = None
|
|
||||||
item.applet_geometry = dock.saveGeometry()
|
item.applet_geometry = dock.saveGeometry()
|
||||||
asyncio.ensure_future(dock.terminate())
|
asyncio.ensure_future(dock.terminate())
|
||||||
del self.dock_to_item[dock]
|
|
||||||
item.setCheckState(0, QtCore.Qt.Unchecked)
|
item.setCheckState(0, QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
def get_untitled(self):
|
def get_untitled(self):
|
||||||
|
|
|
@ -12,17 +12,17 @@
|
||||||
version="1.1"
|
version="1.1"
|
||||||
x="0px"
|
x="0px"
|
||||||
y="0px"
|
y="0px"
|
||||||
width="360.147"
|
width="323.49704"
|
||||||
height="432.04401"
|
height="432.04401"
|
||||||
viewBox="0 0 360.147 432.04401"
|
viewBox="0 0 323.49704 432.04401"
|
||||||
enable-background="new 0 0 800 800"
|
enable-background="new 0 0 800 800"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
id="svg2"
|
id="svg2"
|
||||||
inkscape:version="0.91 r13725"
|
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||||
sodipodi:docname="logo_ver.svg"><metadata
|
sodipodi:docname="logo_ver.svg"><metadata
|
||||||
id="metadata548"><rdf:RDF><cc:Work
|
id="metadata548"><rdf:RDF><cc:Work
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
id="defs546" /><sodipodi:namedview
|
id="defs546" /><sodipodi:namedview
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
bordercolor="#666666"
|
bordercolor="#666666"
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1156"
|
inkscape:window-height="1124"
|
||||||
id="namedview544"
|
id="namedview544"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
|
@ -41,12 +41,12 @@
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
fit-margin-bottom="0"
|
fit-margin-bottom="0"
|
||||||
inkscape:zoom="0.834386"
|
inkscape:zoom="0.834386"
|
||||||
inkscape:cx="-244.98179"
|
inkscape:cx="-466.10247"
|
||||||
inkscape:cy="163.75581"
|
inkscape:cy="163.7558"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="44"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg2" /><path
|
inkscape:current-layer="text3371" /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path381"
|
id="path381"
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path383"
|
id="path383"
|
||||||
d="m 53.761,135.723 10.609,9.812 c 0.215,-0.162 0.461,-0.348 0.689,-0.549 20.543,-18.001 43.98,-33.619 69.662,-46.423 32.912,-16.404 60.969,-25.002 88.295,-27.058 2.504,-0.188 4.811,-0.279 7.051,-0.279 9.105,0 16.873,1.591 23.744,4.864 7.635,3.636 11.473,9.741 11.404,18.146 -0.053,6.609 -1.955,13.229 -5.812,20.239 -2.68,4.868 -5.713,9.68 -8.646,14.332 -1.248,1.979 -2.502,3.969 -3.744,5.982 l 10.135,9.65 c 8.092,-10.235 16.82,-22.731 20.846,-38.001 0.467,-1.765 0.861,-3.586 1.244,-5.348 0.174,-0.804 0.348,-1.606 0.529,-2.408 l 0,-8.887 c -0.049,-0.148 -0.102,-0.297 -0.154,-0.444 -0.141,-0.387 -0.285,-0.787 -0.357,-1.216 -2.037,-12.213 -8.967,-20.778 -21.184,-26.186 -7.824,-3.462 -16.289,-4.355 -23.535,-4.772 -2.264,-0.13 -4.576,-0.196 -6.877,-0.196 -11.945,0 -24.328,1.727 -37.859,5.278 -46.736,12.272 -90.896,35.554 -131.252,69.197 -1.098,0.917 -2.182,1.903 -3.332,2.947 -0.465,0.425 -0.948,0.863 -1.456,1.32 z" /><path
|
d="m 53.761,135.723 10.609,9.812 c 0.215,-0.162 0.461,-0.348 0.689,-0.549 20.543,-18.001 43.98,-33.619 69.662,-46.423 32.912,-16.404 60.969,-25.002 88.295,-27.058 2.504,-0.188 4.811,-0.279 7.051,-0.279 9.105,0 16.873,1.591 23.744,4.864 7.635,3.636 11.473,9.741 11.404,18.146 -0.053,6.609 -1.955,13.229 -5.812,20.239 -2.68,4.868 -5.713,9.68 -8.646,14.332 -1.248,1.979 -2.502,3.969 -3.744,5.982 l 10.135,9.65 c 8.092,-10.235 16.82,-22.731 20.846,-38.001 0.467,-1.765 0.861,-3.586 1.244,-5.348 0.174,-0.804 0.348,-1.606 0.529,-2.408 v -8.887 c -0.049,-0.148 -0.102,-0.297 -0.154,-0.444 -0.141,-0.387 -0.285,-0.787 -0.357,-1.216 -2.037,-12.213 -8.967,-20.778 -21.184,-26.186 -7.824,-3.462 -16.289,-4.355 -23.535,-4.772 -2.264,-0.13 -4.576,-0.196 -6.877,-0.196 -11.945,0 -24.328,1.727 -37.859,5.278 -46.736,12.272 -90.896,35.554 -131.252,69.197 -1.098,0.917 -2.182,1.903 -3.332,2.947 -0.465,0.425 -0.948,0.863 -1.456,1.32 z" /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path385"
|
id="path385"
|
||||||
|
@ -82,11 +82,11 @@
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path397"
|
id="path397"
|
||||||
d="m 180.738,111.632 c -0.625,-4.189 -1.227,-8.218 -1.867,-12.238 -0.326,-2.036 -5.861,-6.224 -8.229,-6.224 -0.156,0 -0.291,0.02 -0.402,0.058 -4.172,1.46 -8.242,3.096 -12.551,4.827 -1.42,0.57 -2.855,1.146 -4.316,1.727 l 28,16.088 -0.635,-4.238 z" /><path
|
d="m 180.738,111.632 c -0.625,-4.189 -1.227,-8.218 -1.867,-12.238 -0.326,-2.036 -5.861,-6.224 -8.229,-6.224 -0.156,0 -0.291,0.02 -0.402,0.058 -4.172,1.46 -8.242,3.096 -12.551,4.827 -1.42,0.57 -2.855,1.146 -4.316,1.727 l 28,16.088 z" /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path399"
|
id="path399"
|
||||||
d="m 90.154,136.545 c -0.338,0.264 -0.668,0.525 -1,0.788 -0.463,0.366 -0.936,0.736 -1.393,1.099 -2.838,2.248 -5.516,4.371 -8.346,6.353 -2.75,1.927 -3.779,4.095 -3.336,7.03 0.102,0.675 0.096,1.436 0.09,2.17 -0.01,1.219 -0.02,2.479 0.488,2.946 3.336,3.059 6.891,5.851 10.654,8.807 0.605,0.477 1.227,0.968 1.842,1.452 0.334,0.264 0.664,0.523 1,0.789 0.188,0.148 0.369,0.29 0.557,0.439 l 0,-32.312 c -0.189,0.148 -0.369,0.291 -0.556,0.439 z" /><path
|
d="m 90.154,136.545 c -0.338,0.264 -0.668,0.525 -1,0.788 -0.463,0.366 -0.936,0.736 -1.393,1.099 -2.838,2.248 -5.516,4.371 -8.346,6.353 -2.75,1.927 -3.779,4.095 -3.336,7.03 0.102,0.675 0.096,1.436 0.09,2.17 -0.01,1.219 -0.02,2.479 0.488,2.946 3.336,3.059 6.891,5.851 10.654,8.807 0.605,0.477 1.227,0.968 1.842,1.452 0.334,0.264 0.664,0.523 1,0.789 0.188,0.148 0.369,0.29 0.557,0.439 v -32.312 c -0.189,0.148 -0.369,0.291 -0.556,0.439 z" /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path401"
|
id="path401"
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path405"
|
id="path405"
|
||||||
d="M 231.324,171.353 221.01,161.63 c -0.9,2.513 -2.059,14.3 -1.457,19.737 l 11.771,-10.014 z" /><path
|
d="M 231.324,171.353 221.01,161.63 c -0.9,2.513 -2.059,14.3 -1.457,19.737 z" /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path407"
|
id="path407"
|
||||||
|
@ -117,25 +117,25 @@
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path415"
|
id="path415"
|
||||||
d="m 366.13,593.558 c 7.498,-5.083 10.756,-15.119 7.922,-24.407 -2.773,-9.089 -10.521,-14.536 -20.727,-14.573 l -26.715,0 0,61.202 14.156,0 0,-18.602 9.979,0 10.836,18.602 16.479,0 0,-1.06 -12.725,-20.623 0.795,-0.539 z m -12.801,-8.731 -12.562,0 0,-17.809 1.004,0 c 1.254,0 2.529,-0.01 3.811,-0.02 2.6,-0.021 5.225,-0.041 7.771,0.02 5.328,0.052 7.74,4.621 7.719,8.845 -0.022,4.454 -2.688,8.964 -7.743,8.964 z" /><polygon
|
d="m 366.13,593.558 c 7.498,-5.083 10.756,-15.119 7.922,-24.407 -2.773,-9.089 -10.521,-14.536 -20.727,-14.573 H 326.61 v 61.202 h 14.156 v -18.602 h 9.979 l 10.836,18.602 h 16.479 v -1.06 l -12.725,-20.623 z m -12.801,-8.731 h -12.562 v -17.809 h 1.004 c 1.254,0 2.529,-0.01 3.811,-0.02 2.6,-0.021 5.225,-0.041 7.771,0.02 5.328,0.052 7.74,4.621 7.719,8.845 -0.022,4.454 -2.688,8.964 -7.743,8.964 z" /><polygon
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="polygon417"
|
id="polygon417"
|
||||||
points="432.151,554.577 387.202,554.577 387.202,567.019 402.733,567.019 402.733,615.779 416.71,615.779 416.71,567.019 432.151,567.019 " /><polygon
|
points="387.202,567.019 402.733,567.019 402.733,615.779 416.71,615.779 416.71,567.019 432.151,567.019 432.151,554.577 387.202,554.577 " /><polygon
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="polygon419"
|
id="polygon419"
|
||||||
points="464.571,566.657 471.976,566.657 471.976,554.577 443.19,554.577 443.19,566.657 450.595,566.657 450.595,603.608 442.558,603.608 442.558,615.779 472.606,615.779 472.606,603.608 464.571,603.608 " /><path
|
points="471.976,554.577 443.19,554.577 443.19,566.657 450.595,566.657 450.595,603.608 442.558,603.608 442.558,615.779 472.606,615.779 472.606,603.608 464.571,603.608 464.571,566.657 471.976,566.657 " /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path421"
|
id="path421"
|
||||||
d="m 542.927,603.421 c 3.252,-5.109 4.9,-11.218 4.9,-18.153 0,-21.831 -16.283,-31.774 -31.414,-31.774 -15.129,0 -31.414,9.943 -31.414,31.774 0,21.683 16.174,31.56 31.199,31.56 5.541,0 10.949,-1.321 15.637,-3.82 l 0.885,-0.472 1.725,3.244 14.828,0 0,-0.794 -6.68,-11.039 0.334,-0.526 z m -26.514,0.053 c -8.094,0 -16.805,-5.697 -16.805,-18.206 0,-12.415 8.711,-18.069 16.805,-18.069 8.094,0 16.807,5.654 16.807,18.069 0,12.508 -8.713,18.206 -16.807,18.206 z" /></g><path
|
d="m 542.927,603.421 c 3.252,-5.109 4.9,-11.218 4.9,-18.153 0,-21.831 -16.283,-31.774 -31.414,-31.774 -15.129,0 -31.414,9.943 -31.414,31.774 0,21.683 16.174,31.56 31.199,31.56 5.541,0 10.949,-1.321 15.637,-3.82 l 0.885,-0.472 1.725,3.244 h 14.828 v -0.794 l -6.68,-11.039 z m -26.514,0.053 c -8.094,0 -16.805,-5.697 -16.805,-18.206 0,-12.415 8.711,-18.069 16.805,-18.069 8.094,0 16.807,5.654 16.807,18.069 0,12.508 -8.713,18.206 -16.807,18.206 z" /></g><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path431"
|
id="path431"
|
||||||
d="m 165.162,221.756 0.006,-0.025 C 140.75,212.522 116.912,200.02 94.189,184.475 69.636,167.678 51.56,151.283 37.304,132.879 28.589,121.632 23.536,112.162 20.923,102.174 c -3.068,-11.729 0.105,-20.54 9.178,-25.482 2.277,-1.241 4.834,-2.269 7.596,-3.054 7.576,-2.153 15.721,-2.812 25.201,-2.015 1.244,0.104 2.52,0.217 3.805,0.332 1.402,0.123 2.803,0.242 4.209,0.368 l 3.176,0.281 3.846,-13.919 c -0.947,-0.121 -1.893,-0.245 -2.83,-0.37 -2.537,-0.337 -4.934,-0.656 -7.25,-0.857 -4.689,-0.406 -8.803,-0.604 -12.578,-0.604 -8.74,0 -16.342,1.076 -23.24,3.29 -14.58,4.68 -23.049,13.281 -25.893,26.297 -1.943,8.9 -0.568,18.38 4.328,29.833 6.098,14.267 15.623,27.692 29.977,42.251 31.707,32.162 69.879,56.911 116.699,75.662 3.182,1.274 6.383,2.416 9.771,3.624 1.434,0.511 2.889,1.029 4.369,1.568 l 2.396,-8.365 -8.521,-9.258 z" /><path
|
d="m 165.162,221.756 0.006,-0.025 C 140.75,212.522 116.912,200.02 94.189,184.475 69.636,167.678 51.56,151.283 37.304,132.879 28.589,121.632 23.536,112.162 20.923,102.174 c -3.068,-11.729 0.105,-20.54 9.178,-25.482 2.277,-1.241 4.834,-2.269 7.596,-3.054 7.576,-2.153 15.721,-2.812 25.201,-2.015 1.244,0.104 2.52,0.217 3.805,0.332 1.402,0.123 2.803,0.242 4.209,0.368 l 3.176,0.281 3.846,-13.919 c -0.947,-0.121 -1.893,-0.245 -2.83,-0.37 -2.537,-0.337 -4.934,-0.656 -7.25,-0.857 -4.689,-0.406 -8.803,-0.604 -12.578,-0.604 -8.74,0 -16.342,1.076 -23.24,3.29 -14.58,4.68 -23.049,13.281 -25.893,26.297 -1.943,8.9 -0.568,18.38 4.328,29.833 6.098,14.267 15.623,27.692 29.977,42.251 31.707,32.162 69.879,56.911 116.699,75.662 3.182,1.274 6.383,2.416 9.771,3.624 1.434,0.511 2.889,1.029 4.369,1.568 l 2.396,-8.365 z" /><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path433"
|
id="path433"
|
||||||
d="m 279.656,208.102 c -0.146,-0.262 -0.314,-0.56 -0.359,-0.905 -0.99,-8.005 -3.834,-16.142 -8.688,-24.875 -7.945,-14.297 -18.83,-27.683 -34.252,-42.126 -3.812,-3.572 -7.723,-6.949 -11.863,-10.523 -1.678,-1.448 -3.377,-2.915 -5.096,-4.419 -0.006,0.032 -0.012,0.062 -0.018,0.092 -0.062,0.355 -0.096,0.551 -0.09,0.713 l 0.148,3.794 c 0.176,4.559 0.359,9.272 0.67,13.896 0.047,0.706 0.615,1.672 1.52,2.583 2.135,2.144 4.346,4.286 6.484,6.358 3.807,3.687 7.742,7.5 11.389,11.467 11.611,12.634 19.076,24.245 23.488,36.543 2.049,5.705 2.707,10.802 2.012,15.581 -1.146,7.896 -6.145,13.235 -15.281,16.322 -2.455,0.829 -5.002,1.474 -7.656,1.956 l 9.738,12.6 c 1.551,-0.468 3.08,-0.975 4.574,-1.562 12.387,-4.858 19.754,-12.956 22.521,-24.758 l 0.869,-3.686 0,-8.847 c -0.034,-0.068 -0.071,-0.136 -0.11,-0.204 z" /><g
|
d="m 279.656,208.102 c -0.146,-0.262 -0.314,-0.56 -0.359,-0.905 -0.99,-8.005 -3.834,-16.142 -8.688,-24.875 -7.945,-14.297 -18.83,-27.683 -34.252,-42.126 -3.812,-3.572 -7.723,-6.949 -11.863,-10.523 -1.678,-1.448 -3.377,-2.915 -5.096,-4.419 -0.006,0.032 -0.012,0.062 -0.018,0.092 -0.062,0.355 -0.096,0.551 -0.09,0.713 l 0.148,3.794 c 0.176,4.559 0.359,9.272 0.67,13.896 0.047,0.706 0.615,1.672 1.52,2.583 2.135,2.144 4.346,4.286 6.484,6.358 3.807,3.687 7.742,7.5 11.389,11.467 11.611,12.634 19.076,24.245 23.488,36.543 2.049,5.705 2.707,10.802 2.012,15.581 -1.146,7.896 -6.145,13.235 -15.281,16.322 -2.455,0.829 -5.002,1.474 -7.656,1.956 l 9.738,12.6 c 1.551,-0.468 3.08,-0.975 4.574,-1.562 12.387,-4.858 19.754,-12.956 22.521,-24.758 l 0.869,-3.686 v -8.847 c -0.034,-0.068 -0.071,-0.136 -0.11,-0.204 z" /><g
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="g435"
|
id="g435"
|
||||||
transform="translate(-250.847,-184.784)"><path
|
transform="translate(-250.847,-184.784)"><path
|
||||||
|
@ -146,19 +146,14 @@
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path439"
|
id="path439"
|
||||||
d="m 433.437,425.474 c -2.322,7.348 -4.98,14.184 -8.043,20.678 -3.967,8.416 -9.191,17.993 -17.877,25.219 -9.297,7.733 -19.082,7.701 -28.365,-0.092 -5.934,-4.982 -10.92,-11.633 -15.691,-20.929 -6.629,-12.926 -11.459,-27.311 -15.66,-46.642 l -0.072,-0.342 c -0.174,-0.828 -0.412,-1.962 -0.893,-2.284 -4.152,-2.786 -8.357,-5.448 -12.807,-8.267 -1.068,-0.677 -2.146,-1.359 -3.238,-2.054 0.164,0.969 0.32,1.911 0.475,2.834 0.434,2.596 0.842,5.047 1.303,7.478 4.703,24.702 10.705,42.76 19.463,58.551 7.541,13.604 17.859,28.05 37.209,32.08 l 8.318,0 c 17.949,-3.632 27.887,-16.568 35.24,-28.748 1.953,-3.234 3.717,-6.507 5.244,-9.726 2.389,-5.035 4.557,-10.249 6.533,-15.655 l -11.139,-12.101 z" /></g><path
|
d="m 433.437,425.474 c -2.322,7.348 -4.98,14.184 -8.043,20.678 -3.967,8.416 -9.191,17.993 -17.877,25.219 -9.297,7.733 -19.082,7.701 -28.365,-0.092 -5.934,-4.982 -10.92,-11.633 -15.691,-20.929 -6.629,-12.926 -11.459,-27.311 -15.66,-46.642 l -0.072,-0.342 c -0.174,-0.828 -0.412,-1.962 -0.893,-2.284 -4.152,-2.786 -8.357,-5.448 -12.807,-8.267 -1.068,-0.677 -2.146,-1.359 -3.238,-2.054 0.164,0.969 0.32,1.911 0.475,2.834 0.434,2.596 0.842,5.047 1.303,7.478 4.703,24.702 10.705,42.76 19.463,58.551 7.541,13.604 17.859,28.05 37.209,32.08 h 8.318 c 17.949,-3.632 27.887,-16.568 35.24,-28.748 1.953,-3.234 3.717,-6.507 5.244,-9.726 2.389,-5.035 4.557,-10.249 6.533,-15.655 z" /></g><path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#ffffff"
|
style="fill:#ffffff"
|
||||||
id="path493"
|
id="path493"
|
||||||
d="M 28.084,368.98 0,429.872 l 0,1.124 14.16,0 4.202,-8.945 25.208,0 4.195,8.945 14.16,0 0,-1.124 -28.172,-60.892 -5.669,0 z m -5.438,41.259 8.215,-19.134 8.424,19.134 -16.639,0 z" /><g
|
d="M 28.084,368.98 0,429.872 v 1.124 h 14.16 l 4.202,-8.945 H 43.57 l 4.195,8.945 h 14.16 v -1.124 L 33.753,368.98 Z m -5.438,41.259 8.215,-19.134 8.424,19.134 z" /><g
|
||||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
id="text3371"><path
|
id="text3371"><path
|
||||||
d="m 318.27705,360.24104 c 2.295,-0.315 4.95,-2.115 4.95,-5.895 0,-3.96 -2.88,-6.57 -8.775,-6.57 -4.635,0 -8.685,1.62 -9.675,6.57 l 4.275,1.17 c 0.27,-1.98 1.395,-3.87 4.95,-3.87 2.925,0 4.635,1.305 4.635,3.51 0,2.295 -1.755,3.555 -4.77,3.555 l -2.475,0 0,3.42 2.655,0 c 2.925,0 4.86,1.215 4.86,3.69 0,2.295 -1.62,4.095 -4.86,4.095 -3.06,0 -5.04,-1.53 -5.625,-4.275 l -4.14,1.17 c 0.9,4.59 4.68,7.155 9.9,7.155 5.715,0 9.315,-3.015 9.315,-7.515 0,-3.69 -2.385,-5.76 -5.22,-6.03 l 0,-0.18 z"
|
d="m 318.27705,360.24104 c 2.295,-0.315 4.95,-2.115 4.95,-5.895 0,-3.96 -2.88,-6.57 -8.775,-6.57 -4.635,0 -8.685,1.62 -9.675,6.57 l 4.275,1.17 c 0.27,-1.98 1.395,-3.87 4.95,-3.87 2.925,0 4.635,1.305 4.635,3.51 0,2.295 -1.755,3.555 -4.77,3.555 h -2.475 v 3.42 h 2.655 c 2.925,0 4.86,1.215 4.86,3.69 0,2.295 -1.62,4.095 -4.86,4.095 -3.06,0 -5.04,-1.53 -5.625,-4.275 l -4.14,1.17 c 0.9,4.59 4.68,7.155 9.9,7.155 5.715,0 9.315,-3.015 9.315,-7.515 0,-3.69 -2.385,-5.76 -5.22,-6.03 z"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
|
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
|
||||||
id="path3376" /><path
|
id="path3376"
|
||||||
d="m 330.56416,373.87604 c 1.89,0 2.88,-1.17 2.88,-2.7 0,-1.485 -0.99,-2.655 -2.88,-2.655 -1.89,0 -2.925,1.17 -2.925,2.655 0,1.485 0.99,2.7 2.925,2.7 z"
|
inkscape:connector-curvature="0" /></g></svg>
|
||||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
|
|
||||||
id="path3378" /><path
|
|
||||||
d="m 346.79791,347.77604 c -6.39,0 -10.26,4.455 -10.26,13.095 0,8.64 3.87,13.095 10.26,13.095 6.345,0 10.215,-4.455 10.215,-13.095 0,-8.64 -3.87,-13.095 -10.215,-13.095 z m 0,4.32 c 3.78,0 5.49,2.79 5.49,8.775 0,5.94 -1.71,8.775 -5.49,8.775 -3.825,0 -5.535,-2.835 -5.535,-8.775 0,-5.985 1.71,-8.775 5.535,-8.775 z"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:expanded;font-size:45px;line-height:125%;font-family:'Novecento sans wide';-inkscape-font-specification:'Novecento sans wide, Semi-Bold Expanded';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff"
|
|
||||||
id="path3380" /></g></svg>
|
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
@ -223,7 +223,7 @@ class ScanWidget(QtWidgets.QWidget):
|
||||||
self._zoom(self.zoomFactor**y, ev.x())
|
self._zoom(self.zoomFactor**y, ev.x())
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
if not ev.oldSize().isValid():
|
if not ev.oldSize().isValid() or not ev.oldSize().width():
|
||||||
self.viewRange()
|
self.viewRange()
|
||||||
return
|
return
|
||||||
self.ticker.min_ticks = max(
|
self.ticker.min_ticks = max(
|
||||||
|
|
|
@ -117,7 +117,7 @@ def syscall(arg=None, flags={}):
|
||||||
def inner_decorator(function):
|
def inner_decorator(function):
|
||||||
function.artiq_embedded = \
|
function.artiq_embedded = \
|
||||||
_ARTIQEmbeddedInfo(core_name=None, portable=False, function=None,
|
_ARTIQEmbeddedInfo(core_name=None, portable=False, function=None,
|
||||||
syscall=function.__name__, forbidden=False,
|
syscall=arg, forbidden=False,
|
||||||
flags=set(flags))
|
flags=set(flags))
|
||||||
return function
|
return function
|
||||||
return inner_decorator
|
return inner_decorator
|
||||||
|
|
|
@ -42,12 +42,10 @@ class NoScan(ScanObject):
|
||||||
self.value = value
|
self.value = value
|
||||||
self.repetitions = repetitions
|
self.repetitions = repetitions
|
||||||
|
|
||||||
@portable
|
|
||||||
def _gen(self):
|
def _gen(self):
|
||||||
for i in range(self.repetitions):
|
for i in range(self.repetitions):
|
||||||
yield self.value
|
yield self.value
|
||||||
|
|
||||||
@portable
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self._gen()
|
return self._gen()
|
||||||
|
|
||||||
|
@ -81,7 +79,6 @@ class RangeScan(ScanObject):
|
||||||
rng = random.Random(seed)
|
rng = random.Random(seed)
|
||||||
random.shuffle(self.sequence, rng.random)
|
random.shuffle(self.sequence, rng.random)
|
||||||
|
|
||||||
@portable
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.sequence)
|
return iter(self.sequence)
|
||||||
|
|
||||||
|
@ -101,7 +98,6 @@ class ExplicitScan(ScanObject):
|
||||||
def __init__(self, sequence):
|
def __init__(self, sequence):
|
||||||
self.sequence = sequence
|
self.sequence = sequence
|
||||||
|
|
||||||
@portable
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.sequence)
|
return iter(self.sequence)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import tokenize
|
||||||
|
|
||||||
from artiq.protocols.sync_struct import Notifier, process_mod
|
from artiq.protocols.sync_struct import Notifier, process_mod
|
||||||
from artiq.protocols import pyon
|
from artiq.protocols import pyon
|
||||||
|
@ -7,7 +8,7 @@ from artiq.tools import TaskObject
|
||||||
|
|
||||||
def device_db_from_file(filename):
|
def device_db_from_file(filename):
|
||||||
glbs = dict()
|
glbs = dict()
|
||||||
with open(filename, "r") as f:
|
with tokenize.open(filename) as f:
|
||||||
exec(f.read(), glbs)
|
exec(f.read(), glbs)
|
||||||
return glbs["device_db"]
|
return glbs["device_db"]
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,8 @@ class ExamineDatasetMgr:
|
||||||
|
|
||||||
|
|
||||||
def examine(device_mgr, dataset_mgr, file):
|
def examine(device_mgr, dataset_mgr, file):
|
||||||
|
previous_keys = set(sys.modules.keys())
|
||||||
|
try:
|
||||||
module = file_import(file)
|
module = file_import(file)
|
||||||
for class_name, exp_class in module.__dict__.items():
|
for class_name, exp_class in module.__dict__.items():
|
||||||
if class_name[0] == "_":
|
if class_name[0] == "_":
|
||||||
|
@ -169,6 +171,10 @@ def examine(device_mgr, dataset_mgr, file):
|
||||||
(k, (proc.describe(), group, tooltip))
|
(k, (proc.describe(), group, tooltip))
|
||||||
for k, (proc, group, tooltip) in argument_mgr.requested_args.items())
|
for k, (proc, group, tooltip) in argument_mgr.requested_args.items())
|
||||||
register_experiment(class_name, name, arginfo)
|
register_experiment(class_name, name, arginfo)
|
||||||
|
finally:
|
||||||
|
new_keys = set(sys.modules.keys())
|
||||||
|
for key in new_keys - previous_keys:
|
||||||
|
del sys.modules[key]
|
||||||
|
|
||||||
|
|
||||||
def setup_diagnostics(experiment_file, repository_path):
|
def setup_diagnostics(experiment_file, repository_path):
|
||||||
|
|
|
@ -1,13 +1,37 @@
|
||||||
import sys
|
import sys
|
||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[:3] >= (3, 5, 2):
|
if sys.version_info[:3] >= (3, 5, 2) and sys.version_info[:3] <= (3, 6, 6):
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
# See https://github.com/m-labs/artiq/issues/506
|
# See https://github.com/m-labs/artiq/issues/506
|
||||||
def _ipaddr_info(host, port, family, type, proto):
|
def _ipaddr_info(host, port, family, type, proto):
|
||||||
return None
|
return None
|
||||||
asyncio.base_events._ipaddr_info = _ipaddr_info
|
asyncio.base_events._ipaddr_info = _ipaddr_info
|
||||||
|
|
||||||
|
# See https://github.com/m-labs/artiq/issues/1016
|
||||||
|
@asyncio.coroutine
|
||||||
|
def sock_connect(self, sock, address):
|
||||||
|
"""Connect to a remote socket at address.
|
||||||
|
|
||||||
|
This method is a coroutine.
|
||||||
|
"""
|
||||||
|
if self._debug and sock.gettimeout() != 0:
|
||||||
|
raise ValueError("the socket must be non-blocking")
|
||||||
|
|
||||||
|
if not hasattr(socket, 'AF_UNIX') or sock.family != socket.AF_UNIX:
|
||||||
|
socktype = sock.type & 0xf # WA https://bugs.python.org/issue21327
|
||||||
|
resolved = asyncio.base_events._ensure_resolved(
|
||||||
|
address, family=sock.family, type=socktype, proto=sock.proto, loop=self)
|
||||||
|
if not resolved.done():
|
||||||
|
yield from resolved
|
||||||
|
_, _, _, _, address = resolved.result()[0]
|
||||||
|
|
||||||
|
fut = self.create_future()
|
||||||
|
self._sock_connect(fut, sock, address)
|
||||||
|
return (yield from fut)
|
||||||
|
asyncio.selector_events.BaseSelectorEventLoop.sock_connect = sock_connect
|
||||||
|
|
|
@ -168,6 +168,7 @@ class LogForwarder(logging.Handler, TaskObject):
|
||||||
self._queue.put_nowait(record.source + ":" + self.format(record))
|
self._queue.put_nowait(record.source + ":" + self.format(record))
|
||||||
|
|
||||||
async def _do(self):
|
async def _do(self):
|
||||||
|
reader = writer = None
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
reader, writer = await asyncio.open_connection(self.host,
|
reader, writer = await asyncio.open_connection(self.host,
|
||||||
|
@ -182,4 +183,5 @@ class LogForwarder(logging.Handler, TaskObject):
|
||||||
except:
|
except:
|
||||||
await asyncio.sleep(self.reconnect_timer)
|
await asyncio.sleep(self.reconnect_timer)
|
||||||
finally:
|
finally:
|
||||||
|
if writer is not None:
|
||||||
writer.close()
|
writer.close()
|
||||||
|
|
|
@ -243,6 +243,7 @@ class AsyncioClient:
|
||||||
|
|
||||||
No further method calls should be done after this method is called.
|
No further method calls should be done after this method is called.
|
||||||
"""
|
"""
|
||||||
|
if self.__writer is not None:
|
||||||
self.__writer.close()
|
self.__writer.close()
|
||||||
self.__reader = None
|
self.__reader = None
|
||||||
self.__writer = None
|
self.__writer = None
|
||||||
|
|
|
@ -188,6 +188,7 @@ _eval_dict = {
|
||||||
"false": False,
|
"false": False,
|
||||||
"true": True,
|
"true": True,
|
||||||
"slice": slice,
|
"slice": slice,
|
||||||
|
"nan": numpy.nan,
|
||||||
|
|
||||||
"Fraction": Fraction,
|
"Fraction": Fraction,
|
||||||
"OrderedDict": OrderedDict,
|
"OrderedDict": OrderedDict,
|
||||||
|
|
|
@ -74,4 +74,4 @@ class AnalyzerTest(ExperimentCase):
|
||||||
log = "".join([_extract_log_chars(msg.data)
|
log = "".join([_extract_log_chars(msg.data)
|
||||||
for msg in dump.messages
|
for msg in dump.messages
|
||||||
if isinstance(msg, OutputMessage) and msg.channel == dump.log_channel])
|
if isinstance(msg, OutputMessage) and msg.channel == dump.log_channel])
|
||||||
self.assertEqual(log, "foo\x1E32\n\x1D")
|
self.assertEqual(log, "foo\x1E32\x1D")
|
||||||
|
|
|
@ -140,6 +140,8 @@ class _RPCTypes(EnvExperiment):
|
||||||
self.accept("foo")
|
self.accept("foo")
|
||||||
self.accept(b"foo")
|
self.accept(b"foo")
|
||||||
self.accept(bytearray(b"foo"))
|
self.accept(bytearray(b"foo"))
|
||||||
|
self.accept(bytes([1, 2]))
|
||||||
|
self.accept(bytearray([1, 2]))
|
||||||
self.accept((2, 3))
|
self.accept((2, 3))
|
||||||
self.accept([1, 2])
|
self.accept([1, 2])
|
||||||
self.accept(range(10))
|
self.accept(range(10))
|
||||||
|
@ -211,10 +213,25 @@ class _RPCCalls(EnvExperiment):
|
||||||
def numpy_full(self):
|
def numpy_full(self):
|
||||||
return numpy.full(10, 20)
|
return numpy.full(10, 20)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def numpy_nan(self):
|
||||||
|
return numpy.full(10, numpy.nan)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def builtin(self):
|
def builtin(self):
|
||||||
sleep(1.0)
|
sleep(1.0)
|
||||||
|
|
||||||
|
@rpc(flags={"async"})
|
||||||
|
def async_rpc(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def async_in_try(self):
|
||||||
|
try:
|
||||||
|
self.async_rpc()
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RPCCallsTest(ExperimentCase):
|
class RPCCallsTest(ExperimentCase):
|
||||||
def test_args(self):
|
def test_args(self):
|
||||||
|
@ -229,7 +246,9 @@ class RPCCallsTest(ExperimentCase):
|
||||||
self.assertEqual(exp.numpy_things(),
|
self.assertEqual(exp.numpy_things(),
|
||||||
(numpy.int32(10), numpy.int64(20), numpy.array([42,])))
|
(numpy.int32(10), numpy.int64(20), numpy.array([42,])))
|
||||||
self.assertTrue((exp.numpy_full() == numpy.full(10, 20)).all())
|
self.assertTrue((exp.numpy_full() == numpy.full(10, 20)).all())
|
||||||
|
self.assertTrue(numpy.isnan(exp.numpy_nan()).all())
|
||||||
exp.builtin()
|
exp.builtin()
|
||||||
|
exp.async_in_try()
|
||||||
|
|
||||||
|
|
||||||
class _Annotation(EnvExperiment):
|
class _Annotation(EnvExperiment):
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import time
|
||||||
|
|
||||||
|
from artiq.experiment import *
|
||||||
|
from artiq.test.hardware_testbench import ExperimentCase
|
||||||
|
|
||||||
|
|
||||||
|
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
|
||||||
|
|
||||||
|
|
||||||
|
class _Transfer(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.data = b"\x00"*(10**6)
|
||||||
|
|
||||||
|
@rpc
|
||||||
|
def source(self) -> TBytes:
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
@rpc(flags={"async"})
|
||||||
|
def sink(self, data):
|
||||||
|
assert data == self.data
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def host_to_device(self):
|
||||||
|
t0 = self.core.get_rtio_counter_mu()
|
||||||
|
data = self.source()
|
||||||
|
t1 = self.core.get_rtio_counter_mu()
|
||||||
|
return len(data)/self.core.mu_to_seconds(t1-t0)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def device_to_host(self):
|
||||||
|
t0 = self.core.get_rtio_counter_mu()
|
||||||
|
self.sink(self.data)
|
||||||
|
t1 = self.core.get_rtio_counter_mu()
|
||||||
|
return len(self.data)/self.core.mu_to_seconds(t1-t0)
|
||||||
|
|
||||||
|
|
||||||
|
class TransferTest(ExperimentCase):
|
||||||
|
@unittest.skipUnless(artiq_low_latency,
|
||||||
|
"timings are dependent on CPU load and network conditions")
|
||||||
|
def test_host_to_device(self):
|
||||||
|
exp = self.create(_Transfer)
|
||||||
|
host_to_device_rate = exp.host_to_device()
|
||||||
|
print(host_to_device_rate, "B/s")
|
||||||
|
self.assertGreater(host_to_device_rate, 1.6e6)
|
||||||
|
|
||||||
|
@unittest.skipUnless(artiq_low_latency,
|
||||||
|
"timings are dependent on CPU load and network conditions")
|
||||||
|
def test_device_to_host(self):
|
||||||
|
exp = self.create(_Transfer)
|
||||||
|
device_to_host_rate = exp.device_to_host()
|
||||||
|
print(device_to_host_rate, "B/s")
|
||||||
|
self.assertGreater(device_to_host_rate, 1.6e6)
|
||||||
|
|
||||||
|
|
||||||
|
class _KernelOverhead(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
|
||||||
|
def kernel_overhead(self):
|
||||||
|
n = 100
|
||||||
|
t0 = time.monotonic()
|
||||||
|
for _ in range(n):
|
||||||
|
self.dummy_kernel()
|
||||||
|
t1 = time.monotonic()
|
||||||
|
return (t1-t0)/n
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def dummy_kernel(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class KernelOverheadTest(ExperimentCase):
|
||||||
|
@unittest.skipUnless(artiq_low_latency,
|
||||||
|
"timings are dependent on CPU load and network conditions")
|
||||||
|
def test_kernel_overhead(self):
|
||||||
|
exp = self.create(_KernelOverhead)
|
||||||
|
kernel_overhead = exp.kernel_overhead()
|
||||||
|
print(kernel_overhead, "s")
|
||||||
|
self.assertGreater(kernel_overhead, 0.001)
|
||||||
|
self.assertLess(kernel_overhead, 0.5)
|
|
@ -202,6 +202,20 @@ class _RPCExceptions(EnvExperiment):
|
||||||
self.success = True
|
self.success = True
|
||||||
|
|
||||||
|
|
||||||
|
class _Keywords(EnvExperiment):
|
||||||
|
def build(self, value, output):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.value = value
|
||||||
|
self.output = output
|
||||||
|
|
||||||
|
def rpc(self, kw):
|
||||||
|
self.output.append(kw)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
self.rpc(kw=self.value)
|
||||||
|
|
||||||
|
|
||||||
class HostVsDeviceCase(ExperimentCase):
|
class HostVsDeviceCase(ExperimentCase):
|
||||||
def test_primes(self):
|
def test_primes(self):
|
||||||
l_device, l_host = [], []
|
l_device, l_host = [], []
|
||||||
|
@ -245,3 +259,18 @@ class HostVsDeviceCase(ExperimentCase):
|
||||||
f(_RPCExceptions, catch=False)
|
f(_RPCExceptions, catch=False)
|
||||||
uut = self.execute(_RPCExceptions, catch=True)
|
uut = self.execute(_RPCExceptions, catch=True)
|
||||||
self.assertTrue(uut.success)
|
self.assertTrue(uut.success)
|
||||||
|
|
||||||
|
def test_keywords(self):
|
||||||
|
for f in self.execute, _run_on_host:
|
||||||
|
output = []
|
||||||
|
f(_Keywords, value=0, output=output)
|
||||||
|
self.assertEqual(output, [0])
|
||||||
|
output = []
|
||||||
|
f(_Keywords, value=1, output=output)
|
||||||
|
self.assertEqual(output, [1])
|
||||||
|
output = []
|
||||||
|
f(_Keywords, value=False, output=output)
|
||||||
|
self.assertEqual(output, [False])
|
||||||
|
output = []
|
||||||
|
f(_Keywords, value=True, output=output)
|
||||||
|
self.assertEqual(output, [True])
|
||||||
|
|
|
@ -81,7 +81,7 @@ class ClockGeneratorLoopback(EnvExperiment):
|
||||||
self.core.reset()
|
self.core.reset()
|
||||||
self.loop_clock_in.input()
|
self.loop_clock_in.input()
|
||||||
self.loop_clock_out.stop()
|
self.loop_clock_out.stop()
|
||||||
delay(20*us)
|
delay(200*us)
|
||||||
with parallel:
|
with parallel:
|
||||||
self.loop_clock_in.gate_rising(10*us)
|
self.loop_clock_in.gate_rising(10*us)
|
||||||
with sequential:
|
with sequential:
|
||||||
|
@ -178,6 +178,61 @@ class LoopbackCount(EnvExperiment):
|
||||||
self.set_dataset("count", self.loop_in.count())
|
self.set_dataset("count", self.loop_in.count())
|
||||||
|
|
||||||
|
|
||||||
|
class IncorrectPulseTiming(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LoopbackGateTiming(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.setattr_device("loop_in")
|
||||||
|
self.setattr_device("loop_out")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
# Make sure there are no leftover events.
|
||||||
|
self.core.reset()
|
||||||
|
|
||||||
|
# Determine loop delay.
|
||||||
|
with parallel:
|
||||||
|
self.loop_in.gate_rising_mu(10000)
|
||||||
|
with sequential:
|
||||||
|
delay_mu(5000)
|
||||||
|
out_mu = now_mu()
|
||||||
|
self.loop_out.pulse_mu(1000)
|
||||||
|
in_mu = self.loop_in.timestamp_mu()
|
||||||
|
if in_mu < 0:
|
||||||
|
raise PulseNotReceived("Cannot determine loop delay")
|
||||||
|
loop_delay_mu = in_mu - out_mu
|
||||||
|
|
||||||
|
# With the exact delay known, make sure tight gate timings work.
|
||||||
|
# In the most common configuration, 24 mu == 24 ns == 3 coarse periods,
|
||||||
|
# which should be plenty of slack.
|
||||||
|
delay_mu(10000)
|
||||||
|
|
||||||
|
gate_start_mu = now_mu()
|
||||||
|
self.loop_in.gate_both_mu(24)
|
||||||
|
gate_end_mu = now_mu()
|
||||||
|
|
||||||
|
# gateware latency offset between gate and input
|
||||||
|
lat_offset = 3*8
|
||||||
|
out_mu = gate_start_mu - loop_delay_mu + lat_offset
|
||||||
|
at_mu(out_mu)
|
||||||
|
self.loop_out.pulse_mu(24)
|
||||||
|
|
||||||
|
in_mu = self.loop_in.timestamp_mu()
|
||||||
|
if in_mu < 0:
|
||||||
|
raise PulseNotReceived()
|
||||||
|
if not (gate_start_mu <= (in_mu - lat_offset) <= gate_end_mu):
|
||||||
|
raise IncorrectPulseTiming("Input event should occur during gate")
|
||||||
|
if not (-2 < (in_mu - out_mu - loop_delay_mu) < 2):
|
||||||
|
raise IncorrectPulseTiming("Loop delay should not change")
|
||||||
|
|
||||||
|
in_mu = self.loop_in.timestamp_mu()
|
||||||
|
if in_mu > 0:
|
||||||
|
raise IncorrectPulseTiming("Only one pulse should be received")
|
||||||
|
|
||||||
|
|
||||||
class IncorrectLevel(Exception):
|
class IncorrectLevel(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -387,6 +442,9 @@ class CoredeviceTest(ExperimentCase):
|
||||||
count = self.dataset_mgr.get("count")
|
count = self.dataset_mgr.get("count")
|
||||||
self.assertEqual(count, npulses)
|
self.assertEqual(count, npulses)
|
||||||
|
|
||||||
|
def test_loopback_gate_timing(self):
|
||||||
|
self.execute(LoopbackGateTiming)
|
||||||
|
|
||||||
def test_level(self):
|
def test_level(self):
|
||||||
self.execute(Level)
|
self.execute(Level)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from artiq.experiment import *
|
||||||
|
from artiq.test.hardware_testbench import ExperimentCase
|
||||||
|
|
||||||
|
|
||||||
|
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
|
||||||
|
|
||||||
|
|
||||||
|
class _Stress(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
|
||||||
|
@rpc(flags={"async"})
|
||||||
|
def sink(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def async_rpc(self, n):
|
||||||
|
for _ in range(n):
|
||||||
|
self.sink(b"")
|
||||||
|
|
||||||
|
|
||||||
|
class StressTest(ExperimentCase):
|
||||||
|
def test_async_rpc(self):
|
||||||
|
exp = self.create(_Stress)
|
||||||
|
exp.async_rpc(16000)
|
|
@ -34,14 +34,33 @@ mod cslice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AsCSlice<'a, T> {
|
||||||
|
fn as_c_slice(&'a self) -> CSlice<'a, T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[path = "../../firmware/ksupport/eh.rs"]
|
impl<'a> AsCSlice<'a, u8> for str {
|
||||||
pub mod eh;
|
fn as_c_slice(&'a self) -> CSlice<'a, u8> {
|
||||||
|
CSlice {
|
||||||
|
base: self.as_ptr(),
|
||||||
|
len: self.len() as u32,
|
||||||
|
phantom: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[path = "."]
|
||||||
|
pub mod eh {
|
||||||
|
#[path = "../../firmware/libeh/dwarf.rs"]
|
||||||
|
pub mod dwarf;
|
||||||
|
}
|
||||||
|
#[path = "../../firmware/ksupport/eh_artiq.rs"]
|
||||||
|
pub mod eh_artiq;
|
||||||
|
|
||||||
use std::{str, process};
|
use std::{str, process};
|
||||||
|
|
||||||
fn terminate(exception: &eh::Exception, mut _backtrace: &mut [usize]) -> ! {
|
fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> ! {
|
||||||
println!("Uncaught {}: {} ({}, {}, {})",
|
println!("Uncaught {}: {} ({}, {}, {})",
|
||||||
str::from_utf8(exception.name.as_ref()).unwrap(),
|
str::from_utf8(exception.name.as_ref()).unwrap(),
|
||||||
str::from_utf8(exception.message.as_ref()).unwrap(),
|
str::from_utf8(exception.message.as_ref()).unwrap(),
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue