2
0
mirror of https://github.com/m-labs/artiq.git synced 2024-12-25 19:28:26 +08:00

Merge branch 'master' into new-py2llvm

This commit is contained in:
whitequark 2015-07-27 03:29:00 +03:00
commit 7903889082
25 changed files with 305 additions and 156 deletions

View File

@ -1,8 +1,7 @@
#!/bin/sh #!/bin/sh
packages="https://people.phys.ethz.ch/~robertjo/artiq-dev/or1k-gcc_20141105-1_amd64.deb packages="http://us.archive.ubuntu.com/ubuntu/pool/universe/i/iverilog/iverilog_0.9.7-1_amd64.deb"
https://people.phys.ethz.ch/~robertjo/artiq-dev/or1k-binutils_20141105-1_amd64.deb archives="http://fehu.whitequark.org/files/binutils-or1k.tbz2 http://fehu.whitequark.org/files/llvm-or1k.tbz2"
http://us.archive.ubuntu.com/ubuntu/pool/universe/i/iverilog/iverilog_0.9.7-1_amd64.deb"
mkdir -p packages mkdir -p packages
@ -13,12 +12,18 @@ do
dpkg -x $pkg_name packages dpkg -x $pkg_name packages
done done
export PATH=$PWD/packages/usr/local/bin:$PWD/packages/usr/bin:$PATH for a in $archives
do
wget $a
(cd packages && tar xvf ../$(basename $a))
done
export PATH=$PWD/packages/usr/local/llvm-or1k/bin:$PWD/packages/usr/local/bin:$PWD/packages/usr/bin:$PATH
export LD_LIBRARY_PATH=$PWD/packages/usr/lib/x86_64-linux-gnu:$PWD/packages/usr/local/x86_64-unknown-linux-gnu/or1k-elf/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=$PWD/packages/usr/lib/x86_64-linux-gnu:$PWD/packages/usr/local/x86_64-unknown-linux-gnu/or1k-elf/lib:$LD_LIBRARY_PATH
echo -e "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $HOME/.mlabs/build_settings.sh echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $HOME/.mlabs/build_settings.sh
echo "export PATH=$PWD/packages/usr/local/llvm-or1k/bin:$PATH" >> $HOME/.mlabs/build_settings.sh
or1k-elf-as --version or1k-linux-as --version
or1k-elf-gcc --version llc --version
clang --version clang --version
llvm-as --version || true

View File

@ -18,6 +18,7 @@ from artiq.gui.results import ResultsDock
from artiq.gui.parameters import ParametersDock from artiq.gui.parameters import ParametersDock
from artiq.gui.schedule import ScheduleDock from artiq.gui.schedule import ScheduleDock
from artiq.gui.log import LogDock from artiq.gui.log import LogDock
from artiq.gui.console import ConsoleDock
data_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), data_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
@ -99,7 +100,7 @@ def main():
area.addDock(d_params, "above", d_results) area.addDock(d_params, "above", d_results)
area.addDock(d_explorer, "above", d_params) area.addDock(d_explorer, "above", d_params)
d_schedule = ScheduleDock(schedule_ctl) d_schedule = ScheduleDock(status_bar, schedule_ctl)
loop.run_until_complete(d_schedule.sub_connect( loop.run_until_complete(d_schedule.sub_connect(
args.server, args.port_notify)) args.server, args.port_notify))
atexit.register(lambda: loop.run_until_complete(d_schedule.sub_close())) atexit.register(lambda: loop.run_until_complete(d_schedule.sub_close()))
@ -109,7 +110,19 @@ def main():
args.server, args.port_notify)) args.server, args.port_notify))
atexit.register(lambda: loop.run_until_complete(d_log.sub_close())) atexit.register(lambda: loop.run_until_complete(d_log.sub_close()))
area.addDock(d_log, "bottom") pdb = AsyncioClient()
loop.run_until_complete(pdb.connect_rpc(
args.server, args.port_control, "master_pdb"))
atexit.register(lambda: pdb.close_rpc())
def _get_parameter(k, v):
asyncio.async(pdb.set(k, v))
d_console = ConsoleDock(
d_params.get_parameter,
_get_parameter,
d_results.get_result)
area.addDock(d_console, "bottom")
area.addDock(d_log, "above", d_console)
area.addDock(d_schedule, "above", d_log) area.addDock(d_schedule, "above", d_log)
win.show() win.show()

View File

@ -26,7 +26,7 @@ class ELFRunner(EnvExperiment):
def run(self): def run(self):
with open(self.file, "rb") as f: with open(self.file, "rb") as f:
self.core.comm.load(f.read()) self.core.comm.load(f.read())
self.core.comm.run("run", False) self.core.comm.run("run")
self.core.comm.serve(dict(), dict()) self.core.comm.serve(dict(), dict())

23
artiq/gui/console.py Normal file
View File

