diff --git a/artiq/coredevice/analyzer.py b/artiq/coredevice/analyzer.py index c69a77183..78d98d382 100644 --- a/artiq/coredevice/analyzer.py +++ b/artiq/coredevice/analyzer.py @@ -113,8 +113,8 @@ class VCDChannel: class VCDManager: - def __init__(self, filename): - self.out = open(filename, "w") + def __init__(self, fileobj): + self.out = fileobj self.codes = vcd_codes() self.current_time = None @@ -132,9 +132,6 @@ class VCDManager: self.out.write("#{}\n".format(time)) self.current_time = time - def close(self): - self.out.close() - class TTLHandler: def __init__(self, vcd_manager, name): @@ -292,27 +289,24 @@ def get_message_time(message): return getattr(message, "timestamp", message.rtio_counter) -def decoded_dump_to_vcd(filename, devices, dump): - vcd_manager = VCDManager(filename) - try: - timescale = get_timescale(devices) - if timescale is not None: - vcd_manager.set_timescale_ns(timescale) - else: - logger.warning("unable to determine VCD timescale") +def decoded_dump_to_vcd(fileobj, devices, dump): + vcd_manager = VCDManager(fileobj) + timescale = get_timescale(devices) + if timescale is not None: + vcd_manager.set_timescale_ns(timescale) + else: + logger.warning("unable to determine VCD timescale") - channel_handlers = create_channel_handlers( - vcd_manager, devices, - dump.log_channel, dump.dds_channel, dump.dds_onehot_sel) + channel_handlers = create_channel_handlers( + vcd_manager, devices, + dump.log_channel, dump.dds_channel, dump.dds_onehot_sel) - vcd_manager.set_time(0) - messages = sorted(dump.messages, key=get_message_time) - if messages: - start_time = get_message_time(messages[0]) - for message in messages: - if message.channel in channel_handlers: - vcd_manager.set_time( - get_message_time(message) - start_time) - channel_handlers[message.channel].process_message(message) - finally: - vcd_manager.close() + vcd_manager.set_time(0) + messages = sorted(dump.messages, key=get_message_time) + if messages: + start_time = get_message_time(messages[0]) + for message in messages: + if message.channel in channel_handlers: + vcd_manager.set_time( + get_message_time(message) - start_time) + channel_handlers[message.channel].process_message(message) diff --git a/artiq/frontend/artiq_coreanalyzer.py b/artiq/frontend/artiq_coreanalyzer.py new file mode 100755 index 000000000..491b276f1 --- /dev/null +++ b/artiq/frontend/artiq_coreanalyzer.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3.5 + +import argparse +import sys + +from artiq.tools import verbosity_args, init_logger +from artiq.master.databases import DeviceDB +from artiq.master.worker_db import DeviceManager +from artiq.coredevice.analyzer import decode_dump, decoded_dump_to_vcd + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ core device " + "RTIO analysis tool") + + verbosity_args(parser) + parser.add_argument("--device-db", default="device_db.pyon", + help="device database file (default: '%(default)s')") + + parser.add_argument("-r", "--read-dump", type=str, default=None, + help="read raw dump file instead of accessing device") + parser.add_argument("-p", "--print-decoded", default=False, action="store_true", + help="print raw decoded messages") + parser.add_argument("-w", "--write-vcd", type=str, default=None, + help="format and write contents to VCD file") + parser.add_argument("-d", "--write-dump", type=str, default=None, + help="write raw dump file") + return parser + + +def main(): + args = get_argparser().parse_args() + init_logger(args) + + if (not args.print_decoded + and args.write_vcd is None and args.write_dump is None): + print("No action selected, use -p, -w and/or -d. See -h for help.") + sys.exit(1) + + device_mgr = DeviceManager(DeviceDB(args.device_db)) + try: + if args.read_dump: + with open(args.read_dump, "rb") as f: + dump = f.read() + else: + comm = device_mgr.get("comm") + dump = comm.get_analyzer_dump() + decoded_dump = decode_dump(dump) + if args.print_decoded: + print("Log channel:", decoded_dump.log_channel) + print("DDS channel:", decoded_dump.dds_channel) + print("DDS one-hot:", decoded_dump.dds_onehot_sel) + for message in decoded_dump.messages: + print(message) + if args.write_vcd: + with open(args.write_vcd, "w") as f: + decoded_dump_to_vcd(f, device_mgr.get_device_db(), + decoded_dump) + if args.write_dump: + with open(args.write_dump, "wb") as f: + f.write(dump) + finally: + device_mgr.close_devices() + + +if __name__ == "__main__": + main() diff --git a/artiq/frontend/artiq_coretool.py b/artiq/frontend/artiq_coreconfig.py similarity index 68% rename from artiq/frontend/artiq_coretool.py rename to artiq/frontend/artiq_coreconfig.py index 79f402f9d..ba04e4fa9 100755 --- a/artiq/frontend/artiq_coretool.py +++ b/artiq/frontend/artiq_coreconfig.py @@ -6,12 +6,11 @@ import struct from artiq.tools import verbosity_args, init_logger from artiq.master.databases import DeviceDB from artiq.master.worker_db import DeviceManager -from artiq.coredevice.analyzer import decode_dump, decoded_dump_to_vcd def get_argparser(): parser = argparse.ArgumentParser(description="ARTIQ core device " - "remote access tool") + "configuration tool") verbosity_args(parser) parser.add_argument("--device-db", default="device_db.pyon", @@ -20,9 +19,6 @@ def get_argparser(): subparsers = parser.add_subparsers(dest="action") subparsers.required = True - subparsers.add_parser("log", - help="read from the core device log ring buffer") - p_read = subparsers.add_parser("cfg-read", help="read key from core device config") p_read.add_argument("key", type=str, @@ -48,14 +44,6 @@ def get_argparser(): help="key to be deleted from core device config") subparsers.add_parser("cfg-erase", help="erase core device config") - - p_analyzer = subparsers.add_parser("analyzer-dump", - help="dump analyzer contents") - p_analyzer.add_argument("-m", default=False, action="store_true", - help="print raw messages") - p_analyzer.add_argument("-f", type=str, default="", - help="format and write contents to VCD file") - return parser @@ -65,12 +53,9 @@ def main(): device_mgr = DeviceManager(DeviceDB(args.device_db)) try: comm = device_mgr.get("comm") - if args.action != "analyzer-dump": - comm.check_ident() + comm.check_ident() - if args.action == "log": - print(comm.get_log(), end="") - elif args.action == "cfg-read": + if args.action == "cfg-read": value = comm.flash_storage_read(args.key) if not value: print("Key {} does not exist".format(args.key)) @@ -87,16 +72,6 @@ def main(): comm.flash_storage_remove(key) elif args.action == "cfg-erase": comm.flash_storage_erase() - elif args.action == "analyzer-dump": - decoded_dump = decode_dump(comm.get_analyzer_dump()) - if args.m: - print("Log channel:", decoded_dump.log_channel) - print("DDS channel:", decoded_dump.dds_channel) - print("DDS one-hot:", decoded_dump.dds_onehot_sel) - for message in decoded_dump.messages: - print(message) - if args.f: - decoded_dump_to_vcd(args.f, device_mgr.get_device_db(), decoded_dump) finally: device_mgr.close_devices() diff --git a/artiq/frontend/artiq_corelog.py b/artiq/frontend/artiq_corelog.py new file mode 100755 index 000000000..df114ec49 --- /dev/null +++ b/artiq/frontend/artiq_corelog.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3.5 + +import argparse + +from artiq.tools import verbosity_args, init_logger +from artiq.master.databases import DeviceDB +from artiq.master.worker_db import DeviceManager + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ core device " + "log download tool") + verbosity_args(parser) + parser.add_argument("--device-db", default="device_db.pyon", + help="device database file (default: '%(default)s')") + return parser + + +def main(): + args = get_argparser().parse_args() + init_logger(args) + device_mgr = DeviceManager(DeviceDB(args.device_db)) + try: + comm = device_mgr.get("comm") + comm.check_ident() + print(comm.get_log(), end="") + finally: + device_mgr.close_devices() + + +if __name__ == "__main__": + main() diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index 1a9e6a19d..3670c0076 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -12,7 +12,9 @@ build: entry_points: - artiq_client = artiq.frontend.artiq_client:main - artiq_compile = artiq.frontend.artiq_compile:main - - artiq_coretool = artiq.frontend.artiq_coretool:main + - artiq_coreanalyzer = artiq.frontend.artiq_coreanalyzer:main + - artiq_coreconfig = artiq.frontend.artiq_coreconfig:main + - artiq_corelog = artiq.frontend.artiq_corelog:main - artiq_ctlmgr = artiq.frontend.artiq_ctlmgr:main - artiq_gui = artiq.frontend.artiq_gui:main - artiq_influxdb = artiq.frontend.artiq_influxdb:main diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index 942a4b844..fe22dd296 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -17,7 +17,7 @@ This storage area is used to store the core device MAC address, IP address and e The flash storage area is one sector (typically 64 kB) large and is organized as a list of key-value records. -This flash storage space can be accessed by using ``artiq_coretool`` (see: :ref:`core-device-access-tool`). +This flash storage space can be accessed by using ``artiq_coreconfig`` (see: :ref:`core-device-configuration-tool`). .. _board-ports: diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 2ed9a368c..4f9d0d25d 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -391,20 +391,20 @@ To flash the idle kernel: * Write it into the core device configuration flash storage: :: - $ artiq_coretool cfg-write -f idle_kernel idle.elf + $ artiq_coreconfig cfg-write -f idle_kernel idle.elf -.. note:: You can find more information about how to use the ``artiq_coretool`` utility on the :ref:`Utilities ` page. +.. note:: You can find more information about how to use the ``artiq_coreconfig`` utility on the :ref:`Utilities ` page. * (optional) Flash the startup kernel -The startup kernel is executed once when the core device powers up. It should initialize DDSes, set up TTL directions, etc. Proceed as with the idle kernel, but using the ``startup_kernel`` key in ``artiq_coretool``. +The startup kernel is executed once when the core device powers up. It should initialize DDSes, set up TTL directions, etc. Proceed as with the idle kernel, but using the ``startup_kernel`` key in ``artiq_coreconfig``. * (optional) Select the startup clock The core device may use either an external clock signal or its internal clock. This clock can be switched dynamically after the PC is connected using the ``external_clock`` parameter of the core device driver; however, one may want to select the clock at power-up so that it is used for the startup and idle kernels. Use one of these commands: :: - $ artiq_coretool cfg-write -s startup_clock i # internal clock (default) - $ artiq_coretool cfg-write -s startup_clock e # external clock + $ artiq_coreconfig cfg-write -s startup_clock i # internal clock (default) + $ artiq_coreconfig cfg-write -s startup_clock e # external clock Ubuntu 15.10+/Debian jessie+ specific instructions -------------------------------------------------- diff --git a/doc/manual/utilities.rst b/doc/manual/utilities.rst index 486b30e97..eb2e88772 100644 --- a/doc/manual/utilities.rst +++ b/doc/manual/utilities.rst @@ -24,7 +24,7 @@ in order to call remote functions of an ARTIQ controller. The ``list-targets`` sub-command will print to standard output the target list of the remote server:: - $ artiq_rpctool.py hostname port list-targets + $ artiq_rpctool hostname port list-targets * Listing callable functions @@ -36,12 +36,12 @@ in order to call remote functions of an ARTIQ controller. If the server has only one target, you can do:: - $ artiq_rpctool.py hostname port list-methods + $ artiq_rpctool hostname port list-methods Otherwise you need to specify the target, using the ``-t target`` option:: - $ artiq_rpctool.py hostname port list-methods -t target_name + $ artiq_rpctool hostname port list-methods -t target_name * Remotely calling a function @@ -53,25 +53,25 @@ in order to call remote functions of an ARTIQ controller. The following example will call the ``set_attenuation`` method of the Lda controller with the argument ``5``:: - $ artiq_rpctool.py ::1 3253 call -t lda set_attenuation 5 + $ artiq_rpctool ::1 3253 call -t lda set_attenuation 5 In general, to call a function named ``f`` with N arguments named respectively ``x1, x2, ..., xN`` you can do:: - $ artiq_rpctool.py hostname port call -t target f x1 x2 ... xN + $ artiq_rpctool hostname port call -t target f x1 x2 ... xN You can use Python syntax to compute arguments as they will be passed to the ``eval()`` primitive. The numpy package is available in the namespace as ``np``. Beware to use quotes to separate arguments which use spaces:: - $ artiq_rpctool.py hostname port call -t target f '3 * 4 + 2' True '[1, 2]' - $ artiq_rpctool.py ::1 3256 call load_sample_values 'np.array([1.0, 2.0], dtype=float)' + $ artiq_rpctool hostname port call -t target f '3 * 4 + 2' True '[1, 2]' + $ artiq_rpctool ::1 3256 call load_sample_values 'np.array([1.0, 2.0], dtype=float)' If the called function has a return value, it will get printed to the standard output if the value is not None like in the standard python interactive console:: - $ artiq_rpctool.py ::1 3253 call get_attenuation + $ artiq_rpctool ::1 3253 call get_attenuation 5.0 dB Static compiler @@ -95,61 +95,69 @@ This tool compiles key/value pairs into a binary image suitable for flashing int .. _core-device-access-tool: -Core device access tool ------------------------ +Core device configuration tool +------------------------------ -The artiq_coretool utility allows to perform maintenance on the core device: - - * read core device logs; - * as well as read, write and remove key-value records from the :ref:`core-device-flash-storage`; - * erase the entire flash storage area. - -To use this tool, you need to specify a ``device_db.pyon`` device database file which contains a ``comm`` device (an example is provided in ``examples/master/device_db.pyon``). This tells the tool how to connect to the core device (via serial or via TCP) and with which parameters (baudrate, serial device, IP address, TCP port). When not specified, the artiq_coretool utility will assume that there is a file named ``device_db.pyon`` in the current directory. +The artiq_coreconfig utility gives remote access to the :ref:`core-device-flash-storage`. +To use this tool, you need to specify a ``device_db.pyon`` device database file which contains a ``comm`` device (an example is provided in ``examples/master/device_db.pyon``). This tells the tool how to connect to the core device and with which parameters (e.g. IP address, TCP port). When not specified, the artiq_coreconfig utility will assume that there is a file named ``device_db.pyon`` in the current directory. To read the record whose key is ``mac``:: - $ artiq_coretool cfg-read mac + $ artiq_coreconfig cfg-read mac To write the value ``test_value`` in the key ``my_key``:: - $ artiq_coretool cfg-write -s my_key test_value - $ artiq_coretool cfg-read my_key + $ artiq_coreconfig cfg-write -s my_key test_value + $ artiq_coreconfig cfg-read my_key b'test_value' You can also write entire files in a record using the ``-f`` parameter. This is useful for instance to write the startup and idle kernels in the flash storage:: - $ artiq_coretool cfg-write -f idle_kernel idle.elf - $ artiq_coretool cfg-read idle_kernel | head -c9 + $ artiq_coreconfig cfg-write -f idle_kernel idle.elf + $ artiq_coreconfig cfg-read idle_kernel | head -c9 b'\x7fELF You can write several records at once:: - $ artiq_coretool cfg-write -s key1 value1 -f key2 filename -s key3 value3 + $ artiq_coreconfig cfg-write -s key1 value1 -f key2 filename -s key3 value3 To remove the previously written key ``my_key``:: - $ artiq_coretool cfg-delete my_key + $ artiq_coreconfig cfg-delete my_key You can remove several keys at once:: - $ artiq_coretool cfg-delete key1 key2 + $ artiq_coreconfig cfg-delete key1 key2 To erase the entire flash storage area:: - $ artiq_coretool cfg-erase + $ artiq_coreconfig cfg-erase -You don't need to remove a record in order to change its value, just overwrite -it:: +You do not need to remove a record in order to change its value, just overwrite it:: - $ artiq_coretool cfg-write -s my_key some_value - $ artiq_coretool cfg-write -s my_key some_other_value - $ artiq_coretool cfg-read my_key + $ artiq_coreconfig cfg-write -s my_key some_value + $ artiq_coreconfig cfg-write -s my_key some_other_value + $ artiq_coreconfig cfg-read my_key b'some_other_value' .. argparse:: - :ref: artiq.frontend.artiq_coretool.get_argparser - :prog: artiq_coretool + :ref: artiq.frontend.artiq_coreconfig.get_argparser + :prog: artiq_coreconfig + +Core device log download tool +----------------------------- + +.. argparse:: + :ref: artiq.frontend.artiq_corelog.get_argparser + :prog: artiq_corelog + +Core device RTIO analyzer tool +------------------------------ + +.. argparse:: + :ref: artiq.frontend.artiq_coreanalyzer.get_argparser + :prog: artiq_coreanalyzer Data to InfluxDB bridge ----------------------- diff --git a/setup.py b/setup.py index a28fa8e78..1a9b8516b 100755 --- a/setup.py +++ b/setup.py @@ -22,7 +22,9 @@ requirements = [ scripts = [ "artiq_client=artiq.frontend.artiq_client:main", "artiq_compile=artiq.frontend.artiq_compile:main", - "artiq_coretool=artiq.frontend.artiq_coretool:main", + "artiq_coreanalyzer=artiq.frontend.artiq_coreanalyzer:main", + "artiq_coreconfig=artiq.frontend.artiq_coreconfig:main", + "artiq_corelog=artiq.frontend.artiq_corelog:main", "artiq_ctlmgr=artiq.frontend.artiq_ctlmgr:main", "artiq_gui=artiq.frontend.artiq_gui:main", "artiq_influxdb=artiq.frontend.artiq_influxdb:main",