From 0e375e498023d86bb9178d826413fe6b42a2c19b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 30 Oct 2015 00:50:18 +0800 Subject: [PATCH 01/11] gui: fix shortcut pipeline --- artiq/gui/explorer.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/artiq/gui/explorer.py b/artiq/gui/explorer.py index 7ee3e311e..c9816b55d 100644 --- a/artiq/gui/explorer.py +++ b/artiq/gui/explorer.py @@ -368,12 +368,9 @@ class ExplorerDock(dockarea.Dock): arguments = self.argeditor_states[key]["argument_values"] except KeyError: arguments = dict() - asyncio.ensure_future(self.submit_task(self.pipeline.text(), - expinfo["file"], + asyncio.ensure_future(self.submit_task(pipeline, expinfo["file"], expinfo["class_name"], - arguments, - priority, - due_date, + arguments, priority, due_date, flush)) def submit_clicked(self): From ff93e3c149786b6b14cb493dd774566647744c67 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 30 Oct 2015 05:07:01 +0300 Subject: [PATCH 02/11] manual: update installing.rst to elaborate QC1/QC2 differences. --- doc/manual/installing.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 263f5fceb..9a3ea155a 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -50,9 +50,14 @@ Then you can install the ARTIQ package, it will pull all the necessary dependenc $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-pipistrello-nist_qc1; \ echo "Created environment $ENV for ARTIQ" -* For the KC705 board:: +* For the KC705 board with SCSI cables and AD9858 DDS chips:: - $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-kc705-nist_qc1 artiq-kc705-nist_qc2; \ + $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-kc705-nist_qc1; \ + echo "Created environment $ENV for ARTIQ" + +* For the KC705 board with the FMC backplane and AD9914 DDS chips:: + + $ ENV=$(date +artiq-%Y-%m-%d); conda create -n $ENV artiq-kc705-nist_qc2; \ echo "Created environment $ENV for ARTIQ" This creates a new Conda "environment" (i.e. an isolated installation) and prints its name. From 2c77c80b4f9af43cb0b6dccd809a807b68b70b00 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 30 Oct 2015 13:41:18 +0800 Subject: [PATCH 03/11] master: expose more scheduler APIs to the experiments --- artiq/frontend/artiq_master.py | 7 ++++++- artiq/frontend/artiq_run.py | 14 +++++++++++--- artiq/master/scheduler.py | 8 ++++++++ artiq/master/worker_impl.py | 10 +++++++--- doc/manual/management_system.rst | 14 ++++++++++++-- examples/master/repository/terminate_all.py | 18 ++++++++++++++++++ 6 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 examples/master/repository/terminate_all.py diff --git a/artiq/frontend/artiq_master.py b/artiq/frontend/artiq_master.py index 186d14e68..4659cf1a0 100755 --- a/artiq/frontend/artiq_master.py +++ b/artiq/frontend/artiq_master.py @@ -83,7 +83,12 @@ def main(): "log": log_worker } scheduler = Scheduler(get_last_rid() + 1, worker_handlers, repo_backend) - worker_handlers["scheduler_submit"] = scheduler.submit + worker_handlers.update({ + "scheduler_submit": scheduler.submit, + "scheduler_delete": scheduler.delete, + "scheduler_request_termination": scheduler.request_termination, + "scheduler_get_status": scheduler.get_status + }) scheduler.start() atexit.register(lambda: loop.run_until_complete(scheduler.stop())) diff --git a/artiq/frontend/artiq_run.py b/artiq/frontend/artiq_run.py index b4477077d..0db16a4d6 100755 --- a/artiq/frontend/artiq_run.py +++ b/artiq/frontend/artiq_run.py @@ -32,20 +32,28 @@ class ELFRunner(EnvExperiment): class DummyScheduler: def __init__(self): - self.next_rid = 0 + self.rid = 0 self.pipeline_name = "main" self.priority = 0 self.expid = None + self._next_rid = 1 + def submit(self, pipeline_name, expid, priority, due_date, flush): - rid = self.next_rid - self.next_rid += 1 + rid = self._next_rid + self._next_rid += 1 logger.info("Submitting: %s, RID=%s", expid, rid) return rid def delete(self, rid): logger.info("Deleting RID %s", rid) + def request_termination(self, rid): + logger.info("Requesting termination of RID %s", rid) + + def get_status(self): + return dict() + def pause(self): pass diff --git a/artiq/master/scheduler.py b/artiq/master/scheduler.py index 11eb6384e..b0987d361 100644 --- a/artiq/master/scheduler.py +++ b/artiq/master/scheduler.py @@ -412,6 +412,7 @@ class Scheduler: logger.warning("some pipelines were not garbage-collected") def submit(self, pipeline_name, expid, priority, due_date, flush): + """Submits a new run.""" # mutates expid to insert head repository revision if None if self._terminated: return @@ -427,9 +428,11 @@ class Scheduler: return pipeline.pool.submit(expid, priority, due_date, flush, pipeline_name) def delete(self, rid): + """Kills the run with the specified RID.""" self._deleter.delete(rid) def request_termination(self, rid): + """Requests graceful termination of the run with the specified RID.""" for pipeline in self._pipelines.values(): if rid in pipeline.pool.runs: run = pipeline.pool.runs[rid] @@ -438,3 +441,8 @@ class Scheduler: else: self.delete(rid) break + + def get_status(self): + """Returns a dictionary containing information about the runs currently + tracked by the scheduler.""" + return self.notifier.read diff --git a/artiq/master/worker_impl.py b/artiq/master/worker_impl.py index 722fcb75c..e4e617e80 100644 --- a/artiq/master/worker_impl.py +++ b/artiq/master/worker_impl.py @@ -95,9 +95,13 @@ class Scheduler: raise TerminationRequested submit = staticmethod(make_parent_action("scheduler_submit")) - cancel = staticmethod(make_parent_action("scheduler_cancel")) + delete = staticmethod(make_parent_action("scheduler_delete")) + request_termination = staticmethod( + make_parent_action("scheduler_request_termination")) + get_status = staticmethod(make_parent_action("scheduler_get_status")) - def set_run_info(self, pipeline_name, expid, priority): + def set_run_info(self, rid, pipeline_name, expid, priority): + self.rid = rid self.pipeline_name = pipeline_name self.expid = expid self.priority = priority @@ -182,7 +186,7 @@ def main(): expf = expid["file"] exp = get_exp(expf, expid["class_name"]) device_mgr.virtual_devices["scheduler"].set_run_info( - obj["pipeline_name"], expid, obj["priority"]) + rid, obj["pipeline_name"], expid, obj["priority"]) exp_inst = exp(device_mgr, dataset_mgr, **expid["arguments"]) put_object({"action": "completed"}) diff --git a/doc/manual/management_system.rst b/doc/manual/management_system.rst index b3072ad49..84a471cce 100644 --- a/doc/manual/management_system.rst +++ b/doc/manual/management_system.rst @@ -111,8 +111,18 @@ Push commits containing experiments to the bare repository using e.g. Git over S The GUI always runs experiments from the repository. The command-line client, by default, runs experiment from the raw filesystem (which is useful for iterating rapidly without creating many disorganized commits). If you want to use the repository instead, simply pass the ``-R`` option. -Reference -********* +Scheduler API reference +*********************** + +The scheduler is exposed to the experiments via a virtual device called ``scheduler``. It can be requested like any regular device, and then the methods below can be called on the returned object. + +The scheduler virtual device also contains the attributes ``rid``, ``pipeline_name``, ``priority`` and ``expid`` that contain the corresponding information about the current run. + +.. autoclass:: artiq.master.scheduler.Scheduler + :members: + +Front-end tool reference +************************ .. argparse:: :ref: artiq.frontend.artiq_master.get_argparser diff --git a/examples/master/repository/terminate_all.py b/examples/master/repository/terminate_all.py new file mode 100644 index 000000000..e9f7c9242 --- /dev/null +++ b/examples/master/repository/terminate_all.py @@ -0,0 +1,18 @@ +from artiq import * + + +class TerminateAll(EnvExperiment): + def build(self): + self.setattr_device("scheduler") + self.setattr_argument("graceful_termination", BooleanValue(True)) + + def run(self): + if self.graceful_termination: + terminate = self.scheduler.request_termination + else: + terminate = self.scheduler.delete + + print("our RID", self.scheduler.rid) + for rid in self.scheduler.get_status().keys(): + if rid != self.scheduler.rid: + terminate(rid) From 41543662b277f9afdf31b9eef69e4884f4e600c5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 30 Oct 2015 13:42:59 +0800 Subject: [PATCH 04/11] examples/terminate_all: remove debug print --- examples/master/repository/terminate_all.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/master/repository/terminate_all.py b/examples/master/repository/terminate_all.py index e9f7c9242..9e1f38ff3 100644 --- a/examples/master/repository/terminate_all.py +++ b/examples/master/repository/terminate_all.py @@ -12,7 +12,6 @@ class TerminateAll(EnvExperiment): else: terminate = self.scheduler.delete - print("our RID", self.scheduler.rid) for rid in self.scheduler.get_status().keys(): if rid != self.scheduler.rid: terminate(rid) From 58f55fc63060bbbc51661028098358fd9750e987 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 30 Oct 2015 15:52:03 +0800 Subject: [PATCH 05/11] artiq_flash: check that xc3sprog is found. closes #162 --- artiq/frontend/artiq_flash.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/artiq/frontend/artiq_flash.sh b/artiq/frontend/artiq_flash.sh index 155fcb422..94773ec9b 100755 --- a/artiq/frontend/artiq_flash.sh +++ b/artiq/frontend/artiq_flash.sh @@ -166,9 +166,17 @@ fi set +e xc3sprog -c $CABLE -R > /dev/null 2>&1 -if [ "$?" != "0" ] +STATUS=$? +set -e +if [ "$STATUS" == "127" ] then - echo "Flashing failed. Maybe you do not have permission to access the USB device?" + echo "xc3sprog not found. Please install it or check your PATH." + exit +fi +if [ "$STATUS" != "0" ] +then + echo "Failed to connect to FPGA." + echo "Maybe you do not have permission to access the USB device?" echo "To fix this you might want to add a udev rule by doing:" echo "$ sudo cp $ARTIQ_PREFIX/misc/$UDEV_RULES /etc/udev/rules.d" echo "Then unplug/replug your device and try flashing again" @@ -177,7 +185,6 @@ then echo "Please make sure you used the correct -t option (currently: $BOARD)" exit fi -set -e if [ ! -z "$FILENAME" ] then From f3ee97a4ff41356af4b4b12dfdec0223de778a65 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 30 Oct 2015 19:58:15 +0800 Subject: [PATCH 06/11] fix typo --- artiq/gui/datasets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gui/datasets.py b/artiq/gui/datasets.py index fcaa36ac1..edf116b92 100644 --- a/artiq/gui/datasets.py +++ b/artiq/gui/datasets.py @@ -58,7 +58,7 @@ class DatasetsDock(dockarea.Dock): self.search = QtGui.QLineEdit() self.search.setPlaceholderText("search...") self.search.editingFinished.connect(self._search_datasets) - grid.addWidget(self.search, 0, ) + grid.addWidget(self.search, 0, 0) self.table = QtGui.QTableView() self.table.setSelectionMode(QtGui.QAbstractItemView.NoSelection) From b61eb866b6eb945f99b5d61f0ff04b883b27447f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 30 Oct 2015 19:58:33 +0800 Subject: [PATCH 07/11] gui: log freetext filter --- artiq/gui/log.py | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/artiq/gui/log.py b/artiq/gui/log.py index f5b4aa15e..295798035 100644 --- a/artiq/gui/log.py +++ b/artiq/gui/log.py @@ -72,21 +72,39 @@ class _LogModel(ListSyncModel): return v[3] -class _LevelFilterProxyModel(QSortFilterProxyModel): - def __init__(self, min_level): +class _LogFilterProxyModel(QSortFilterProxyModel): + def __init__(self, min_level, freetext): QSortFilterProxyModel.__init__(self) self.min_level = min_level + self.freetext = freetext def filterAcceptsRow(self, sourceRow, sourceParent): model = self.sourceModel() + index = model.index(sourceRow, 0, sourceParent) data = model.data(index, QtCore.Qt.DisplayRole) - return getattr(logging, data) >= self.min_level + accepted_level = getattr(logging, data) >= self.min_level + + if self.freetext: + index = model.index(sourceRow, 1, sourceParent) + data_source = model.data(index, QtCore.Qt.DisplayRole) + index = model.index(sourceRow, 3, sourceParent) + data_message = model.data(index, QtCore.Qt.DisplayRole) + accepted_freetext = (self.freetext in data_source + or self.freetext in data_message) + else: + accepted_freetext = True + + return accepted_level and accepted_freetext def set_min_level(self, min_level): self.min_level = min_level self.invalidateFilter() + def set_freetext(self, freetext): + self.freetext = freetext + self.invalidateFilter() + class LogDock(dockarea.Dock): def __init__(self): @@ -96,14 +114,17 @@ class LogDock(dockarea.Dock): self.addWidget(grid) grid.addWidget(QtGui.QLabel("Minimum level: "), 0, 0) - grid.layout.setColumnStretch(0, 0) - grid.layout.setColumnStretch(1, 0) - grid.layout.setColumnStretch(2, 1) - self.filterbox = QtGui.QComboBox() - self.filterbox.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]) - self.filterbox.setToolTip("Display entries at or above this level") - grid.addWidget(self.filterbox, 0, 1) - self.filterbox.currentIndexChanged.connect(self.filter_changed) + self.filter_level = QtGui.QComboBox() + self.filter_level.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]) + self.filter_level.setToolTip("Display entries at or above this level") + grid.addWidget(self.filter_level, 0, 1) + self.filter_level.currentIndexChanged.connect( + self.filter_level_changed) + self.filter_freetext = QtGui.QLineEdit() + self.filter_freetext.setPlaceholderText("freetext filter...") + self.filter_freetext.editingFinished.connect( + self.filter_freetext_changed) + grid.addWidget(self.filter_freetext, 0, 2) self.log = QtGui.QTableView() self.log.setSelectionMode(QtGui.QAbstractItemView.NoSelection) @@ -113,7 +134,7 @@ class LogDock(dockarea.Dock): QtGui.QAbstractItemView.ScrollPerPixel) self.log.setShowGrid(False) self.log.setTextElideMode(QtCore.Qt.ElideNone) - grid.addWidget(self.log, 1, 0, colspan=3) + grid.addWidget(self.log, 1, 0, colspan=4) self.scroll_at_bottom = False async def sub_connect(self, host, port): @@ -123,9 +144,12 @@ class LogDock(dockarea.Dock): async def sub_close(self): await self.subscriber.close() - def filter_changed(self): + def filter_level_changed(self): self.table_model_filter.set_min_level( - getattr(logging, self.filterbox.currentText())) + getattr(logging, self.filter_level.currentText())) + + def filter_freetext_changed(self): + self.table_model_filter.set_freetext(self.filter_freetext.text()) def rows_inserted_before(self): scrollbar = self.log.verticalScrollBar() @@ -137,8 +161,9 @@ class LogDock(dockarea.Dock): def init_log_model(self, init): table_model = _LogModel(self.log, init) - self.table_model_filter = _LevelFilterProxyModel( - getattr(logging, self.filterbox.currentText())) + self.table_model_filter = _LogFilterProxyModel( + getattr(logging, self.filter_level.currentText()), + self.filter_freetext.text()) self.table_model_filter.setSourceModel(table_model) self.log.setModel(self.table_model_filter) self.table_model_filter.rowsAboutToBeInserted.connect(self.rows_inserted_before) @@ -146,7 +171,7 @@ class LogDock(dockarea.Dock): return table_model def save_state(self): - return {"min_level_idx": self.filterbox.currentIndex()} + return {"min_level_idx": self.filter_level.currentIndex()} def restore_state(self, state): try: @@ -154,4 +179,4 @@ class LogDock(dockarea.Dock): except KeyError: pass else: - self.filterbox.setCurrentIndex(idx) + self.filter_level.setCurrentIndex(idx) From d8b98ae5d43056c341e3851af4e4ea92a610eaf4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 31 Oct 2015 00:34:14 +0800 Subject: [PATCH 08/11] gui/log: fix performance problems --- artiq/gui/log.py | 114 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/artiq/gui/log.py b/artiq/gui/log.py index 295798035..893e3cb43 100644 --- a/artiq/gui/log.py +++ b/artiq/gui/log.py @@ -6,7 +6,6 @@ from quamash import QtGui, QtCore from pyqtgraph import dockarea, LayoutWidget from artiq.protocols.sync_struct import Subscriber -from artiq.gui.tools import ListSyncModel try: QSortFilterProxyModel = QtCore.QSortFilterProxyModel @@ -26,11 +25,19 @@ def _level_to_name(level): return "DEBUG" -class _LogModel(ListSyncModel): +class _LogModel(QtCore.QAbstractTableModel): def __init__(self, parent, init): - ListSyncModel.__init__(self, - ["Level", "Source", "Time", "Message"], - parent, init) + QtCore.QAbstractTableModel.__init__(self, parent) + + self.headers = ["Level", "Source", "Time", "Message"] + + self.entries = init + self.pending_entries = [] + self.depth = 1000 + timer = QtCore.QTimer(self) + timer.timeout.connect(self.timer_tick) + timer.start(100) + self.fixed_font = QtGui.QFont() self.fixed_font.setFamily("Monospace") @@ -40,36 +47,75 @@ class _LogModel(ListSyncModel): self.warning_bg = QtGui.QBrush(QtGui.QColor(255, 255, 180)) self.error_bg = QtGui.QBrush(QtGui.QColor(255, 150, 150)) - def data(self, index, role): - if (role == QtCore.Qt.FontRole and index.isValid() - and index.column() == 3): - return self.fixed_font - elif role == QtCore.Qt.BackgroundRole and index.isValid(): - level = self.backing_store[index.row()][0] - if level >= logging.ERROR: - return self.error_bg - elif level >= logging.WARNING: - return self.warning_bg - else: - return self.white - elif role == QtCore.Qt.ForegroundRole and index.isValid(): - level = self.backing_store[index.row()][0] - if level <= logging.DEBUG: - return self.debug_fg - else: - return self.black - else: - return ListSyncModel.data(self, index, role) + def headerData(self, col, orientation, role): + if (orientation == QtCore.Qt.Horizontal + and role == QtCore.Qt.DisplayRole): + return self.headers[col] + return None - def convert(self, v, column): - if column == 0: - return _level_to_name(v[0]) - elif column == 1: - return v[1] - elif column == 2: - return time.strftime("%m/%d %H:%M:%S", time.localtime(v[2])) - else: - return v[3] + def rowCount(self, parent): + return len(self.entries) + + def columnCount(self, parent): + return len(self.headers) + + def __delitem__(self, k): + pass + + def append(self, v): + self.pending_entries.append(v) + + def insertRows(self, position, rows=1, index=QtCore.QModelIndex()): + self.beginInsertRows(QtCore.QModelIndex(), position, position+rows-1) + self.endInsertRows() + + def removeRows(self, position, rows=1, index=QtCore.QModelIndex()): + self.beginRemoveRows(QtCore.QModelIndex(), position, position+rows-1) + self.endRemoveRows() + + def timer_tick(self): + if not self.pending_entries: + return + nrows = len(self.entries) + records = self.pending_entries + self.pending_entries = [] + self.entries.extend(records) + self.insertRows(nrows, len(records)) + if len(self.entries) > self.depth: + start = len(self.entries) - self.depth + self.entries = self.entries[start:] + self.removeRows(0, start) + + def data(self, index, role): + if index.isValid(): + if (role == QtCore.Qt.FontRole + and index.column() == 3): + return self.fixed_font + elif role == QtCore.Qt.BackgroundRole: + level = self.entries[index.row()][0] + if level >= logging.ERROR: + return self.error_bg + elif level >= logging.WARNING: + return self.warning_bg + else: + return self.white + elif role == QtCore.Qt.ForegroundRole: + level = self.entries[index.row()][0] + if level <= logging.DEBUG: + return self.debug_fg + else: + return self.black + elif role == QtCore.Qt.DisplayRole: + v = self.entries[index.row()] + column = index.column() + if column == 0: + return _level_to_name(v[0]) + elif column == 1: + return v[1] + elif column == 2: + return time.strftime("%m/%d %H:%M:%S", time.localtime(v[2])) + else: + return v[3] class _LogFilterProxyModel(QSortFilterProxyModel): From 0a36b03a12978278f60311f479999975a850f562 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 31 Oct 2015 10:25:01 +0800 Subject: [PATCH 09/11] gui/log: work around Qt scrolling bug. Closes #151 --- artiq/gui/log.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/artiq/gui/log.py b/artiq/gui/log.py index 893e3cb43..819c13d95 100644 --- a/artiq/gui/log.py +++ b/artiq/gui/log.py @@ -199,22 +199,37 @@ class LogDock(dockarea.Dock): def rows_inserted_before(self): scrollbar = self.log.verticalScrollBar() - self.scroll_at_bottom = scrollbar.value() == scrollbar.maximum() + self.scroll_value = scrollbar.value() + self.scroll_at_bottom = self.scroll_value == scrollbar.maximum() def rows_inserted_after(self): if self.scroll_at_bottom: self.log.scrollToBottom() + # HACK: + # Qt intermittently likes to scroll back to the top when rows are removed. + # Work around this by restoring the scrollbar to the previously memorized + # position, after the removal. + # Note that this works because _LogModel always does the insertion right + # before the removal. + def rows_removed(self): + if self.scroll_at_bottom: + self.log.scrollToBottom() + else: + scrollbar = self.log.verticalScrollBar() + scrollbar.setValue(self.scroll_value) + def init_log_model(self, init): - table_model = _LogModel(self.log, init) + self.table_model = _LogModel(self.log, init) self.table_model_filter = _LogFilterProxyModel( getattr(logging, self.filter_level.currentText()), self.filter_freetext.text()) - self.table_model_filter.setSourceModel(table_model) + self.table_model_filter.setSourceModel(self.table_model) self.log.setModel(self.table_model_filter) self.table_model_filter.rowsAboutToBeInserted.connect(self.rows_inserted_before) self.table_model_filter.rowsInserted.connect(self.rows_inserted_after) - return table_model + self.table_model_filter.rowsRemoved.connect(self.rows_removed) + return self.table_model def save_state(self): return {"min_level_idx": self.filter_level.currentIndex()} From 592663a649e6210e2d99d0c5533a7bffb7e87098 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 31 Oct 2015 23:17:29 +0800 Subject: [PATCH 10/11] add "blink forever" example This is useful for quickly demonstrating the idle kernel. --- examples/master/repository/blink_forever.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/master/repository/blink_forever.py diff --git a/examples/master/repository/blink_forever.py b/examples/master/repository/blink_forever.py new file mode 100644 index 000000000..9abd3968f --- /dev/null +++ b/examples/master/repository/blink_forever.py @@ -0,0 +1,13 @@ +from artiq import * + + +class BlinkForever(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("led") + + @kernel + def run(self): + while True: + self.led.pulse(100*ms) + delay(100*ms) From d0b5c3ba7fb6d428b8c8d42cb024942a0815d203 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 31 Oct 2015 23:26:09 +0800 Subject: [PATCH 11/11] runtime: startup kernel support --- soc/runtime/bridge.c | 4 --- soc/runtime/bridge_ctl.c | 13 --------- soc/runtime/bridge_ctl.h | 2 -- soc/runtime/clock.c | 2 +- soc/runtime/dds.c | 13 --------- soc/runtime/dds.h | 1 - soc/runtime/kloader.c | 56 ++++++++++++++++++++++++++++++++------- soc/runtime/kloader.h | 3 ++- soc/runtime/main.c | 9 +------ soc/runtime/messages.h | 1 - soc/runtime/rtiocrg.c | 14 +++++----- soc/runtime/session.c | 57 ++++++++++++++++++++++++++++------------ soc/runtime/session.h | 1 + 13 files changed, 98 insertions(+), 78 deletions(-) diff --git a/soc/runtime/bridge.c b/soc/runtime/bridge.c index 3c4097c39..8ae83c1cc 100644 --- a/soc/runtime/bridge.c +++ b/soc/runtime/bridge.c @@ -66,10 +66,6 @@ void bridge_main(void) mailbox_acknowledge(); break; } - case MESSAGE_TYPE_BRG_DDS_INITALL: - dds_init_all(); - mailbox_acknowledge(); - break; case MESSAGE_TYPE_BRG_DDS_SEL: { struct msg_brg_dds_sel *msg; diff --git a/soc/runtime/bridge_ctl.c b/soc/runtime/bridge_ctl.c index 14a042a0d..11182efb9 100644 --- a/soc/runtime/bridge_ctl.c +++ b/soc/runtime/bridge_ctl.c @@ -23,11 +23,6 @@ void brg_start(void) } } -void brg_stop(void) -{ - kloader_stop(); -} - void brg_ttloe(int n, int value) { struct msg_brg_ttl_out msg; @@ -48,14 +43,6 @@ void brg_ttlo(int n, int value) mailbox_send_and_wait(&msg); } -void brg_ddsinitall(void) -{ - struct msg_base msg; - - msg.type = MESSAGE_TYPE_BRG_DDS_INITALL; - mailbox_send_and_wait(&msg); -} - void brg_ddssel(int channel) { struct msg_brg_dds_sel msg; diff --git a/soc/runtime/bridge_ctl.h b/soc/runtime/bridge_ctl.h index d87f83812..e6d2b7306 100644 --- a/soc/runtime/bridge_ctl.h +++ b/soc/runtime/bridge_ctl.h @@ -2,12 +2,10 @@ #define __BRIDGE_CTL_H void brg_start(void); -void brg_stop(void); void brg_ttloe(int n, int value); void brg_ttlo(int n, int value); -void brg_ddsinitall(void); void brg_ddssel(int channel); void brg_ddsreset(void); unsigned int brg_ddsread(unsigned int address); diff --git a/soc/runtime/clock.c b/soc/runtime/clock.c index 171fb6f30..0d0d3f4c3 100644 --- a/soc/runtime/clock.c +++ b/soc/runtime/clock.c @@ -62,7 +62,7 @@ int watchdog_set(int ms) break; } if(id < 0) { - log("Failed to add watchdog"); + log("WARNING: Failed to add watchdog"); return id; } diff --git a/soc/runtime/dds.c b/soc/runtime/dds.c index f07684265..7d120a0cd 100644 --- a/soc/runtime/dds.c +++ b/soc/runtime/dds.c @@ -33,19 +33,6 @@ now += DURATION_WRITE; \ } while(0) -void dds_init_all(void) -{ - int i; - long long int now; - - now = rtio_get_counter() + 10000; - for(i=0;i #include "log.h" +#include "clock.h" #include "flash_storage.h" #include "mailbox.h" #include "messages.h" @@ -91,33 +92,50 @@ void kloader_start_user_kernel(kernel_function k) start_kernel_cpu((void *)k); } -void kloader_start_idle_kernel(void) +static int kloader_start_flash_kernel(char *key) { char buffer[32*1024]; - int len; + unsigned int len, remain; kernel_function k; if(!kernel_cpu_reset_read()) { - log("BUG: attempted to start kernel CPU while already running (idle kernel)"); - return; + log("BUG: attempted to start kernel CPU while already running (%s)", key); + return 0; } #if (defined CSR_SPIFLASH_BASE && defined SPIFLASH_PAGE_SIZE) - len = fs_read("idle_kernel", buffer, sizeof(buffer), NULL); + len = fs_read(key, buffer, sizeof(buffer), &remain); if(len <= 0) - return; + return 0; + if(remain) { + log("ERROR: %s too long", key); + return 0; + } if(!kloader_load(buffer, len)) { - log("Failed to load ELF binary for idle kernel"); - return; + log("ERROR: failed to load ELF binary (%s)", key); + return 0; } k = kloader_find("run"); if(!k) { - log("Failed to find entry point for ELF kernel"); - return; + log("ERROR: failed to find entry point for ELF kernel (%s)", key); + return 0; } start_kernel_cpu((void *)k); + return 1; +#else + return 0; #endif } +int kloader_start_startup_kernel(void) +{ + return kloader_start_flash_kernel("startup_kernel"); +} + +int kloader_start_idle_kernel(void) +{ + return kloader_start_flash_kernel("idle_kernel"); +} + void kloader_stop(void) { kernel_cpu_reset_write(1); @@ -140,6 +158,8 @@ int kloader_is_essential_kmsg(int msgtype) case MESSAGE_TYPE_NOW_INIT_REQUEST: case MESSAGE_TYPE_NOW_SAVE: case MESSAGE_TYPE_LOG: + case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: + case MESSAGE_TYPE_WATCHDOG_CLEAR: return 1; default: return 0; @@ -179,6 +199,22 @@ void kloader_service_essential_kmsg(void) mailbox_acknowledge(); break; } + case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: { + struct msg_watchdog_set_request *msg = (struct msg_watchdog_set_request *)umsg; + struct msg_watchdog_set_reply reply; + + reply.type = MESSAGE_TYPE_WATCHDOG_SET_REPLY; + reply.id = watchdog_set(msg->ms); + mailbox_send_and_wait(&reply); + break; + } + case MESSAGE_TYPE_WATCHDOG_CLEAR: { + struct msg_watchdog_clear *msg = (struct msg_watchdog_clear *)umsg; + + watchdog_clear(msg->id); + mailbox_acknowledge(); + break; + } default: /* handled elsewhere */ break; diff --git a/soc/runtime/kloader.h b/soc/runtime/kloader.h index ceefbc89d..8f6091d28 100644 --- a/soc/runtime/kloader.h +++ b/soc/runtime/kloader.h @@ -12,7 +12,8 @@ int kloader_load(void *buffer, int length); kernel_function kloader_find(const char *name); void kloader_start_bridge(void); -void kloader_start_idle_kernel(void); +int kloader_start_startup_kernel(void); +int kloader_start_idle_kernel(void); void kloader_start_user_kernel(kernel_function k); void kloader_stop(void); diff --git a/soc/runtime/main.c b/soc/runtime/main.c index bdd9d4b8c..f020b74ea 100644 --- a/soc/runtime/main.c +++ b/soc/runtime/main.c @@ -30,13 +30,6 @@ #include "session.h" #include "moninj.h" -static void common_init(void) -{ - brg_start(); - brg_ddsinitall(); - kloader_stop(); -} - #ifdef CSR_ETHMAC_BASE u32_t sys_now(void) @@ -260,7 +253,7 @@ int main(void) test_main(); } else { puts("Entering regular mode."); - common_init(); + session_startup_kernel(); regular_main(); } return 0; diff --git a/soc/runtime/messages.h b/soc/runtime/messages.h index 221b394a3..4a4e10737 100644 --- a/soc/runtime/messages.h +++ b/soc/runtime/messages.h @@ -19,7 +19,6 @@ enum { MESSAGE_TYPE_BRG_READY, MESSAGE_TYPE_BRG_TTL_O, MESSAGE_TYPE_BRG_TTL_OE, - MESSAGE_TYPE_BRG_DDS_INITALL, MESSAGE_TYPE_BRG_DDS_SEL, MESSAGE_TYPE_BRG_DDS_RESET, MESSAGE_TYPE_BRG_DDS_READ_REQUEST, diff --git a/soc/runtime/rtiocrg.c b/soc/runtime/rtiocrg.c index 255f2a308..de56fde0d 100644 --- a/soc/runtime/rtiocrg.c +++ b/soc/runtime/rtiocrg.c @@ -1,6 +1,6 @@ -#include #include +#include "log.h" #include "clock.h" #include "flash_storage.h" #include "rtiocrg.h" @@ -17,17 +17,17 @@ void rtiocrg_init(void) clk = 0; fs_read("startup_clock", &b, 1, NULL); if(b == 'i') - printf("Startup RTIO clock: internal\n"); + log("Startup RTIO clock: internal"); else if(b == 'e') { - printf("Startup RTIO clock: external\n"); + log("Startup RTIO clock: external"); clk = 1; } else - printf("WARNING: unknown startup_clock entry in flash storage\n"); + log("ERROR: unrecognized startup_clock entry in flash storage"); if(!rtiocrg_switch_clock(clk)) { - printf("WARNING: startup RTIO clock failed\n"); - printf("WARNING: this may cause the system initialization to fail\n"); - printf("WARNING: fix clocking and reset the device\n"); + log("ERROR: startup RTIO clock failed"); + log("WARNING: this may cause the system initialization to fail"); + log("WARNING: fix clocking and reset the device"); } } diff --git a/soc/runtime/session.c b/soc/runtime/session.c index c0908639c..a76a51687 100644 --- a/soc/runtime/session.c +++ b/soc/runtime/session.c @@ -64,6 +64,44 @@ enum { USER_KERNEL_WAIT_RPC /* < must come after _RUNNING */ }; +void session_startup_kernel(void) +{ + struct msg_base *umsg; + + now = -1; + watchdog_init(); + if(!kloader_start_startup_kernel()) + return; + + while(1) { + kloader_service_essential_kmsg(); + + umsg = mailbox_receive(); + if(umsg) { + if(!kloader_validate_kpointer(umsg)) + break; + if(kloader_is_essential_kmsg(umsg->type)) + continue; + if(umsg->type == MESSAGE_TYPE_FINISHED) + break; + else if(umsg->type == MESSAGE_TYPE_EXCEPTION) { + log("WARNING: startup kernel ended with exception"); + break; + } else { + log("ERROR: received invalid message type from kernel CPU"); + break; + } + } + + if(watchdog_expired()) { + log("WARNING: watchdog expired in startup kernel"); + break; + } + } + kloader_stop(); + log("Startup kernel terminated"); +} + void session_start(void) { buffer_in_index = 0; @@ -77,6 +115,7 @@ void session_end(void) { kloader_stop(); now = -1; + watchdog_init(); kloader_start_idle_kernel(); } @@ -490,22 +529,6 @@ static int process_kmsg(struct msg_base *umsg) mailbox_acknowledge(); break; } - case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: { - struct msg_watchdog_set_request *msg = (struct msg_watchdog_set_request *)umsg; - struct msg_watchdog_set_reply reply; - - reply.type = MESSAGE_TYPE_WATCHDOG_SET_REPLY; - reply.id = watchdog_set(msg->ms); - mailbox_send_and_wait(&reply); - break; - } - case MESSAGE_TYPE_WATCHDOG_CLEAR: { - struct msg_watchdog_clear *msg = (struct msg_watchdog_clear *)umsg; - - watchdog_clear(msg->id); - mailbox_acknowledge(); - break; - } case MESSAGE_TYPE_RPC_REQUEST: { struct msg_rpc_request *msg = (struct msg_rpc_request *)umsg; @@ -516,7 +539,7 @@ static int process_kmsg(struct msg_base *umsg) break; } default: { - log("Received invalid message type from kernel CPU"); + log("ERROR: received invalid message type from kernel CPU"); return 0; } } diff --git a/soc/runtime/session.h b/soc/runtime/session.h index 988d0f6b0..5728103ae 100644 --- a/soc/runtime/session.h +++ b/soc/runtime/session.h @@ -1,6 +1,7 @@ #ifndef __SESSION_H #define __SESSION_H +void session_startup_kernel(void); void session_start(void); void session_end(void);