artiq/artiq/coredevice/comm_serial.py

206 lines
7.0 KiB
Python
Raw Normal View History

2014-09-05 12:03:22 +08:00
import os
import termios
import struct
import zlib
2014-07-08 01:11:43 +08:00
from enum import Enum
from fractions import Fraction
2014-09-18 01:17:58 +08:00
import logging
2014-07-08 01:11:43 +08:00
from artiq.language import core as core_language
2014-12-02 14:06:32 +08:00
from artiq.language import units
2014-10-19 23:51:49 +08:00
from artiq.coredevice.runtime import Environment
from artiq.coredevice import runtime_exceptions
2014-09-05 12:03:22 +08:00
2014-09-18 01:17:58 +08:00
logger = logging.getLogger(__name__)
class UnsupportedDevice(Exception):
2014-09-05 12:03:22 +08:00
pass
class _H2DMsgType(Enum):
REQUEST_IDENT = 1
LOAD_OBJECT = 2
RUN_KERNEL = 3
2014-12-02 11:09:02 +08:00
SET_BAUD_RATE = 4
2014-12-02 14:06:32 +08:00
SWITCH_CLOCK = 5
class _D2HMsgType(Enum):
LOG = 1
MESSAGE_UNRECOGNIZED = 2
IDENT = 3
OBJECT_LOADED = 4
INCORRECT_LENGTH = 5
CRC_FAILED = 6
OBJECT_UNRECOGNIZED = 7
KERNEL_FINISHED = 8
2014-09-21 23:36:10 +08:00
KERNEL_EXCEPTION = 9
KERNEL_STARTUP_FAILED = 10
RPC_REQUEST = 11
2014-12-02 14:06:32 +08:00
CLOCK_SWITCH_COMPLETED = 12
CLOCK_SWITCH_FAILED = 13
2014-09-05 12:03:22 +08:00
2014-07-08 01:11:43 +08:00
def _write_exactly(f, data):
2014-09-05 12:03:22 +08:00
remaining = len(data)
pos = 0
while remaining:
written = f.write(data[pos:])
remaining -= written
pos += written
2014-07-08 01:11:43 +08:00
def _read_exactly(f, n):
2014-09-05 12:03:22 +08:00
r = bytes()
while(len(r) < n):
r += f.read(n - len(r))
return r
2014-10-19 23:51:49 +08:00
class Comm:
2014-09-05 12:03:22 +08:00
def __init__(self, dev="/dev/ttyUSB1", baud=115200):
self._fd = os.open(dev, os.O_RDWR | os.O_NOCTTY)
self.port = os.fdopen(self._fd, "r+b", buffering=0)
2014-12-02 11:09:02 +08:00
self.set_baud(115200)
self.set_remote_baud(baud)
self.set_baud(baud)
def set_baud(self, baud):
2014-09-05 12:03:22 +08:00
iflag, oflag, cflag, lflag, ispeed, ospeed, cc = \
termios.tcgetattr(self._fd)
iflag = termios.IGNBRK | termios.IGNPAR
oflag = 0
cflag |= termios.CLOCAL | termios.CREAD | termios.CS8
lflag = 0
ispeed = ospeed = getattr(termios, "B"+str(baud))
cc[termios.VMIN] = 1
cc[termios.VTIME] = 0
termios.tcsetattr(self._fd, termios.TCSANOW, [
iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
termios.tcdrain(self._fd)
termios.tcflush(self._fd, termios.TCOFLUSH)
termios.tcflush(self._fd, termios.TCIFLUSH)
2014-12-02 11:09:02 +08:00
logger.debug("baud rate set to".format(baud))
def set_remote_baud(self, baud):
_write_exactly(self.port, struct.pack(
">lbl", 0x5a5a5a5a, _H2DMsgType.SET_BAUD_RATE.value, baud))
handshake = 0
fails = 0
2014-12-02 11:09:02 +08:00
while handshake < 4:
(recv, ) = struct.unpack("B", _read_exactly(self.port, 1))
if recv == 0x5a:
2014-12-02 11:09:02 +08:00
handshake += 1
else:
# FIXME: when loading immediately after a board reset,
# we erroneously get some zeros back.
logger.warning("unexpected sync character: {:02x}".format(int(recv)))
2014-12-02 11:09:02 +08:00
handshake = 0
if recv != 0:
fails += 1
if fails > 3:
raise IOError("Baudrate ack failed")
2014-12-02 11:09:02 +08:00
self.set_baud(baud)
logger.debug("synchronized")
2014-09-05 12:03:22 +08:00
def close(self):
2014-12-02 11:09:02 +08:00
self.set_remote_baud(115200)
2014-09-05 12:03:22 +08:00
self.port.close()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
def _get_device_msg(self):
2014-09-05 12:03:22 +08:00
while True:
2014-12-02 11:09:02 +08:00
(reply, ) = struct.unpack("B", _read_exactly(self.port, 1))
msg = _D2HMsgType(reply)
if msg == _D2HMsgType.LOG:
(length, ) = struct.unpack(">h", _read_exactly(self.port, 2))
log_message = ""
for i in range(length):
(c, ) = struct.unpack("B", _read_exactly(self.port, 1))
log_message += chr(c)
2014-09-18 01:17:58 +08:00
logger.info("DEVICE LOG: " + log_message)
2014-09-05 12:03:22 +08:00
else:
2014-09-18 01:17:58 +08:00
logger.debug("message received: {!r}".format(msg))
return msg
def get_runtime_env(self):
_write_exactly(self.port, struct.pack(
">lb", 0x5a5a5a5a, _H2DMsgType.REQUEST_IDENT.value))
msg = self._get_device_msg()
if msg != _D2HMsgType.IDENT:
raise IOError("Incorrect reply from device: "+str(msg))
(reply, ) = struct.unpack("B", _read_exactly(self.port, 1))
2014-09-05 12:03:22 +08:00
runtime_id = chr(reply)
for i in range(3):
(reply, ) = struct.unpack("B", _read_exactly(self.port, 1))
2014-09-05 12:03:22 +08:00
runtime_id += chr(reply)
if runtime_id != "AROR":
raise UnsupportedDevice("Unsupported runtime ID: "+runtime_id)
2014-12-02 14:06:32 +08:00
ref_freq_i, ref_freq_fn, ref_freq_fd = struct.unpack(
">lBB", _read_exactly(self.port, 6))
2014-12-02 14:06:32 +08:00
ref_freq = (ref_freq_i + Fraction(ref_freq_fn, ref_freq_fd))*units.Hz
ref_period = 1/ref_freq
logger.debug("environment ref_period: {}".format(ref_period))
return Environment(ref_period)
2014-09-05 12:03:22 +08:00
2014-12-02 14:06:32 +08:00
def switch_clock(self, external):
_write_exactly(self.port, struct.pack(
">lbb", 0x5a5a5a5a, _H2DMsgType.SWITCH_CLOCK.value,
int(external)))
msg = self._get_device_msg()
if msg != _D2HMsgType.CLOCK_SWITCH_COMPLETED:
raise IOError("Incorrect reply from device: "+str(msg))
def load(self, kcode):
2014-09-05 12:03:22 +08:00
_write_exactly(self.port, struct.pack(
">lblL",
0x5a5a5a5a, _H2DMsgType.LOAD_OBJECT.value,
2014-09-05 12:03:22 +08:00
len(kcode), zlib.crc32(kcode)))
_write_exactly(self.port, kcode)
msg = self._get_device_msg()
if msg != _D2HMsgType.OBJECT_LOADED:
raise IOError("Incorrect reply from device: "+str(msg))
def run(self, kname):
_write_exactly(self.port, struct.pack(
">lbl", 0x5a5a5a5a, _H2DMsgType.RUN_KERNEL.value, len(kname)))
for c in kname:
_write_exactly(self.port, struct.pack("B", ord(c)))
2014-09-18 01:17:58 +08:00
logger.debug("running kernel: {}".format(kname))
2014-09-05 12:03:22 +08:00
def serve(self, rpc_map, user_exception_map):
2014-09-05 12:03:22 +08:00
while True:
msg = self._get_device_msg()
2014-09-21 23:36:10 +08:00
if msg == _D2HMsgType.RPC_REQUEST:
rpc_num, n_args = struct.unpack(">hB",
2014-09-05 12:03:22 +08:00
_read_exactly(self.port, 3))
args = []
for i in range(n_args):
args.append(*struct.unpack(">l",
_read_exactly(self.port, 4)))
2014-09-18 01:17:58 +08:00
logger.debug("rpc service: {} ({})".format(rpc_num, args))
2014-09-05 12:03:22 +08:00
r = rpc_map[rpc_num](*args)
if r is None:
r = 0
_write_exactly(self.port, struct.pack(">l", r))
2014-09-18 01:17:58 +08:00
logger.debug("rpc service: {} ({}) == {}".format(
rpc_num, args, r))
2014-09-21 23:36:10 +08:00
elif msg == _D2HMsgType.KERNEL_EXCEPTION:
(eid, ) = struct.unpack(">l", _read_exactly(self.port, 4))
if eid < core_language.first_user_eid:
exception = runtime_exceptions.exception_map[eid]
else:
exception = user_exception_map[eid]
raise exception
2014-09-21 23:36:10 +08:00
elif msg == _D2HMsgType.KERNEL_FINISHED:
return
else:
raise IOError("Incorrect request from device: "+str(msg))