@ -0,0 +1,23 @@
from pyqtgraph import console, dockarea
_help = """
This is an interactive Python console.
The following functions are available:
get_parameter(key)
set_parameter(key, value) [asynchronous update]
get_result(key) [real-time results only]
"""
class ConsoleDock(dockarea.Dock):
def __init__(self, get_parameter, set_parameter, get_result):
dockarea.Dock.__init__(self, "Console", size=(1000, 300))
ns = {
"get_parameter": get_parameter,
"set_parameter": set_parameter,
"get_result": get_result
}
c = console.ConsoleWidget(namespace=ns, text=_help)
self.addWidget(c)

View File

@ -7,7 +7,7 @@ from pyqtgraph import LayoutWidget
from artiq.protocols.sync_struct import Subscriber from artiq.protocols.sync_struct import Subscriber
from artiq.protocols import pyon from artiq.protocols import pyon
from artiq.gui.tools import DictSyncModel from artiq.gui.tools import DictSyncModel, force_spinbox_value
from artiq.gui.scan import ScanController from artiq.gui.scan import ScanController
@ -73,7 +73,7 @@ class _NumberEntry(QtGui.QDoubleSpinBox):
if procdesc["unit"]: if procdesc["unit"]:
self.setSuffix(" " + procdesc["unit"]) self.setSuffix(" " + procdesc["unit"])
if "default" in procdesc: if "default" in procdesc:
self.setValue(procdesc["default"]) force_spinbox_value(self, procdesc["default"])
def get_argument_value(self): def get_argument_value(self):
return self.value() return self.value()

View File

@ -12,6 +12,15 @@ class _LogModel(ListSyncModel):
ListSyncModel.__init__(self, ListSyncModel.__init__(self,
["RID", "Message"], ["RID", "Message"],
parent, init) parent, init)
self.fixed_font = QtGui.QFont()
self.fixed_font.setFamily("Monospace")
def data(self, index, role):
if (role == QtCore.Qt.FontRole and index.isValid()
and index.column() == 1):
return self.fixed_font
else:
return ListSyncModel.data(self, index, role)
def convert(self, v, column): def convert(self, v, column):
return v[column] return v[column]

View File

@ -34,7 +34,7 @@ class ParametersDock(dockarea.Dock):
self.search = QtGui.QLineEdit() self.search = QtGui.QLineEdit()
self.search.setPlaceholderText("search...") self.search.setPlaceholderText("search...")
self.search.editingFinished.connect(self.search_parameters) self.search.editingFinished.connect(self._search_parameters)
grid.addWidget(self.search, 0, 0) grid.addWidget(self.search, 0, 0)
self.table = QtGui.QTableView() self.table = QtGui.QTableView()
@ -43,7 +43,10 @@ class ParametersDock(dockarea.Dock):
QtGui.QHeaderView.ResizeToContents) QtGui.QHeaderView.ResizeToContents)
grid.addWidget(self.table, 1, 0) grid.addWidget(self.table, 1, 0)
def search_parameters(self): def get_parameter(self, key):
return self.table_model.backing_store[key]
def _search_parameters(self):
model = self.table.model() model = self.table.model()
parentIndex = model.index(0, 0) parentIndex = model.index(0, 0)
numRows = model.rowCount(parentIndex) numRows = model.rowCount(parentIndex)
@ -66,6 +69,6 @@ class ParametersDock(dockarea.Dock):
yield from self.subscriber.close() yield from self.subscriber.close()
def init_parameters_model(self, init): def init_parameters_model(self, init):
table_model = ParametersModel(self.table, init) self.table_model = ParametersModel(self.table, init)
self.table.setModel(table_model) self.table.setModel(self.table_model)
return table_model return self.table_model

View File

