diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index e9de36161..55148402a 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -34,8 +34,8 @@ class PCA9548: On the KC705, this chip is used for selecting the I2C buses on the two FMC connectors. HPC=1, LPC=2. """ - def __init__(self, dmgr, busno=0, address=0xe8): - self.core = dmgr.get("core") + def __init__(self, dmgr, busno=0, address=0xe8, core_device="core"): + self.core = dmgr.get(core_device) self.busno = busno self.address = address @@ -77,8 +77,8 @@ class TCA6424A: On the NIST QC2 hardware, this chip is used for switching the directions of TTL buffers.""" - def __init__(self, dmgr, busno=0, address=0x44): - self.core = dmgr.get("core") + def __init__(self, dmgr, busno=0, address=0x44, core_device="core"): + self.core = dmgr.get(core_device) self.busno = busno self.address = address diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 6221c8f53..20095a8ff 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -40,8 +40,8 @@ class SPIMaster: :param channel: RTIO channel number of the SPI bus to control. """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period, self.core) self.channel = channel diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 9ac12ca48..ef855d6b2 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -10,8 +10,8 @@ class TTLOut: :param channel: channel number """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.channel = channel # in RTIO cycles @@ -82,8 +82,8 @@ class TTLInOut: :param channel: channel number """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.channel = channel # in RTIO cycles @@ -232,8 +232,8 @@ class TTLClockGen: :param channel: channel number """ - def __init__(self, dmgr, channel): - self.core = dmgr.get("core") + def __init__(self, dmgr, channel, core_device="core"): + self.core = dmgr.get(core_device) self.channel = channel # in RTIO cycles diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index d9dcb3bcc..620810b60 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -203,13 +203,6 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd spi_pins = self.platform.request("pmod_extended_spi", 0) - for i, p in enumerate((spi_pins.int, spi_pins.rst, - spi_pins.d0, spi_pins.d1)): - phy = ttl_simple.Inout(p) - self.submodules += phy - rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4, - ofifo_depth=4)) - self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) phy = ttl_simple.ClockGen(platform.request("ttl", 15)) @@ -220,7 +213,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd self.submodules += phy self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( - phy, ofifo_depth=64, ififo_depth=64)) + phy, ofifo_depth=256, ififo_depth=256)) self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) self.config["DDS_CHANNEL_COUNT"] = 8 diff --git a/artiq/tools.py b/artiq/tools.py index ef666cabf..8d2424cdc 100644 --- a/artiq/tools.py +++ b/artiq/tools.py @@ -7,6 +7,8 @@ import asyncio import time import collections import os +import socket +import itertools import atexit import string @@ -243,3 +245,109 @@ def get_windows_drives(): drives.append(letter) bitmask >>= 1 return drives + +if sys.version_info[:3] == (3, 5, 1): + # See https://github.com/m-labs/artiq/issues/253 + @asyncio.coroutines.coroutine + def create_server(self, protocol_factory, host=None, port=None, + *, + family=socket.AF_UNSPEC, + flags=socket.AI_PASSIVE, + sock=None, + backlog=100, + ssl=None, + reuse_address=None, + reuse_port=None): + """Create a TCP server. + The host parameter can be a string, in that case the TCP server is bound + to host and port. + The host parameter can also be a sequence of strings and in that case + the TCP server is bound to all hosts of the sequence. If a host + appears multiple times (possibly indirectly e.g. when hostnames + resolve to the same IP address), the server is only bound once to that + host. + Return a Server object which can be used to stop the service. + This method is a coroutine. + """ + if isinstance(ssl, bool): + raise TypeError('ssl argument must be an SSLContext or None') + if host is not None or port is not None: + if sock is not None: + raise ValueError( + 'host/port and sock can not be specified at the same time') + + AF_INET6 = getattr(socket, 'AF_INET6', 0) + if reuse_address is None: + reuse_address = os.name == 'posix' and sys.platform != 'cygwin' + sockets = [] + if host == '': + hosts = [None] + elif (isinstance(host, str) or + not isinstance(host, collections.Iterable)): + hosts = [host] + else: + hosts = host + + fs = [self._create_server_getaddrinfo(host, port, family=family, + flags=flags) + for host in hosts] + infos = yield from asyncio.tasks.gather(*fs, loop=self) + infos = set(itertools.chain.from_iterable(infos)) + + completed = False + try: + for res in infos: + af, socktype, proto, canonname, sa = res + try: + sock = socket.socket(af, socktype, proto) + except socket.error: + # Assume it's a bad family/type/protocol combination. + if self._debug: + asyncio.log.logger.warning('create_server() failed to create ' + 'socket.socket(%r, %r, %r)', + af, socktype, proto, exc_info=True) + continue + sockets.append(sock) + if reuse_address: + sock.setsockopt( + socket.SOL_SOCKET, socket.SO_REUSEADDR, True) + if reuse_port: + if not hasattr(socket, 'SO_REUSEPORT'): + raise ValueError( + 'reuse_port not supported by socket module') + else: + sock.setsockopt( + socket.SOL_SOCKET, socket.SO_REUSEPORT, True) + # Disable IPv4/IPv6 dual stack support (enabled by + # default on Linux) which makes a single socket + # listen on both address families. + if af == AF_INET6 and hasattr(socket, 'IPPROTO_IPV6'): + sock.setsockopt(socket.IPPROTO_IPV6, + socket.IPV6_V6ONLY, + True) + try: + sock.bind(sa) + except OSError as err: + raise OSError(err.errno, 'error while attempting ' + 'to bind on address %r: %s' + % (sa, err.strerror.lower())) + completed = True + finally: + if not completed: + for sock in sockets: + sock.close() + else: + if sock is None: + raise ValueError('Neither host/port nor sock were specified') + sockets = [sock] + + server = asyncio.base_events.Server(self, sockets) + for sock in sockets: + sock.listen(backlog) + sock.setblocking(False) + self._start_serving(protocol_factory, sock, ssl, server) + if self._debug: + asyncio.log.logger.info("%r is serving", server) + return server + + asyncio.base_events.BaseEventLoop.create_server = create_server diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index 8a947dab6..484f69358 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -132,15 +132,7 @@ When plugged to an adapter, the NIST QC1 hardware can be used. The TTL lines are +--------------+------------+--------------+ | 21 | USER_LED_4 | Output | +--------------+------------+--------------+ -| 22 | PMOD_4 | Input+Output | -+--------------+------------+--------------+ -| 23 | PMOD_5 | Input+Output | -+--------------+------------+--------------+ -| 24 | PMOD_6 | Input+Output | -+--------------+------------+--------------+ -| 25 | PMOD_7 | Input+Output | -+--------------+------------+--------------+ -| 26 | TTL15 | Clock | +| 22 | TTL15 | Clock | +--------------+------------+--------------+ The input only limitation on channels 0 and 1 comes from the QC-DAQ adapter. When the adapter is not used (and physically unplugged from the Pipistrello board), the corresponding pins on the Pipistrello can be used as outputs. Do not configure these channels as outputs when the adapter is plugged, as this would cause electrical contention. @@ -153,5 +145,5 @@ Interface Type 2 (SPI) and 2A (expanded SPI): +--------------+--------+--------+--------+--------+ | RTIO channel | CS_N | MOSI | MISO | CLK | +==============+========+========+========+========+ -| 27 | PMOD_0 | PMOD_1 | PMOD_2 | PMOD_3 | +| 23 | PMOD_0 | PMOD_1 | PMOD_2 | PMOD_3 | +--------------+--------+--------+--------+--------+ diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index ee475911a..f10b1e6b2 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -154,25 +154,25 @@ # that it always resolves to a network-visible IP address (see documentation). "host": "::1", "port": 4000, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_0.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_0.bin" }, "qc_q1_1": { "type": "controller", "host": "::1", "port": 4001, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_1.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_1.bin" }, "qc_q1_2": { "type": "controller", "host": "::1", "port": 4002, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_2.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_2.bin" }, "qc_q1_3": { "type": "controller", "host": "::1", "port": 4003, - "command": "pdq2_controller --no-localhost-bind -p {port} --bind {bind} --simulation --dump qc_q1_3.bin" + "command": "pdq2_controller -p {port} --bind {bind} --simulation --dump qc_q1_3.bin" }, "electrodes": { "type": "local",