@ -55,6 +55,9 @@ class ResultsDock(dockarea.Dock):
self.displays = dict() self.displays = dict()
def get_result(self, key):
return self.table_model.backing_store[key]
@asyncio.coroutine @asyncio.coroutine
def sub_connect(self, host, port): def sub_connect(self, host, port):
self.subscriber = Subscriber("rt_results", self.init_results_model, self.subscriber = Subscriber("rt_results", self.init_results_model,

View File

@ -1,6 +1,8 @@
from quamash import QtGui from quamash import QtGui
from pyqtgraph import LayoutWidget from pyqtgraph import LayoutWidget
from artiq.gui.tools import force_spinbox_value
class _Range(LayoutWidget): class _Range(LayoutWidget):
def __init__(self, global_min, global_max, global_step, unit): def __init__(self, global_min, global_max, global_step, unit):
@ -33,9 +35,9 @@ class _Range(LayoutWidget):
self.addWidget(self.npoints, 0, 5) self.addWidget(self.npoints, 0, 5)
def set_values(self, min, max, npoints): def set_values(self, min, max, npoints):
self.min.setValue(min) force_spinbox_value(self.min, min)
self.max.setValue(max) force_spinbox_value(self.max, max)
self.npoints.setValue(npoints) force_spinbox_value(self.npoints, npoints)
def get_values(self): def get_values(self):
return { return {
@ -97,13 +99,13 @@ class ScanController(LayoutWidget):
d = procdesc["default"] d = procdesc["default"]
if d["ty"] == "NoScan": if d["ty"] == "NoScan":
self.noscan.setChecked(True) self.noscan.setChecked(True)
self.v_noscan.setValue(d["value"]) force_spinbox_value(self.v_noscan, d["value"])
elif d["ty"] == "LinearScan": elif d["ty"] == "LinearScan":
self.linear.setChecked(True) self.linear.setChecked(True)
self.v_linear.set_values(d["min"], d["max"], d["step"]) self.v_linear.set_values(d["min"], d["max"], d["npoints"])
elif d["ty"] == "RandomScan": elif d["ty"] == "RandomScan":
self.random.setChecked(True) self.random.setChecked(True)
self.v_random.set_values(d["min"], d["max"], d["step"]) self.v_random.set_values(d["min"], d["max"], d["npoints"])
elif d["ty"] == "ExplicitScan": elif d["ty"] == "ExplicitScan":
self.explicit.setChecked(True) self.explicit.setChecked(True)
self.v_explicit.insert(" ".join( self.v_explicit.insert(" ".join(

View File

@ -46,9 +46,10 @@ class _ScheduleModel(DictSyncModel):
class ScheduleDock(dockarea.Dock): class ScheduleDock(dockarea.Dock):
def __init__(self, schedule_ctl): def __init__(self, status_bar, schedule_ctl):
dockarea.Dock.__init__(self, "Schedule", size=(1000, 300)) dockarea.Dock.__init__(self, "Schedule", size=(1000, 300))
self.status_bar = status_bar
self.schedule_ctl = schedule_ctl self.schedule_ctl = schedule_ctl
self.table = QtGui.QTableView() self.table = QtGui.QTableView()
@ -86,4 +87,5 @@ class ScheduleDock(dockarea.Dock):
if idx: if idx:
row = idx[0].row() row = idx[0].row()
rid = self.table_model.row_to_key[row] rid = self.table_model.row_to_key[row]
self.status_bar.showMessage("Deleted RID {}".format(rid))
asyncio.async(self.delete(rid)) asyncio.async(self.delete(rid))

View File

@ -1,10 +1,23 @@
from quamash import QtCore from quamash import QtCore
def force_spinbox_value(spinbox, value):
if spinbox.minimum() > value:
spinbox.setMinimum(value)
if spinbox.maximum() < value:
spinbox.setMaximum(value)
spinbox.setValue(value)
def short_format(v): def short_format(v):
t = type(v) t = type(v)
if t is int or t is float: if t is int or t is float:
return str(v) return str(v)
elif t is str:
if len(v) < 15:
return "\"" + v + "\""
else:
return "\"" + v[:12] + "\"..."
else: else:
r = t.__name__ r = t.__name__
if t is list or t is dict or t is set: if t is list or t is dict or t is set:

View File

@ -9,7 +9,6 @@ and modifications to mutable types are not written back. For example, if the
client passes a list as a parameter of an RPC method, and that method client passes a list as a parameter of an RPC method, and that method
``append()s`` an element to the list, the element is not appended to the ``append()s`` an element to the list, the element is not appended to the
client's list. client's list.
""" """
import socket import socket
@ -29,17 +28,13 @@ logger = logging.getLogger(__name__)
class RemoteError(Exception): class RemoteError(Exception):
"""Raised when a RPC failed or raised an exception on the remote (server) """Raised when a RPC failed or raised an exception on the remote (server)
side. side."""
"""
pass pass
class IncompatibleServer(Exception): class IncompatibleServer(Exception):
"""Raised by the client when attempting to connect to a server that does """Raised by the client when attempting to connect to a server that does
not have the expected target. not have the expected target."""
"""
pass pass
@ -74,7 +69,6 @@ class Client:
Use ``None`` to skip selecting a target. The list of targets can then Use ``None`` to skip selecting a target. The list of targets can then
be retrieved using ``get_rpc_id`` and then one can be selected later be retrieved using ``get_rpc_id`` and then one can be selected later
using ``select_rpc_target``. using ``select_rpc_target``.
""" """
def __init__(self, host, port, target_name): def __init__(self, host, port, target_name):
self.__socket = socket.create_connection((host, port)) self.__socket = socket.create_connection((host, port))
@ -93,25 +87,20 @@ class Client:
def select_rpc_target(self, target_name): def select_rpc_target(self, target_name):
"""Selects a RPC target by name. This function should be called """Selects a RPC target by name. This function should be called
exactly once if the object was created with ``target_name=None``. exactly once if the object was created with ``target_name=None``."""
"""
if target_name not in self.__target_names: if target_name not in self.__target_names:
raise IncompatibleServer raise IncompatibleServer
self.__socket.sendall((target_name + "\n").encode()) self.__socket.sendall((target_name + "\n").encode())
def get_rpc_id(self): def get_rpc_id(self):
"""Returns a tuple (target_names, id_parameters) containing the """Returns a tuple (target_names, id_parameters) containing the
identification information of the server. identification information of the server."""
"""
return (self.__target_names, self.__id_parameters) return (self.__target_names, self.__id_parameters)
def close_rpc(self): def close_rpc(self):
"""Closes the connection to the RPC server. """Closes the connection to the RPC server.
No further method calls should be done after this method is called. No further method calls should be done after this method is called.
""" """
self.__socket.close() self.__socket.close()
@ -159,6 +148,8 @@ class AsyncioClient:
All RPC methods are coroutines. All RPC methods are coroutines.
Concurrent access from different asyncio tasks is supported; all calls
use a single lock.
""" """
def __init__(self): def __init__(self):
self.__lock = asyncio.Lock() self.__lock = asyncio.Lock()
@ -171,9 +162,7 @@ class AsyncioClient:
def connect_rpc(self, host, port, target_name): def connect_rpc(self, host, port, target_name):
"""Connects to the server. This cannot be done in __init__ because """Connects to the server. This cannot be done in __init__ because
this method is a coroutine. See ``Client`` for a description of the this method is a coroutine. See ``Client`` for a description of the
parameters. parameters."""
"""
self.__reader, self.__writer = \ self.__reader, self.__writer = \
yield from asyncio.open_connection(host, port) yield from asyncio.open_connection(host, port)
try: try:
@ -190,7 +179,6 @@ class AsyncioClient:
def select_rpc_target(self, target_name): def select_rpc_target(self, target_name):
"""Selects a RPC target by name. This function should be called """Selects a RPC target by name. This function should be called
exactly once if the connection was created with ``target_name=None``. exactly once if the connection was created with ``target_name=None``.
""" """
if target_name not in self.__target_names: if target_name not in self.__target_names:
raise IncompatibleServer raise IncompatibleServer
@ -198,16 +186,13 @@ class AsyncioClient:
def get_rpc_id(self): def get_rpc_id(self):
"""Returns a tuple (target_names, id_parameters) containing the """Returns a tuple (target_names, id_parameters) containing the
identification information of the server. identification information of the server."""
"""
return (self.__target_names, self.__id_parameters) return (self.__target_names, self.__id_parameters)
def close_rpc(self): def close_rpc(self):
"""Closes the connection to the RPC server. """Closes the connection to the RPC server.
No further method calls should be done after this method is called. No further method calls should be done after this method is called.
""" """
self.__writer.close() self.__writer.close()
self.__reader = None self.__reader = None
@ -261,7 +246,6 @@ class BestEffortClient:
connection attempt at object initialization. connection attempt at object initialization.
:param retry: Amount of time to wait between retries when reconnecting :param retry: Amount of time to wait between retries when reconnecting
in the background. in the background.
""" """
def __init__(self, host, port, target_name, def __init__(self, host, port, target_name,
firstcon_timeout=0.5, retry=5.0): firstcon_timeout=0.5, retry=5.0):
@ -322,7 +306,6 @@ class BestEffortClient:
"""Closes the connection to the RPC server. """Closes the connection to the RPC server.
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.__conretry_thread is None: if self.__conretry_thread is None:
if self.__socket is not None: if self.__socket is not None:
@ -415,7 +398,6 @@ class Server(_AsyncioServer):
Clients select one of these objects using its name upon connection. Clients select one of these objects using its name upon connection.
:param id_parameters: An optional human-readable string giving more :param id_parameters: An optional human-readable string giving more
information about the parameters of the server. information about the parameters of the server.
""" """
def __init__(self, targets, id_parameters=None): def __init__(self, targets, id_parameters=None):
_AsyncioServer.__init__(self) _AsyncioServer.__init__(self)
@ -485,7 +467,6 @@ def simple_server_loop(targets, host, port, id_parameters=None):
"""Runs a server until an exception is raised (e.g. the user hits Ctrl-C). """Runs a server until an exception is raised (e.g. the user hits Ctrl-C).
See ``Server`` for a description of the parameters. See ``Server`` for a description of the parameters.
""" """
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
try: try:

View File

@ -39,6 +39,16 @@ _encode_map = {
numpy.ndarray: "nparray" numpy.ndarray: "nparray"
} }
_numpy_scalar = {
"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64",
"float16", "float32", "float64"
}
for _t in _numpy_scalar:
_encode_map[getattr(numpy, _t)] = "npscalar"
class _Encoder: class _Encoder:
def __init__(self, pretty): def __init__(self, pretty):
self.pretty = pretty self.pretty = pretty
@ -114,6 +124,13 @@ class _Encoder:
r += ")" r += ")"
return r return r
def encode_npscalar(self, x):
r = "npscalar("
r += "\"" + type(x).__name__ + "\", "
r += encode(base64.b64encode(x).decode())
r += ")"
return r
def encode(self, x): def encode(self, x):
return getattr(self, "encode_" + _encode_map[type(x)])(x) return getattr(self, "encode_" + _encode_map[type(x)])(x)
@ -131,6 +148,10 @@ def _nparray(shape, dtype, data):
return a.reshape(shape) return a.reshape(shape)
def _npscalar(ty, data):
return numpy.frombuffer(base64.b64decode(data), dtype=ty)[0]
_eval_dict = { _eval_dict = {
"__builtins__": None, "__builtins__": None,
@ -139,7 +160,8 @@ _eval_dict = {
"true": True, "true": True,
"Fraction": Fraction, "Fraction": Fraction,
"nparray": _nparray "nparray": _nparray,
"npscalar": _npscalar
} }
def decode(s): def decode(s):

View File

@ -4,14 +4,15 @@ from fractions import Fraction
import numpy as np import numpy as np
from artiq.language.units import *
from artiq.protocols import pyon from artiq.protocols import pyon
_pyon_test_object = { _pyon_test_object = {
(1, 2): [(3, 4.2), (2, )], (1, 2): [(3, 4.2), (2, )],
Fraction(3, 4): np.linspace(5, 10, 1), Fraction(3, 4): np.linspace(5, 10, 1),
"frequency": 10*GHz "a": np.int8(9), "b": np.int16(-98), "c": np.int32(42), "d": np.int64(-5),
"e": np.uint8(8), "f": np.uint16(5), "g": np.uint32(4), "h": np.uint64(9),
"x": np.float16(9.0), "y": np.float32(9.0), "z": np.float64(9.0),
} }

View File

@ -22,7 +22,9 @@ from unittest.mock import MagicMock
class Mock(MagicMock): class Mock(MagicMock):
@classmethod @classmethod
def __getattr__(cls, name): def __getattr__(cls, name):
return Mock() if name == "_mock_methods":
return None
return Mock()
mock_modules = ["artiq.gui.moninj", "quamash", "pyqtgraph", "matplotlib"] mock_modules = ["artiq.gui.moninj", "quamash", "pyqtgraph", "matplotlib"]

View File

@ -70,7 +70,52 @@ Next step (for KC705) is to flash MAC and IP addresses to the board:
Installing from source Installing from source
---------------------- ----------------------
You can skip this if you already installed from conda. You can skip the first two steps if you already installed from conda.
Preparing the build environment for the core device
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
These steps are required to generate code that can run on the core
device. They are necessary both for building the MiSoC BIOS
and the ARTIQ kernels.
* Create a development directory: ::
$ mkdir ~/artiq-dev
* Install OpenRISC binutils (or1k-linux-...): ::
$ cd ~/artiq-dev
$ wget https://ftp.gnu.org/gnu/binutils/binutils-2.25.1.tar.bz2
$ tar xvf binutils-2.25.1.tar.bz2
$ rm binutils-2.25.1.tar.bz2
$ mkdir binutils-2.25.1/build
$ cd binutils-2.25.1/build
$ ../configure --target=or1k-linux --prefix=/usr/local
$ make -j4
$ sudo make install
.. note::
We're using an ``or1k-linux`` target because it is necessary to enable
shared library support in ``ld``, not because Linux is involved.
* Install LLVM and Clang: ::
$ cd ~/artiq-dev
$ git clone https://github.com/openrisc/llvm-or1k
$ cd llvm-or1k/tools
$ git clone https://github.com/openrisc/clang-or1k clang
$ cd ..
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/llvm-or1k -DLLVM_TARGETS_TO_BUILD="OR1K;X86" -DCMAKE_BUILD_TYPE=Rel -DLLVM_ENABLE_ASSERTIONS=ON
$ make -j4
$ sudo make install
.. note::
Compilation of LLVM can take more than 30 min on some machines.
Preparing the core device FPGA board Preparing the core device FPGA board
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -85,10 +130,6 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC
* During the Xilinx toolchain installation, uncheck ``Install cable drivers`` (they are not required as we use better and open source alternatives). * During the Xilinx toolchain installation, uncheck ``Install cable drivers`` (they are not required as we use better and open source alternatives).
* Create a development directory: ::
$ mkdir ~/artiq-dev
* Install Migen: :: * Install Migen: ::
$ cd ~/artiq-dev $ cd ~/artiq-dev
@ -99,31 +140,6 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC
.. note:: .. note::
The options ``develop`` and ``--user`` are for setup.py to install Migen in ``~/.local/lib/python3.4``. The options ``develop`` and ``--user`` are for setup.py to install Migen in ``~/.local/lib/python3.4``.
* Install OpenRISC GCC/binutils toolchain (or1k-elf-...): ::
$ cd ~/artiq-dev
$ git clone https://github.com/openrisc/or1k-src
$ cd or1k-src
$ mkdir build
$ cd build
$ ../configure --target=or1k-elf --enable-shared --disable-itcl \
--disable-tk --disable-tcl --disable-winsup \
--disable-gdbtk --disable-libgui --disable-rda \
--disable-sid --disable-sim --disable-gdb \
--disable-newlib --disable-libgloss --disable-werror
$ make -j4
$ sudo make install
$ cd ~/artiq-dev
$ git clone https://github.com/openrisc/or1k-gcc
$ cd or1k-gcc
$ mkdir build
$ cd build
$ ../configure --target=or1k-elf --enable-languages=c \
--disable-shared --disable-libssp
$ make -j4
$ sudo make install
.. _install-xc3sprog: .. _install-xc3sprog:
* Install JTAG tools needed to program the Pipistrello and KC705: * Install JTAG tools needed to program the Pipistrello and KC705:
@ -181,6 +197,7 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC
:: ::
$ cd ~/artiq-dev/misoc $ cd ~/artiq-dev/misoc
$ export PATH=$PATH:/usr/local/llvm-or1k/bin
* For Pipistrello:: * For Pipistrello::
@ -279,19 +296,7 @@ To flash the ``idle`` kernel:
Installing the host-side software Installing the host-side software
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Install LLVM and the llvmlite Python bindings: :: * Install the llvmlite Python bindings: ::
$ cd ~/artiq-dev
$ git clone https://github.com/openrisc/llvm-or1k
$ cd llvm-or1k/tools
$ git clone https://github.com/openrisc/clang-or1k clang
$ cd ..
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/llvm-or1k -DLLVM_TARGETS_TO_BUILD="OR1K;X86" -DCMAKE_BUILD_TYPE=Debug
$ make -j4
$ sudo make install
$ cd ~/artiq-dev $ cd ~/artiq-dev
$ git clone https://github.com/numba/llvmlite $ git clone https://github.com/numba/llvmlite
@ -299,14 +304,11 @@ Installing the host-side software
$ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-add-all-targets.patch $ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-add-all-targets.patch
$ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-rename.patch $ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-rename.patch
$ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-build-as-debug-on-windows.patch $ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-build-as-debug-on-windows.patch
$ PATH=/usr/local/llvm-or1k/bin:$PATH sudo -E python3 setup.py install $ LLVM_CONFIG=/usr/local/llvm-or1k/bin/llvm-config python3 setup.py install --user
.. note:: .. note::
llvmlite is in development and its API is not stable yet. Commit ID ``11a8303d02e3d6dd2d1e0e9065701795cd8a979f`` is known to work. llvmlite is in development and its API is not stable yet. Commit ID ``11a8303d02e3d6dd2d1e0e9065701795cd8a979f`` is known to work.
.. note::
Compilation of LLVM can take more than 30 min on some machines.
* Install ARTIQ: :: * Install ARTIQ: ::
$ cd ~/artiq-dev $ cd ~/artiq-dev

View File

@ -53,7 +53,7 @@
"type": "local", "type": "local",
"module": "artiq.coredevice.ttl", "module": "artiq.coredevice.ttl",
"class": "TTLOut", "class": "TTLOut",
"arguments": {"channel": 18} "arguments": {"channel": 17}
}, },
"dds_bus": { "dds_bus": {

View File

@ -27,12 +27,11 @@ class FloppingF(EnvExperiment):
"""Flopping F simulation""" """Flopping F simulation"""
def build(self): def build(self):
self.attr_argument("npoints", FreeValue(100)) self.attr_argument("frequency_scan", Scannable(
self.attr_argument("min_freq", FreeValue(1000)) default=LinearScan(1000, 2000, 100)))
self.attr_argument("max_freq", FreeValue(2000))
self.attr_argument("F0", FreeValue(1500)) self.attr_argument("F0", NumberValue(1500, min=1000, max=2000))
self.attr_argument("noise_amplitude", FreeValue(0.1)) self.attr_argument("noise_amplitude", NumberValue(0.1, min=0, max=100))
self.frequency = self.set_result("flopping_f_frequency", [], True) self.frequency = self.set_result("flopping_f_frequency", [], True)
self.brightness = self.set_result("flopping_f_brightness", [], True) self.brightness = self.set_result("flopping_f_brightness", [], True)
@ -40,8 +39,7 @@ class FloppingF(EnvExperiment):
self.attr_device("scheduler") self.attr_device("scheduler")
def run(self): def run(self):
for i in range(self.npoints): for frequency in self.frequency_scan:
frequency = (self.max_freq-self.min_freq)*i/(self.npoints - 1) + self.min_freq
brightness = model(frequency, self.F0) + self.noise_amplitude*random.random() brightness = model(frequency, self.F0) + self.noise_amplitude*random.random()
self.frequency.append(frequency) self.frequency.append(frequency)
self.brightness.append(brightness) self.brightness.append(brightness)

View File

@ -1,12 +1,22 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from setuptools import setup, find_packages from setuptools import setup, find_packages, Command
import sys import sys
import os import os
if sys.version_info[:3] < (3, 4, 3): if sys.version_info[:3] < (3, 4, 3):
raise Exception("You need at least Python 3.4.3 to run ARTIQ") raise Exception("You need at least Python 3.4.3 to run ARTIQ")
class PushDocCommand(Command):
description = "uploads the documentation to m-labs.hk"
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
os.system("rsync -avz doc/manual/_build/html/ shell.serverraum.org:~/web/m-labs.hk/artiq/manual")
requirements = [ requirements = [
"sphinx", "sphinx-argparse", "pyserial", "numpy", "scipy", "sphinx", "sphinx-argparse", "pyserial", "numpy", "scipy",
"python-dateutil", "prettytable", "h5py", "pydaqmx", "pyelftools", "python-dateutil", "prettytable", "h5py", "pydaqmx", "pyelftools",
@ -52,5 +62,6 @@ setup(
ext_modules=[], ext_modules=[],
entry_points={ entry_points={
"console_scripts": scripts, "console_scripts": scripts,
} },
cmdclass={"push_doc":PushDocCommand}
) )

View File

@ -4,6 +4,7 @@
#include "log.h" #include "log.h"
#include "flash_storage.h" #include "flash_storage.h"
#include "mailbox.h" #include "mailbox.h"
#include "messages.h"
#include "elf_loader.h" #include "elf_loader.h"
#include "services.h" #include "services.h"
#include "kloader.h" #include "kloader.h"
@ -122,3 +123,65 @@ void kloader_stop(void)
kernel_cpu_reset_write(1); kernel_cpu_reset_write(1);
mailbox_acknowledge(); mailbox_acknowledge();
} }
int kloader_validate_kpointer(void *p)
{
unsigned int v = (unsigned int)p;
if((v < 0x40400000) || (v > (0x4fffffff - 1024*1024))) {
log("Received invalid pointer from kernel CPU: 0x%08x", v);
return 0;
}
return 1;
}
int kloader_is_essential_kmsg(int msgtype)
{
switch(msgtype) {
case MESSAGE_TYPE_NOW_INIT_REQUEST:
case MESSAGE_TYPE_NOW_SAVE:
case MESSAGE_TYPE_LOG:
return 1;
default:
return 0;
}
}
long long int now;
void kloader_service_essential_kmsg(void)
{
struct msg_base *umsg;
umsg = mailbox_receive();
if(umsg) {
if(!kloader_validate_kpointer(umsg))
return;
switch(umsg->type) {
case MESSAGE_TYPE_NOW_INIT_REQUEST: {
struct msg_now_init_reply reply;
reply.type = MESSAGE_TYPE_NOW_INIT_REPLY;
reply.now = now;
mailbox_send_and_wait(&reply);
break;
}
case MESSAGE_TYPE_NOW_SAVE: {
struct msg_now_save *msg = (struct msg_now_save *)umsg;
now = msg->now;
mailbox_acknowledge();
break;
}
case MESSAGE_TYPE_LOG: {
struct msg_log *msg = (struct msg_log *)umsg;
log_va(msg->fmt, msg->args);
mailbox_acknowledge();
break;
}
default:
/* handled elsewhere */
break;
}
}
}

View File

@ -4,6 +4,8 @@
#define KERNELCPU_EXEC_ADDRESS 0x40400000 #define KERNELCPU_EXEC_ADDRESS 0x40400000
#define KERNELCPU_PAYLOAD_ADDRESS 0x40404000 #define KERNELCPU_PAYLOAD_ADDRESS 0x40404000
extern long long int now;
typedef void (*kernel_function)(void); typedef void (*kernel_function)(void);
int kloader_load(void *buffer, int length); int kloader_load(void *buffer, int length);
@ -14,4 +16,8 @@ void kloader_start_idle_kernel(void);
void kloader_start_user_kernel(kernel_function k); void kloader_start_user_kernel(kernel_function k);
void kloader_stop(void); void kloader_stop(void);
int kloader_validate_kpointer(void *p);
int kloader_is_essential_kmsg(int msgtype);
void kloader_service_essential_kmsg(void);
#endif /* __KLOADER_H */ #endif /* __KLOADER_H */

View File

@ -24,6 +24,17 @@ SECTIONS
_etext = .; _etext = .;
} > ksupport } > ksupport
.got :
{
_GLOBAL_OFFSET_TABLE_ = .;
*(.got)
} > ksupport
.got.plt :
{
*(.got.plt)
} > ksupport
.rodata : .rodata :
{ {
. = ALIGN(4); . = ALIGN(4);
@ -39,7 +50,6 @@ SECTIONS
_fdata = .; _fdata = .;
*(.data .data.* .gnu.linkonce.d.*) *(.data .data.* .gnu.linkonce.d.*)
*(.data1) *(.data1)
_gp = ALIGN(16);
*(.sdata .sdata.* .gnu.linkonce.s.*) *(.sdata .sdata.* .gnu.linkonce.s.*)
_edata = .; _edata = .;
} > ksupport } > ksupport

View File

@ -26,6 +26,17 @@ SECTIONS
_etext = .; _etext = .;
} > runtime } > runtime
.got :
{
_GLOBAL_OFFSET_TABLE_ = .;
*(.got)
} > runtime
.got.plt :
{
*(.got.plt)
} > runtime
.rodata : .rodata :
{ {
. = ALIGN(4); . = ALIGN(4);
@ -41,7 +52,6 @@ SECTIONS
_fdata = .; _fdata = .;
*(.data .data.* .gnu.linkonce.d.*) *(.data .data.* .gnu.linkonce.d.*)
*(.data1) *(.data1)
_gp = ALIGN(16);
*(.sdata .sdata.* .gnu.linkonce.s.*) *(.sdata .sdata.* .gnu.linkonce.s.*)
_edata = .; _edata = .;
} > runtime } > runtime

View File

@ -143,6 +143,7 @@ static void regular_main(void)
session_end(); session_end();
while(1) { while(1) {
lwip_service(); lwip_service();
kloader_service_essential_kmsg();
kserver_service(); kserver_service();
} }
} }
@ -201,8 +202,10 @@ static void regular_main(void)
/* Open the session for the serial control. */ /* Open the session for the serial control. */
session_start(); session_start();
while(1) while(1) {
kloader_service_essential_kmsg();
serial_service(); serial_service();
}
} }
#endif #endif

View File

@ -11,8 +11,8 @@
#include "log.h" #include "log.h"
#include "kloader.h" #include "kloader.h"
#include "exceptions.h" #include "exceptions.h"
#include "session.h"
#include "flash_storage.h" #include "flash_storage.h"
#include "session.h"
#define BUFFER_IN_SIZE (1024*1024) #define BUFFER_IN_SIZE (1024*1024)
#define BUFFER_OUT_SIZE (1024*1024) #define BUFFER_OUT_SIZE (1024*1024)
@ -55,7 +55,6 @@ static void submit_output(int len)
} }
static int user_kernel_state; static int user_kernel_state;
static long long int now;
enum { enum {
USER_KERNEL_NONE = 0, USER_KERNEL_NONE = 0,
@ -421,16 +420,6 @@ static int add_rpc_value(int bi, int type_tag, void *value)
return bi - obi; return bi - obi;
} }
static int validate_kpointer(void *p)
{
unsigned int v = (unsigned int)p;
if((v < 0x40400000) || (v > (0x4fffffff - 1024*1024))) {
log("Received invalid pointer from kernel CPU: 0x%08x", v);
return 0;
}
return 1;
}
static int send_rpc_request(int rpc_num, va_list args) static int send_rpc_request(int rpc_num, va_list args)
{ {
int r; int r;
@ -448,7 +437,7 @@ static int send_rpc_request(int rpc_num, va_list args)
v = NULL; v = NULL;
else { else {
v = va_arg(args, void *); v = va_arg(args, void *);
if(!validate_kpointer(v)) if(!kloader_validate_kpointer(v))
return 0; return 0;
} }
r = add_rpc_value(bi, type_tag, v); r = add_rpc_value(bi, type_tag, v);
@ -467,31 +456,16 @@ static int send_rpc_request(int rpc_num, va_list args)
/* assumes output buffer is empty when called */ /* assumes output buffer is empty when called */
static int process_kmsg(struct msg_base *umsg) static int process_kmsg(struct msg_base *umsg)
{ {
if(!validate_kpointer(umsg)) if(!kloader_validate_kpointer(umsg))
return 0; return 0;
if((user_kernel_state != USER_KERNEL_RUNNING) if(kloader_is_essential_kmsg(umsg->type))
&& (umsg->type != MESSAGE_TYPE_NOW_INIT_REQUEST) return 1; /* handled elsewhere */
&& (umsg->type != MESSAGE_TYPE_NOW_SAVE)) { if(user_kernel_state != USER_KERNEL_RUNNING) {
log("Received unexpected message from kernel CPU while not in running state"); log("Received unexpected message from kernel CPU while not in running state");
return 0; return 0;
} }
switch(umsg->type) { switch(umsg->type) {
case MESSAGE_TYPE_NOW_INIT_REQUEST: {
struct msg_now_init_reply reply;
reply.type = MESSAGE_TYPE_NOW_INIT_REPLY;
reply.now = now;
mailbox_send_and_wait(&reply);
break;
}
case MESSAGE_TYPE_NOW_SAVE: {
struct msg_now_save *msg = (struct msg_now_save *)umsg;
now = msg->now;
mailbox_acknowledge();
break;
}
case MESSAGE_TYPE_FINISHED: case MESSAGE_TYPE_FINISHED:
buffer_out[8] = REMOTEMSG_TYPE_KERNEL_FINISHED; buffer_out[8] = REMOTEMSG_TYPE_KERNEL_FINISHED;
submit_output(9); submit_output(9);
@ -538,13 +512,6 @@ static int process_kmsg(struct msg_base *umsg)
mailbox_acknowledge(); mailbox_acknowledge();
break; break;
} }
case MESSAGE_TYPE_LOG: {
struct msg_log *msg = (struct msg_log *)umsg;
log_va(msg->fmt, msg->args);
mailbox_acknowledge();
break;
}
default: { default: {
log("Received invalid message type from kernel CPU"); log("Received invalid message type from kernel CPU");
return 0; return 0;