From 9169dc66301e31ab315783ef628eaa5a5c772984 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 11 Mar 2021 12:55:29 -0800 Subject: [PATCH 01/47] Initial commit of API server impl. --- python/tvm/micro/project_api/server.py | 675 ++++++++++++++++++ .../python/unittest/test_micro_project_api.py | 386 ++++++++++ 2 files changed, 1061 insertions(+) create mode 100644 python/tvm/micro/project_api/server.py create mode 100644 tests/python/unittest/test_micro_project_api.py diff --git a/python/tvm/micro/project_api/server.py b/python/tvm/micro/project_api/server.py new file mode 100644 index 000000000000..aae1ba5ffe8a --- /dev/null +++ b/python/tvm/micro/project_api/server.py @@ -0,0 +1,675 @@ +"""Defines a basic Project API server template. + +This file is meant to be imported or copied into Project API servers, so it should not have any +imports or dependencies outside of things strictly required to run the API server. +""" + +import abc +import argparse +import base64 +import collections +import enum +import io +import json +import logging +import os +import pathlib +import re +import select +import sys +import textwrap +import time +import traceback +import typing + + +_LOG = logging.getLogger(__name__) + + +_ProjectOption = collections.namedtuple("ProjectOption", ("name", "choices", "help")) +class ProjectOption(_ProjectOption): + def __new__(cls, name, **kw): + """Override __new__ to force all options except name to be specified as kwargs.""" + assert "name" not in kw + kw["name"] = name + kw.setdefault("choices", None) + return super().__new__(cls, **kw) + + +ServerInfo = collections.namedtuple('ServerInfo', ('platform_name', 'is_template', 'model_library_format_path', 'project_options')) + + +# Timeouts supported by the underlying C++ MicroSession. +# +# session_start_retry_timeout_sec : float +# Number of seconds to wait for the device to send a kSessionStartReply after sending the +# initial session start message. After this time elapses another +# kSessionTerminated-kSessionStartInit train is sent. 0 disables this. +# session_start_timeout_sec : float +# Total number of seconds to wait for the session to be established. After this time, the +# client gives up trying to establish a session and raises an exception. +# session_established_timeout_sec : float +# Number of seconds to wait for a reply message after a session has been established. 0 +# disables this. +TransportTimeouts = collections.namedtuple( + "TransportTimeouts", + [ + "session_start_retry_timeout_sec", + "session_start_timeout_sec", + "session_established_timeout_sec", + ], +) + + +class ErrorCode(enum.IntEnum): + """Enumerates error codes which can be returned. Includes JSON-RPC standard and custom codes.""" + # Custom (in reserved error code space). + SERVER_ERROR = -32000 # A generic error was raised while processing the request. + + # JSON-RPC standard + PARSE_ERROR = -32700 + INVALID_REQUEST = -32600 + METHOD_NOT_FOUND = -32601 + INVALID_PARAMS = -32602 + INTERNAL_ERROR = -32603 + + +class JSONRPCError(Exception): + """An error class with properties that meet the JSON-RPC error spec.""" + + def __init__(self, code, message, data, client_context=None): + self.code = code + self.message = message + self.data = data + self.client_context = client_context + + def to_json(self): + return {"code": self.code, + "message": self.message, + "data": self.data, + } + + def __str__(self): + data_str = '' + if self.data: + if isinstance(self.data, dict) and self.data.get("traceback"): + data_str = f'\n{self.data["traceback"]}' + else: + data_str = f'\n{self.data!r}' + return f"JSON-RPC error # {self.code}: {self.message}" + data_str + + @classmethod + def from_json(cls, client_context, json_error): + # Subclasses of ServerError capture exceptions that occur in the Handler, and thus return a + # traceback. The encoding in `json_error` is also slightly different to allow the specific subclass + # to be identified. + found_server_error = False + try: + if ErrorCode(json_error["code"]) == ErrorCode.SERVER_ERROR: + found_server_error = True + except ValueError: + ServerError.from_json(client_context, json_error) + + if found_server_error: + return ServerError.from_json(client_context, json_error) + + return cls(json_error["code"], json_error["message"], json_error.get("data", None), client_context=client_context) + + +class ServerError(JSONRPCError): + + @classmethod + def from_exception(cls, exc, **kw): + to_return = cls(**kw) + to_return.set_traceback(traceback.TracebackException.from_exception(exc).format()) + return to_return + + def __init__(self, message=None, data=None, client_context=None): + if message is None: + message = self.__class__.__name__ + super(ServerError, self).__init__(ErrorCode.SERVER_ERROR, message, data) + self.client_context = client_context + + def __str__(self): + context_str = f"{self.client_context}: " if self.client_context is not None else "" + super_str = super(ServerError, self).__str__() + return context_str + super_str + + def set_traceback(self, traceback): + if self.data is None: + self.data = {} + + if "traceback" not in self.data: + # NOTE: TVM's FFI layer reorders Python stack traces several times and strips + # intermediary lines that start with "Traceback". This logic adds a comment to the first + # stack frame to explicitly identify the first stack frame line that occurs on the server. + traceback_list = list(traceback) + + # The traceback list contains one entry per stack frame, and each entry contains 1-2 lines: + # File "path/to/file", line 123, in : + # + # We want to place a comment on the first line of the outermost frame to indicate this is the + # server-side stack frame. + first_frame_list = traceback_list[1].split('\n') + self.data["traceback"] = ( + traceback_list[0] + + f"{first_frame_list[0]} # <--- Outermost server-side stack frame\n" + + "\n".join(first_frame_list[1:]) + + "".join(traceback_list[2:]) + ) + + @classmethod + def from_json(cls, client_context, json_error): + assert json_error["code"] == ErrorCode.SERVER_ERROR + + for sub_cls in cls.__subclasses__(): + if sub_cls.__name__ == json_error["message"]: + return sub_cls(message=json_error["message"], data=json_error.get("data"), client_context=client_context) + + return cls(json_error["message"], data=json_error.get("data"), client_context=client_context) + + +class TransportClosedError(ServerError): + """Raised when a transport can no longer be used due to underlying I/O problems.""" + + +class IoTimeoutError(ServerError): + """Raised when the I/O operation could not be completed before the timeout. + + Specifically: + - when no data could be read before the timeout + - when some of the write data could be written before the timeout + + Note the asymmetric behavior of read() vs write(), since in one case the total length of the + data to transfer is known. + """ + + +class ProjectAPIHandler(metaclass=abc.ABCMeta): + + @abc.abstractmethod + def server_info_query(self) -> ServerInfo: + raise NotImplementedError() + + @abc.abstractmethod + def generate_project(self, model_library_format_path : pathlib.Path, standalone_crt_dir : pathlib.Path, project_dir : pathlib.Path, options : dict): + """Generate a project from the given artifacts, copying ourselves to that project. + + Parameters + ---------- + model_library_format_path : pathlib.Path + Path to the Model Library Format tar archive. + standalone_crt_dir : pathlib.Path + Path to the root directory of the "standalone_crt" TVM build artifact. This contains the + TVM C runtime. + project_dir : pathlib.Path + Path to a nonexistent directory which should be created and filled with the generated + project. + options : dict + Dict mapping option name to ProjectOption. + """ + raise NotImplementedError() + + @abc.abstractmethod + def build(self, options : dict): + """Build the project, enabling the flash() call to made. + + Parameters + ---------- + options : Dict[str, ProjectOption] + ProjectOption which may influence the build, keyed by option name. + """ + raise NotImplementedError() + + @abc.abstractmethod + def flash(self, options : dict): + """Program the project onto the device. + + Parameters + ---------- + options : Dict[str, ProjectOption] + ProjectOption which may influence the programming process, keyed by option name. + """ + raise NotImplementedError() + + @abc.abstractmethod + def open_transport(self, options : dict) -> TransportTimeouts: + """Open resources needed for the transport layer. + + This function might e.g. open files or serial ports needed in write_transport or read_transport. + + Calling this function enables the write_transport and read_transport calls. If the + transport is not open, this method is a no-op. + + Parameters + ---------- + options : Dict[str, ProjectOption] + ProjectOption which may influence the programming process, keyed by option name. + """ + raise NotImplementedError() + + @abc.abstractmethod + def close_transport(self): + """Close resources needed to operate the transport layer. + + This function might e.g. close files or serial ports needed in write_transport or read_transport. + + Calling this function disables the write_transport and read_transport calls. If the + transport is not open, this method is a no-op. + """ + raise NotImplementedError() + + @abc.abstractmethod + def read_transport(self, n : int, timeout_sec : typing.Union[float, type(None)]) -> bytes: + """Read data from the transport. + + Parameters + ---------- + n : int + The exact number of bytes to read from the transport. + timeout_sec : Union[float, None] + Number of seconds to wait for at least one byte to be written before timing out. If + timeout_sec is 0, write should attempt to service the request in a non-blocking fashion. + If timeout_sec is None, write should block until all `n` bytes of data can be returned. + + Returns + ------- + bytes : + Data read from the channel. Should be exactly `n` bytes long. + + Raises + ------ + TransportClosedError : + When the transport layer determines that the transport can no longer send or receive + data due to an underlying I/O problem (i.e. file descriptor closed, cable removed, etc). + + IoTimeoutError : + When `timeout_sec` elapses without receiving any data. + """ + raise NotImplementedError() + + @abc.abstractmethod + def write_transport(self, data : bytes, timeout_sec : float): + """Write data to the transport. + + This function should either write all bytes in `data` or raise an exception. + + Parameters + ---------- + data : bytes + The data to write over the channel. + timeout_sec : Union[float, None] + Number of seconds to wait for all bytes to be written before timing out. If timeout_sec + is 0, write should attempt to service the request in a non-blocking fashion. If + timeout_sec is None, write should block until it has written all data. + + Raises + ------ + TransportClosedError : + When the transport layer determines that the transport can no longer send or receive + data due to an underlying I/O problem (i.e. file descriptor closed, cable removed, etc). + + IoTimeoutError : + When `timeout_sec` elapses without receiving any data. + """ + raise NotImplementedError() + + + +class ProjectAPIServer: + """Base class for Project API Servers. + + This API server implements communication using JSON-RPC 2.0: https://www.jsonrpc.org/specification + + Suggested use of this class is to import this module or copy this file into Project Generator + implementations, then instantiate it with server.start(). + + This RPC server is single-threaded, blocking, and one-request-at-a-time. Don't get anxious. + """ + + _PROTOCOL_VERSION = 1 + + def __init__(self, read_file : typing.BinaryIO, write_file : typing.BinaryIO, + handler : ProjectAPIHandler): + """Initialize a new ProjectAPIServer. + + Parameters + ---------- + read_file : BinaryIO + A file-like object used to read binary data from the client. + write_file : BinaryIO + A file-like object used to write binary data to the client. + handler : ProjectAPIHandler + A class which extends the abstract class ProjectAPIHandler and implements the server RPC + functions. + """ + self._read_file = io.TextIOWrapper(read_file, encoding='UTF-8', errors='strict') + self._write_file = io.TextIOWrapper(write_file, encoding='UTF-8', errors='strict', write_through=True) + self._handler = handler + + def serve_forever(self): + """Serve requests until no more are available.""" + has_more = True + while has_more: + has_more = self.serve_one_request() + + def serve_one_request(self): + """Read, process, and reply to a single request from read_file. + + When errors occur reading the request line or loading the request into JSON, they are + propagated to the caller (the stream is then likely corrupted and no further requests + should be served. When errors occur past this point, they are caught and send back to the + client. + + Return + ---------- + bool : + True when more data could be read from read_file, False otherwise. + """ + try: + line = self._read_file.readline() + _LOG.debug('read request <- %s', line) + if not line: + return False + + request = json.loads(line) + + except EOFError: + _LOG.error('EOF') + return False + + except Exception as exc: + _LOG.error("Caught error reading request", exc_info=1) + return False + + did_validate = False + try: + self._validate_request(request) + did_validate = True + self._dispatch_request(request) + except JSONRPCError as exc: + if isinstance(exc, ServerError): + exc.set_traceback(traceback.TracebackException.from_exception(exc).format()) + request_id = None if not did_validate else request.get("id") + self._reply_error(request_id, exc) + return did_validate + except Exception as exc: + message = "validating request" + if did_validate: + message = f"calling method {request['method']}" + + exc = ServerError.from_exception(exc, message=message) + request_id = None if not isinstance(request, dict) else request.get('id') + self._reply_error(request_id, exc) + return did_validate + + return True + + VALID_METHOD_RE = re.compile('^[a-zA-Z0-9_]+$') + + def _validate_request(self, request): + if type(request) is not dict: + raise JSONRPCError(ErrorCode.INVALID_REQUEST, f"request: want dict; got {request!r}", None) + + jsonrpc = request.get('jsonrpc') + if jsonrpc != "2.0": + raise JSONRPCError( + ErrorCode.INVALID_REQUEST, f'request["jsonrpc"]: want "2.0"; got {jsonrpc!r}', None + ) + + method = request.get('method') + if type(method) != str: + raise JSONRPCError( + ErrorCode.INVALID_REQUEST, f'request["method"]: want str; got {method!r}', None + ) + + if not self.VALID_METHOD_RE.match(method): + raise JSONRPCError( + ErrorCode.INVALID_REQUEST, + f'request["method"]: should match regex {self.VALID_METHOD_RE.pattern}; got {method!r}', + None + ) + + params = request.get('params') + if type(params) != dict: + raise JSONRPCError( + ErrorCode.INVALID_REQUEST, f'request["params"]: want dict; got {type(params)}', None + ) + + request_id = request.get('id') + if type(request_id) not in (str, int, type(None)): + raise JSONRPCError( + ErrorCode.INVALID_REQUEST, + f'request["id"]: want str, number, null; got {request_id!r}', + None, + ) + + def _dispatch_request(self, request): + method = request['method'] + + interface_method = getattr(ProjectAPIHandler, method, None) + if interface_method is None: + raise JSONRPCError( + ErrorCode.METHOD_NOT_FOUND, f'{request["method"]}: no such method', None) + + has_preprocessing = True + dispatch_method = getattr(self, f'_dispatch_{method}', None) + if dispatch_method is None: + dispatch_method = getattr(self._handler, method) + has_preprocessing = False + + request_params = request['params'] + params = {} + + for var_name, var_type in typing.get_type_hints(interface_method).items(): + if var_name == 'self' or var_name == 'return': + continue + + # NOTE: types can only be JSON-compatible types, so var_type is expected to be of type 'type'. + if var_name not in request_params: + raise JSONRPCError(ErrorCode.INVALID_PARAMS, f'method {request["method"]}: parameter {var_name} not given', None) + + param = request_params[var_name] + if not has_preprocessing and not isinstance(param, var_type): + raise JSONRPCError( + ErrorCode.INVALID_PARAMS, + f'method {request["method"]}: parameter {var_name}: want {var_type!r}, got {type(param)!r}', None) + + params[var_name] = param + + extra_params = [p for p in request['params'] if p not in params] + if extra_params: + raise JSONRPCError(ErrorCode.INVALID_PARAMS, + f'{request["method"]}: extra parameters: {", ".join(extra_params)}', + None) + + return_value = dispatch_method(**params) + self._write_reply(request['id'], result=return_value) + + def _write_reply(self, request_id, result=None, error=None): + reply_dict = { + 'jsonrpc': "2.0", + 'id': request_id, + } + + if error is not None: + assert result is None, f'Want either result= or error=, got result={result!r} and error={error!r})' + reply_dict["error"] = error + else: + reply_dict["result"] = result + + reply_str = json.dumps(reply_dict) + _LOG.debug('write reply -> %r', reply_dict) + self._write_file.write(reply_str) + self._write_file.write('\n') + + def _reply_error(self, request_id, exception): + self._write_reply(request_id, error=exception.to_json()) + + def _dispatch_generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): + return self._handler.generate_project(pathlib.Path(model_library_format_path), + pathlib.Path(standalone_crt_dir), + pathlib.Path(project_dir), + options) + + def _dispatch_server_info_query(self): + query_reply = self._handler.server_info_query() + to_return = query_reply._asdict() + if to_return["model_library_format_path"] is not None: + to_return["model_library_format_path"] = str(to_return["model_library_format_path"]) + to_return["protocol_version"] = self._PROTOCOL_VERSION + to_return["project_options"] = [o._asdict() for o in query_reply.project_options] + return to_return + + def _dispatch_open_transport(self, options): + reply = self._handler.open_transport(options) + return {"timeouts": reply._asdict()} + + def _dispatch_read_transport(self, n, timeout_sec): + reply_data = self._handler.read_transport(n, timeout_sec) + return {'data': str(base64.b85encode(reply_data), 'utf-8')} + + def _dispatch_write_transport(self, data, timeout_sec): + self._handler.write_transport(base64.b85decode(data), timeout_sec) + + +def _await_nonblocking_ready(rlist, wlist, timeout_sec=None, end_time=None): + if end_time is None: + return True + + if timeout_sec is None: + timeout_sec = max(0, end_time - time.monotonic()) + rlist, wlist, xlist = select.select(rlist, wlist, rlist + wlist, timeout_sec) + if not rlist and not wlist and not xlist: + raise IoTimeoutError() + + return True + + +def read_with_timeout(fd, n, timeout_sec): + """Read data from a file descriptor, with timeout. + + This function is intended as a helper function for implementations of ProjectAPIHandler + read_transport. Tested on Linux and OS X. Not tested on Windows. + + Parameters + ---------- + fd : int + File descriptor to read from. Must be opened in non-blocking mode (e.g. with O_NONBLOCK) + if timeout_sec is not None. + + n : int + Maximum number of bytes to read. + + timeout_sec : float or None + If not None, maximum number of seconds to wait before raising IoTimeoutError. + + Returns + ------- + bytes : + If at least one byte was received before timeout_sec, returns a bytes object with length + in [1, n]. If timeout_sec is None, returns the equivalent of os.read(fd, n). + + Raises + ------ + IoTimeoutException : + When timeout_sec is not None and that number of seconds elapses before any data is read. + """ + end_time = None if timeout_sec is None else time.monotonic() + timeout_sec + + while True: + _await_nonblocking_ready([fd], [], end_time=end_time) + try: + to_return = os.read(fd, n) + break + except BlockingIOError: + pass + + # When EOF is reached, close the file. + if not to_return: + os.close(fd) + raise TransportClosedError() + + return to_return + + +def write_with_timeout(fd, data, timeout_sec): + """Write data to a file descriptor, with timeout. + + This function is intended as a helper function for implementations of ProjectAPIHandler + write_transport. Tested on Linux and OS X. Not tested on Windows. + + Parameters + ---------- + fd : int + File descriptor to read from. Must be opened in non-blocking mode (e.g. with O_NONBLOCK) + if timeout_sec is not None. + + data : bytes + Data to write. + + timeout_sec : float or None + If not None, maximum number of seconds to wait before raising IoTimeoutError. + + Returns + ------- + int : + The number of bytes written to the file descriptor, if any bytes were written. A value + in [1, len(data)]. If timeout_sec is None, returns the equivalent of os.write(fd, data). + + Raises + ------ + IoTimeoutException : + When timeout_sec is not None and that number of seconds elapses before any data is read. + """ + end_time = None if timeout_sec is None else time.monotonic() + timeout_sec + + num_written = 0 + while data: + try: + _await_nonblocking_ready([], [fd], end_time=end_time) + except IoTimeoutError as exc: + if num_written: + return num_written + + raise exc + + num_written_this_cycle = os.write(fd, data) + + if not num_written_this_cycle: + os.close(fd) + raise base.TransportClosedError() + + data = data[num_written_this_cycle:] + num_written += num_written_this_cycle + + return num_written + + +def main(handler : ProjectAPIHandler, argv : typing.List[str] = None): + """Start a Project API server. + + Parameters + ---------- + argv : list[str] + Command-line parameters to this program. If not given, sys.argv is used. + handler : ProjectAPIHandler + Handler class that implements the API server RPC calls. + """ + if argv is None: + argv = sys.argv[1:] + + parser = argparse.ArgumentParser(description="Generic TVM Project API server entry point") + parser.add_argument("--read-fd", type=int, required=True, help="Numeric file descriptor where RPC requests should be read.") + parser.add_argument("--write-fd", type=int, required=True, help="Numeric file descriptor where RPC replies should be written.") + parser.add_argument("--debug", action="store_true", help="When given, configure logging at DEBUG level.") + args = parser.parse_args() + + logging.basicConfig(level='DEBUG' if args.debug else 'INFO', + stream=sys.stderr) + + read_file = os.fdopen(args.read_fd, 'rb', buffering=0) + write_file = os.fdopen(args.write_fd, 'wb', buffering=0) + + server = ProjectAPIServer(read_file, write_file, handler) + server.serve_forever() diff --git a/tests/python/unittest/test_micro_project_api.py b/tests/python/unittest/test_micro_project_api.py new file mode 100644 index 000000000000..e2122b06af0b --- /dev/null +++ b/tests/python/unittest/test_micro_project_api.py @@ -0,0 +1,386 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import io +import json +import sys +import unittest +from unittest import mock + +import pytest + +from tvm.micro import project_api + + +class BaseTestHandler(project_api.server.ProjectAPIHandler): + + DEFAULT_TEST_SERVER_INFO = project_api.server.ServerInfo( + platform_name="platform_name", + is_template=True, + model_library_format_path="./model-library-format-path.sh", + project_options=[ + project_api.server.ProjectOption(name="foo", help="Option foo"), + project_api.server.ProjectOption(name="bar", choices=["qux"], help="Option bar"), + ], + ) + + # Overridable by test case. + TEST_SERVER_INFO = None + + def server_info_query(self): + return ( + self.TEST_SERVER_INFO + if self.TEST_SERVER_INFO is not None + else self.DEFAULT_TEST_SERVER_INFO + ) + + def generate_project(self, model_library_format_path, crt_path, project_path, options): + assert False, "generate_project is not implemented for this test" + + def build(self, options): + assert False, "build is not implemented for this test" + + def flash(self, options): + assert False, "flash is not implemented for this test" + + def open_transport(self, options): + assert False, "open_transport is not implemented for this test" + + def close_transport(self, options): + assert False, "open_transport is not implemented for this test" + + def read_transport(self, n, timeout_sec): + assert False, "read_transport is not implemented for this test" + + def write_transport(self, data, timeout_sec): + assert False, "write_transport is not implemented for this test" + + +class Transport: + def readable(self): + return True + + def writable(self): + return True + + def seekable(self): + return False + + closed = False + + def __init__(self): + self.data = bytearray() + self.rpos = 0 + + self.items = [] + + def read(self, size=-1): + to_read = len(self.data) - self.rpos + if size != -1: + to_read = min(size, to_read) + + rpos = self.rpos + self.rpos += to_read + return self.data[rpos : self.rpos] + + def write(self, data): + self.data.extend(data) + + +class ClientServerFixture: + + def __init__(self, handler): + self.handler = handler + self.client_to_server = Transport() + self.server_to_client = Transport() + + self.server = project_api.server.ProjectAPIServer(self.client_to_server, self.server_to_client, handler) + self.client = project_api.client.ProjectAPIClient( + self.server_to_client, self.client_to_server, testonly_did_write_request=self._process_server_request + ) + + self.expect_failure = False + + def _process_server_request(self): + assert self.server.serve_one_request() == (not self.expect_failure), "Server failed to process request" + + +def test_server_info_query(): + fixture = ClientServerFixture(BaseTestHandler()) + + # Examine reply explicitly because these are the defaults for all derivative test cases. + reply = fixture.client.server_info_query() + assert reply["protocol_version"] == 1 + assert reply["platform_name"] == "platform_name" + assert reply["is_template"] == True + assert reply["model_library_format_path"] == "./model-library-format-path.sh" + assert reply["project_options"] == [ + {"name": "foo", "choices": None, "help": "Option foo"}, + {"name": "bar", "choices": ["qux"], "help": "Option bar"}, + ] + + fixture.handler.TEST_SERVER_INFO = project_api.server.ServerInfo( + platform_name="foo_bar", + is_template=False, + model_library_format_path=None, + project_options=[]) + reply = fixture.client.server_info_query() + expected_reply = dict(fixture.handler.TEST_SERVER_INFO._asdict()) + expected_reply["protocol_version"] = 1 + assert reply == expected_reply + + +def test_base_test_handler(): + """All methods should raise AssertionError on BaseTestHandler.""" + fixture = ClientServerFixture(BaseTestHandler()) + + for method in dir(fixture.handler): + if method.startswith("_") or not callable(method) or method == "server_info_query": + continue + + with self.assertThrows(AssertionError) as exc_info: + getattr(fixture.client, method)() + + assert (exc_info.exception) == f"{method} is not implemented for this test" + + +def test_build(): + with mock.patch.object(BaseTestHandler, "build", return_value=None) as patch: + fixture = ClientServerFixture(BaseTestHandler()) + fixture.client.build(options={"bar": "baz"}) + + fixture.handler.build.assert_called_once_with(options={"bar": "baz"}) + + +def test_flash(): + with mock.patch.object(BaseTestHandler, "flash", return_value=None) as patch: + fixture = ClientServerFixture(BaseTestHandler()) + fixture.client.flash(options={"bar": "baz"}) + fixture.handler.flash.assert_called_once_with(options={"bar": "baz"}) + + +def test_open_transport(): + timeouts = project_api.server.TransportTimeouts( + session_start_retry_timeout_sec=1.0, + session_start_timeout_sec=2.0, + session_established_timeout_sec=3.0, + ) + + with mock.patch.object(BaseTestHandler, "open_transport", return_value=timeouts) as patch: + fixture = ClientServerFixture(BaseTestHandler()) + assert fixture.client.open_transport(options={"bar": "baz"}) == {"timeouts": dict(timeouts._asdict())} + fixture.handler.open_transport.assert_called_once_with({"bar": "baz"}) + + +def test_close_transport(): + with mock.patch.object(BaseTestHandler, "close_transport", return_value=None) as patch: + fixture = ClientServerFixture(BaseTestHandler()) + fixture.client.close_transport() + fixture.handler.close_transport.assert_called_once_with() + + +def test_read_transport(): + with mock.patch.object(BaseTestHandler, "read_transport", return_value=b"foo\x1b") as patch: + fixture = ClientServerFixture(BaseTestHandler()) + assert fixture.client.read_transport(128, timeout_sec=5.0) == {"data": b"foo\x1b"} + + fixture.handler.read_transport.assert_called_with(128, 5.0) + + fixture.handler.read_transport.side_effect = project_api.server.IoTimeoutError + with pytest.raises(project_api.server.IoTimeoutError) as exc_info: + fixture.client.read_transport(256, timeout_sec=10.0) + + fixture.handler.read_transport.assert_called_with(256, 10.0) + + fixture.handler.read_transport.side_effect = project_api.server.TransportClosedError + with pytest.raises(project_api.server.TransportClosedError) as exc_info: + fixture.client.read_transport(512, timeout_sec=15.0) + + fixture.handler.read_transport.assert_called_with(512, 15.0) + + assert fixture.handler.read_transport.call_count == 3 + + +def test_write_transport(): + with mock.patch.object(BaseTestHandler, "write_transport", return_value=None) as patch: + fixture = ClientServerFixture(BaseTestHandler()) + assert fixture.client.write_transport(b"foo", timeout_sec=5.0) is None + fixture.handler.write_transport.assert_called_with(b"foo", 5.0) + + fixture.handler.write_transport.side_effect = project_api.server.IoTimeoutError + with pytest.raises(project_api.server.IoTimeoutError) as exc_info: + fixture.client.write_transport(b"bar", timeout_sec=10.0) + + fixture.handler.write_transport.assert_called_with(b"bar", 10.0) + + fixture.handler.write_transport.side_effect = project_api.server.TransportClosedError + with pytest.raises(project_api.server.TransportClosedError) as exc_info: + fixture.client.write_transport(b"baz", timeout_sec=15.0) + + fixture.handler.write_transport.assert_called_with(b"baz", 15.0) + + assert fixture.handler.write_transport.call_count == 3 + + +class ProjectAPITestError(Exception): + """An error raised in test.""" + + +def test_method_raises_error(): + with mock.patch.object(BaseTestHandler, "close_transport", side_effect=ProjectAPITestError) as patch: + fixture = ClientServerFixture(BaseTestHandler()) + with pytest.raises(project_api.server.ServerError) as exc_info: + fixture.client.close_transport() + + fixture.handler.close_transport.assert_called_once_with() + assert "ProjectAPITestError" in str(exc_info.value) + + +def test_method_not_found(): + fixture = ClientServerFixture(BaseTestHandler()) + + with pytest.raises(project_api.server.JSONRPCError) as exc_info: + fixture.client._request_reply("invalid_method", {"bar": None}) + + assert exc_info.value.code == project_api.server.ErrorCode.METHOD_NOT_FOUND + + +def test_extra_param(): + fixture = ClientServerFixture(BaseTestHandler()) + + # test one with has_preprocssing and one without + assert hasattr(fixture.server, '_dispatch_build') == False + with pytest.raises(project_api.server.JSONRPCError) as exc_info: + fixture.client._request_reply("build", {"invalid_param_name": None, "options": {}}) + + assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS + assert "build: extra parameters: invalid_param_name" in str(exc_info.value) + + assert hasattr(fixture.server, '_dispatch_open_transport') == True + with pytest.raises(project_api.server.JSONRPCError) as exc_info: + fixture.client._request_reply("open_transport", {"invalid_param_name": None, "options": {}}) + + assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS + assert "open_transport: extra parameters: invalid_param_name" in str(exc_info.value) + + +def test_missing_param(): + fixture = ClientServerFixture(BaseTestHandler()) + + # test one with has_preprocssing and one without + assert hasattr(fixture.server, '_dispatch_build') == False + with pytest.raises(project_api.server.JSONRPCError) as exc_info: + fixture.client._request_reply("build", {}) + + assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS + assert "build: parameter options not given" in str(exc_info.value) + + assert hasattr(fixture.server, '_dispatch_open_transport') == True + with pytest.raises(project_api.server.JSONRPCError) as exc_info: + fixture.client._request_reply("open_transport", {}) + + assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS + assert "open_transport: parameter options not given" in str(exc_info.value) + + +def test_incorrect_param_type(): + fixture = ClientServerFixture(BaseTestHandler()) + + # The error message given at the JSON-RPC server level doesn't make sense when preprocessing is + # used. Only test without preprocessing here. + assert hasattr(fixture.server, '_dispatch_build') == False + with pytest.raises(project_api.server.JSONRPCError) as exc_info: + fixture.client._request_reply("build", {"options": None}) + + assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS + assert "build: parameter options: want , got " in str(exc_info.value) + + +def test_invalid_request(): + fixture = ClientServerFixture(BaseTestHandler()) + + # Invalid JSON does not get a reply. + fixture.client_to_server.write(b"foobar\n") + assert fixture.server.serve_one_request() == False + assert fixture.server_to_client.read() == b"" + + # EOF causes a clean return + assert fixture.server.serve_one_request() == False + assert fixture.server_to_client.read() == b"" + + def _request_reply(request): + fixture.client_to_server.write(request + b"\n") + assert fixture.server.serve_one_request() == False + return json.loads(fixture.server_to_client.read()) + + # Parseable JSON with the wrong schema gets a reply. + assert _request_reply(b"1") == { + "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": "request: want dict; got 1"}, + "id": None, + "jsonrpc": "2.0", + } + + # Incorrect JSON-RPC spec version. + assert _request_reply(b'{"jsonrpc": 1.0}') == { + "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["jsonrpc"]: want "2.0"; got 1.0'}, + "id": None, + "jsonrpc": "2.0", + } + + # Method not a str + assert _request_reply(b'{"jsonrpc": "2.0", "method": 123}') == { + "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["method"]: want str; got 123'}, + "id": None, + "jsonrpc": "2.0", + } + + # Method name has invalid characters + assert _request_reply(b'{"jsonrpc": "2.0", "method": "bar!"}') == { + "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["method"]: should match regex ^[a-zA-Z0-9_]+$; got \'bar!\''}, + "id": None, + "jsonrpc": "2.0", + } + + # params not a dict + assert _request_reply(b'{"jsonrpc": "2.0", "method": "bar", "params": 123}') == { + "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["params"]: want dict; got '}, + "id": None, + "jsonrpc": "2.0", + } + + # id not valid + assert _request_reply(b'{"jsonrpc": "2.0", "method": "bar", "params": {}, "id": {}}') == { + "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["id"]: want str, number, null; got {}'}, + "id": None, + "jsonrpc": "2.0", + } + + +if __name__ == "__main__": + sys.exit(pytest.main([__file__] + sys.argv[1:])) From 64c184d03fc3c265ce65ddda84880952015dd868 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 11 Mar 2021 14:05:02 -0800 Subject: [PATCH 02/47] initial commit of api client --- python/tvm/micro/project_api/client.py | 177 ++++++++++++++++++ .../python/unittest/test_micro_project_api.py | 1 + 2 files changed, 178 insertions(+) create mode 100644 python/tvm/micro/project_api/client.py diff --git a/python/tvm/micro/project_api/client.py b/python/tvm/micro/project_api/client.py new file mode 100644 index 000000000000..b1f5e479e0c0 --- /dev/null +++ b/python/tvm/micro/project_api/client.py @@ -0,0 +1,177 @@ +import base64 +import io +import json +import logging +import os +import subprocess +import sys +import typing + +from . import server + +_LOG = logging.getLogger(__name__) + + +class ProjectAPIErrorBase(Exception): + """Base class for all Project API errors.""" + + +class ConnectionShutdownError(ProjectAPIErrorBase): + """Raised when a request is made but the connection has been closed.""" + + +class MalformedReplyError(ProjectAPIErrorBase): + """Raised when the server responds with an invalid reply.""" + + +class MismatchedIdError(ProjectAPIErrorBase): + """Raised when the reply ID does not match the request.""" + + +class ProjectAPIServerNotFoundError(ProjectAPIErrorBase): + """Raised when the Project API server can't be found in the repo.""" + + +class RPCError(ProjectAPIErrorBase): + + def __init__(self, request, error): + self.request = request + self.error = error + + def __str__(self): + return (f"Calling project API method {self.request['method']}:" "\n" + f"{self.error}") + + +class ProjectAPIClient: + """A client for the Project API.""" + + def __init__(self, read_file : typing.BinaryIO, write_file : typing.BinaryIO, + testonly_did_write_request : typing.Optional[typing.Callable] = None): + self.read_file = io.TextIOWrapper(read_file, encoding='UTF-8', errors='strict') + self.write_file = io.TextIOWrapper(write_file, encoding='UTF-8', errors='strict', write_through=True) + self.testonly_did_write_request = testonly_did_write_request + self.next_request_id = 1 + + @property + def is_shutdown(self): + return self.read_file is None + + def shutdown(self): + if self.is_shutdown: + return + + self.read_file.close() + self.write_file.close() + + def _request_reply(self, method, params): + if self.is_shutdown: + raise ConnectionShutdownError("connection already closed") + + request = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": self.next_request_id, + } + self.next_request_id += 1 + + request_str = json.dumps(request) + self.write_file.write(request_str) + _LOG.debug("send -> %s", request_str) + self.write_file.write('\n') + if self.testonly_did_write_request: + self.testonly_did_write_request() # Allow test to assert on server processing. + reply_line = self.read_file.readline() + _LOG.debug("recv <- %s", reply_line) + if not reply_line: + self.shutdown() + raise ConnectionShutdownError("got EOF reading reply from API server") + + reply = json.loads(reply_line) + + if reply.get("jsonrpc") != "2.0": + raise MalformedReplyError( + f"Server reply should include 'jsonrpc': '2.0'; " + f"saw jsonrpc={reply.get('jsonrpc')!r}") + + if reply["id"] != request["id"]: + raise MismatchedIdError( + f"Reply id ({reply['id']}) does not equal request id ({request['id']}") + + if "error" in reply: + raise server.JSONRPCError.from_json(f"calling method {method}", reply["error"]) + elif "result" not in reply: + raise MalformedReplyError(f"Expected 'result' key in server reply, got {reply!r}") + + return reply["result"] + + def server_info_query(self): + return self._request_reply("server_info_query", {}) + + def generate_project(self, model_library_format_path : str, standalone_crt_dir : str, project_dir : str, options : dict = None): + return self._request_reply("generate_project", {"model_library_format_path": model_library_format_path, + "standalone_crt_dir": standalone_crt_dir, + "project_dir": project_dir, + "options": (options if options is not None else {})}) + + def build(self, options : dict = None): + return self._request_reply("build", {"options": (options if options is not None else {})}) + + def flash(self, options : dict = None): + return self._request_reply("flash", {"options": (options if options is not None else {})}) + + def open_transport(self, options : dict = None): + return self._request_reply("open_transport", {"options": (options if options is not None else {})}) + + def close_transport(self): + return self._request_reply("close_transport", {}) + + def read_transport(self, n, timeout_sec): + reply = self._request_reply("read_transport", {"n": n, "timeout_sec": timeout_sec}) + reply['data'] = base64.b85decode(reply['data']) + return reply + + def write_transport(self, data, timeout_sec): + return self._request_reply("write_transport", {"data": str(base64.b85encode(data), 'utf-8'), + "timeout_sec": timeout_sec}) + + +# NOTE: windows support untested +SERVER_LAUNCH_SCRIPT_FILENAME = f"launch_microtvm_api_server.{'sh' if os.system != 'win32' else '.bat'}" + + +SERVER_PYTHON_FILENAME = "microtvm_api_server.py" + + +def instantiate_from_dir(project_dir : str, debug : bool = False): + """Launch server located in project_dir, and instantiate a Project API Client connected to it.""" + args = None + + launch_script = os.path.join(project_dir, SERVER_LAUNCH_SCRIPT_FILENAME) + if os.path.exists(launch_script): + args = [launch_script] + + python_script = os.path.join(project_dir, SERVER_PYTHON_FILENAME) + if os.path.exists(python_script): + args = [sys.executable, python_script] + + if args is None: + raise ProjectAPIServerNotFoundError( + f"No Project API server found in project directory: {project_dir}" "\n" + f"Tried: {SERVER_LAUNCH_SCRIPT_FILENAME}, {SERVER_PYTHON_FILENAME}") + + api_server_read_fd, tvm_write_fd = os.pipe() + tvm_read_fd, api_server_write_fd = os.pipe() + + args.extend(["--read-fd", str(api_server_read_fd), + "--write-fd", str(api_server_write_fd)]) + if debug: + args.append("--debug") + + api_server_proc = subprocess.Popen(args, bufsize=0, pass_fds=(api_server_read_fd, api_server_write_fd), + cwd=project_dir) + os.close(api_server_read_fd) + os.close(api_server_write_fd) + + return ProjectAPIClient(os.fdopen(tvm_read_fd, 'rb', buffering=0), os.fdopen(tvm_write_fd, 'wb', buffering=0)) diff --git a/tests/python/unittest/test_micro_project_api.py b/tests/python/unittest/test_micro_project_api.py index e2122b06af0b..f00fc5244ec8 100644 --- a/tests/python/unittest/test_micro_project_api.py +++ b/tests/python/unittest/test_micro_project_api.py @@ -71,6 +71,7 @@ def write_transport(self, data, timeout_sec): class Transport: + def readable(self): return True From e1718ac69df552fdeddc7c69dee28e036cee0c51 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 11 Mar 2021 14:05:11 -0800 Subject: [PATCH 03/47] Add TVM-side glue code to use Project API --- python/tvm/micro/__init__.py | 3 +- python/tvm/micro/build.py | 3 +- python/tvm/micro/project.py | 99 ++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 python/tvm/micro/project.py diff --git a/python/tvm/micro/__init__.py b/python/tvm/micro/__init__.py index a70cb96d9b13..f7f8b007da2f 100644 --- a/python/tvm/micro/__init__.py +++ b/python/tvm/micro/__init__.py @@ -17,13 +17,14 @@ """MicroTVM module for bare-metal backends""" from .artifact import Artifact -from .build import build_static_runtime, default_options, get_standalone_crt_dir +from .build import default_options, get_standalone_crt_dir from .build import get_standalone_crt_lib, Workspace from .compiler import Compiler, DefaultCompiler, Flasher from .debugger import GdbRemoteDebugger from .micro_library import MicroLibrary from .micro_binary import MicroBinary from .model_library_format import export_model_library_format, UnsupportedInModelLibraryFormatError +from .project import generate_project, GeneratedProject, TemplateProject from .session import ( create_local_graph_executor, create_local_debug_executor, diff --git a/python/tvm/micro/build.py b/python/tvm/micro/build.py index a83ccaa47cda..85932f63e6c4 100644 --- a/python/tvm/micro/build.py +++ b/python/tvm/micro/build.py @@ -24,8 +24,9 @@ import typing from tvm.contrib import utils -from .micro_library import MicroLibrary from .._ffi import libinfo +from .micro_library import MicroLibrary +from . import model_library_format _LOG = logging.getLogger(__name__) diff --git a/python/tvm/micro/project.py b/python/tvm/micro/project.py new file mode 100644 index 000000000000..2d353d37a9a2 --- /dev/null +++ b/python/tvm/micro/project.py @@ -0,0 +1,99 @@ +"""Defines glue wrappers around the Project API which mate to TVM interfaces.""" + +from ..contrib import utils +from .build import get_standalone_crt_dir +from .model_library_format import export_model_library_format +from .project_api import client +from .transport import Transport, TransportTimeouts + + +class ProjectTransport(Transport): + + def __init__(self, client, options): + self._client = client + self._options = options + self._timeouts = None + + def timeouts(self): + assert self._timeouts is not None, "Transport not yet opened" + return self._timeouts + + def open(self): + reply = self._client.open_transport(self._options) + self._timeouts = TransportTimeouts(**reply["timeouts"]) + + def close(self): + if not self._client.shutdown: + self._client.close_transport() + + def write(self, data, timeout_sec): + self._client.write_transport(data, timeout_sec) + + def read(self, n, timeout_sec): + return self._client.read_transport(n, timeout_sec)["data"] + + +class TemplateProjectError(Exception): + """Raised when the Project API server given to GeneratedProject reports is_template=True.""" + + +class GeneratedProject: + """Defines a glue interface to interact with a generated project through the API server.""" + + @classmethod + def from_directory(cls, project_dir, options): + return cls(client.instantiate_from_dir(project_dir), options) + + def __init__(self, client, options): + self._client = client + self._options = options + self._info = self._client.server_info_query() + if self._info['is_template']: + raise TemplateProjectError() + + def build(self): + self._client.build(self._options) + + def flash(self): + self._client.flash(self._options) + + def transport(self): + return ProjectTransport(self._client, self._options) + + +class NotATemplateProjectError(Exception): + """Raised when the Project API server given to TemplateProject reports is_template=false.""" + + +class TemplateProject: + + @classmethod + def from_directory(cls, template_project_dir, options): + return cls(client.instantiate_from_dir(template_project_dir), options) + + def __init__(self, client, options): + self._client = client + self._options = options + self._info = self._client.server_info_query() + if not self._info['is_template']: + raise NotATemplateProjectError() + + def generate_project(self, graph_executor_factory, project_dir): + """Generate a project given GraphRuntimeFactory.""" + model_library_dir = utils.tempdir() + model_library_format_path = model_library_dir.relpath('model.tar') + export_model_library_format( + graph_executor_factory, model_library_format_path) + + self._client.generate_project( + model_library_format_path=model_library_format_path, + standalone_crt_dir=get_standalone_crt_dir(), + project_dir=project_dir, + options=self._options) + + return GeneratedProject.from_directory(project_dir, self._options) + + +def generate_project(template_project_dir : str, graph_executor_factory, project_dir : str, options : dict = None): + template = TemplateProject.from_directory(template_project_dir, options) + return template.generate_project(graph_executor_factory, project_dir) From f9286b5b7a569bab8b6b9042e6818ba7ee8dec68 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:02:54 -0700 Subject: [PATCH 04/47] Change tvm.micro.Session to use Project API --- python/tvm/micro/session.py | 13 -------- tests/micro/zephyr/test_zephyr.py | 54 ++++++------------------------- tests/python/unittest/test_crt.py | 24 ++++---------- 3 files changed, 16 insertions(+), 75 deletions(-) diff --git a/python/tvm/micro/session.py b/python/tvm/micro/session.py index 78bf03379939..abe805da68d6 100644 --- a/python/tvm/micro/session.py +++ b/python/tvm/micro/session.py @@ -60,8 +60,6 @@ class Session: def __init__( self, - binary=None, - flasher=None, transport_context_manager=None, session_name="micro-rpc", timeout_override=None, @@ -70,12 +68,6 @@ def __init__( Parameters ---------- - binary : MicroBinary - If given, `flasher` must also be given. During session initialization, this binary will - be flashed to the device before the transport is created. - flasher : Flasher - If given, `binary` must also be given. Used to flash `binary` during session - initialization. transport_context_manager : ContextManager[transport.Transport] If given, `flasher` and `binary` should not be given. On entry, this context manager should establish a tarnsport between this TVM instance and the device. @@ -85,8 +77,6 @@ def __init__( If given, TransportTimeouts that govern the way Receive() behaves. If not given, this is determined by calling has_flow_control() on the transport. """ - self.binary = binary - self.flasher = flasher self.transport_context_manager = transport_context_manager self.session_name = session_name self.timeout_override = timeout_override @@ -121,9 +111,6 @@ def __enter__(self): Session : Returns self. """ - if self.flasher is not None: - self.transport_context_manager = self.flasher.flash(self.binary) - self.transport = TransportLogger( self.session_name, self.transport_context_manager, level=logging.DEBUG ).__enter__() diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 18587acd46ae..7182ed855afa 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -21,6 +21,7 @@ import glob import logging import os +import pathlib import subprocess import sys import logging @@ -70,50 +71,15 @@ def _make_session(model, target, zephyr_board, west_cmd, mod, build_config): os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) - test_dir = os.path.dirname(os.path.realpath(os.path.expanduser(__file__))) - tvm_source_dir = os.path.join(test_dir, "..", "..", "..") - runtime_path = os.path.join(tvm_source_dir, "apps", "microtvm", "zephyr", "host_driven") - compiler = zephyr.ZephyrCompiler( - project_dir=runtime_path, - board=zephyr_board, - zephyr_toolchain_variant="zephyr", - west_cmd=west_cmd, - ) - - opts = tvm.micro.default_options(os.path.join(runtime_path, "crt")) - # TODO(weberlo) verify this is necessary - opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] - opts["lib_opts"]["ccflags"] = ["-std=gnu++14"] - - flasher_kw = {} - if build_config["debug"]: - flasher_kw["debug_rpc_session"] = tvm.rpc.connect("127.0.0.1", 9090) - - session_kw = { - "flasher": compiler.flasher(**flasher_kw), - } - - if not build_config["skip_build"]: - session_kw["binary"] = tvm.micro.build_static_runtime( - # the x86 compiler *expects* you to give the exact same dictionary for both - # lib_opts and bin_opts. so the library compiler is mutating lib_opts and - # the binary compiler is expecting those mutations to be in bin_opts. - # TODO(weberlo) fix this very bizarre behavior - workspace, - compiler, - mod, - opts, - ) - if os.path.exists(prev_build): - os.unlink(prev_build) - session_kw["binary"].archive(prev_build, metadata_only=True) - else: - unarchive_dir = utils.tempdir() - session_kw["binary"] = tvm.micro.MicroBinary.unarchive( - prev_build, unarchive_dir.relpath("binary") - ) - - return tvm.micro.Session(**session_kw) + template_project_dir = ( + pathlib.Path(__file__).parent / ".." / ".." / ".." / "apps" / "microtvm" / "zephyr" / "demo_runtime").resolve() + project = tvm.micro.generate_project( + str(template_project_dir), mod, workspace.relpath("project"), {"zephyr_board": zephyr_board, + "west_cmd": west_cmd, + "verbose": 1}) + project.build() + project.flash() + return tvm.micro.Session(project.transport()) def _make_add_sess(model, zephyr_board, west_cmd, build_config): diff --git a/tests/python/unittest/test_crt.py b/tests/python/unittest/test_crt.py index 3ba508a40a77..408ad5b890c7 100644 --- a/tests/python/unittest/test_crt.py +++ b/tests/python/unittest/test_crt.py @@ -51,23 +51,11 @@ def _make_sess_from_op(workspace, op_name, sched, arg_bufs): def _make_session(workspace, mod): - compiler = tvm.micro.DefaultCompiler(target=TARGET) - opts = tvm.micro.default_options( - os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") - ) - micro_binary = tvm.micro.build_static_runtime( - workspace, - compiler, - mod, - opts, - extra_libs=[tvm.micro.get_standalone_crt_lib("memory")], - ) - - flasher_kw = { - "debug": DEBUG, - } - flasher = compiler.flasher(**flasher_kw) - return tvm.micro.Session(binary=micro_binary, flasher=flasher) + template_project_dir = os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") + project = tvm.micro.generate_project(template_project_dir, mod, workspace.relpath("project"), {"verbose": 1}) + project.build() + project.flash() + return tvm.micro.Session(project.transport()) def _make_add_sess(workspace): @@ -156,7 +144,7 @@ def @main(%a : Tensor[(1, 2), uint8], %b : Tensor[(1, 2), uint8]) { with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): factory = tvm.relay.build(relay_mod, target=TARGET) - with _make_session(workspace, factory.get_lib()) as sess: + with _make_session(workspace, factory) as sess: graph_mod = tvm.micro.create_local_graph_executor( factory.get_graph_json(), sess.get_system_lib(), sess.device ) From f49f08826418ca9ad89668740fd65b03fae317c6 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:03:17 -0700 Subject: [PATCH 05/47] Rework how crt_config.h is used on the host. * use template crt_config.h for host test runtime; delete src/runtime/crt/host/crt_config.h so that it doesn't diverge from the template * bring template crt_config.h inline with the one actually in use * rename to MAX_STRLEN_DLTYPE * Create a dedicated TVM-side host crt_config.h in src/runtime/micro --- apps/bundle_deploy/crt_config/crt_config.h | 4 ++-- cmake/modules/StandaloneCrt.cmake | 5 ++--- src/runtime/crt/crt_config-template.h | 11 ++++++---- .../crt/graph_executor/graph_executor.c | 22 +++++++++---------- src/runtime/{crt/host => micro}/crt_config.h | 0 src/runtime/micro/micro_session.cc | 2 +- 6 files changed, 23 insertions(+), 21 deletions(-) rename src/runtime/{crt/host => micro}/crt_config.h (100%) diff --git a/apps/bundle_deploy/crt_config/crt_config.h b/apps/bundle_deploy/crt_config/crt_config.h index 11086c0e9a15..58f923512d2e 100644 --- a/apps/bundle_deploy/crt_config/crt_config.h +++ b/apps/bundle_deploy/crt_config/crt_config.h @@ -35,9 +35,9 @@ /*! Maximum supported arguments in generated functions */ #define TVM_CRT_MAX_ARGS 10 /*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ -#define TVM_CRT_STRLEN_DLTYPE 10 +#define TVM_CRT_MAX_STRLEN_DLTYPE 10 /*! Maximum supported string length in function names */ -#define TVM_CRT_STRLEN_NAME 80 +#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 /*! Maximum number of registered modules. */ #define TVM_CRT_MAX_REGISTERED_MODULES 2 diff --git a/cmake/modules/StandaloneCrt.cmake b/cmake/modules/StandaloneCrt.cmake index 09f2ccc95d85..3cd8028b24ac 100644 --- a/cmake/modules/StandaloneCrt.cmake +++ b/cmake/modules/StandaloneCrt.cmake @@ -46,7 +46,6 @@ if(USE_MICRO) "src/runtime/crt/graph_executor *.c -> src/runtime/crt/graph_executor" "src/runtime/crt/aot_executor *.c -> src/runtime/crt/aot_executor" "src/runtime/crt/graph_executor_module *.c -> src/runtime/crt/graph_executor_module" - "src/runtime/crt/host crt_config.h -> template/host" "src/runtime/crt/host *.cc -> template/host" "src/runtime/crt/memory *.c -> src/runtime/crt/memory" "src/runtime/crt/microtvm_rpc_common *.cc -> src/runtime/crt/microtvm_rpc_common" @@ -104,7 +103,7 @@ if(USE_MICRO) endforeach() set(make_common_args - "CRT_CONFIG=template/host/crt_config.h" + "CRT_CONFIG=${CMAKE_SOURCE_DIR}/src/runtime/micro/crt_config.h" "BUILD_DIR=${host_build_dir_abspath}" "EXTRA_CFLAGS=-fPIC" "EXTRA_CXXFLAGS=-fPIC" @@ -145,7 +144,7 @@ if(USE_MICRO) string(REPLACE ".cc" "" __execname ${__srcname}) add_executable(${__execname} ${__srcpath}) list(APPEND TEST_EXECS ${__execname}) - target_include_directories(${__execname} PUBLIC ${GTEST_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/standalone_crt/include ${CMAKE_SOURCE_DIR}/src/runtime/crt/host) + target_include_directories(${__execname} PUBLIC ${GTEST_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/standalone_crt/include ${CMAKE_SOURCE_DIR}/src/runtime/micro) target_compile_options(${__execname} PRIVATE -pthread) target_link_libraries(${__execname} ${cmake_crt_libraries} ${GTEST_LIB} pthread) set_target_properties(${__execname} PROPERTIES EXCLUDE_FROM_ALL 1) diff --git a/src/runtime/crt/crt_config-template.h b/src/runtime/crt/crt_config-template.h index 907559421e5d..7949aea6f171 100644 --- a/src/runtime/crt/crt_config-template.h +++ b/src/runtime/crt/crt_config-template.h @@ -24,6 +24,12 @@ #ifndef TVM_RUNTIME_CRT_CRT_CONFIG_TEMPLATE_H_ #define TVM_RUNTIME_CRT_CRT_CONFIG_TEMPLATE_H_ +/*! Log level of the CRT runtime */ +#define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG + +/*! Support low-level debugging in MISRA-C runtime */ +#define TVM_CRT_DEBUG 0 + /*! Maximum supported dimension in NDArray */ #define TVM_CRT_MAX_NDIM 6 @@ -31,7 +37,7 @@ #define TVM_CRT_MAX_ARGS 10 /*! Size of the global function registry, in bytes. */ -#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 200 +#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 250 /*! Maximum number of registered modules. */ #define TVM_CRT_MAX_REGISTERED_MODULES 2 @@ -48,9 +54,6 @@ /*! \brief Maximum length of a PackedFunc function name. */ #define TVM_CRT_MAX_FUNCTION_NAME_LENGTH_BYTES 30 -/*! \brief DLDataType for the return value from strlen */ -#define TVM_CRT_STRLEN_DLTYPE 10 - /*! \brief Enable checks to enforce the stack allocator with a FIFO ordering. Off by default */ // #define TVM_CRT_STACK_ALLOCATOR_ENABLE_FIFO_CHECK diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index bf64096441be..8b4e20e31edc 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -266,7 +266,7 @@ int TVMGraphExecutorGraphAttr_Load(TVMGraphExecutorGraphAttr* attr, JSONReader* } DLDevice dev = {kDLCPU, 0}; tvm_crt_error_t err = - TVMPlatformMemoryAllocate(TVM_CRT_STRLEN_DLTYPE * num_items, dev, (void**)&attr->dltype); + TVMPlatformMemoryAllocate(TVM_CRT_MAX_STRLEN_DLTYPE * num_items, dev, (void**)&attr->dltype); if (err != kTvmErrorNoError) { fprintf(stderr, "memory allocate error: %08x", err); return -1; @@ -278,8 +278,8 @@ int TVMGraphExecutorGraphAttr_Load(TVMGraphExecutorGraphAttr* attr, JSONReader* status = -1; return status; } - status = reader->ReadString(reader, attr->dltype + dltype_count * TVM_CRT_STRLEN_DLTYPE, - TVM_CRT_STRLEN_DLTYPE); + status = reader->ReadString(reader, attr->dltype + dltype_count * TVM_CRT_MAX_STRLEN_DLTYPE, + TVM_CRT_MAX_STRLEN_DLTYPE); if (status != 0) { fprintf(stderr, "error reading dltype array item"); break; @@ -793,13 +793,13 @@ int TVMGraphExecutor_LoadParams(TVMGraphExecutor* executor, const char* param_bl char* names = NULL; DLDevice dev = {kDLCPU, 0}; tvm_crt_error_t err = - TVMPlatformMemoryAllocate(TVM_CRT_STRLEN_NAME * executor->nodes_count, dev, (void**)&names); + TVMPlatformMemoryAllocate(TVM_CRT_MAX_STRLEN_FUNCTION_NAME * executor->nodes_count, dev, (void**)&names); if (err != kTvmErrorNoError) { fprintf(stderr, "memory allocate error: %08x", err); status = -1; return status; } - memset(names, 0, TVM_CRT_STRLEN_NAME * executor->nodes_count); + memset(names, 0, TVM_CRT_MAX_STRLEN_FUNCTION_NAME * executor->nodes_count); uint64_t names_count; int idx; memcpy(&names_count, bptr, sizeof(names_count)); @@ -808,11 +808,11 @@ int TVMGraphExecutor_LoadParams(TVMGraphExecutor* executor, const char* param_bl uint64_t name_length; memcpy(&name_length, bptr, sizeof(name_length)); bptr += sizeof(name_length); - if (name_length >= TVM_CRT_STRLEN_NAME) { + if (name_length >= TVM_CRT_MAX_STRLEN_FUNCTION_NAME) { fprintf(stderr, "Error: function name longer than expected.\n"); status = -1; } - memcpy(names + TVM_CRT_STRLEN_NAME * idx, bptr, name_length); + memcpy(names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx, bptr, name_length); bptr += name_length; } @@ -827,9 +827,9 @@ int TVMGraphExecutor_LoadParams(TVMGraphExecutor* executor, const char* param_bl } for (idx = 0; idx < size; idx++) { - int32_t in_idx = TVMGraphExecutor_GetInputIndex(executor, names + TVM_CRT_STRLEN_NAME * idx); + int32_t in_idx = TVMGraphExecutor_GetInputIndex(executor, names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx); CHECK_GT(in_idx, 0, "Found param for non-existent input: %s\n", - names + TVM_CRT_STRLEN_NAME * idx); + names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx); uint32_t eid = TVMGraphExecutor_GetEntryId(executor, executor->input_nodes[in_idx], 0); if (!(eid < executor->data_entry_count)) { fprintf(stderr, "`entry_id`=%d is greater than expected(%d).\n", eid, @@ -855,7 +855,7 @@ int TVMGraphExecutor_LoadParams(TVMGraphExecutor* executor, const char* param_bl #if TVM_CRT_DEBUG TVMNDArray* entry = &(executor->data_entry[eid]); printf("loading: param %s loaded, in_idx=%d, eid=%d, ndim=%d, data[0]=%f\n", - names + TVM_CRT_STRLEN_NAME * idx, in_idx, eid, entry->dl_tensor.ndim, + names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx, in_idx, eid, entry->dl_tensor.ndim, ((float*)entry->dl_tensor.data)[0]); // NOLINT(*) #endif // TVM_CRT_DEBUG } @@ -937,7 +937,7 @@ int TVMGraphExecutor_SetupStorage(TVMGraphExecutor* executor) { return -1; } for (idx = 0; idx < attrs->dltype_count; idx++) { - vtype[idx] = String2DLDataType(attrs->dltype + idx * TVM_CRT_STRLEN_DLTYPE); + vtype[idx] = String2DLDataType(attrs->dltype + idx * TVM_CRT_MAX_STRLEN_DLTYPE); } // Size and device type of each storage pool entry. diff --git a/src/runtime/crt/host/crt_config.h b/src/runtime/micro/crt_config.h similarity index 100% rename from src/runtime/crt/host/crt_config.h rename to src/runtime/micro/crt_config.h diff --git a/src/runtime/micro/micro_session.cc b/src/runtime/micro/micro_session.cc index cd916d46971d..0ce07ebff749 100644 --- a/src/runtime/micro/micro_session.cc +++ b/src/runtime/micro/micro_session.cc @@ -37,7 +37,7 @@ #include #include "../../support/str_escape.h" -#include "../crt/host/crt_config.h" +#include "crt_config.h" #include "../rpc/rpc_channel.h" #include "../rpc/rpc_endpoint.h" #include "../rpc/rpc_session.h" From a0f5daf3233b3102be7eefee4c15a9bf51c8244d Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:07:06 -0700 Subject: [PATCH 06/47] Modify Transport infrastructure to work with Project API --- python/tvm/micro/session.py | 11 ++--- python/tvm/micro/transport/base.py | 49 +++---------------- .../crt/microtvm_rpc_common/framing.cc | 20 ++++++++ src/runtime/micro/micro_session.cc | 13 +++-- tests/python/unittest/test_micro_transport.py | 12 ++--- 5 files changed, 45 insertions(+), 60 deletions(-) diff --git a/python/tvm/micro/session.py b/python/tvm/micro/session.py index abe805da68d6..d4ad5b84fb76 100644 --- a/python/tvm/micro/session.py +++ b/python/tvm/micro/session.py @@ -96,12 +96,11 @@ def _wrap_transport_read(self, n, timeout_microsec): return bytes([]) def _wrap_transport_write(self, data, timeout_microsec): - try: - return self.transport.write( - data, float(timeout_microsec) / 1e6 if timeout_microsec is not None else None - ) - except IoTimeoutError: - return 0 + self.transport.write( + data, float(timeout_microsec) / 1e6 if timeout_microsec is not None else None + ) + + return len(data) # TODO(areusch): delete def __enter__(self): """Initialize this session and establish an RPC session with the on-device RPC server. diff --git a/python/tvm/micro/transport/base.py b/python/tvm/micro/transport/base.py index fdc7e9b2afce..6d4205a5ca5e 100644 --- a/python/tvm/micro/transport/base.py +++ b/python/tvm/micro/transport/base.py @@ -23,45 +23,9 @@ import string import typing -_LOG = logging.getLogger(__name__) - - -class TransportClosedError(Exception): - """Raised when a transport can no longer be used due to underlying I/O problems.""" - - -class IoTimeoutError(Exception): - """Raised when the I/O operation could not be completed before the timeout. - - Specifically: - - when no data could be read before the timeout - - when some of the write data could be written before the timeout - - Note the asymmetric behavior of read() vs write(), since in one case the total length of the - data to transfer is known. - """ +from .project_api.server import IoTimeoutError, TransportClosedError, TransportTimeouts - -# Timeouts supported by the underlying C++ MicroSession. -# -# session_start_retry_timeout_sec : float -# Number of seconds to wait for the device to send a kSessionStartReply after sending the -# initial session start message. After this time elapses another -# kSessionTerminated-kSessionStartInit train is sent. 0 disables this. -# session_start_timeout_sec : float -# Total number of seconds to wait for the session to be established. After this time, the -# client gives up trying to establish a session and raises an exception. -# session_established_timeout_sec : float -# Number of seconds to wait for a reply message after a session has been established. 0 -# disables this. -TransportTimeouts = collections.namedtuple( - "TransportTimeouts", - [ - "session_start_retry_timeout_sec", - "session_start_timeout_sec", - "session_established_timeout_sec", - ], -) +_LOG = logging.getLogger(__name__) def debug_transport_timeouts(session_start_retry_timeout_sec=0): @@ -263,7 +227,7 @@ def read(self, n, timeout_sec): def write(self, data, timeout_sec): timeout_str = f"{timeout_sec:5.2f}s" if timeout_sec is not None else " None " try: - bytes_written = self.child.write(data, timeout_sec) + self.child.write(data, timeout_sec) except IoTimeoutError: self.logger.log( self.level, @@ -286,14 +250,14 @@ def write(self, data, timeout_sec): ) raise err - hex_lines = self._to_hex(data[:bytes_written]) + hex_lines = self._to_hex(data) if len(hex_lines) > 1: self.logger.log( self.level, "%s: write {%s} <- [%3d B]:\n%s", self.name, timeout_str, - bytes_written, + len(data), "\n".join(hex_lines), ) else: @@ -302,11 +266,10 @@ def write(self, data, timeout_sec): "%s: write {%s} <- [%3d B]: %s", self.name, timeout_str, - bytes_written, + len(data), hex_lines[0], ) - return bytes_written TransportContextManager = typing.ContextManager[Transport] diff --git a/src/runtime/crt/microtvm_rpc_common/framing.cc b/src/runtime/crt/microtvm_rpc_common/framing.cc index f89c6e5688c0..87f152d6aaf0 100644 --- a/src/runtime/crt/microtvm_rpc_common/framing.cc +++ b/src/runtime/crt/microtvm_rpc_common/framing.cc @@ -66,6 +66,26 @@ void Unframer::Reset() { num_buffer_bytes_valid_ = 0; } +size_t Unframer::BytesNeeded() { + size_t bytes_needed = 0; + switch (state_) { + case State::kFindPacketStart: + return 1; + case State::kFindPacketLength: + bytes_needed = PacketFieldSizeBytes::kPayloadLength; + break; + case State::kFindPacketCrc: + return num_payload_bytes_remaining_; + case State::kFindCrcEnd: + bytes_needed = PacketFieldSizeBytes::kCrc; + break; + default: + CHECK(false); + } + + return bytes_needed > num_buffer_bytes_valid_ ? bytes_needed - num_buffer_bytes_valid_ : 0; +} + tvm_crt_error_t Unframer::Write(const uint8_t* data, size_t data_size_bytes, size_t* bytes_consumed) { tvm_crt_error_t return_code = kTvmErrorNoError; diff --git a/src/runtime/micro/micro_session.cc b/src/runtime/micro/micro_session.cc index 0ce07ebff749..f18177d4c4cc 100644 --- a/src/runtime/micro/micro_session.cc +++ b/src/runtime/micro/micro_session.cc @@ -56,10 +56,12 @@ class CallbackWriteStream : public WriteStream { bytes.data = (const char*)data; bytes.size = data_size_bytes; if (write_timeout_ == ::std::chrono::microseconds::zero()) { - return static_cast(fsend_(bytes, nullptr)); + fsend_(bytes, nullptr); } else { - return static_cast(fsend_(bytes, write_timeout_.count())); + fsend_(bytes, write_timeout_.count()); } + + return static_cast(data_size_bytes); } void PacketDone(bool is_valid) override {} @@ -143,15 +145,16 @@ class MicroTransportChannel : public RPCChannel { } ::std::string chunk; + size_t bytes_needed = unframer_.BytesNeeded(); + CHECK_GT(bytes_needed, 0) << "unframer unexpectedly needs no data"; if (timeout != nullptr) { ::std::chrono::microseconds iter_timeout{ ::std::max(::std::chrono::microseconds{0}, ::std::chrono::duration_cast<::std::chrono::microseconds>( end_time - ::std::chrono::steady_clock::now()))}; - chunk = - frecv_(size_t(kReceiveBufferSizeBytes), iter_timeout.count()).operator std::string(); + chunk = frecv_(bytes_needed, iter_timeout.count()).operator std::string(); } else { - chunk = frecv_(size_t(kReceiveBufferSizeBytes), nullptr).operator std::string(); + chunk = frecv_(bytes_needed, nullptr).operator std::string(); } pending_chunk_ = chunk; if (pending_chunk_.size() == 0) { diff --git a/tests/python/unittest/test_micro_transport.py b/tests/python/unittest/test_micro_transport.py index b0f99681af2e..a188e612763f 100644 --- a/tests/python/unittest/test_micro_transport.py +++ b/tests/python/unittest/test_micro_transport.py @@ -132,25 +132,25 @@ def test_transport_logger(self): transport.to_return = 3 transport_logger.write(b"data", 3.0) assert test_log.records[-1].getMessage() == ( - "foo: write { 3.00s} <- [ 3 B]: 64 61 74 " - " dat" + "foo: write { 3.00s} <- [ 4 B]: 64 61 74 61" + " data" ) # Normal log, multi-line data written. transport.to_return = 20 transport_logger.write(b"data" * 6, 3.0) assert test_log.records[-1].getMessage() == ( - "foo: write { 3.00s} <- [ 20 B]:\n" + "foo: write { 3.00s} <- [ 24 B]:\n" "0000 64 61 74 61 64 61 74 61 64 61 74 61 64 61 74 61 datadatadatadata\n" - "0010 64 61 74 61 data" + "0010 64 61 74 61 64 61 74 61 datadata" ) # Lack of timeout prints. transport.to_return = 3 transport_logger.write(b"data", None) assert test_log.records[-1].getMessage() == ( - "foo: write { None } <- [ 3 B]: 64 61 74 " - " dat" + "foo: write { None } <- [ 4 B]: 64 61 74 61" + " data" ) # IoTimeoutError includes the timeout value. From c7ad52e2e8469c9f07baa6b71cba37c8e46510f4 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:00:38 -0700 Subject: [PATCH 07/47] Add host microTVM API server --- cmake/modules/StandaloneCrt.cmake | 2 + src/runtime/crt/host/Makefile | 76 +++++++++ src/runtime/crt/host/microtvm_api_server.py | 173 ++++++++++++++++++++ src/runtime/micro/crt_config.h | 4 +- 4 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 src/runtime/crt/host/Makefile create mode 100644 src/runtime/crt/host/microtvm_api_server.py diff --git a/cmake/modules/StandaloneCrt.cmake b/cmake/modules/StandaloneCrt.cmake index 3cd8028b24ac..32d7e59f2058 100644 --- a/cmake/modules/StandaloneCrt.cmake +++ b/cmake/modules/StandaloneCrt.cmake @@ -47,6 +47,8 @@ if(USE_MICRO) "src/runtime/crt/aot_executor *.c -> src/runtime/crt/aot_executor" "src/runtime/crt/graph_executor_module *.c -> src/runtime/crt/graph_executor_module" "src/runtime/crt/host *.cc -> template/host" + "src/runtime/crt/host *.py -> template/host" + "src/runtime/crt/host Makefile -> template/host" "src/runtime/crt/memory *.c -> src/runtime/crt/memory" "src/runtime/crt/microtvm_rpc_common *.cc -> src/runtime/crt/microtvm_rpc_common" "src/runtime/crt/microtvm_rpc_server *.cc -> src/runtime/crt/microtvm_rpc_server" diff --git a/src/runtime/crt/host/Makefile b/src/runtime/crt/host/Makefile new file mode 100644 index 000000000000..ef094cb084ac --- /dev/null +++ b/src/runtime/crt/host/Makefile @@ -0,0 +1,76 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +INCLUDES ?= -isystem crt/include -Icrt_config +CFLAGS ?= -Werror -Wall +CXXFLAGS ?= -Werror -Wall -std=c++11 +LDFLAGS ?= -Werror -Wall + +# Codegen produces spurious lines like: int32_t arg2_code = ((int32_t*)arg_type_ids)[(2)]; +MODEL_CFLAGS ?= -Wno-error=unused-variable + +AR ?= ${PREFIX}ar +CC ?= ${PREFIX}gcc +CXX ?= ${PREFIX}g++ +RANLIB ?= ${PREFIX}ranlib + +QUIET ?= @ + +PWD = $(shell pwd) +BUILD_DIR = build +CRT_LIB_NAMES = utvm_rpc_server utvm_rpc_common graph_executor graph_executor_module common memory +CRT_LIBS = $(patsubst %, $(BUILD_DIR)/crt/lib%.a, $(CRT_LIB_NAMES)) + +CRT_INCLUDES = $(glob crt/include/**) + +$(BUILD_DIR)/crt/lib%.a: $(glob crt/src/runtime/%/*.c) + ${QUIET}cd crt && $(MAKE) \ + BUILD_DIR=../$(BUILD_DIR)/crt \ + CRT_CONFIG=$(PWD)/crt_config/crt_config.h \ + EXTRA_CFLAGS="$(CFLAGS)" \ + EXTRA_CXXFLAGS="$(CXXFLAGS)" \ + EXTRA_LDFLAGS="$(EXTRA_LDFLAGS)" \ + $(patsubst $(BUILD_DIR)/crt/lib%.a,%,$@) + +crt: $(CRT_LIBS) +.PHONY: crt + +# Compile codegen files +$(BUILD_DIR)/model/codegen/host/%.o: model/codegen/host/%.c + ${QUIET}mkdir -p $(dir $@) + ${QUIET}$(CC) $(INCLUDES) $(CFLAGS) $(MODEL_CFLAGS) -c -o "$@" "$<" + +MODEL_LIBS = \ + $(patsubst model/codegen/host/src/%.c, $(BUILD_DIR)/model/codegen/host/src/%.o, $(wildcard model/codegen/host/src/*.c)) \ + $(wildcard model/codegen/host/lib/*.o) + +# Compile src/ files +build/%.o: src/%.cc + ${QUIET}mkdir -p $(dir $@) + ${QUIET}$(CXX) $(INCLUDES) $(CXXFLAGS) -c -o "$@" "$<" + +SRCS = $(wildcard src/*.cc) +OBJS = $(patsubst src/%.cc,build/%.o,$(SRCS)) + +build/main: ${OBJS} ${MODEL_LIBS} ${CRT_LIBS} + ${QUIET}mkdir -p $(dir $@) + ${QUIET}$(CXX) $(LDFLAGS) -o "$@" $^ + +all: build/main +.PHONY = all + +.DEFAULT_GOAL = all diff --git a/src/runtime/crt/host/microtvm_api_server.py b/src/runtime/crt/host/microtvm_api_server.py new file mode 100644 index 000000000000..225723d243ce --- /dev/null +++ b/src/runtime/crt/host/microtvm_api_server.py @@ -0,0 +1,173 @@ +import fcntl +import os +import os.path +import pathlib +import select +import shutil +import subprocess +import tarfile +import time +from tvm.micro.project_api import server + + +PROJECT_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) + + +MODEL_LIBRARY_FORMAT_RELPATH = "model.tar" + + +IS_TEMPLATE = not os.path.exists(os.path.join(PROJECT_DIR, MODEL_LIBRARY_FORMAT_RELPATH)) + + +class Handler(server.ProjectAPIHandler): + + BUILD_TARGET = "build/main" + + def __init__(self): + super(Handler, self).__init__() + self._proc = None + + def server_info_query(self): + return server.ServerInfo( + platform_name="host", + is_template=IS_TEMPLATE, + model_library_format_path="" if IS_TEMPLATE else PROJECT_DIR / MODEL_LIBRARY_FORMAT_RELPATH, + project_options=[server.ProjectOption("verbose", help="Run make with verbose output")]) + + # These files and directories will be recursively copied into generated projects from the CRT. + CRT_COPY_ITEMS = ("include", "Makefile", "src") + + # The build target given to make + BUILD_TARGET = "build/main" + + def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): + # Make project directory. + project_dir.mkdir(parents=True) + + # Copy ourselves to the generated project. TVM may perform further build steps on the generated project + # by launching the copy. + shutil.copy2(__file__, project_dir / os.path.basename(__file__)) + + # Place Model Library Format tarball in the special location, which this script uses to decide + # whether it's being invoked in a template or generated project. + project_model_library_format_path = project_dir / MODEL_LIBRARY_FORMAT_RELPATH + shutil.copy2(model_library_format_path, project_model_library_format_path) + + # Extract Model Library Format tarball.into /model. + extract_path = project_dir / project_model_library_format_path.stem + with tarfile.TarFile(project_model_library_format_path) as tf: + os.makedirs(extract_path) + tf.extractall(path=extract_path) + + # Populate CRT. + crt_path = project_dir / "crt" + os.mkdir(crt_path) + for item in self.CRT_COPY_ITEMS: + src_path = standalone_crt_dir / item + dst_path = crt_path / item + if os.path.isdir(src_path): + shutil.copytree(src_path, dst_path) + else: + shutil.copy2(src_path, dst_path) + + # Populate Makefile. + shutil.copy2(pathlib.Path(__file__).parent / "Makefile", project_dir / "Makefile") + + # Populate crt-config.h + crt_config_dir = project_dir / "crt_config" + crt_config_dir.mkdir() + shutil.copy2(os.path.join(os.path.dirname(__file__), "..", "crt_config-template.h"), + os.path.join(crt_config_dir, "crt_config.h")) + + # Populate src/ + src_dir = os.path.join(project_dir, "src") + os.mkdir(src_dir) + shutil.copy2(os.path.join(os.path.dirname(__file__), "main.cc"), os.path.join(src_dir, "main.cc")) + + def build(self, options): + args = ["make"] + if options.get("verbose"): + args.append("QUIET=") + + args.append(self.BUILD_TARGET) + + subprocess.check_call(args, cwd=PROJECT_DIR) + + def flash(self, options): + pass # Flashing does nothing on host. + + def _set_nonblock(self, fd): + flag = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK) + new_flag = fcntl.fcntl(fd, fcntl.F_GETFL) + assert (new_flag & os.O_NONBLOCK) != 0, "Cannot set file descriptor {fd} to non-blocking" + + def open_transport(self, options): + self._proc = subprocess.Popen([self.BUILD_TARGET], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0) + self._set_nonblock(self._proc.stdin.fileno()) + self._set_nonblock(self._proc.stdout.fileno()) + return server.TransportTimeouts(session_start_retry_timeout_sec=0, + session_start_timeout_sec=0, + session_established_timeout_sec=0) + + def close_transport(self): + if self._proc is not None: + proc = self._proc + self._proc = None + proc.terminate() + proc.wait() + + def _await_ready(self, rlist, wlist, timeout_sec=None, end_time=None): + if timeout_sec is None and end_time is not None: + timeout_sec = max(0, end_time - time.monotonic()) + + rlist, wlist, xlist = select.select(rlist, wlist, rlist + wlist, timeout_sec) + if not rlist and not wlist and not xlist: + raise server.IoTimeoutError() + + return True + + def read_transport(self, n, timeout_sec): + if self._proc is None: + raise server.TransportClosedError() + + fd = self._proc.stdout.fileno() + end_time = None if timeout_sec is None else time.monotonic() + timeout_sec + + try: + self._await_ready([fd], [], end_time=end_time) + to_return = os.read(fd, n) + except BrokenPipeError: + to_return = 0 + + if not to_return: + self.disconnect_transport() + raise server.TransportClosedError() + + return to_return + + def write_transport(self, data, timeout_sec): + if self._proc is None: + raise server.TransportClosedError() + + fd = self._proc.stdin.fileno() + end_time = None if timeout_sec is None else time.monotonic() + timeout_sec + + data_len = len(data) + while data: + self._await_ready([], [fd], end_time=end_time) + try: + num_written = os.write(fd, data) + except BrokenPipeError: + num_written = 0 + + if not num_written: + self.disconnect_transport() + raise server.TransportClosedError() + + data = data[num_written:] + + + +if __name__ == '__main__': + server.main(Handler()) diff --git a/src/runtime/micro/crt_config.h b/src/runtime/micro/crt_config.h index b81a74eb4ae6..ab6e277b150f 100644 --- a/src/runtime/micro/crt_config.h +++ b/src/runtime/micro/crt_config.h @@ -35,9 +35,9 @@ /*! Maximum supported arguments in generated functions */ #define TVM_CRT_MAX_ARGS 10 /*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ -#define TVM_CRT_STRLEN_DLTYPE 10 +#define TVM_CRT_MAX_STRLEN_DLTYPE 10 /*! Maximum supported string length in function names */ -#define TVM_CRT_STRLEN_NAME 80 +#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 /*! Maximum number of registered modules. */ #define TVM_CRT_MAX_REGISTERED_MODULES 2 From af6f690f8222234dc6b53bbd0e724bf1faf934d7 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:24:12 -0700 Subject: [PATCH 08/47] Zephyr implementation of microTVM API server * move all zephyr projects to apps/microtvm/zephyr/template_project --- apps/microtvm/zephyr/aot_demo/CMakeLists.txt | 27 - apps/microtvm/zephyr/aot_demo/README.md | 20 - .../zephyr/aot_demo/boards/mps2_an521.conf | 28 - .../boards/nrf5340dk_nrf5340_cpuapp.conf | 34 - .../zephyr/aot_demo/boards/nucleo_l4r5zi.conf | 31 - .../zephyr/aot_demo/boards/qemu_x86.conf | 28 - apps/microtvm/zephyr/aot_demo/prj.conf | 32 - apps/microtvm/zephyr/aot_demo/qemu-hack | 1 - .../zephyr/host_driven/CMakeLists.txt | 26 - .../zephyr/host_driven/boards/mps2_an521.conf | 28 - .../boards/nrf5340dk_nrf5340_cpuapp.conf | 34 - .../host_driven/boards/nucleo_f746zg.conf | 33 - .../host_driven/boards/qemu_riscv32.conf | 32 - .../host_driven/boards/qemu_riscv64.conf | 28 - .../zephyr/host_driven/boards/qemu_x86.conf | 25 - .../host_driven/boards/stm32f746g_disco.conf | 31 - apps/microtvm/zephyr/host_driven/prj.conf | 32 - apps/microtvm/zephyr/host_driven/qemu-hack | 1 - .../template_project/CMakeLists.txt.template | 32 + .../README.md | 0 .../crt/crt_config.h | 0 .../crt_config}/crt_config.h | 4 +- .../template_project/microtvm_api_server.py | 663 ++++++++++++++++++ .../qemu-hack/qemu-system-arm | 0 .../qemu-hack/qemu-system-i386 | 4 +- .../qemu-hack/qemu-system-riscv32 | 0 .../qemu-hack/qemu-system-riscv64 | 0 .../src/aot_demo}/main.c | 3 +- .../src/aot_demo}/zephyr_uart.c | 0 .../src/aot_demo}/zephyr_uart.h | 0 .../src/host_driven}/main.c | 0 src/runtime/crt/host/Makefile | 2 +- tests/micro/zephyr/conftest.py | 2 +- tests/micro/zephyr/test_zephyr.py | 2 +- 34 files changed, 704 insertions(+), 479 deletions(-) delete mode 100644 apps/microtvm/zephyr/aot_demo/CMakeLists.txt delete mode 100644 apps/microtvm/zephyr/aot_demo/README.md delete mode 100644 apps/microtvm/zephyr/aot_demo/boards/mps2_an521.conf delete mode 100644 apps/microtvm/zephyr/aot_demo/boards/nrf5340dk_nrf5340_cpuapp.conf delete mode 100644 apps/microtvm/zephyr/aot_demo/boards/nucleo_l4r5zi.conf delete mode 100644 apps/microtvm/zephyr/aot_demo/boards/qemu_x86.conf delete mode 100644 apps/microtvm/zephyr/aot_demo/prj.conf delete mode 120000 apps/microtvm/zephyr/aot_demo/qemu-hack delete mode 100644 apps/microtvm/zephyr/host_driven/CMakeLists.txt delete mode 100644 apps/microtvm/zephyr/host_driven/boards/mps2_an521.conf delete mode 100644 apps/microtvm/zephyr/host_driven/boards/nrf5340dk_nrf5340_cpuapp.conf delete mode 100644 apps/microtvm/zephyr/host_driven/boards/nucleo_f746zg.conf delete mode 100644 apps/microtvm/zephyr/host_driven/boards/qemu_riscv32.conf delete mode 100644 apps/microtvm/zephyr/host_driven/boards/qemu_riscv64.conf delete mode 100644 apps/microtvm/zephyr/host_driven/boards/qemu_x86.conf delete mode 100644 apps/microtvm/zephyr/host_driven/boards/stm32f746g_disco.conf delete mode 100644 apps/microtvm/zephyr/host_driven/prj.conf delete mode 120000 apps/microtvm/zephyr/host_driven/qemu-hack create mode 100644 apps/microtvm/zephyr/template_project/CMakeLists.txt.template rename apps/microtvm/zephyr/{host_driven => template_project}/README.md (100%) rename apps/microtvm/zephyr/{host_driven => template_project}/crt/crt_config.h (100%) rename apps/microtvm/zephyr/{aot_demo/crt => template_project/crt_config}/crt_config.h (96%) create mode 100644 apps/microtvm/zephyr/template_project/microtvm_api_server.py rename apps/microtvm/zephyr/{ => template_project}/qemu-hack/qemu-system-arm (100%) rename apps/microtvm/zephyr/{ => template_project}/qemu-hack/qemu-system-i386 (91%) rename apps/microtvm/zephyr/{ => template_project}/qemu-hack/qemu-system-riscv32 (100%) rename apps/microtvm/zephyr/{ => template_project}/qemu-hack/qemu-system-riscv64 (100%) rename apps/microtvm/zephyr/{aot_demo/src => template_project/src/aot_demo}/main.c (97%) rename apps/microtvm/zephyr/{aot_demo/src => template_project/src/aot_demo}/zephyr_uart.c (100%) rename apps/microtvm/zephyr/{aot_demo/include => template_project/src/aot_demo}/zephyr_uart.h (100%) rename apps/microtvm/zephyr/{host_driven/src => template_project/src/host_driven}/main.c (100%) diff --git a/apps/microtvm/zephyr/aot_demo/CMakeLists.txt b/apps/microtvm/zephyr/aot_demo/CMakeLists.txt deleted file mode 100644 index d7ec2a25db14..000000000000 --- a/apps/microtvm/zephyr/aot_demo/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.13.1) - -set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") - -set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. - -find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) -project(microtvm_zephyr_runtime) - -set(CMAKE_VERBOSE_MAKEFILE ON) - -target_sources(app PRIVATE src/zephyr_uart.c) -target_sources(app PRIVATE src/main.c) - -foreach(tvm_lib ${TVM_LIBS}) - string(LENGTH ${tvm_lib} tvm_lib_length) - math(EXPR tvm_lib_cut "${tvm_lib_length} - 2") - string(SUBSTRING ${tvm_lib} 3 ${tvm_lib_cut} tvm_lib_name) - add_library(${tvm_lib_name} STATIC IMPORTED) - set_target_properties(${tvm_lib_name} PROPERTIES - IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/${tvm_lib}) - target_link_libraries(app PRIVATE ${tvm_lib_name}) -endforeach(tvm_lib ${TVM_LIBS}) - -target_include_directories(app PRIVATE ${TVM_INCLUDE_DIRS}) diff --git a/apps/microtvm/zephyr/aot_demo/README.md b/apps/microtvm/zephyr/aot_demo/README.md deleted file mode 100644 index a718da65e2fa..000000000000 --- a/apps/microtvm/zephyr/aot_demo/README.md +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - -This directory contains a Zephyr-based ahead of time (AOT) "demo" runtime environment that -pulls together the microTVM runtime dependencies into a single application -that can run TVM on a microTVM device without the need to a host. diff --git a/apps/microtvm/zephyr/aot_demo/boards/mps2_an521.conf b/apps/microtvm/zephyr/aot_demo/boards/mps2_an521.conf deleted file mode 100644 index 3916b17c49cf..000000000000 --- a/apps/microtvm/zephyr/aot_demo/boards/mps2_an521.conf +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the MPS2-AN512 board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# For debugging. -CONFIG_LED=n diff --git a/apps/microtvm/zephyr/aot_demo/boards/nrf5340dk_nrf5340_cpuapp.conf b/apps/microtvm/zephyr/aot_demo/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 6c588c86b0d5..000000000000 --- a/apps/microtvm/zephyr/aot_demo/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the nRF5340 DK board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# For AOT runtime which requires lots of function call. -CONFIG_MAIN_STACK_SIZE=2000 - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# For debugging. -CONFIG_LED=y - -# For models with floating point. -CONFIG_FPU=y diff --git a/apps/microtvm/zephyr/aot_demo/boards/nucleo_l4r5zi.conf b/apps/microtvm/zephyr/aot_demo/boards/nucleo_l4r5zi.conf deleted file mode 100644 index 52a6753c733b..000000000000 --- a/apps/microtvm/zephyr/aot_demo/boards/nucleo_l4r5zi.conf +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the STM32L4R5ZI Nucleo board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# For AOT runtime which requires lots of function call. -CONFIG_MAIN_STACK_SIZE=3000 - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# For debugging. -CONFIG_LED=y diff --git a/apps/microtvm/zephyr/aot_demo/boards/qemu_x86.conf b/apps/microtvm/zephyr/aot_demo/boards/qemu_x86.conf deleted file mode 100644 index 4b0e494068fa..000000000000 --- a/apps/microtvm/zephyr/aot_demo/boards/qemu_x86.conf +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# This file is specific to the QEMU-emulated microTVM board. - -# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_TIMER_RANDOM_GENERATOR=y - -# Default stack size is 1k, this is required for debug mode. -CONFIG_MAIN_STACK_SIZE=2000 - -# For models with floating point. -CONFIG_FPU=y diff --git a/apps/microtvm/zephyr/aot_demo/prj.conf b/apps/microtvm/zephyr/aot_demo/prj.conf deleted file mode 100644 index c6ab10e9d86e..000000000000 --- a/apps/microtvm/zephyr/aot_demo/prj.conf +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# The settings in this file are generic for all boards, and are merged -# with the settings in the file boards/.conf by the Zephyr build -# process. - -# For UART implementation in main(). -CONFIG_RING_BUFFER=y -CONFIG_UART_CONSOLE=n -CONFIG_UART_INTERRUPT_DRIVEN=y - -# For RPC server C++ bindings. -CONFIG_CPLUSPLUS=y -CONFIG_NEWLIB_LIBC=y - -# For TVMPlatformAbort(). -CONFIG_REBOOT=y diff --git a/apps/microtvm/zephyr/aot_demo/qemu-hack b/apps/microtvm/zephyr/aot_demo/qemu-hack deleted file mode 120000 index b4810f2aab6e..000000000000 --- a/apps/microtvm/zephyr/aot_demo/qemu-hack +++ /dev/null @@ -1 +0,0 @@ -../qemu-hack \ No newline at end of file diff --git a/apps/microtvm/zephyr/host_driven/CMakeLists.txt b/apps/microtvm/zephyr/host_driven/CMakeLists.txt deleted file mode 100644 index f04a792086cb..000000000000 --- a/apps/microtvm/zephyr/host_driven/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.13.1) - -set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") - -set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. - -find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) -project(microtvm_zephyr_runtime) - -set(CMAKE_VERBOSE_MAKEFILE ON) - -target_sources(app PRIVATE src/main.c) - -foreach(tvm_lib ${TVM_LIBS}) - string(LENGTH ${tvm_lib} tvm_lib_length) - math(EXPR tvm_lib_cut "${tvm_lib_length} - 2") - string(SUBSTRING ${tvm_lib} 3 ${tvm_lib_cut} tvm_lib_name) - add_library(${tvm_lib_name} STATIC IMPORTED) - set_target_properties(${tvm_lib_name} PROPERTIES - IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/${tvm_lib}) - target_link_libraries(app PRIVATE ${tvm_lib_name}) -endforeach(tvm_lib ${TVM_LIBS}) - -target_include_directories(app PRIVATE ${TVM_INCLUDE_DIRS}) diff --git a/apps/microtvm/zephyr/host_driven/boards/mps2_an521.conf b/apps/microtvm/zephyr/host_driven/boards/mps2_an521.conf deleted file mode 100644 index 3916b17c49cf..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/mps2_an521.conf +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the MPS2-AN512 board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# For debugging. -CONFIG_LED=n diff --git a/apps/microtvm/zephyr/host_driven/boards/nrf5340dk_nrf5340_cpuapp.conf b/apps/microtvm/zephyr/host_driven/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 511ff0121d32..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the nRF5340 DK board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# Required for Cortex-M33 devices. -CONFIG_MAIN_STACK_SIZE=1536 - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# For debugging. -CONFIG_LED=y - -# For models with floating point. -CONFIG_FPU=y diff --git a/apps/microtvm/zephyr/host_driven/boards/nucleo_f746zg.conf b/apps/microtvm/zephyr/host_driven/boards/nucleo_f746zg.conf deleted file mode 100644 index 33b08032c32e..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/nucleo_f746zg.conf +++ /dev/null @@ -1,33 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the STM32F746 Nucleo board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# For operations that stack allocates a large float array. -CONFIG_MAIN_STACK_SIZE=1536 - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y - -# For debugging. -CONFIG_LED=y - -# For models with floating point. -CONFIG_FPU=y diff --git a/apps/microtvm/zephyr/host_driven/boards/qemu_riscv32.conf b/apps/microtvm/zephyr/host_driven/boards/qemu_riscv32.conf deleted file mode 100644 index b94d96b11fba..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/qemu_riscv32.conf +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# This file is specific to the QEMU-emulated RISCV32 microTVM board. - -# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_TIMER_RANDOM_GENERATOR=y - -# Default is 512, raised here for operations with large floating point data. -CONFIG_MAIN_STACK_SIZE=2048 - -# For models with floating point. -CONFIG_FPU=y - -# For floating point operations. It has exception on floating point operations -# without this flag. -CONFIG_FPU_SHARING=y diff --git a/apps/microtvm/zephyr/host_driven/boards/qemu_riscv64.conf b/apps/microtvm/zephyr/host_driven/boards/qemu_riscv64.conf deleted file mode 100644 index 1da5f054da46..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/qemu_riscv64.conf +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# This file is specific to the QEMU-emulated RISCV64 microTVM board. - -# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_TIMER_RANDOM_GENERATOR=y - -# Default 512, for operations with large floating point data. -CONFIG_MAIN_STACK_SIZE=2048 - -# For models with floating point. -CONFIG_FPU=y diff --git a/apps/microtvm/zephyr/host_driven/boards/qemu_x86.conf b/apps/microtvm/zephyr/host_driven/boards/qemu_x86.conf deleted file mode 100644 index f314f59a597a..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/qemu_x86.conf +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# This file is specific to the QEMU-emulated microTVM board. - -# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_TIMER_RANDOM_GENERATOR=y - -# Default stack size is 1k, this is required for debug mode. -CONFIG_MAIN_STACK_SIZE=1536 diff --git a/apps/microtvm/zephyr/host_driven/boards/stm32f746g_disco.conf b/apps/microtvm/zephyr/host_driven/boards/stm32f746g_disco.conf deleted file mode 100644 index 542faf28cd67..000000000000 --- a/apps/microtvm/zephyr/host_driven/boards/stm32f746g_disco.conf +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# This file is specific to the STM32F746G Discovery board. - -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y -CONFIG_TEST_RANDOM_GENERATOR=y - -# For debugging. -CONFIG_LED=n - -# For models with floating point. -CONFIG_FPU=y diff --git a/apps/microtvm/zephyr/host_driven/prj.conf b/apps/microtvm/zephyr/host_driven/prj.conf deleted file mode 100644 index c6ab10e9d86e..000000000000 --- a/apps/microtvm/zephyr/host_driven/prj.conf +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# The settings in this file are generic for all boards, and are merged -# with the settings in the file boards/.conf by the Zephyr build -# process. - -# For UART implementation in main(). -CONFIG_RING_BUFFER=y -CONFIG_UART_CONSOLE=n -CONFIG_UART_INTERRUPT_DRIVEN=y - -# For RPC server C++ bindings. -CONFIG_CPLUSPLUS=y -CONFIG_NEWLIB_LIBC=y - -# For TVMPlatformAbort(). -CONFIG_REBOOT=y diff --git a/apps/microtvm/zephyr/host_driven/qemu-hack b/apps/microtvm/zephyr/host_driven/qemu-hack deleted file mode 120000 index b4810f2aab6e..000000000000 --- a/apps/microtvm/zephyr/host_driven/qemu-hack +++ /dev/null @@ -1 +0,0 @@ -../qemu-hack \ No newline at end of file diff --git a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template new file mode 100644 index 000000000000..6bfea191cb99 --- /dev/null +++ b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") + +set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(microtvm_autogenerated_project) + +set(CRT_LIBS ) +set(CRT_LIB_BASE crt/src/runtime/crt) +foreach(crt_lib_name ${CRT_LIBS}) + zephyr_library_named(${crt_lib_name}) + file(GLOB_RECURSE crt_lib_srcs ${CRT_LIB_BASE}/${crt_lib_name}/*.c ${CRT_LIB_BASE}/${crt_lib_name}/*.cc) + target_sources(${crt_lib_name} PRIVATE ${crt_lib_srcs}) + zephyr_library_include_directories(${crt_lib_name} PRIVATE crt_config crt/include) + target_link_libraries(app PRIVATE ${crt_lib_name}) +endforeach(crt_lib_name ${CRT_LIBS}) + +# define a library for the model sources. +zephyr_library_named(tvm_model) +file(GLOB_RECURSE tvm_model_srcs model/codegen/host/src/*.c model/codegen/host/lib/*.o) +target_sources(tvm_model PRIVATE ${tvm_model_srcs}) +target_include_directories(tvm_model PRIVATE crt_config crt/include) +target_compile_options(tvm_model PRIVATE -Wno-unused-variable) # TVM-generated code tends to include lots of these. +target_link_libraries(app PRIVATE tvm_model) + +file(GLOB_RECURSE app_srcs src/**.c) +target_sources(app PRIVATE ${app_srcs}) +target_include_directories(app PRIVATE crt_config ${CMAKE_SOURCE_DIR}/include crt/include) diff --git a/apps/microtvm/zephyr/host_driven/README.md b/apps/microtvm/zephyr/template_project/README.md similarity index 100% rename from apps/microtvm/zephyr/host_driven/README.md rename to apps/microtvm/zephyr/template_project/README.md diff --git a/apps/microtvm/zephyr/host_driven/crt/crt_config.h b/apps/microtvm/zephyr/template_project/crt/crt_config.h similarity index 100% rename from apps/microtvm/zephyr/host_driven/crt/crt_config.h rename to apps/microtvm/zephyr/template_project/crt/crt_config.h diff --git a/apps/microtvm/zephyr/aot_demo/crt/crt_config.h b/apps/microtvm/zephyr/template_project/crt_config/crt_config.h similarity index 96% rename from apps/microtvm/zephyr/aot_demo/crt/crt_config.h rename to apps/microtvm/zephyr/template_project/crt_config/crt_config.h index 9ee315aa1763..f8fc7514a28d 100644 --- a/apps/microtvm/zephyr/aot_demo/crt/crt_config.h +++ b/apps/microtvm/zephyr/template_project/crt_config/crt_config.h @@ -42,7 +42,7 @@ #define TVM_CRT_MAX_REGISTERED_MODULES 2 /*! Maximum packet size, in bytes, including the length header. */ -#define TVM_CRT_MAX_PACKET_SIZE_BYTES (1 * 1024) +#define TVM_CRT_MAX_PACKET_SIZE_BYTES 8192 /*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ #define TVM_CRT_MAX_STRLEN_DLTYPE 10 @@ -59,4 +59,6 @@ /*! \brief Number of pages on device. */ #define TVM_CRT_MAX_PAGES 300 +// #define TVM_CRT_FRAMER_ENABLE_LOGS + #endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py new file mode 100644 index 000000000000..c48a3a22447a --- /dev/null +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -0,0 +1,663 @@ +import collections +import enum +import fcntl +import logging +import os +import os.path +import pathlib +import queue +import re +import select +import shlex +import shutil +import subprocess +import sys +import tarfile +import tempfile +import threading +import time + +import serial +import serial.tools.list_ports +import yaml + +from tvm.micro.project_api import server + + +_LOG = logging.getLogger(__name__) + + +API_SERVER_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) + + +BUILD_DIR = API_SERVER_DIR / "build" + + +MODEL_LIBRARY_FORMAT_RELPATH = "model.tar" + + +IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() + + +def check_call(cmd_args, *args, **kwargs): + cwd_str = '' if 'cwd' not in kwargs else f" (in cwd: {kwargs['cwd']})" + _LOG.info("run%s: %s", cwd_str, " ".join(shlex.quote(a) for a in cmd_args)) + return subprocess.check_call(cmd_args, *args, **kwargs) + + +CACHE_ENTRY_RE = re.compile(r"(?P[^:]+):(?P[^=]+)=(?P.*)") + + +CMAKE_BOOL_MAP = dict( + [(k, True) for k in ("1", "ON", "YES", "TRUE", "Y")] + + [(k, False) for k in ("0", "OFF", "NO", "FALSE", "N", "IGNORE", "NOTFOUND", "")] +) + + +class CMakeCache(collections.Mapping): + + def __init__(self, path): + self._path = path + self._dict = None + + def __iter__(self): + return iter(self._dict) + + def __getitem__(self, key): + if self._dict is None: + self._dict = self._read_cmake_cache() + + return self._dict[key] + + def __len__(self): + return len(self._dict) + + def _read_cmake_cache(self): + """Read a CMakeCache.txt-like file and return a dictionary of values.""" + entries = collections.OrderedDict() + with open(self._path, encoding="utf-8") as f: + for line in f: + m = CACHE_ENTRY_RE.match(line.rstrip("\n")) + if not m: + continue + + if m.group("type") == "BOOL": + value = CMAKE_BOOL_MAP[m.group("value").upper()] + else: + value = m.group("value") + + entries[m.group("name")] = value + + return entries + + +CMAKE_CACHE = CMakeCache(BUILD_DIR / "CMakeCache.txt") + + +class BoardError(Exception): + """Raised when an attached board cannot be opened (i.e. missing /dev nodes, etc).""" + + +class BoardAutodetectFailed(Exception): + """Raised when no attached hardware is found matching the board= given to ZephyrCompiler.""" + + +def _get_flash_runner(): + flash_runner = CMAKE_CACHE.get("ZEPHYR_BOARD_FLASH_RUNNER") + if flash_runner is not None: + return flash_runner + + with open(CMAKE_CACHE["ZEPHYR_RUNNERS_YAML"]) as f: + doc = yaml.load(f, Loader=yaml.FullLoader) + return doc["flash-runner"] + + +def _get_device_args(options): + flash_runner = _get_flash_runner() + + if flash_runner == "nrfjprog": + return _get_nrf_device_args(options) + + if flash_runner == "openocd": + return _get_openocd_device_args(options) + + raise BoardError( + f"Don't know how to find serial terminal for board {CMAKE_CACHE['BOARD']} with flash " + f"runner {flash_runner}") + +# kwargs passed to usb.core.find to find attached boards for the openocd flash runner. +BOARD_USB_FIND_KW = { + "nucleo_l4r5zi": {"idVendor": 0x0483, "idProduct": 0x374B}, + "nucleo_f746zg": {"idVendor": 0x0483, "idProduct": 0x374B}, + "stm32f746g_disco": {"idVendor": 0x0483, "idProduct": 0x374B}, +} + +def openocd_serial(options): + """Find the serial port to use for a board with OpenOCD flash strategy.""" + if "openocd_serial" in options: + return options["openocd_serial"] + + import usb # pylint: disable=import-outside-toplevel + + find_kw = BOARD_USB_FIND_KW[CMAKE_CACHE["BOARD"]] + boards = usb.core.find(find_all=True, **find_kw) + serials = [] + for b in boards: + serials.append(b.serial_number) + + if len(serials) == 0: + raise BoardAutodetectFailed(f"No attached USB devices matching: {find_kw!r}") + serials.sort() + + autodetected_openocd_serial = serials[0] + _LOG.debug("zephyr openocd driver: autodetected serial %s", serials[0]) + + return autodetected_openocd_serial + + +def _get_openocd_device_args(options): + return ["--serial", openocd_serial(options)] + + +def _get_nrf_device_args(options): + nrfjprog_args = ["nrfjprog", "--ids"] + nrfjprog_ids = subprocess.check_output(nrfjprog_args, encoding="utf-8") + if not nrfjprog_ids.strip("\n"): + raise BoardAutodetectFailed( + f'No attached boards recognized by {" ".join(nrfjprog_args)}' + ) + + boards = nrfjprog_ids.split("\n")[:-1] + if len(boards) > 1: + if options['nrfjprog_snr'] is None: + raise BoardError( + "Multiple boards connected; specify one with nrfjprog_snr=: " + f'{", ".join(boards)}' + ) + + if str(options['nrfjprog_snr']) not in boards: + raise BoardError( + f"nrfjprog_snr ({options['nrfjprog_snr']}) not found in {nrfjprog_args}: {boards}" + ) + + return ["--snr", options['nrfjprog_snr']] + + if not boards: + return [] + + return ["--snr", boards[0]] + + +PROJECT_TYPES = [] +if IS_TEMPLATE: + for d in (API_SERVER_DIR / "src").iterdir(): + if d.is_dir(): + PROJECT_TYPES.append(d.name) + + +PROJECT_OPTIONS = [ + server.ProjectOption( + "extra_files", help="If given, during generate_project, uncompress the tarball at this path into the project dir"), + server.ProjectOption( + "gdbserver_port", help=("If given, port number to use when running the local gdbserver") + ), + server.ProjectOption( + "nrfjprog_snr", + help=( + "When used with nRF targets, serial # of the " "attached board to use, from nrfjprog" + ), + ), + server.ProjectOption( + "openocd_serial", + help=("When used with OpenOCD targets, serial # of the " "attached board to use"), + ), + server.ProjectOption( + "project_type", + help="Type of project to generate.", + choices=tuple(PROJECT_TYPES), + ), + server.ProjectOption("verbose", help="Run build with verbose output"), + server.ProjectOption("west_cmd", + help=("Path to the west tool. If given, supersedes both the zephyr_base " + "option and ZEPHYR_BASE environment variable.")), + server.ProjectOption("zephyr_base", + help="Path to the zephyr base directory."), + server.ProjectOption("zephyr_board", help="Name of the Zephyr board to build for"), +] + + +class Handler(server.ProjectAPIHandler): + + def __init__(self): + super(Handler, self).__init__() + self._proc = None + + def server_info_query(self): + return server.ServerInfo( + platform_name="zephyr", + is_template=IS_TEMPLATE, + model_library_format_path="" if IS_TEMPLATE else (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH), + project_options=PROJECT_OPTIONS) + + # These files and directories will be recursively copied into generated projects from the CRT. + CRT_COPY_ITEMS = ("include", "Makefile", "src") + + def _create_prj_conf(self, project_dir, options): + with open(project_dir / "prj.conf", "w") as f: + f.write( + "# For UART used from main().\n" + "CONFIG_RING_BUFFER=y\n" + "CONFIG_UART_CONSOLE=n\n" + "CONFIG_UART_INTERRUPT_DRIVEN=y\n" + "\n" + ) + f.write( + "# For TVMPlatformAbort().\n" + "CONFIG_REBOOT=y\n" + "\n" + ) + + if True: # options["project_type"] == "host_driven": + f.write( + "# For RPC server C++ bindings.\n" + "CONFIG_CPLUSPLUS=y\n" + "\n" + ) + + f.write( + "# For math routines\n" + "CONFIG_NEWLIB_LIBC=y\n" + "\n" + ) + + + f.write( + "# For models with floating point.\n" + "CONFIG_FPU=y\n" + "\n" + ) + + main_stack_size = None + if self._is_qemu(options) and options["project_type"] == "host_driven": + main_stack_size = 1536 + + # Set main stack size, if needed. + if main_stack_size is not None: + f.write(f"CONFIG_MAIN_STACK_SIZE={main_stack_size}\n") + + f.write( + "# For random number generation.\n" + "CONFIG_TEST_RANDOM_GENERATOR=y\n" + ) + + if self._is_qemu(options): + f.write("CONFIG_TIMER_RANDOM_GENERATOR=y\n") + else: + f.write("CONFIG_ENTROPY_GENERATOR=y\n") + f.write("\n") + + API_SERVER_CRT_LIBS_TOKEN = "" + + CRT_LIBS_BY_PROJECT_TYPE = { + "host_driven": "microtvm_rpc_server microtvm_rpc_common common", + "aot_demo": "aot_executor memory microtvm_rpc_common common", + } + + def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): + project_dir = pathlib.Path(project_dir) + # Make project directory. + project_dir.mkdir() + + # Copy ourselves to the generated project. TVM may perform further build steps on the generated project + # by launching the copy. + shutil.copy2(__file__, project_dir / os.path.basename(__file__)) + + # Place Model Library Format tarball in the special location, which this script uses to decide + # whether it's being invoked in a template or generated project. + project_model_library_format_tar_path = project_dir / MODEL_LIBRARY_FORMAT_RELPATH + shutil.copy2(model_library_format_path, project_model_library_format_tar_path) + + # Extract Model Library Format tarball.into /model. + extract_path = os.path.splitext(project_model_library_format_tar_path)[0] + with tarfile.TarFile(project_model_library_format_tar_path) as tf: + os.makedirs(extract_path) + tf.extractall(path=extract_path) + + if self._is_qemu(options): + shutil.copytree(API_SERVER_DIR / "qemu-hack", project_dir / "qemu-hack") + + # Populate CRT. + crt_path = project_dir / "crt" + crt_path.mkdir() + for item in self.CRT_COPY_ITEMS: + src_path = os.path.join(standalone_crt_dir, item) + dst_path = crt_path / item + if os.path.isdir(src_path): + shutil.copytree(src_path, dst_path) + else: + shutil.copy2(src_path, dst_path) + + # Populate Makefile. + with open(API_SERVER_DIR / "CMakeLists.txt.template", "r") as cmake_template_f: + with open(project_dir / "CMakeLists.txt", "w") as cmake_f: + for line in cmake_template_f: + if self.API_SERVER_CRT_LIBS_TOKEN in line: + crt_libs = self.CRT_LIBS_BY_PROJECT_TYPE[options["project_type"]] + line = line.replace("", crt_libs) + + cmake_f.write(line) + + self._create_prj_conf(project_dir, options) + + # Populate crt-config.h + crt_config_dir = project_dir / "crt_config" + crt_config_dir.mkdir() + shutil.copy2(API_SERVER_DIR / "crt_config" / "crt_config.h", crt_config_dir / "crt_config.h") + + # Populate src/ + src_dir = project_dir / "src" + shutil.copytree(API_SERVER_DIR / "src" / options["project_type"], src_dir) + + # Populate extra_files + if options.get("extra_files_tar"): + with tarfile.open(options["extra_files_tar"], mode="r:*") as tf: + tf.extractall(project_dir) + + def build(self, options): + BUILD_DIR.mkdir() + + cmake_args = ["cmake", ".."] + if options.get("verbose"): + cmake_args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE") + + if options.get("zephyr_base"): + cmake_args.append(f"-DZEPHYR_BASE:STRING={options['zephyr_base']}") + + cmake_args.append(f"-DBOARD:STRING={options['zephyr_board']}") + + check_call(cmake_args, cwd=BUILD_DIR) + + args = ["make", "-j2"] + if options.get("verbose"): + args.append("VERBOSE=1") + check_call(args, cwd=BUILD_DIR) + + @classmethod + def _is_qemu(cls, options): + return ( + "qemu" in options["zephyr_board"] or + # NOTE: mps2_an521 naming breaks the rule that "qemu" is included when qemu is the + # launch method. + "mps2_an521" in options["zephyr_board"]) + + def flash(self, options): + if self._is_qemu(options): + return # NOTE: qemu requires no flash step--it is launched from open_transport. + + zephyr_board = options["zephyr_board"] + + # The nRF5340DK requires an additional `nrfjprog --recover` before each flash cycle. + # This is because readback protection is enabled by default when this device is flashed. + # Otherwise, flashing may fail with an error such as the following: + # ERROR: The operation attempted is unavailable due to readback protection in + # ERROR: your device. Please use --recover to unlock the device. + if zephyr_board.startswith("nrf5340dk") and _get_flash_runner() == "nrfjprog": + recover_args = ["nrfjprog", "--recover"] + recover_args.extend(_get_nrf_device_args(options)) + check_call(recover_args, cwd=API_SERVER_DIR / "build") + + check_call(["make", "flash"], cwd=API_SERVER_DIR / "build") + + def open_transport(self, options): + if self._is_qemu(options): + transport = ZephyrQemuTransport(options) + else: + transport = ZephyrSerialTransport(options) + + to_return = transport.open() + self._transport = transport + return to_return + + def close_transport(self): + if self._transport is not None: + self._transport.close() + self._transport = None + + def read_transport(self, n, timeout_sec): + if self._transport is None: + raise server.TransportClosedError() + + return self._transport.read(n, timeout_sec) + + def write_transport(self, data, timeout_sec): + if self._transport is None: + raise server.TransportClosedError() + + return self._transport.write(data, timeout_sec) + + +def _set_nonblock(fd): + flag = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK) + new_flag = fcntl.fcntl(fd, fcntl.F_GETFL) + assert (new_flag & os.O_NONBLOCK) != 0, "Cannot set file descriptor {fd} to non-blocking" + + +class ZephyrSerialTransport: + + @classmethod + def _lookup_baud_rate(cls, options): + zephyr_base = options.get("zephyr_base", os.environ["ZEPHYR_BASE"]) + sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts")) + try: + import dtlib # pylint: disable=import-outside-toplevel + finally: + sys.path.pop(0) + + dt_inst = dtlib.DT(BUILD_DIR / "zephyr" / "zephyr.dts") + uart_baud = ( + dt_inst.get_node("/chosen") + .props["zephyr,console"] + .to_path() + .props["current-speed"] + .to_num() + ) + _LOG.debug("zephyr transport: found UART baudrate from devicetree: %d", uart_baud) + + return uart_baud + + @classmethod + def _find_nrf_serial_port(cls, options): + com_ports = subprocess.check_output( + ["nrfjprog", "--com"] + _get_device_args(options), encoding="utf-8" + ) + ports_by_vcom = {} + for line in com_ports.split("\n")[:-1]: + parts = line.split() + ports_by_vcom[parts[2]] = parts[1] + + return ports_by_vcom["VCOM2"] + + @classmethod + def _find_openocd_serial_port(cls, options): + serial_number = openocd_serial(options) + ports = [p for p in serial.tools.list_ports.grep(serial_number)] + if len(ports) != 1: + raise Exception(f"_find_openocd_serial_port: expected 1 port to match {serial_number}, " + f"found: {ports!r}") + + return ports[0].device + + @classmethod + def _find_serial_port(cls, options): + flash_runner = _get_flash_runner() + + if flash_runner == "nrfjprog": + return cls._find_nrf_serial_port(options) + + if flash_runner == "openocd": + return cls._find_openocd_serial_port(options) + + raise FlashRunnerNotSupported( + f"Don't know how to deduce serial port for flash runner {flash_runner}" + ) + + def __init__(self, options): + self._options = options + self._port = None + + def open(self): + port_path = self._find_serial_port(self._options) + self._port = serial.Serial(port_path, baudrate=self._lookup_baud_rate(self._options)) + return server.TransportTimeouts( + session_start_retry_timeout_sec=2.0, + session_start_timeout_sec=5.0, + session_established_timeout_sec=5.0, + ) + + def close(self): + self._port.close() + self._port = None + + def read(self, n, timeout_sec): + self._port.timeout = timeout_sec + to_return = self._port.read(n) + if not to_return: + raise server.IoTimeoutError() + + return to_return + + def write(self, data, timeout_sec): + self._port.write_timeout = timeout_sec + bytes_written = 0 + while bytes_written < len(data): + n = self._port.write(data) + data = data[n:] + bytes_written += n + +class ZephyrQemuMakeResult(enum.Enum): + QEMU_STARTED = "qemu_started" + MAKE_FAILED = "make_failed" + EOF = "eof" + + +class ZephyrQemuTransport: + """The user-facing Zephyr QEMU transport class.""" + + def __init__(self, options): + self.options = options + self.proc = None + self.pipe_dir = None + self.read_fd = None + self.write_fd = None + self._queue = queue.Queue() + + def open(self): + self.pipe_dir = pathlib.Path(tempfile.mkdtemp()) + self.pipe = self.pipe_dir / "fifo" + self.write_pipe = self.pipe_dir / "fifo.in" + self.read_pipe = self.pipe_dir / "fifo.out" + os.mkfifo(self.write_pipe) + os.mkfifo(self.read_pipe) + + if "gdbserver_port" in self.options: + if "env" in self.kwargs: + self.kwargs["env"] = copy.copy(self.kwargs["env"]) + else: + self.kwargs["env"] = os.environ.copy() + + self.kwargs["env"]["TVM_QEMU_GDBSERVER_PORT"] = str(self.options["gdbserver_port"]) + + self.proc = subprocess.Popen( + ["make", "run", f"QEMU_PIPE={self.pipe}"], + cwd=BUILD_DIR, + stdout=subprocess.PIPE, + ) + self._wait_for_qemu() + + # NOTE: although each pipe is unidirectional, open both as RDWR to work around a select + # limitation on linux. Without this, non-blocking I/O can't use timeouts because named + # FIFO are always considered ready to read when no one has opened them for writing. + self.read_fd = os.open(self.read_pipe, os.O_RDWR | os.O_NONBLOCK) + self.write_fd = os.open(self.write_pipe, os.O_RDWR | os.O_NONBLOCK) + _set_nonblock(self.read_fd) + _set_nonblock(self.write_fd) + + return server.TransportTimeouts( + session_start_retry_timeout_sec=2.0, + session_start_timeout_sec=5.0, + session_established_timeout_sec=5.0, + ) + + def close(self): + did_write = False + if self.write_fd: + try: + self.write(b"\x01x", 1.0) # Use a short timeout since we will kill the process + did_write = True + except server.IoTimeoutError: + pass + os.close(self.write_fd) + + if self.proc: + if not did_write: + self.proc.terminate() + try: + self.proc.wait(5.0) + except subprocess.TimeoutExpired: + self.proc.kill() + + if self.read_fd: + os.close(self.read_fd) + + if self.pipe_dir is not None: + shutil.rmtree(self.pipe_dir) + self.pipe_dir = None + + def read(self, n, timeout_sec): + return server.read_with_timeout(self.read_fd, n, timeout_sec) + + def write(self, data, timeout_sec): + to_write = bytearray() + escape_pos = [] + for i, b in enumerate(data): + if b == 0x01: + to_write.append(b) + escape_pos.append(i) + to_write.append(b) + + num_written = server.write_with_timeout(self.write_fd, to_write, timeout_sec) + num_written -= sum(1 if x < num_written else 0 for x in escape_pos) + return num_written + + def _qemu_check_stdout(self): + for line in self.proc.stdout: + line = str(line) + _LOG.info("%s", line) + if "[QEMU] CPU" in line: + self._queue.put(ZephyrQemuMakeResult.QEMU_STARTED) + else: + line = re.sub("[^a-zA-Z0-9 \n]", "", line) + pattern = r"recipe for target (\w*) failed" + if re.search(pattern, line, re.IGNORECASE): + self._queue.put(ZephyrQemuMakeResult.MAKE_FAILED) + self._queue.put(ZephyrQemuMakeResult.EOF) + + def _wait_for_qemu(self): + threading.Thread(target=self._qemu_check_stdout, daemon=True).start() + while True: + try: + item = self._queue.get(timeout=120) + except Exception: + raise TimeoutError("QEMU setup timeout.") + + if item == ZephyrQemuMakeResult.QEMU_STARTED: + break + + if item in [ZephyrQemuMakeResult.MAKE_FAILED, ZephyrQemuMakeResult.EOF]: + raise RuntimeError("QEMU setup failed.") + + raise ValueError(f"{item} not expected.") + +if __name__ == '__main__': + server.main(Handler()) diff --git a/apps/microtvm/zephyr/qemu-hack/qemu-system-arm b/apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-arm similarity index 100% rename from apps/microtvm/zephyr/qemu-hack/qemu-system-arm rename to apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-arm diff --git a/apps/microtvm/zephyr/qemu-hack/qemu-system-i386 b/apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-i386 similarity index 91% rename from apps/microtvm/zephyr/qemu-hack/qemu-system-i386 rename to apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-i386 index a30605204d31..6871efbc8b6f 100755 --- a/apps/microtvm/zephyr/qemu-hack/qemu-system-i386 +++ b/apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-i386 @@ -31,8 +31,8 @@ while [ "$#" -gt 0 ]; do done # For debugging -if [ "${TVM_QEMU_DEBUG}" != "" ]; then - ARGS=( "${ARGS[@]}" -s -S ) +if [ "${TVM_QEMU_GDBSERVER_PORT}" != "" ]; then + ARGS=( "${ARGS[@]}" -gdb "tcp::${TVM_QEMU_GDBSERVER_PORT}" -S ) fi "${ARGS[@]}" diff --git a/apps/microtvm/zephyr/qemu-hack/qemu-system-riscv32 b/apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-riscv32 similarity index 100% rename from apps/microtvm/zephyr/qemu-hack/qemu-system-riscv32 rename to apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-riscv32 diff --git a/apps/microtvm/zephyr/qemu-hack/qemu-system-riscv64 b/apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-riscv64 similarity index 100% rename from apps/microtvm/zephyr/qemu-hack/qemu-system-riscv64 rename to apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-riscv64 diff --git a/apps/microtvm/zephyr/aot_demo/src/main.c b/apps/microtvm/zephyr/template_project/src/aot_demo/main.c similarity index 97% rename from apps/microtvm/zephyr/aot_demo/src/main.c rename to apps/microtvm/zephyr/template_project/src/aot_demo/main.c index 43cc7b33987b..fc070127a50f 100644 --- a/apps/microtvm/zephyr/aot_demo/src/main.c +++ b/apps/microtvm/zephyr/template_project/src/aot_demo/main.c @@ -45,8 +45,7 @@ extern tvm_model_t tvmgen_default_network; tvm_workspace_t app_workspace; // Wakeup sequence used to wake up QEMU on the host. -const unsigned char g_wakeup_sequence[12] = {0xfe, 0xff, 0xfd, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x66, 0x77}; +const unsigned char g_wakeup_sequence[] = "#wakeup\n"; const char g_start_cmd[] = "start\n"; size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, diff --git a/apps/microtvm/zephyr/aot_demo/src/zephyr_uart.c b/apps/microtvm/zephyr/template_project/src/aot_demo/zephyr_uart.c similarity index 100% rename from apps/microtvm/zephyr/aot_demo/src/zephyr_uart.c rename to apps/microtvm/zephyr/template_project/src/aot_demo/zephyr_uart.c diff --git a/apps/microtvm/zephyr/aot_demo/include/zephyr_uart.h b/apps/microtvm/zephyr/template_project/src/aot_demo/zephyr_uart.h similarity index 100% rename from apps/microtvm/zephyr/aot_demo/include/zephyr_uart.h rename to apps/microtvm/zephyr/template_project/src/aot_demo/zephyr_uart.h diff --git a/apps/microtvm/zephyr/host_driven/src/main.c b/apps/microtvm/zephyr/template_project/src/host_driven/main.c similarity index 100% rename from apps/microtvm/zephyr/host_driven/src/main.c rename to apps/microtvm/zephyr/template_project/src/host_driven/main.c diff --git a/src/runtime/crt/host/Makefile b/src/runtime/crt/host/Makefile index ef094cb084ac..efed3c438699 100644 --- a/src/runtime/crt/host/Makefile +++ b/src/runtime/crt/host/Makefile @@ -32,7 +32,7 @@ QUIET ?= @ PWD = $(shell pwd) BUILD_DIR = build -CRT_LIB_NAMES = utvm_rpc_server utvm_rpc_common graph_executor graph_executor_module common memory +CRT_LIB_NAMES = microtvm_rpc_server microtvm_rpc_common graph_executor graph_executor_module common memory CRT_LIBS = $(patsubst %, $(BUILD_DIR)/crt/lib%.a, $(CRT_LIB_NAMES)) CRT_INCLUDES = $(glob crt/include/**) diff --git a/tests/micro/zephyr/conftest.py b/tests/micro/zephyr/conftest.py index 0b50ecd12ec5..5ebef06bdf94 100644 --- a/tests/micro/zephyr/conftest.py +++ b/tests/micro/zephyr/conftest.py @@ -24,7 +24,7 @@ "host": ("host", "qemu_x86"), "host_riscv32": ("host", "qemu_riscv32"), "host_riscv64": ("host", "qemu_riscv64"), - "mps2_an521": ("mps2_an521", "mps2_an521-qemu"), + "mps2_an521": ("mps2_an521", "mps2_an521"), "nrf5340dk": ("nrf5340dk", "nrf5340dk_nrf5340_cpuapp"), "stm32f746xx_disco": ("stm32f746xx", "stm32f746g_disco"), "stm32f746xx_nucleo": ("stm32f746xx", "nucleo_f746zg"), diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 7182ed855afa..3b5176ea94fc 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -72,7 +72,7 @@ def _make_session(model, target, zephyr_board, west_cmd, mod, build_config): workspace = tvm.micro.Workspace(debug=True, root=workspace_root) template_project_dir = ( - pathlib.Path(__file__).parent / ".." / ".." / ".." / "apps" / "microtvm" / "zephyr" / "demo_runtime").resolve() + pathlib.Path(__file__).parent / ".." / ".." / ".." / "apps" / "microtvm" / "zephyr" / "template_project").resolve() project = tvm.micro.generate_project( str(template_project_dir), mod, workspace.relpath("project"), {"zephyr_board": zephyr_board, "west_cmd": west_cmd, From ebdace584ad006b8f185764495953975a9745d41 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Mon, 19 Jul 2021 22:30:43 -0700 Subject: [PATCH 09/47] consolidate CcompilerAnnotator --- python/tvm/relay/testing/byoc.py | 76 +++++++++++++++++++ tests/micro/zephyr/test_zephyr.py | 56 +------------- tests/python/relay/aot/test_crt_aot.py | 57 +------------- .../python/relay/test_pass_partition_graph.py | 56 +------------- 4 files changed, 81 insertions(+), 164 deletions(-) create mode 100644 python/tvm/relay/testing/byoc.py diff --git a/python/tvm/relay/testing/byoc.py b/python/tvm/relay/testing/byoc.py new file mode 100644 index 000000000000..619c9b99ca1d --- /dev/null +++ b/python/tvm/relay/testing/byoc.py @@ -0,0 +1,76 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Defines test utilties useful for testing BYOC flows.""" + +from tvm import relay +from tvm.relay.expr_functor import ExprMutator +from tvm.relay.op.annotation import compiler_begin, compiler_end + + +class CcompilerAnnotator(ExprMutator): + """ + This is used to create external functions for ccompiler. + A simple annotator that creates the following program: + | + -- begin -- + | + add + | + subtract + | + multiply + | + -- end -- + | + """ + + def __init__(self): + super(CcompilerAnnotator, self).__init__() + self.in_compiler = 0 + + def visit_call(self, call): + if call.op.name == "add": # Annotate begin at args + if self.in_compiler == 1: + lhs = compiler_begin(super().visit(call.args[0]), "ccompiler") + rhs = compiler_begin(super().visit(call.args[1]), "ccompiler") + op = relay.add(lhs, rhs) + self.in_compiler = 2 + return op + elif call.op.name == "subtract": + if self.in_compiler == 1: + lhs = super().visit(call.args[0]) + rhs = super().visit(call.args[1]) + if isinstance(lhs, relay.expr.Var): + lhs = compiler_begin(lhs, "ccompiler") + if isinstance(rhs, relay.expr.Var): + rhs = compiler_begin(rhs, "ccompiler") + return relay.subtract(lhs, rhs) + elif call.op.name == "multiply": # Annotate end at output + self.in_compiler = 1 + lhs = super().visit(call.args[0]) + rhs = super().visit(call.args[1]) + if isinstance(lhs, relay.expr.Var): + lhs = compiler_begin(lhs, "ccompiler") + if isinstance(rhs, relay.expr.Var): + rhs = compiler_begin(rhs, "ccompiler") + op = relay.multiply(lhs, rhs) + if self.in_compiler == 2: + op = compiler_end(op, "ccompiler") + self.in_compiler = 0 + return op + return super().visit_call(call) diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 3b5176ea94fc..fb238a92272f 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -223,60 +223,6 @@ def test_onnx(platform, west_cmd, skip_build, tvm_debug): assert np.argmax(result) == 9 -class CcompilerAnnotator(ExprMutator): - """ - This is used to create external functions for ccompiler. - A simple annotator that creates the following program: - | - -- begin -- - | - add - | - subtract - | - multiply - | - -- end -- - | - """ - - def __init__(self): - super(CcompilerAnnotator, self).__init__() - self.in_compiler = 0 - - def visit_call(self, call): - if call.op.name == "add": # Annotate begin at args - if self.in_compiler == 1: - lhs = compiler_begin(super().visit(call.args[0]), "ccompiler") - rhs = compiler_begin(super().visit(call.args[1]), "ccompiler") - op = relay.add(lhs, rhs) - self.in_compiler = 2 - return op - elif call.op.name == "subtract": - if self.in_compiler == 1: - lhs = super().visit(call.args[0]) - rhs = super().visit(call.args[1]) - if isinstance(lhs, relay.expr.Var): - lhs = compiler_begin(lhs, "ccompiler") - if isinstance(rhs, relay.expr.Var): - rhs = compiler_begin(rhs, "ccompiler") - return relay.subtract(lhs, rhs) - elif call.op.name == "multiply": # Annotate end at output - self.in_compiler = 1 - lhs = super().visit(call.args[0]) - rhs = super().visit(call.args[1]) - if isinstance(lhs, relay.expr.Var): - lhs = compiler_begin(lhs, "ccompiler") - if isinstance(rhs, relay.expr.Var): - rhs = compiler_begin(rhs, "ccompiler") - op = relay.multiply(lhs, rhs) - if self.in_compiler == 2: - op = compiler_end(op, "ccompiler") - self.in_compiler = 0 - return op - return super().visit_call(call) - - def check_result( relay_mod, model, zephyr_board, west_cmd, map_inputs, out_shape, result, build_config ): @@ -336,7 +282,7 @@ def test_byoc_microtvm(platform, west_cmd, skip_build, tvm_debug): r = relay.concatenate((q0, q1, q2), axis=0) f = relay.Function([x, w0, w1, w2, w3, w4, w5, w6, w7], r) mod = tvm.IRModule() - ann = CcompilerAnnotator() + ann = tvm.testing.CcompilerAnnotator() mod["main"] = ann.visit(f) mod = tvm.relay.transform.PartitionGraph()(mod) mod = tvm.relay.transform.InferType()(mod) diff --git a/tests/python/relay/aot/test_crt_aot.py b/tests/python/relay/aot/test_crt_aot.py index 13cbfa71b6ae..033064881c2d 100644 --- a/tests/python/relay/aot/test_crt_aot.py +++ b/tests/python/relay/aot/test_crt_aot.py @@ -36,6 +36,7 @@ from tvm.contrib import graph_executor from tvm.micro import export_model_library_format from tvm.relay import testing +from tvm.relay.testing import byoc from tvm.relay.op.annotation import compiler_begin, compiler_end from tvm.contrib import utils from tvm.relay.expr_functor import ExprMutator @@ -284,60 +285,6 @@ def test_mobilenet(use_calculated_workspaces_and_alignment, target_options): ) -class CcompilerAnnotator(ExprMutator): - """ - This is used to create external functions for ccompiler. - A simple annotator that creates the following program: - | - -- begin -- - | - add - | - subtract - | - multiply - | - -- end -- - | - """ - - def __init__(self): - super(CcompilerAnnotator, self).__init__() - self.in_compiler = 0 - - def visit_call(self, call): - if call.op.name == "add": # Annotate begin at args - if self.in_compiler == 1: - lhs = compiler_begin(super().visit(call.args[0]), "ccompiler") - rhs = compiler_begin(super().visit(call.args[1]), "ccompiler") - op = relay.add(lhs, rhs) - self.in_compiler = 2 - return op - elif call.op.name == "subtract": - if self.in_compiler == 1: - lhs = super().visit(call.args[0]) - rhs = super().visit(call.args[1]) - if isinstance(lhs, relay.expr.Var): - lhs = compiler_begin(lhs, "ccompiler") - if isinstance(rhs, relay.expr.Var): - rhs = compiler_begin(rhs, "ccompiler") - return relay.subtract(lhs, rhs) - elif call.op.name == "multiply": # Annotate end at output - self.in_compiler = 1 - lhs = super().visit(call.args[0]) - rhs = super().visit(call.args[1]) - if isinstance(lhs, relay.expr.Var): - lhs = compiler_begin(lhs, "ccompiler") - if isinstance(rhs, relay.expr.Var): - rhs = compiler_begin(rhs, "ccompiler") - op = relay.multiply(lhs, rhs) - if self.in_compiler == 2: - op = compiler_end(op, "ccompiler") - self.in_compiler = 0 - return op - return super().visit_call(call) - - @pytest.mark.parametrize("use_calculated_workspaces", [True, False]) @pytest.mark.parametrize("target_options", [""]) def test_byoc_microtvm(use_calculated_workspaces, target_options): @@ -368,7 +315,7 @@ def test_byoc_microtvm(use_calculated_workspaces, target_options): r = relay.concatenate((q0, q1, q2), axis=0) f = relay.Function([x, w0, w1, w2, w3, w4, w5, w6, w7], r) mod = tvm.IRModule() - ann = CcompilerAnnotator() + ann = byoc.CcompilerAnnotator() mod["main"] = ann.visit(f) mod = tvm.relay.transform.PartitionGraph("mod_name")(mod) diff --git a/tests/python/relay/test_pass_partition_graph.py b/tests/python/relay/test_pass_partition_graph.py index 55b150d948c1..76bb219942dc 100644 --- a/tests/python/relay/test_pass_partition_graph.py +++ b/tests/python/relay/test_pass_partition_graph.py @@ -27,6 +27,7 @@ from tvm import relay from tvm import runtime from tvm.relay import transform +from tvm.relay.testing import byoc from tvm.contrib import utils from tvm.relay.backend import compile_engine from tvm.relay.expr_functor import ExprMutator @@ -63,59 +64,6 @@ def visit_call(self, call): return Annotator().visit(func) -class CcompilerAnnotator(ExprMutator): - """ - A simple annotator that creates the following program: - | - -- begin -- - | - add - | - subtract - | - multiply - | - -- end -- - | - """ - - def __init__(self): - super(CcompilerAnnotator, self).__init__() - self.in_compiler = 0 - - def visit_call(self, call): - if call.op.name == "add": # Annotate begin at args - if self.in_compiler == 1: - lhs = compiler_begin(super().visit(call.args[0]), "ccompiler") - rhs = compiler_begin(super().visit(call.args[1]), "ccompiler") - op = relay.add(lhs, rhs) - self.in_compiler = 2 - return op - elif call.op.name == "subtract": - if self.in_compiler == 1: - lhs = super().visit(call.args[0]) - rhs = super().visit(call.args[1]) - if isinstance(lhs, relay.expr.Var): - lhs = compiler_begin(lhs, "ccompiler") - if isinstance(rhs, relay.expr.Var): - rhs = compiler_begin(rhs, "ccompiler") - return relay.subtract(lhs, rhs) - elif call.op.name == "multiply": # Annotate end at output - self.in_compiler = 1 - lhs = super().visit(call.args[0]) - rhs = super().visit(call.args[1]) - if isinstance(lhs, relay.expr.Var): - lhs = compiler_begin(lhs, "ccompiler") - if isinstance(rhs, relay.expr.Var): - rhs = compiler_begin(rhs, "ccompiler") - op = relay.multiply(lhs, rhs) - if self.in_compiler == 2: - op = compiler_end(op, "ccompiler") - self.in_compiler = 0 - return op - return super().visit_call(call) - - class WholeGraphAnnotator(ExprMutator): """ An annotator that creates a compiler for an entire graph. @@ -261,7 +209,7 @@ def test_multi_node_compiler(): r = relay.concatenate((q0, q1, q2), axis=0) f = relay.Function([x, w0, w1, w2, w3, w4, w5, w6, w7], r) mod = tvm.IRModule() - ann = CcompilerAnnotator() + ann = byoc.CcompilerAnnotator() mod["main"] = ann.visit(f) mod = transform.PartitionGraph()(mod) mod = transform.InferType()(mod) From a690762156e903915f1e65c3526a3fbb2594228b Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Mon, 19 Jul 2021 22:27:44 -0700 Subject: [PATCH 10/47] Allow model library format with c backend, add test. --- python/tvm/micro/model_library_format.py | 7 ++- .../test_micro_model_library_format.py | 63 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/python/tvm/micro/model_library_format.py b/python/tvm/micro/model_library_format.py index ad49ee7d9578..738060be533d 100644 --- a/python/tvm/micro/model_library_format.py +++ b/python/tvm/micro/model_library_format.py @@ -169,9 +169,12 @@ def _build_function_memory_map(function_metadata): target_local_entries[func_name] = list() for func_name, finfo in function_metadata.items(): - if func_name == MAIN_FUNC_NAME_STR: + # Skip a few unsupported cases: + # 1. The main function metadata is exported elsewhere. + # 2. BYOC operator implementations do not currently export useful FunctionInfo. + if func_name == MAIN_FUNC_NAME_STR or not finfo.tir_primfuncs: continue - assert len(finfo.constant_sizes.items()) == num_targets + assert len(finfo.constant_sizes.items()) == num_targets, f"{func_name}: found {finfo.constant_sizes!r} vs {num_targets}" assert len(finfo.io_sizes.items()) == num_targets target = finfo.workspace_sizes.items()[i][0] workspace_size = finfo.workspace_sizes.items()[i][1] diff --git a/tests/python/unittest/test_micro_model_library_format.py b/tests/python/unittest/test_micro_model_library_format.py index a15e37925eea..1e9bada2a7a3 100644 --- a/tests/python/unittest/test_micro_model_library_format.py +++ b/tests/python/unittest/test_micro_model_library_format.py @@ -27,6 +27,7 @@ import tvm import tvm.relay from tvm.relay.backend import executor_factory +from tvm.relay.testing import byoc import tvm.runtime.module import tvm.testing from tvm.contrib import utils @@ -343,5 +344,67 @@ def test_export_non_dso_exportable(): ) +@tvm.testing.requires_micro +def test_export_byoc_c_module(): + """Test BYOC flow when it produces DSO-exportable modules. + + NOTE the general BYOC flow is not fully supported by Model Library Format right now. + """ + x = tvm.relay.var("x", shape=(10, 10)) + w0 = tvm.relay.var("w0", shape=(10, 10)) + w1 = tvm.relay.var("w1", shape=(10, 10)) + w2 = tvm.relay.var("w2", shape=(10, 10)) + w3 = tvm.relay.var("w3", shape=(10, 10)) + w4 = tvm.relay.var("w4", shape=(10, 10)) + w5 = tvm.relay.var("w5", shape=(10, 10)) + w6 = tvm.relay.var("w6", shape=(10, 10)) + w7 = tvm.relay.var("w7", shape=(10, 10)) + + # C compiler + z0 = tvm.relay.add(x, w0) + p0 = tvm.relay.subtract(z0, w1) + q0 = tvm.relay.multiply(p0, w2) + + z1 = tvm.relay.add(x, w3) + p1 = tvm.relay.subtract(z1, w4) + q1 = tvm.relay.multiply(p1, w5) + + # Other parts on TVM + z2 = tvm.relay.add(x, w6) + q2 = tvm.relay.subtract(z2, w7) + + r = tvm.relay.concatenate((q0, q1, q2), axis=0) + f = tvm.relay.Function([x, w0, w1, w2, w3, w4, w5, w6, w7], r) + mod = tvm.IRModule() + ann = byoc.CcompilerAnnotator() + mod["main"] = ann.visit(f) + mod = tvm.relay.transform.PartitionGraph("mod_name")(mod) + mod = tvm.relay.transform.InferType()(mod) + + + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + factory = tvm.relay.build(mod, tvm.target.target.micro("host")) + + temp_dir = utils.tempdir() + mlf_tar_path = temp_dir.relpath("lib.tar") + + from tvm import micro + micro.export_model_library_format(factory, mlf_tar_path) + + with tarfile.open(mlf_tar_path, "r:*") as tf: + tar_members = [ti.name for ti in tf.getmembers()] + print("tar members", tar_members) + assert './metadata.json' in tar_members + with tf.extractfile("./metadata.json") as f: + metadata = json.load(f) + main_md = metadata["memory"]["functions"]["main"] + assert main_md == [{ + "constants_size_bytes": 0, + "device": 1, + "io_size_bytes": 4800, + "workspace_size_bytes": 800, + }] + + if __name__ == "__main__": sys.exit(pytest.main([__file__] + sys.argv[1:])) From 90a4464da4b209d56bbc1909bc1f03bdb3f9f98b Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 27 May 2021 09:19:38 -0700 Subject: [PATCH 11/47] Update unit tests --- tests/micro/zephyr/test_zephyr.py | 27 ++-- tests/micro/zephyr/test_zephyr_aot.py | 181 +++++++++++------------ tests/python/relay/aot/aot_test.mk | 24 +-- tests/python/relay/aot/aot_test_utils.py | 36 ++++- tests/python/relay/aot/test_crt_aot.py | 6 +- tests/python/unittest/test_crt.py | 10 +- 6 files changed, 149 insertions(+), 135 deletions(-) diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index fb238a92272f..2b81adf381db 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -36,6 +36,7 @@ import tvm.micro import tvm.testing import tvm.relay as relay +from tvm.relay.testing import byoc from tvm.micro.contrib import zephyr from tvm.contrib import utils @@ -74,9 +75,11 @@ def _make_session(model, target, zephyr_board, west_cmd, mod, build_config): template_project_dir = ( pathlib.Path(__file__).parent / ".." / ".." / ".." / "apps" / "microtvm" / "zephyr" / "template_project").resolve() project = tvm.micro.generate_project( - str(template_project_dir), mod, workspace.relpath("project"), {"zephyr_board": zephyr_board, - "west_cmd": west_cmd, - "verbose": 1}) + str(template_project_dir), + mod, + workspace.relpath("project"), + {"project_type": "host_driven", "west_cmd": west_cmd, "verbose": 0, "zephyr_board": zephyr_board}, + ) project.build() project.flash() return tvm.micro.Session(project.transport()) @@ -160,13 +163,13 @@ def test_relay(platform, west_cmd, skip_build, tvm_debug): target = tvm.target.target.micro(model) with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): - graph, mod, params = tvm.relay.build(func, target=target) + mod = tvm.relay.build(func, target=target) with _make_session(model, target, zephyr_board, west_cmd, mod, build_config) as session: graph_mod = tvm.micro.create_local_graph_executor( - graph, session.get_system_lib(), session.device + mod.get_graph_json(), session.get_system_lib(), session.device ) - graph_mod.set_input(**params) + graph_mod.set_input(**mod.get_params()) x_in = np.random.randint(10, size=shape[0], dtype=dtype) graph_mod.run(x=x_in) result = graph_mod.get_output(0).numpy() @@ -205,7 +208,7 @@ def test_onnx(platform, west_cmd, skip_build, tvm_debug): lowered = relay.build(relay_mod, target, params=params) graph = lowered.get_graph_json() - with _make_session(model, target, zephyr_board, west_cmd, lowered.lib, build_config) as session: + with _make_session(model, target, zephyr_board, west_cmd, lowered, build_config) as session: graph_mod = tvm.micro.create_local_graph_executor( graph, session.get_system_lib(), session.device ) @@ -230,16 +233,16 @@ def check_result( TOL = 1e-5 target = tvm.target.target.micro(model) with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): - graph, mod, params = tvm.relay.build(relay_mod, target=target) + mod = tvm.relay.build(relay_mod, target=target) with _make_session(model, target, zephyr_board, west_cmd, mod, build_config) as session: rt_mod = tvm.micro.create_local_graph_executor( - graph, session.get_system_lib(), session.device + mod.get_graph_json(), session.get_system_lib(), session.device ) - rt_mod.set_input(**params) + rt_mod.set_input(**mod.get_params()) for name, data in map_inputs.items(): rt_mod.set_input(name, data) - rt_mod.set_input(**params) + rt_mod.set_input(**mod.get_params()) rt_mod.run() out_shapes = out_shape if isinstance(out_shape, list) else [out_shape] @@ -282,7 +285,7 @@ def test_byoc_microtvm(platform, west_cmd, skip_build, tvm_debug): r = relay.concatenate((q0, q1, q2), axis=0) f = relay.Function([x, w0, w1, w2, w3, w4, w5, w6, w7], r) mod = tvm.IRModule() - ann = tvm.testing.CcompilerAnnotator() + ann = byoc.CcompilerAnnotator() mod["main"] = ann.visit(f) mod = tvm.relay.transform.PartitionGraph()(mod) mod = tvm.relay.transform.InferType()(mod) diff --git a/tests/micro/zephyr/test_zephyr_aot.py b/tests/micro/zephyr/test_zephyr_aot.py index 48bdc5d3a283..29470c1b717a 100644 --- a/tests/micro/zephyr/test_zephyr_aot.py +++ b/tests/micro/zephyr/test_zephyr_aot.py @@ -17,11 +17,14 @@ import datetime from hashlib import new +import io import logging import os import sys import logging import pathlib +import tarfile +import tempfile import pytest import numpy as np @@ -29,10 +32,10 @@ import tvm import tvm.rpc import tvm.micro +from tvm.micro.project_api import server import tvm.testing import tvm.relay as relay -from tvm.micro.contrib import zephyr from tvm.contrib import utils from tvm.contrib.download import download_testdata @@ -43,7 +46,7 @@ PLATFORMS = conftest.PLATFORMS -def _build_session_kw(model, target, zephyr_board, west_cmd, mod, runtime_path, build_config): +def _build_project(model, target, zephyr_board, west_cmd, mod, build_config, extra_files_tar=None): parent_dir = os.path.dirname(__file__) filename = os.path.splitext(os.path.basename(__file__))[0] prev_build = f"{os.path.join(parent_dir, 'archive')}_{filename}_{zephyr_board}_last_build.micro" @@ -56,76 +59,61 @@ def _build_session_kw(model, target, zephyr_board, west_cmd, mod, runtime_path, os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) - compiler = zephyr.ZephyrCompiler( - project_dir=runtime_path, - board=zephyr_board, - zephyr_toolchain_variant="zephyr", - west_cmd=west_cmd, - env_vars={"ZEPHYR_RUNTIME": "ZEPHYR-AOT"}, + template_project_dir = ( + pathlib.Path(__file__).parent + / ".." + / ".." + / ".." + / "apps" + / "microtvm" + / "zephyr" + / "template_project" + ).resolve() + project_dir = workspace.relpath("project") + project = tvm.micro.generate_project( + str(template_project_dir), + mod, + project_dir, + {"extra_files_tar": extra_files_tar, "project_type": "aot_demo", "west_cmd": west_cmd, "verbose": 0, "zephyr_board": zephyr_board}, ) + project.build() + return project, project_dir - opts = tvm.micro.default_options(os.path.join(runtime_path, "crt")) - opts["bin_opts"]["include_dirs"].append(os.path.join(runtime_path, "include")) - opts["lib_opts"]["include_dirs"].append(os.path.join(runtime_path, "include")) - - flasher_kw = {} - if build_config["debug"]: - flasher_kw["debug_rpc_session"] = tvm.rpc.connect("127.0.0.1", 9090) - - session_kw = { - "flasher": compiler.flasher(**flasher_kw), - } - - if not build_config["skip_build"]: - session_kw["binary"] = tvm.micro.build_static_runtime( - workspace, - compiler, - mod, - opts, - executor="aot", - extra_libs=[tvm.micro.get_standalone_crt_lib("memory")], - ) - if os.path.exists(prev_build): - os.unlink(prev_build) - session_kw["binary"].archive(prev_build, metadata_only=True) - else: - unarchive_dir = utils.tempdir() - session_kw["binary"] = tvm.micro.MicroBinary.unarchive( - prev_build, unarchive_dir.relpath("binary") - ) - return session_kw - - -def _create_header_file(tensor_name, npy_data, output_path): +def _create_header_file(tensor_name, npy_data, output_path, tar_file): """ This method generates a header file containing the data contained in the numpy array provided. It is used to capture the tensor data (for both inputs and expected outputs). """ - file_path = pathlib.Path(f"{output_path}/" + tensor_name).resolve() - # create header file - raw_path = file_path.with_suffix(".h").resolve() - with open(raw_path, "w") as header_file: - header_file.write("#include \n") - header_file.write("#include \n") - header_file.write("#include \n") - header_file.write(f"const size_t {tensor_name}_len = {npy_data.size};\n") - - if npy_data.dtype == "int8": - header_file.write(f"int8_t {tensor_name}[] =") - elif npy_data.dtype == "int32": - header_file.write(f"int32_t {tensor_name}[] = ") - elif npy_data.dtype == "uint8": - header_file.write(f"uint8_t {tensor_name}[] = ") - elif npy_data.dtype == "float32": - header_file.write(f"float {tensor_name}[] = ") - else: - raise ValueError("Data type not expected.") - - header_file.write("{") - for i in np.ndindex(npy_data.shape): - header_file.write(f"{npy_data[i]}, ") - header_file.write("};\n\n") + header_file = io.StringIO() + header_file.write("#include \n") + header_file.write("#include \n") + header_file.write("#include \n") + header_file.write(f"const size_t {tensor_name}_len = {npy_data.size};\n") + + if npy_data.dtype == "int8": + header_file.write(f"int8_t {tensor_name}[] =") + elif npy_data.dtype == "int32": + header_file.write(f"int32_t {tensor_name}[] = ") + elif npy_data.dtype == "uint8": + header_file.write(f"uint8_t {tensor_name}[] = ") + elif npy_data.dtype == "float32": + header_file.write(f"float {tensor_name}[] = ") + else: + raise ValueError("Data type not expected.") + + header_file.write("{") + for i in np.ndindex(npy_data.shape): + header_file.write(f"{npy_data[i]}, ") + header_file.write("};\n\n") + + header_file_bytes = bytes(header_file.getvalue(), "utf-8") + raw_path = (pathlib.Path(output_path) / f"{tensor_name}.h") + ti = tarfile.TarInfo(name=str(raw_path)) + ti.size = len(header_file_bytes) + ti.mode = 0o644 + ti.type = tarfile.REGTYPE + tar_file.addfile(ti, io.BytesIO(header_file_bytes)) def _read_line(fd): @@ -161,9 +149,9 @@ def test_tflite(platform, west_cmd, skip_build, tvm_debug): output_shape = (1, 10) build_config = {"skip_build": skip_build, "debug": tvm_debug} - this_dir = os.path.dirname(__file__) - tvm_source_dir = os.path.join(this_dir, "..", "..", "..") - runtime_path = os.path.join(tvm_source_dir, "apps", "microtvm", "zephyr", "aot_demo") + this_dir = pathlib.Path(os.path.dirname(__file__)) + tvm_source_dir = this_dir / ".." / ".." / ".." + runtime_path = tvm_source_dir / "apps" / "microtvm" / "zephyr" / "aot_demo" model_url = "https://github.com/eembc/ulpmark-ml/raw/fc1499c7cc83681a02820d5ddf5d97fe75d4f663/base_models/ic01/ic01_fp32.tflite" model_path = download_testdata(model_url, "ic01_fp32.tflite", module="model") @@ -196,19 +184,25 @@ def test_tflite(platform, west_cmd, skip_build, tvm_debug): ) sample = np.load(sample_path) model_files_path = os.path.join(runtime_path, "include") - _create_header_file((f"input_data"), sample, model_files_path) - _create_header_file( - "output_data", np.zeros(shape=output_shape, dtype="float32"), model_files_path - ) - session_kw = _build_session_kw( - model, target, zephyr_board, west_cmd, lowered.lib, runtime_path, build_config - ) - transport = session_kw["flasher"].flash(session_kw["binary"]) - transport.open() - transport.write(b"start\n", timeout_sec=5) + with tempfile.NamedTemporaryFile() as temp_file: + with tarfile.open(temp_file.name, "w:gz") as tf: + _create_header_file("input_data", sample, "include", tf) + _create_header_file( + "output_data", np.zeros(shape=output_shape, dtype="float32"), "include", tf + ) + + project, _ = _build_project( + model, target, zephyr_board, west_cmd, lowered, build_config, extra_files_tar=temp_file.name + ) + + project.flash() + with project.transport() as transport: + _get_message(transport, "#wakeup") + transport.write(b"start\n", timeout_sec=5) + + result_line = _get_message(transport, "#result") - result_line = _get_message(transport, "#result") result_line = result_line.strip("\n") result_line = result_line.split(":") result = int(result_line[1]) @@ -219,19 +213,15 @@ def test_tflite(platform, west_cmd, skip_build, tvm_debug): @tvm.testing.requires_micro def test_qemu_make_fail(platform, west_cmd, skip_build, tvm_debug): + """Testing QEMU make fail.""" if platform not in ["host", "mps2_an521"]: pytest.skip(msg="Only for QEMU targets.") - """Testing QEMU make fail.""" model, zephyr_board = PLATFORMS[platform] build_config = {"skip_build": skip_build, "debug": tvm_debug} shape = (10,) dtype = "float32" - this_dir = pathlib.Path(__file__).parent - tvm_source_dir = this_dir / ".." / ".." / ".." - runtime_path = tvm_source_dir / "apps" / "microtvm" / "zephyr" / "aot_demo" - # Construct Relay program. x = relay.var("x", relay.TensorType(shape=shape, dtype=dtype)) xx = relay.multiply(x, x) @@ -243,22 +233,23 @@ def test_qemu_make_fail(platform, west_cmd, skip_build, tvm_debug): lowered = relay.build(func, target) # Generate input/output header files - model_files_path = os.path.join(runtime_path, "include") - _create_header_file((f"input_data"), np.zeros(shape=shape, dtype=dtype), model_files_path) - _create_header_file("output_data", np.zeros(shape=shape, dtype=dtype), model_files_path) + with tempfile.NamedTemporaryFile() as temp_file: + with tarfile.open(temp_file.name, "w:gz") as tf: + _create_header_file("input_data", np.zeros(shape=shape, dtype=dtype), "include", tf) + _create_header_file("output_data", np.zeros(shape=shape, dtype=dtype), "include", tf) - session_kw = _build_session_kw( - model, target, zephyr_board, west_cmd, lowered.lib, runtime_path, build_config - ) + project, project_dir = _build_project( + model, target, zephyr_board, west_cmd, lowered, build_config, extra_files_tar=temp_file.name + ) - file_path = os.path.join(session_kw["binary"].base_dir, "zephyr/CMakeFiles/run.dir/build.make") - assert os.path.isfile(file_path), f"[{file_path}] does not exist." + file_path = pathlib.Path(project_dir) / "build" / "zephyr" / "CMakeFiles" / "run.dir" / "build.make" + assert file_path.is_file(), f"[{file_path}] does not exist." # Remove a file to create make failure. os.remove(file_path) - transport = session_kw["flasher"].flash(session_kw["binary"]) - with pytest.raises(RuntimeError) as excinfo: - transport.open() + project.flash() + with pytest.raises(server.JSONRPCError) as excinfo: + project.transport().open() assert "QEMU setup failed" in str(excinfo.value) diff --git a/tests/python/relay/aot/aot_test.mk b/tests/python/relay/aot/aot_test.mk index 2426d9fd2963..c74465304827 100644 --- a/tests/python/relay/aot/aot_test.mk +++ b/tests/python/relay/aot/aot_test.mk @@ -16,25 +16,19 @@ # under the License. # Setup build environment # -AOT_ROOT ?= $(TVM_ROOT)/src/runtime/crt/aot +AOT_ROOT ?= $(CRT_ROOT)/aot ENABLE_TVM_PLATFORM_ABORT_BACKTRACE = 0 DMLC_CORE=$(TVM_ROOT)/3rdparty/dmlc-core -PKG_COMPILE_OPTS = -g +PKG_COMPILE_OPTS = -g CC = gcc AR = ar RANLIB = ranlib CC_OPTS = CC=$(CC) AR=$(AR) RANLIB=$(RANLIB) - PKG_CFLAGS = ${PKG_COMPILE_OPTS} \ - -I$(TVM_ROOT)/src/runtime/crt/include \ - -I$(TVM_ROOT)/src/runtime/crt/host \ - -I$(TVM_ROOT)/include \ - -I$(DMLC_CORE)/include \ - -I$(TVM_ROOT)/3rdparty/dlpack/include \ - -I$(AOT_ROOT)\ - -I$(build_dir) + -I$(build_dir)/../include \ + -isystem$(STANDALONE_CRT_DIR)/include $(ifeq VERBOSE,1) QUIET ?= @@ -42,12 +36,10 @@ $(else) QUIET ?= @ $(endif) -CRT_SRCS = $(shell find $(CRT_ROOT)) - aot_test_runner: $(build_dir)/aot_test_runner source_libs= $(wildcard $(build_dir)/../codegen/host/src/*.c) -lib_objs =$(source_libs:.c=.o) +lib_objs =$(source_libs:.c=.o) $(build_dir)/aot_test_runner: $(build_dir)/test.c $(build_dir)/aot_executor.o $(source_libs) $(build_dir)/stack_allocator.o $(build_dir)/crt_backend_api.o $(QUIET)mkdir -p $(@D) @@ -57,15 +49,15 @@ $(build_dir)/%.o: $(build_dir)/../codegen/host/src/%.c $(QUIET)mkdir -p $(@D) $(QUIET)$(CC) $(CFLAGS) -c $(PKG_CFLAGS) -o $@ $^ $(BACKTRACE_CFLAGS) -$(build_dir)/aot_executor.o: $(TVM_ROOT)/src/runtime/crt/aot_executor/aot_executor.c +$(build_dir)/aot_executor.o: $(STANDALONE_CRT_DIR)/src/runtime/crt/aot_executor/aot_executor.c $(QUIET)mkdir -p $(@D) $(QUIET)$(CC) $(CFLAGS) -c $(PKG_CFLAGS) -o $@ $^ $(BACKTRACE_CFLAGS) -$(build_dir)/stack_allocator.o: $(TVM_ROOT)/src/runtime/crt/memory/stack_allocator.c +$(build_dir)/stack_allocator.o: $(STANDALONE_CRT_DIR)/src/runtime/crt/memory/stack_allocator.c $(QUIET)mkdir -p $(@D) $(QUIET)$(CC) $(CFLAGS) -c $(PKG_CFLAGS) -o $@ $^ $(BACKTRACE_CFLAGS) -$(build_dir)/crt_backend_api.o: $(TVM_ROOT)/src/runtime/crt/common/crt_backend_api.c +$(build_dir)/crt_backend_api.o: $(STANDALONE_CRT_DIR)/src/runtime/crt/common/crt_backend_api.c $(QUIET)mkdir -p $(@D) $(QUIET)$(CC) $(CFLAGS) -c $(PKG_CFLAGS) -o $@ $^ $(BACKTRACE_CFLAGS) diff --git a/tests/python/relay/aot/aot_test_utils.py b/tests/python/relay/aot/aot_test_utils.py index 836ff4b22b20..cf95843bc41b 100644 --- a/tests/python/relay/aot/aot_test_utils.py +++ b/tests/python/relay/aot/aot_test_utils.py @@ -120,13 +120,13 @@ def emit_main_prologue(main_file, workspace_bytes): return StackMemoryManager_Free(&app_workspace,ptr); } -void TVMPlatformAbort(tvm_crt_error_t code) { } +void TVMPlatformAbort(tvm_crt_error_t code) { } void TVMLogf(const char* msg, ...) { } TVM_DLL int TVMFuncRegisterGlobal(const char* name, TVMFunctionHandle f, int override) {} int main(){\n - + """ ) @@ -296,17 +296,27 @@ def compile_and_run( else: workspace_bytes = 16384 * 1024 + include_path = os.path.join(base_path, "include") + os.mkdir(include_path) + crt_root = tvm.micro.get_standalone_crt_dir() + shutil.copy2(os.path.join(crt_root, "template", "crt_config-template.h"), + os.path.join(include_path, "crt_config.h")) + for i in range(len(input_list)): - create_header_file((f'{mangle_name(mod_name, "input_data")}{i}'), input_list[i], build_path) + create_header_file(f'{mangle_name(mod_name, "input_data")}{i}', + input_list[i], + os.path.join(base_path, "include")) for i in range(len(output_list)): create_header_file( - (f'{mangle_name(mod_name,"output_data")}{i}'), + f'{mangle_name(mod_name,"output_data")}{i}', np.zeros(output_list[i].shape, output_list[i].dtype), - build_path, + os.path.join(base_path, "include") ) create_header_file( - (f'{mangle_name(mod_name, "expected_output_data")}{i}'), output_list[i], build_path + f'{mangle_name(mod_name, "expected_output_data")}{i}', + output_list[i], + os.path.join(base_path, "include") ) create_main( @@ -320,6 +330,7 @@ def compile_and_run( f"make CFLAGS='{cflags}' -f {makefile} build_dir=" + build_path + f" TVM_ROOT={file_dir}/../../../.." + + f" STANDALONE_CRT_DIR={tvm.micro.get_standalone_crt_dir()}" ) compile_log_path = os.path.join(build_path, "test_compile.log") @@ -345,6 +356,13 @@ def compile_and_run_multiple_models( base_path = os.path.join(tmp_dir, "test") build_path = os.path.join(base_path, "build") os.makedirs(build_path, exist_ok=True) + + include_path = os.path.join(base_path, "include") + os.mkdir(include_path) + crt_root = tvm.micro.get_standalone_crt_dir() + shutil.copy2(os.path.join(crt_root, "template", "crt_config-template.h"), + os.path.join(include_path, "crt_config.h")) + for mod_name, mod in mod_map.items(): with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): @@ -380,7 +398,11 @@ def compile_and_run_multiple_models( # Verify that compiles fine file_dir = os.path.dirname(os.path.abspath(__file__)) makefile = os.path.join(file_dir, "aot_test.mk") - make_cmd = f"make -f {makefile} build_dir=" + build_path + f" TVM_ROOT={file_dir}/../../../.." + make_cmd = ( + f"make -f {makefile} build_dir=" + + build_path + + f" TVM_ROOT={file_dir}/../../../.." + + f" STANDALONE_CRT_DIR={tvm.micro.get_standalone_crt_dir()}") compile_log_path = os.path.join(build_path, "test_compile.log") ret = subprocess_with_stdout_and_log(make_cmd, ".", compile_log_path, False) diff --git a/tests/python/relay/aot/test_crt_aot.py b/tests/python/relay/aot/test_crt_aot.py index 033064881c2d..88d41444c775 100644 --- a/tests/python/relay/aot/test_crt_aot.py +++ b/tests/python/relay/aot/test_crt_aot.py @@ -17,13 +17,15 @@ import os import io -import struct import numpy as np import pathlib import shutil +import struct import subprocess +import sys import tempfile import tarfile + import pytest import tvm @@ -461,4 +463,4 @@ def test_transpose(target_options): if __name__ == "__main__": - pytest.main([__file__]) + sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/python/unittest/test_crt.py b/tests/python/unittest/test_crt.py index 408ad5b890c7..cd91af9de425 100644 --- a/tests/python/unittest/test_crt.py +++ b/tests/python/unittest/test_crt.py @@ -19,7 +19,9 @@ import copy import glob import os +import pathlib import pytest +import shutil pytest.importorskip("pty") import sys @@ -131,7 +133,10 @@ def test_graph_executor(): """Test use of the graph executor with microTVM.""" import tvm.micro - workspace = tvm.micro.Workspace(debug=True) + ws_root = pathlib.Path(os.path.dirname(__file__) + "/micro-workspace") + if ws_root.exists(): + shutil.rmtree(ws_root) + workspace = tvm.micro.Workspace(debug=True, root=ws_root.resolve()) relay_mod = tvm.parser.fromtext( """ #[version = "0.0.5"] @@ -214,5 +219,4 @@ def test_platform_timer(): if __name__ == "__main__": - test_graph_executor() -# sys.exit(pytest.main([__file__] + sys.argv[1:])) + sys.exit(pytest.main([__file__] + sys.argv[1:])) From 61cf49f1e85cee6c6b5c6a255a776949501cc531 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Tue, 6 Jul 2021 22:01:39 -0700 Subject: [PATCH 12/47] fix incorrect doc --- include/tvm/runtime/crt/rpc_common/framing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tvm/runtime/crt/rpc_common/framing.h b/include/tvm/runtime/crt/rpc_common/framing.h index 32a0f56dab11..33f37a0af03f 100644 --- a/include/tvm/runtime/crt/rpc_common/framing.h +++ b/include/tvm/runtime/crt/rpc_common/framing.h @@ -134,7 +134,7 @@ class Unframer { /*! \brief number of bytes in buffer that are currently valid. */ size_t num_buffer_bytes_valid_; - /*! \brief number of payload bytes left to write before the CRC begins. */ + /*! \brief number of payload bytes left to receive before the CRC begins. */ size_t num_payload_bytes_remaining_; /*! \brief Running CRC value. */ From 1a7e0ef25cedc06ee144c6b6f00f3ec768106270 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:07:56 -0700 Subject: [PATCH 13/47] Delete old Zephyr build infrastructure --- python/tvm/micro/contrib/__init__.py | 16 - python/tvm/micro/contrib/base.py | 67 --- python/tvm/micro/contrib/zephyr.py | 789 --------------------------- tests/micro/zephyr/test_zephyr.py | 1 - 4 files changed, 873 deletions(-) delete mode 100644 python/tvm/micro/contrib/__init__.py delete mode 100644 python/tvm/micro/contrib/base.py delete mode 100644 python/tvm/micro/contrib/zephyr.py diff --git a/python/tvm/micro/contrib/__init__.py b/python/tvm/micro/contrib/__init__.py deleted file mode 100644 index 13a83393a912..000000000000 --- a/python/tvm/micro/contrib/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. diff --git a/python/tvm/micro/contrib/base.py b/python/tvm/micro/contrib/base.py deleted file mode 100644 index 9c4f4863e3bc..000000000000 --- a/python/tvm/micro/contrib/base.py +++ /dev/null @@ -1,67 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines common helper functions useful for integrating custom compiler toolchains.""" - -import glob -import os -import shutil - - -GLOB_PATTERNS = ["__tvm_*", "libtvm__*"] - - -def populate_tvm_objs(dest_dir, objs): - """Replace tvm-prefixed files in a build worktree. - - This function is intended to be used to place TVM source files and libraries into a - template on-device runtime project. - - Parameters - ---------- - dest_dir : str - Path to the destination directory. - - objs : List[MicroLibrary] - List of MicroLibrary to place in the project directory. - - Returns - ------- - List[str] : - List of paths, each relative to `dest_dir` to the newly-copied MicroLibrary files. - """ - copied = [] - for p in GLOB_PATTERNS: - for f in glob.glob(os.path.join(dest_dir, p)): - if os.path.isdir(f): - shutil.rmtree(f) - else: - os.unlink(f) - - for obj in objs: - for lib_file in obj.library_files: - obj_base = os.path.basename(lib_file) - if obj_base.endswith(".a"): - dest_basename = f"libtvm__{obj_base}" - else: - dest_basename = f"__tvm_{obj_base}" - - copied.append(dest_basename) - dest = os.path.join(dest_dir, dest_basename) - shutil.copy(obj.abspath(lib_file), dest) - - return copied diff --git a/python/tvm/micro/contrib/zephyr.py b/python/tvm/micro/contrib/zephyr.py deleted file mode 100644 index 77cfb8d09bf2..000000000000 --- a/python/tvm/micro/contrib/zephyr.py +++ /dev/null @@ -1,789 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines a compiler integration that uses an externally-supplied Zephyr project.""" - -import collections -import copy -import logging -import multiprocessing -import os -import pathlib -import re -import tempfile -import textwrap -import shlex -import shutil -import subprocess -import sys -import threading -import queue -import enum - -import yaml - -import tvm.micro -from . import base -from .. import compiler -from .. import debugger -from ..transport import debug -from ..transport import file_descriptor - -from ..transport import serial -from ..transport import Transport, TransportClosedError, TransportTimeouts -from ..transport import wakeup - - -_LOG = logging.getLogger(__name__) - - -class SubprocessEnv(object): - def __init__(self, default_overrides): - self.default_overrides = default_overrides - - def run(self, cmd, **kw): - env = dict(os.environ) - for k, v in self.default_overrides.items(): - env[k] = v - - return subprocess.check_output(cmd, env=env, **kw, universal_newlines=True) - - -class ProjectNotFoundError(Exception): - """Raised when the project_dir supplied to ZephyrCompiler does not exist.""" - - -class FlashRunnerNotSupported(Exception): - """Raised when the FLASH_RUNNER for a project isn't supported by this Zephyr adapter.""" - - -class ZephyrCompiler(tvm.micro.Compiler): - """A Compiler instance that builds against a pre-existing zephyr project.""" - - def __init__( - self, - project_dir=None, - board=None, - west_cmd=None, - zephyr_base=None, - zephyr_toolchain_variant=None, - env_vars=None, - ): - """Configure the compiler for use. - - Parameters - ---------- - project_dir : str - Path to the pre-existing Zephyr project. - board : str - Name of the Zephyr board to build for (i.e. passed to `west build -b`) - west_cmd : Optional[list] - If given, argv that invoke the west build tool. Used only for flashing. - zephyr_base : Optional[str] - If given, path to Zephyr, as would normally be present in the ZEPHYR_BASE environment - variable. If not given, consults this environment variable. This value must be set in - one of those two places. - zephyr_toolchain_variant: Optional[str] - If given, overrides the toolchain used by Zephyr. If not given, uses the default - zephyr toolchain. When running on OS X outside of docker, you need to specify this. - env_vars : Optional[Dict[str,str]] - If given, additional environment variables present when invoking west, cmake, or make. - """ - self._project_dir = project_dir - if not os.path.exists(project_dir): - # Raise this error instead of a potentially-more-cryptic compiler error due to a missing - # prj.conf. - raise ProjectNotFoundError( - f"project_dir supplied to ZephyrCompiler does not exist: {project_dir}" - ) - - self._qemu = "qemu" in board - - # For Zephyr boards that run emulated by default but don't have the prefix "qemu_" in their - # board names, a suffix "-qemu" is added by users of microTVM when specifying the board - # name to inform that the QEMU transporter must be used just like for the boards with - # the prefix. Zephyr does not recognize the suffix, so we trim it off before passing it. - if "-qemu" in board: - board = board.replace("-qemu", "") - - self._board = board - - if west_cmd is None: - self._west_cmd = [sys.executable, "-mwest.app.main"] - elif isinstance(west_cmd, str): - self._west_cmd = [west_cmd] - elif isinstance(west_cmd, list): - self._west_cmd = west_cmd - else: - raise TypeError("west_cmd: expected string, list, or None; got %r" % (west_cmd,)) - - env = {} - if zephyr_toolchain_variant is not None: - env["ZEPHYR_TOOLCHAIN_VARIANT"] = zephyr_toolchain_variant - - self._zephyr_base = zephyr_base or os.environ["ZEPHYR_BASE"] - assert ( - self._zephyr_base is not None - ), f"Must specify zephyr_base=, or ZEPHYR_BASE must be in environment variables" - env["ZEPHYR_BASE"] = self._zephyr_base - - if env_vars: - env.update(env_vars) - - self._subprocess_env = SubprocessEnv(env) - - OPT_KEY_TO_CMAKE_DEFINE = { - "cflags": "CFLAGS", - "ccflags": "CXXFLAGS", - "ldflags": "LDFLAGS", - } - - @classmethod - def _options_to_cmake_args(cls, options): - args = [] - for key, define in cls.OPT_KEY_TO_CMAKE_DEFINE.items(): - if key in options: - quoted_opts = [shlex.quote(o).replace(";", "\\;") for o in options[key]] - args.append(f'-DEXTRA_{define}={" ".join(quoted_opts)}') - - if "cmake_args" in options: - args.extend(options["cmake_args"]) - - return args - - def library(self, output, sources, options=None): - project_name = os.path.basename(output) - if project_name.startswith("lib"): - project_name = project_name[3:] - - lib_prj_conf = os.path.join(output, "prj.conf") - if self._project_dir is not None: - project_dir_conf = os.path.join(self._project_dir, "prj.conf") - if os.path.exists(project_dir_conf): - shutil.copy(project_dir_conf, lib_prj_conf) - - # Copy board-specific Zephyr config file from the project_dir to - # the build lib dir so board-specific configs can be found and used by - # Zephyr's build system in conjunction with the generic prj.conf configs. - board_conf = os.path.join("boards", self._board + ".conf") - project_dir_board_conf = os.path.join(self._project_dir, board_conf) - if os.path.exists(project_dir_board_conf): - os.mkdir(os.path.join(output, "boards")) - lib_dir_board_conf = os.path.join(output, board_conf) - shutil.copy(project_dir_board_conf, lib_dir_board_conf) - - else: - with open(lib_prj_conf, "w") as prj_conf_f: - prj_conf_f.write("CONFIG_CPLUSPLUS=y\n") - - cmakelists_path = os.path.join(output, "CMakeLists.txt") - with open(cmakelists_path, "w") as cmake_f: - sources = " ".join(f'"{o}"' for o in sources) - cmake_f.write( - textwrap.dedent( - f"""\ - cmake_minimum_required(VERSION 3.13.1) - - find_package(Zephyr HINTS $ENV{{ZEPHYR_BASE}}) - project({project_name}_prj) - target_sources(app PRIVATE) - zephyr_library_named({project_name}) - target_sources({project_name} PRIVATE {sources}) - target_sources(app PRIVATE main.c) - target_link_libraries(app PUBLIC {project_name}) - """ - ) - ) - if "include_dirs" in options: - cmake_f.write( - f"target_include_directories({project_name} PRIVATE " - f'{" ".join(os.path.abspath(d) for d in options["include_dirs"])})\n' - ) - - with open(os.path.join(output, "main.c"), "w"): - pass - - # expected not to exist after populate_tvm_libs - build_dir = os.path.join(output, "__tvm_build") - os.mkdir(build_dir) - self._subprocess_env.run( - ["cmake", "..", f"-DBOARD={self._board}"] + self._options_to_cmake_args(options), - cwd=build_dir, - ) - num_cpus = multiprocessing.cpu_count() - self._subprocess_env.run( - ["make", f"-j{num_cpus}", "VERBOSE=1", project_name], cwd=build_dir - ) - return tvm.micro.MicroLibrary(build_dir, [f"lib{project_name}.a"]) - - def _print_make_statistics(self, output): - output = output.splitlines() - lines = iter(output) - for line in lines: - if line.startswith("Memory region"): - # print statistics header - _LOG.info(line) - _LOG.info("--------------------- ---------- ------------ ---------") - line = next(lines) - # while there is a region print it - try: - while ":" in line: - _LOG.info(line) - line = next(lines) - else: - break - except StopIteration: - pass - - def binary(self, output, objects, options=None, link_main=True, main_options=None): - assert link_main, "Must pass link_main=True" - assert self._project_dir is not None, "Must supply project_dir= to build binaries" - - copied_libs = base.populate_tvm_objs(self._project_dir, objects) - - # expected not to exist after populate_tvm_objs - cmake_args = [ - "cmake", - os.path.abspath(self._project_dir), - f"-DBOARD={self._board}", - ] + self._options_to_cmake_args(options) - if "include_dirs" in options: - cmake_args.append( - "-DTVM_INCLUDE_DIRS=" - f'{";".join(os.path.abspath(d) for d in options["include_dirs"])}' - ) - cmake_args.append(f'-DTVM_LIBS={";".join(copied_libs)}') - self._subprocess_env.run(cmake_args, cwd=output) - - make_output = self._subprocess_env.run(["make"], cwd=output) - - self._print_make_statistics(make_output) - - return tvm.micro.MicroBinary( - output, - binary_file=os.path.join("zephyr", "zephyr.elf"), - debug_files=[os.path.join("zephyr", "zephyr.elf")], - labelled_files={ - "cmake_cache": ["CMakeCache.txt"], - "device_tree": [os.path.join("zephyr", "zephyr.dts")], - }, - immobile=bool(self._qemu), - ) - - @property - def flasher_factory(self): - return compiler.FlasherFactory( - ZephyrFlasher, - ( - self._board, - self._qemu, - ), - dict( - zephyr_base=self._zephyr_base, - project_dir=self._project_dir, - subprocess_env=self._subprocess_env.default_overrides, - west_cmd=self._west_cmd, - ), - ) - - -CACHE_ENTRY_RE = re.compile(r"(?P[^:]+):(?P[^=]+)=(?P.*)") - - -CMAKE_BOOL_MAP = dict( - [(k, True) for k in ("1", "ON", "YES", "TRUE", "Y")] - + [(k, False) for k in ("0", "OFF", "NO", "FALSE", "N", "IGNORE", "NOTFOUND", "")] -) - - -def read_cmake_cache(file_name): - """Read a CMakeCache.txt-like file and return a dictionary of values.""" - entries = collections.OrderedDict() - with open(file_name, encoding="utf-8") as f: - for line in f: - m = CACHE_ENTRY_RE.match(line.rstrip("\n")) - if not m: - continue - - if m.group("type") == "BOOL": - value = CMAKE_BOOL_MAP[m.group("value").upper()] - else: - value = m.group("value") - - entries[m.group("name")] = value - - return entries - - -class BoardError(Exception): - """Raised when an attached board cannot be opened (i.e. missing /dev nodes, etc).""" - - -class BoardAutodetectFailed(Exception): - """Raised when no attached hardware is found matching the board= given to ZephyrCompiler.""" - - -class ZephyrFlasher(tvm.micro.compiler.Flasher): - """A Flasher implementation that delegates to Zephyr/west.""" - - def __init__( - self, - board, - qemu, - zephyr_base=None, - project_dir=None, - subprocess_env=None, - nrfjprog_snr=None, - openocd_serial=None, - flash_args=None, - debug_rpc_session=None, - serial_timeouts=None, - west_cmd=None, - ): - zephyr_base = zephyr_base or os.environ["ZEPHYR_BASE"] - sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts")) - try: - import dtlib # pylint: disable=import-outside-toplevel - - self._dtlib = dtlib - finally: - sys.path.pop(0) - - self._board = board - self._qemu = qemu - self._zephyr_base = zephyr_base - self._project_dir = project_dir - self._west_cmd = west_cmd - self._flash_args = flash_args - self._openocd_serial = openocd_serial - self._autodetected_openocd_serial = None - self._subprocess_env = SubprocessEnv(subprocess_env) - self._debug_rpc_session = debug_rpc_session - self._nrfjprog_snr = nrfjprog_snr - self._serial_timeouts = serial_timeouts - - def _get_nrf_device_args(self): - nrfjprog_args = ["nrfjprog", "--ids"] - nrfjprog_ids = subprocess.check_output(nrfjprog_args, encoding="utf-8") - if not nrfjprog_ids.strip("\n"): - raise BoardAutodetectFailed( - f'No attached boards recognized by {" ".join(nrfjprog_args)}' - ) - - boards = nrfjprog_ids.split("\n")[:-1] - if len(boards) > 1: - if self._nrfjprog_snr is None: - raise BoardError( - "Multiple boards connected; specify one with nrfjprog_snr=: " - f'{", ".join(boards)}' - ) - - if str(self._nrfjprog_snr) not in boards: - raise BoardError( - f"nrfjprog_snr ({self._nrfjprog_snr}) not found in {nrfjprog_args}: {boards}" - ) - - return ["--snr", str(self._nrfjprog_snr)] - - if not boards: - return [] - - return ["--snr", boards[0]] - - # kwargs passed to usb.core.find to find attached boards for the openocd flash runner. - BOARD_USB_FIND_KW = { - "nucleo_l4r5zi": {"idVendor": 0x0483, "idProduct": 0x374B}, - "nucleo_f746zg": {"idVendor": 0x0483, "idProduct": 0x374B}, - "stm32f746g_disco": {"idVendor": 0x0483, "idProduct": 0x374B}, - } - - def openocd_serial(self, cmake_entries): - """Find the serial port to use for a board with OpenOCD flash strategy.""" - if self._openocd_serial is not None: - return self._openocd_serial - - if self._autodetected_openocd_serial is None: - import usb # pylint: disable=import-outside-toplevel - - find_kw = self.BOARD_USB_FIND_KW[cmake_entries["BOARD"]] - boards = usb.core.find(find_all=True, **find_kw) - serials = [] - for b in boards: - serials.append(b.serial_number) - - if len(serials) == 0: - raise BoardAutodetectFailed(f"No attached USB devices matching: {find_kw!r}") - serials.sort() - - self._autodetected_openocd_serial = serials[0] - _LOG.debug("zephyr openocd driver: autodetected serial %s", serials[0]) - - return self._autodetected_openocd_serial - - def _get_openocd_device_args(self, cmake_entries): - return ["--serial", self.openocd_serial(cmake_entries)] - - @classmethod - def _get_flash_runner(cls, cmake_entries): - flash_runner = cmake_entries.get("ZEPHYR_BOARD_FLASH_RUNNER") - if flash_runner is not None: - return flash_runner - - with open(cmake_entries["ZEPHYR_RUNNERS_YAML"]) as f: - doc = yaml.load(f, Loader=yaml.FullLoader) - return doc["flash-runner"] - - def _get_device_args(self, cmake_entries): - flash_runner = self._get_flash_runner(cmake_entries) - - if flash_runner == "nrfjprog": - return self._get_nrf_device_args() - if flash_runner == "openocd": - return self._get_openocd_device_args(cmake_entries) - - raise BoardError( - f"Don't know how to find serial terminal for board {cmake_entries['BOARD']} with flash " - f"runner {flash_runner}" - ) - - def _zephyr_transport(self, micro_binary): - qemu_debugger = None - if self._debug_rpc_session: - qemu_debugger = debugger.RpcDebugger( - self._debug_rpc_session, - debugger.DebuggerFactory( - QemuGdbDebugger, - (micro_binary.abspath(micro_binary.debug_files[0]),), - {}, - ), - ) - - return ZephyrQemuTransport( - micro_binary.base_dir, startup_timeout_sec=30.0, qemu_debugger=qemu_debugger - ) - - def flash(self, micro_binary): - if self._qemu: - return self._zephyr_transport(micro_binary) - - cmake_cache_path = micro_binary.abspath(micro_binary.labelled_files["cmake_cache"][0]) - cmake_entries = read_cmake_cache(cmake_cache_path) - - build_dir = os.path.dirname(cmake_cache_path) - - # The nRF5340DK requires an additional `nrfjprog --recover` before each flash cycle. - # This is because readback protection is enabled by default when this device is flashed. - # Otherwise, flashing may fail with an error such as the following: - # ERROR: The operation attempted is unavailable due to readback protection in - # ERROR: your device. Please use --recover to unlock the device. - if ( - self._board.startswith("nrf5340dk") - and self._get_flash_runner(cmake_entries) == "nrfjprog" - ): - recover_args = ["nrfjprog", "--recover"] - recover_args.extend(self._get_nrf_device_args()) - self._subprocess_env.run(recover_args, cwd=build_dir) - - west_args = ( - self._west_cmd - + ["flash", "--build-dir", build_dir, "--skip-rebuild"] - + self._get_device_args(cmake_entries) - ) - if self._flash_args is not None: - west_args.extend(self._flash_args) - self._subprocess_env.run(west_args, cwd=build_dir) - - return self.transport(micro_binary) - - def _find_nrf_serial_port(self, cmake_entries): - com_ports = subprocess.check_output( - ["nrfjprog", "--com"] + self._get_device_args(cmake_entries), encoding="utf-8" - ) - ports_by_vcom = {} - for line in com_ports.split("\n")[:-1]: - parts = line.split() - ports_by_vcom[parts[2]] = parts[1] - - return {"port_path": ports_by_vcom["VCOM2"]} - - def _find_openocd_serial_port(self, cmake_entries): - return {"grep": self.openocd_serial(cmake_entries)} - - def _find_serial_port(self, micro_binary): - cmake_entries = read_cmake_cache( - micro_binary.abspath(micro_binary.labelled_files["cmake_cache"][0]) - ) - flash_runner = self._get_flash_runner(cmake_entries) - - if flash_runner == "nrfjprog": - return self._find_nrf_serial_port(cmake_entries) - - if flash_runner == "openocd": - return self._find_openocd_serial_port(cmake_entries) - - raise FlashRunnerNotSupported( - f"Don't know how to deduce serial port for flash runner {flash_runner}" - ) - - def transport(self, micro_binary): - """Instantiate the transport for use with non-QEMU Zephyr.""" - dt_inst = self._dtlib.DT( - micro_binary.abspath(micro_binary.labelled_files["device_tree"][0]) - ) - uart_baud = ( - dt_inst.get_node("/chosen") - .props["zephyr,console"] - .to_path() - .props["current-speed"] - .to_num() - ) - _LOG.debug("zephyr transport: found UART baudrate from devicetree: %d", uart_baud) - - port_kwargs = self._find_serial_port(micro_binary) - serial_transport = serial.SerialTransport( - timeouts=self._serial_timeouts, baudrate=uart_baud, **port_kwargs - ) - if self._debug_rpc_session is None: - return serial_transport - - return debug.DebugWrapperTransport( - debugger.RpcDebugger( - self._debug_rpc_session, - debugger.DebuggerFactory( - ZephyrDebugger, - ( - " ".join(shlex.quote(x) for x in self._west_cmd), - os.path.dirname(micro_binary.abspath(micro_binary.label("cmake_cache")[0])), - micro_binary.abspath(micro_binary.debug_files[0]), - self._zephyr_base, - ), - {}, - ), - ), - serial_transport, - ) - - -class QemuGdbDebugger(debugger.GdbDebugger): - def __init__(self, elf_file): - super(QemuGdbDebugger, self).__init__() - self._elf_file = elf_file - - def popen_kwargs(self): - # expect self._elf file to follow the form .../zephyr/zephyr.elf - cmake_cache_path = pathlib.Path(self._elf_file).parent.parent / "CMakeCache.txt" - cmake_cache = read_cmake_cache(cmake_cache_path) - return { - "args": [ - cmake_cache["CMAKE_GDB"], - "-ex", - "target remote localhost:1234", - "-ex", - f"file {self._elf_file}", - ], - } - - -class QemuStartupFailureError(Exception): - """Raised when the qemu pipe is not present within startup_timeout_sec.""" - - -class QemuFdTransport(file_descriptor.FdTransport): - """An FdTransport subclass that escapes written data to accommodate the QEMU monitor. - - It's supposedly possible to disable the monitor, but Zephyr controls most of the command-line - arguments for QEMU and there are too many options which implictly enable the monitor, so this - approach seems more robust. - """ - - def write_monitor_quit(self): - file_descriptor.FdTransport.write(self, b"\x01x", 1.0) - - def close(self): - file_descriptor.FdTransport.close(self) - - def timeouts(self): - assert False, "should not get here" - - def write(self, data, timeout_sec): - """Write data, escaping for QEMU monitor.""" - to_write = bytearray() - escape_pos = [] - for i, b in enumerate(data): - if b == 0x01: - to_write.append(b) - escape_pos.append(i) - to_write.append(b) - - num_written = file_descriptor.FdTransport.write(self, to_write, timeout_sec) - num_written -= sum(1 if x < num_written else 0 for x in escape_pos) - return num_written - - -class ZephyrQemuMakeResult(enum.Enum): - QEMU_STARTED = "qemu_started" - MAKE_FAILED = "make_failed" - EOF = "eof" - - -class ZephyrQemuTransport(Transport): - """The user-facing Zephyr QEMU transport class.""" - - def __init__(self, base_dir, startup_timeout_sec=5.0, qemu_debugger=None, **kwargs): - self.base_dir = base_dir - self.startup_timeout_sec = startup_timeout_sec - self.kwargs = kwargs - self.proc = None - self.fd_transport = None - self.pipe_dir = None - self.qemu_debugger = qemu_debugger - self._queue = queue.Queue() - - def timeouts(self): - return TransportTimeouts( - session_start_retry_timeout_sec=2.0, - session_start_timeout_sec=self.startup_timeout_sec, - session_established_timeout_sec=5.0 if self.qemu_debugger is None else 0, - ) - - def open(self): - self.pipe_dir = tempfile.mkdtemp() - self.pipe = os.path.join(self.pipe_dir, "fifo") - self.write_pipe = os.path.join(self.pipe_dir, "fifo.in") - self.read_pipe = os.path.join(self.pipe_dir, "fifo.out") - - os.mkfifo(self.write_pipe) - os.mkfifo(self.read_pipe) - if self.qemu_debugger is not None: - if "env" in self.kwargs: - self.kwargs["env"] = copy.copy(self.kwargs["env"]) - else: - self.kwargs["env"] = os.environ.copy() - - self.kwargs["env"]["TVM_QEMU_DEBUG"] = "1" - - self.proc = subprocess.Popen( - ["make", "run", f"QEMU_PIPE={self.pipe}"], - cwd=self.base_dir, - **self.kwargs, - stdout=subprocess.PIPE, - ) - try: - self._wait_for_qemu() - except Exception as error: - raise error - - if self.qemu_debugger is not None: - self.qemu_debugger.start() - - # NOTE: although each pipe is unidirectional, open both as RDWR to work around a select - # limitation on linux. Without this, non-blocking I/O can't use timeouts because named - # FIFO are always considered ready to read when no one has opened them for writing. - self.fd_transport = wakeup.WakeupTransport( - QemuFdTransport( - os.open(self.read_pipe, os.O_RDWR | os.O_NONBLOCK), - os.open(self.write_pipe, os.O_RDWR | os.O_NONBLOCK), - self.timeouts(), - ), - b"\xfe\xff\xfd\x03\0\0\0\0\0\x02" b"fw", - ) - self.fd_transport.open() - - def close(self): - if self.qemu_debugger is not None: - self.qemu_debugger.stop() - - if self.fd_transport is not None: - self.fd_transport.child_transport.write_monitor_quit() - self.proc.wait() - self.fd_transport.close() - self.fd_transport = None - - if self.proc is not None: - self.proc = None - - if self.pipe_dir is not None: - shutil.rmtree(self.pipe_dir) - self.pipe_dir = None - - def read(self, n, timeout_sec): - if self.fd_transport is None: - raise TransportClosedError() - return self.fd_transport.read(n, timeout_sec) - - def write(self, data, timeout_sec): - if self.fd_transport is None: - raise TransportClosedError() - return self.fd_transport.write(data, timeout_sec) - - def _qemu_check_stdout(self): - for line in self.proc.stdout: - line = str(line) - _LOG.debug(line) - if "[QEMU] CPU" in line: - self._queue.put(ZephyrQemuMakeResult.QEMU_STARTED) - else: - line = re.sub("[^a-zA-Z0-9 \n]", "", line) - pattern = r"recipe for target (\w*) failed" - if re.search(pattern, line, re.IGNORECASE): - self._queue.put(ZephyrQemuMakeResult.MAKE_FAILED) - self._queue.put(ZephyrQemuMakeResult.EOF) - - def _wait_for_qemu(self): - threading.Thread(target=self._qemu_check_stdout, daemon=True).start() - while True: - try: - item = self._queue.get(timeout=120) - except Exception: - raise TimeoutError("QEMU setup timeout.") - - if item == ZephyrQemuMakeResult.QEMU_STARTED: - break - - if item in [ZephyrQemuMakeResult.MAKE_FAILED, ZephyrQemuMakeResult.EOF]: - raise RuntimeError("QEMU setup failed.") - - raise ValueError(f"{item} not expected.") - - -class ZephyrDebugger(debugger.GdbDebugger): - """A Zephyr debugger implementation.""" - - def __init__(self, west_cmd, build_dir, elf_path, zephyr_base): - super(ZephyrDebugger, self).__init__() - self._west_cmd = shlex.split(west_cmd) - self._build_dir = build_dir - self._elf_path = elf_path - self._zephyr_base = zephyr_base - - def popen_kwargs(self): - env = dict(os.environ) - env["ZEPHYR_BASE"] = self._zephyr_base - - args = dict( - args=self._west_cmd - + [ - "debug", - "--skip-rebuild", - "--build-dir", - self._build_dir, - "--elf-file", - self._elf_path, - ], - env=env, - ) - return args diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 2b81adf381db..8b1271de4fed 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -38,7 +38,6 @@ import tvm.relay as relay from tvm.relay.testing import byoc -from tvm.micro.contrib import zephyr from tvm.contrib import utils from tvm.relay.expr_functor import ExprMutator from tvm.relay.op.annotation import compiler_begin, compiler_end From e84aa8ea578e8408bd656a61e2529159c1ec52dc Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:53:18 -0700 Subject: [PATCH 14/47] Delete old build abstractions --- python/tvm/micro/__init__.py | 5 - python/tvm/micro/artifact.py | 295 --------------- python/tvm/micro/build.py | 1 - python/tvm/micro/compiler.py | 361 ------------------- python/tvm/micro/micro_binary.py | 65 ---- python/tvm/micro/micro_library.py | 93 ----- tests/python/unittest/test_link_params.py | 33 +- tests/python/unittest/test_micro_artifact.py | 149 -------- 8 files changed, 10 insertions(+), 992 deletions(-) delete mode 100644 python/tvm/micro/artifact.py delete mode 100644 python/tvm/micro/compiler.py delete mode 100644 python/tvm/micro/micro_binary.py delete mode 100644 python/tvm/micro/micro_library.py delete mode 100644 tests/python/unittest/test_micro_artifact.py diff --git a/python/tvm/micro/__init__.py b/python/tvm/micro/__init__.py index f7f8b007da2f..3b8c27bcf985 100644 --- a/python/tvm/micro/__init__.py +++ b/python/tvm/micro/__init__.py @@ -16,13 +16,8 @@ # under the License. """MicroTVM module for bare-metal backends""" -from .artifact import Artifact from .build import default_options, get_standalone_crt_dir from .build import get_standalone_crt_lib, Workspace -from .compiler import Compiler, DefaultCompiler, Flasher -from .debugger import GdbRemoteDebugger -from .micro_library import MicroLibrary -from .micro_binary import MicroBinary from .model_library_format import export_model_library_format, UnsupportedInModelLibraryFormatError from .project import generate_project, GeneratedProject, TemplateProject from .session import ( diff --git a/python/tvm/micro/artifact.py b/python/tvm/micro/artifact.py deleted file mode 100644 index c8faccb3f512..000000000000 --- a/python/tvm/micro/artifact.py +++ /dev/null @@ -1,295 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -""""Defines abstractions around compiler artifacts produced in compiling micro TVM binaries.""" - -import hashlib -import io -import os -import json -import shutil -import tarfile - - -class ArtifactFileNotFoundError(Exception): - """Raised when an artifact file cannot be found on disk.""" - - -class ArtifactBadSymlinkError(Exception): - """Raised when an artifact symlink points outside the base directory.""" - - -class ArtifactBadArchiveError(Exception): - """Raised when an artifact archive is malformed.""" - - -class ImmobileArtifactError(Exception): - """Raised when an artifact is declared immobile and thus cannot be archived.""" - - -class ArchiveModifiedError(Exception): - """Raised when the underlying files in a metadata-only archive were modified after archiving.""" - - -def sha256_hexdigest(path): - with open(path, "rb") as path_fd: - h = hashlib.sha256() - chunk = path_fd.read(1 * 1024 * 1024) - while chunk: - h.update(chunk) - chunk = path_fd.read(1 * 1024 * 1024) - - return h.hexdigest() - - -def _validate_metadata_only(metadata): - """Validate that the files in a metadata-only archive have not changed.""" - problems = [] - for files in metadata["labelled_files"].values(): - for f in files: - disk_path = os.path.join(metadata["base_dir"], f) - try: - sha = sha256_hexdigest(disk_path) - except FileNotFoundError: - problems.append(f"{f}: original file not found") - continue - - expected_sha = metadata["file_digests"][f] - if sha != expected_sha: - problems.append(f"{f}: sha256 mismatch: expected {expected_sha}, got {sha}") - - if problems: - raise ArchiveModifiedError( - "Files in metadata-only archive have been modified:\n" - + "\n".join([f" * {p}" for p in problems]) - ) - - -class Artifact: - """Describes a compiler artifact and defines common logic to archive it for transport.""" - - # A version number written to the archive. - ENCODING_VERSION = 2 - - # A unique string identifying the type of artifact in an archive. Subclasses must redefine this - # variable. - ARTIFACT_TYPE = None - - @classmethod - def unarchive(cls, archive_path, base_dir): - """Unarchive an artifact into base_dir. - - Parameters - ---------- - archive_path : str - Path to the archive file. - base_dir : str - Path to a non-existent, empty directory under which the artifact will live. If working - with a metadata-only archive, this directory will just hold the metadata.json. - - Returns - ------- - Artifact : - The unarchived artifact. - """ - if os.path.exists(base_dir): - raise ValueError(f"base_dir exists: {base_dir}") - - base_dir_parent, base_dir_name = os.path.split(base_dir) - temp_dir = os.path.join(base_dir_parent, f"__tvm__{base_dir_name}") - os.mkdir(temp_dir) - try: - with tarfile.open(archive_path) as tar_f: - tar_f.extractall(temp_dir) - - temp_dir_contents = os.listdir(temp_dir) - if len(temp_dir_contents) != 1: - raise ArtifactBadArchiveError( - "Expected exactly 1 subdirectory at root of archive, got " - f"{temp_dir_contents!r}" - ) - - metadata_path = os.path.join(temp_dir, temp_dir_contents[0], "metadata.json") - if not metadata_path: - raise ArtifactBadArchiveError("No metadata.json found in archive") - - with open(metadata_path) as metadata_f: - metadata = json.load(metadata_f) - - version = metadata.get("version") - if version != cls.ENCODING_VERSION: - raise ArtifactBadArchiveError( - f"archive version: expect {cls.EXPECTED_VERSION}, found {version}" - ) - - metadata_only = metadata.get("metadata_only") - if metadata_only: - _validate_metadata_only(metadata) - - os.rename(os.path.join(temp_dir, temp_dir_contents[0]), base_dir) - - artifact_cls = cls - for sub_cls in cls.__subclasses__(): - if sub_cls.ARTIFACT_TYPE is not None and sub_cls.ARTIFACT_TYPE == metadata.get( - "artifact_type" - ): - artifact_cls = sub_cls - break - - return artifact_cls.from_unarchived( - base_dir if not metadata_only else metadata["base_dir"], - metadata["labelled_files"], - metadata["metadata"], - immobile=metadata.get("immobile"), - ) - finally: - shutil.rmtree(temp_dir) - - @classmethod - def from_unarchived(cls, base_dir, labelled_files, metadata, immobile): - return cls(base_dir, labelled_files, metadata, immobile) - - def __init__(self, base_dir, labelled_files, metadata, immobile=False): - """Create a new artifact. - - Parameters - ---------- - base_dir : str - The path to a directory on disk which contains all the files in this artifact. - labelled_files : Dict[str, str] - A dict mapping a file label to the relative paths of the files that carry that label. - metadata : Dict - A dict containing artitrary JSON-serializable key-value data describing the artifact. - immobile : bool - True when this artifact can't be used after being moved out of its current location on - disk. This can happen when artifacts contain absolute paths or when it's not feasible to - include enough files in the artifact to reliably re-run commands in arbitrary locations. - Setting this flag will cause archive() to raise ImmboileArtifactError. - """ - self.base_dir = os.path.realpath(base_dir) - self.labelled_files = labelled_files - self.metadata = metadata - self.immobile = immobile - - for label, files in labelled_files.items(): - for f in files: - f_path = os.path.join(self.base_dir, f) - if not os.path.lexists(f_path): - raise ArtifactFileNotFoundError(f"{f} (label {label}): not found at {f_path}") - - if os.path.islink(f_path): - link_path = os.path.readlink(f_path) - if os.path.isabs(link_path): - link_fullpath = link_path - else: - link_fullpath = os.path.join(os.path.dirname(f_path), link_path) - - link_fullpath = os.path.realpath(link_fullpath) - if not link_fullpath.startswith(self.base_dir): - raise ArtifactBadSymlinkError( - f"{f} (label {label}): symlink points outside artifact tree" - ) - - def abspath(self, rel_path): - """Return absolute path to the member with the given relative path.""" - return os.path.join(self.base_dir, rel_path) - - def label(self, label): - """Return a list of relative paths to files with the given label.""" - return self.labelled_files[label] - - def label_abspath(self, label): - return [self.abspath(p) for p in self.labelled_files[label]] - - def archive(self, archive_path, metadata_only=False): - """Create a relocatable tar archive of the artifacts. - - Parameters - ---------- - archive_path : str - Path to the tar file to create. Or, path to a directory, under which a tar file will be - created named {base_dir}.tar. - metadata_only : bool - If true, don't archive artifacts; instead, just archive metadata plus original - base_path. A metadata-only archive can be unarchived and used like a regular archive - provided none of the files have changed in their original locations on-disk. - - Returns - ------- - str : - The value of archive_path, after potentially making the computation describe above. - - Raises - ------ - ImmboileArtifactError : - When immobile=True was passed to the constructor. - """ - if self.immobile and not metadata_only: - raise ImmobileArtifactError("This artifact can't be moved") - - if os.path.isdir(archive_path): - archive_path = os.path.join(archive_path, f"{os.path.basename(self.base_dir)}.tar") - - archive_name = os.path.splitext(os.path.basename(archive_path))[0] - with tarfile.open(archive_path, "w") as tar_f: - - def _add_file(name, data, f_type): - tar_info = tarfile.TarInfo(name=name) - tar_info.type = f_type - data_bytes = bytes(data, "utf-8") - tar_info.size = len(data) - tar_f.addfile(tar_info, io.BytesIO(data_bytes)) - - metadata = { - "version": self.ENCODING_VERSION, - "labelled_files": self.labelled_files, - "metadata": self.metadata, - "metadata_only": False, - } - if metadata_only: - metadata["metadata_only"] = True - metadata["base_dir"] = self.base_dir - metadata["immobile"] = self.immobile - metadata["file_digests"] = {} - for files in self.labelled_files.values(): - for f in files: - metadata["file_digests"][f] = sha256_hexdigest(self.abspath(f)) - - _add_file( - f"{archive_name}/metadata.json", - json.dumps(metadata, indent=2, sort_keys=True), - tarfile.REGTYPE, - ) - for dir_path, _, files in os.walk(self.base_dir): - for f in files: - file_path = os.path.join(dir_path, f) - archive_file_path = os.path.join( - archive_name, os.path.relpath(file_path, self.base_dir) - ) - if not os.path.islink(file_path): - tar_f.add(file_path, archive_file_path, recursive=False) - continue - - link_path = os.readlink(file_path) - if not os.path.isabs(link_path): - tar_f.add(file_path, archive_file_path, recursive=False) - continue - - relpath = os.path.relpath(link_path, os.path.dirname(file_path)) - _add_file(archive_file_path, relpath, tarfile.LNKTYPE) - - return archive_path diff --git a/python/tvm/micro/build.py b/python/tvm/micro/build.py index 85932f63e6c4..ae533c17ffa7 100644 --- a/python/tvm/micro/build.py +++ b/python/tvm/micro/build.py @@ -25,7 +25,6 @@ from tvm.contrib import utils from .._ffi import libinfo -from .micro_library import MicroLibrary from . import model_library_format diff --git a/python/tvm/micro/compiler.py b/python/tvm/micro/compiler.py deleted file mode 100644 index 5bc5aba8a1be..000000000000 --- a/python/tvm/micro/compiler.py +++ /dev/null @@ -1,361 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines interfaces and default implementations for compiling and flashing code.""" - -import abc -import glob -import os -import re -import subprocess - -import tvm.target -from . import class_factory -from . import debugger -from . import transport - - -def run_cmd(cmd): - """Runs `cmd` in a subprocess and awaits its completion. - - Parameters - ---------- - cmd : List[str] - list of command-line arguments - - Returns - ------- - output : str - resulting stdout capture from the subprocess - """ - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (output, _) = proc.communicate() - output = output.decode("utf-8") - if proc.returncode != 0: - cmd_str = " ".join(cmd) - msg = f'error while running command "{cmd_str}":\n{output}' - raise RuntimeError(msg) - - -class DetectTargetError(Exception): - """Raised when no target comment was detected in the sources given.""" - - -class NoDefaultToolchainMatchedError(Exception): - """Raised when no default toolchain matches the target string.""" - - -class Compiler(metaclass=abc.ABCMeta): - """The compiler abstraction used with micro TVM.""" - - TVM_TARGET_RE = re.compile(r"^// tvm target: (.*)$") - - @classmethod - def _target_from_sources(cls, sources): - """Determine the target used to generate the given source files. - - Parameters - ---------- - sources : List[str] - The paths to source files to analyze. - - Returns - ------- - tvm.target.Target : - A Target instance reconstructed from the target string listed in the source files. - """ - target_strs = set() - - for obj in sources: - if os.path.splitext(obj)[1] not in (".cc", ".c"): - continue - - with open(obj) as obj_f: - for line in obj_f: - m = cls.TVM_TARGET_RE.match(line) - if m: - target_strs.add(m.group(1)) - - if len(target_strs) != 1: - raise DetectTargetError( - "autodetecting cross-compiler: could not extract TVM target from C source; regex " - f"{cls.TVM_TARGET_RE.pattern} does not match any line in sources: " - f'{", ".join(sources)}' - ) - - target_str = next(iter(target_strs)) - return tvm.target.Target(target_str) - - # Maps regexes identifying CPUs to the default toolchain prefix for that CPU. - TOOLCHAIN_PREFIX_BY_CPU_REGEX = { - r"cortex-[am].*": "arm-none-eabi-", - "x86[_-]64": "", - "native": "", - } - - def _autodetect_toolchain_prefix(self, target): - # Treat absence of -mcpu as if -mcpu=native is specified. The gcc shipped with OS X - # complains if -mcpu=native is given, so this approach allows model targets to avoid - # specifying this flag e.g. for tutorials. - if "mcpu" not in target.attrs: - return self.TOOLCHAIN_PREFIX_BY_CPU_REGEX["native"] - - matches = [] - for regex, prefix in self.TOOLCHAIN_PREFIX_BY_CPU_REGEX.items(): - if re.match(regex, target.attrs["mcpu"]): - matches.append(prefix) - - if matches: - if len(matches) != 1: - raise NoDefaultToolchainMatchedError( - f'{opt} matched more than 1 default toolchain prefix: {", ".join(matches)}. ' - "Specify cc.cross_compiler to create_micro_library()" - ) - - return matches[0] - - raise NoDefaultToolchainMatchedError( - f"target {str(target)} did not match any default toolchains" - ) - - def _defaults_from_target(self, target): - """Determine the default compiler options from the target specified. - - Parameters - ---------- - target : tvm.target.Target - - Returns - ------- - List[str] : - Default options used the configure the compiler for that target. - """ - opts = [] - # TODO use march for arm(https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html)? - if target.attrs.get("mcpu"): - opts.append(f'-mcpu={target.attrs["mcpu"]}') - if target.attrs.get("mfpu"): - opts.append(f'-mfpu={target.attrs["mfpu"]}') - if target.attrs.get("march"): - opts.append(f'-march={target.attrs["march"]}') - - return opts - - @abc.abstractmethod - def library(self, output, sources, options=None): - """Build a library from the given source files. - - Parameters - ---------- - output : str - The path to the library that should be created. The containing directory - is guaranteed to be empty and should be the base_dir for the returned - Artifact. - sources : List[str] - A list of paths to source files that should be compiled. - options : Optional[List[str]] - If given, additional command-line flags to pass to the compiler. - - Returns - ------- - MicroLibrary : - The compiled library, as a MicroLibrary instance. - """ - raise NotImplementedError() - - @abc.abstractmethod - def binary(self, output, objects, options=None, link_main=True, main_options=None): - """Link a binary from the given object and/or source files. - - Parameters - ---------- - output : str - The path to the binary that should be created. The containing directory - is guaranteed to be empty and should be the base_dir for the returned - Artifact. - objects : List[MicroLibrary] - A list of paths to source files or libraries that should be compiled. The final binary - should be statically-linked. - options: Optional[List[str]] - If given, additional command-line flags to pass to the compiler. - link_main: Optional[bool] - True if the standard main entry point for this Compiler should be included in the - binary. False if a main entry point is provided in one of `objects`. - main_options: Optional[List[str]] - If given, additional command-line flags to pass to the compiler when compiling the - main() library. In some cases, the main() may be compiled directly into the final binary - along with `objects` for logistical reasons. In those cases, specifying main_options is - an error and ValueError will be raised. - - Returns - ------- - MicroBinary : - The compiled binary, as a MicroBinary instance. - """ - raise NotImplementedError() - - @property - def flasher_factory(self): - """Produce a FlasherFactory for a Flasher instance suitable for this Compiler.""" - raise NotImplementedError("The Compiler base class doesn't define a flasher.") - - def flasher(self, **kw): - """Return a Flasher that can be used to program a produced MicroBinary onto the target.""" - return self.flasher_factory.override_kw(**kw).instantiate() - - -class IncompatibleTargetError(Exception): - """Raised when source files specify a target that differs from the compiler target.""" - - -class DefaultCompiler(Compiler): - """A Compiler implementation that attempts to use the system-installed GCC.""" - - def __init__(self, target=None): - super(DefaultCompiler, self).__init__() - self.target = target - if isinstance(target, str): - self.target = tvm.target.create(target) - - def library(self, output, sources, options=None): - options = options if options is not None else {} - try: - target = self._target_from_sources(sources) - except DetectTargetError: - assert self.target is not None, ( - "Must specify target= to constructor when compiling sources which don't specify a " - "target" - ) - - target = self.target - - if self.target is not None and str(self.target) != str(target): - raise IncompatibleTargetError( - f"auto-detected target {target} differs from configured {self.target}" - ) - - prefix = self._autodetect_toolchain_prefix(target) - outputs = [s for s in sources if os.path.splitext(s)[1] == ".o"] - sources = [s for s in sources if s not in outputs] - for src in sources: - src_base, src_ext = os.path.splitext(os.path.basename(src)) - - compiler_name = {".c": "gcc", ".cc": "g++", ".cpp": "g++"}[src_ext] - args = [prefix + compiler_name, "-g"] - args.extend(self._defaults_from_target(target)) - - args.extend(options.get(f"{src_ext[1:]}flags", [])) - - for include_dir in options.get("include_dirs", []): - args.extend(["-I", include_dir]) - - output_filename = f"{src_base}.o" - output_abspath = os.path.join(output, output_filename) - run_cmd(args + ["-c", "-o", output_abspath, src]) - outputs.append(output_abspath) - - output_filename = f"{os.path.basename(output)}.a" - output_abspath = os.path.join(output, output_filename) - run_cmd([prefix + "ar", "-r", output_abspath] + outputs) - run_cmd([prefix + "ranlib", output_abspath]) - - return tvm.micro.MicroLibrary(output, [output_filename]) - - def binary(self, output, objects, options=None, link_main=True, main_options=None): - assert self.target is not None, ( - "must specify target= to constructor, or compile sources which specify the target " - "first" - ) - - args = [self._autodetect_toolchain_prefix(self.target) + "g++"] - args.extend(self._defaults_from_target(self.target)) - if options is not None: - args.extend(options.get("ldflags", [])) - - for include_dir in options.get("include_dirs", []): - args.extend(["-I", include_dir]) - - output_filename = os.path.basename(output) - output_abspath = os.path.join(output, output_filename) - args.extend(["-g", "-o", output_abspath]) - - if link_main: - host_main_srcs = glob.glob( - os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host", "*.cc") - ) - if main_options: - main_lib = self.library(os.path.join(output, "host"), host_main_srcs, main_options) - for lib_name in main_lib.library_files: - args.append(main_lib.abspath(lib_name)) - else: - args.extend(host_main_srcs) - - for obj in objects: - for lib_name in obj.library_files: - args.append(obj.abspath(lib_name)) - - run_cmd(args) - return tvm.micro.MicroBinary(output, output_filename, []) - - @property - def flasher_factory(self): - return FlasherFactory(HostFlasher, [], {}) - - -class Flasher(metaclass=abc.ABCMeta): - """An interface for flashing binaries and returning a transport factory.""" - - @abc.abstractmethod - def flash(self, micro_binary): - """Flash a binary onto the device. - - Parameters - ---------- - micro_binary : MicroBinary - A MicroBinary instance. - - Returns - ------- - transport.TransportContextManager : - A ContextManager that can be used to create and tear down an RPC transport layer between - this TVM instance and the newly-flashed binary. - """ - raise NotImplementedError() - - -class FlasherFactory(class_factory.ClassFactory): - """A ClassFactory for Flasher instances.""" - - SUPERCLASS = Flasher - - -class HostFlasher(Flasher): - """A Flasher implementation that spawns a subprocess on the host.""" - - def __init__(self, debug=False): - self.debug = debug - - def flash(self, micro_binary): - if self.debug: - gdb_wrapper = debugger.GdbTransportDebugger( - [micro_binary.abspath(micro_binary.binary_file)] - ) - return transport.DebugWrapperTransport( - debugger=gdb_wrapper, transport=gdb_wrapper.transport() - ) - - return transport.SubprocessTransport([micro_binary.abspath(micro_binary.binary_file)]) diff --git a/python/tvm/micro/micro_binary.py b/python/tvm/micro/micro_binary.py deleted file mode 100644 index 74b760b67650..000000000000 --- a/python/tvm/micro/micro_binary.py +++ /dev/null @@ -1,65 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines an Artifact implementation for representing compiled micro TVM binaries.""" - -from . import artifact - - -class MicroBinary(artifact.Artifact): - """An Artifact that describes a compiled binary.""" - - ARTIFACT_TYPE = "micro_binary" - - @classmethod - def from_unarchived(cls, base_dir, labelled_files, metadata, immobile): - binary_file = labelled_files["binary_file"][0] - del labelled_files["binary_file"] - - debug_files = None - if "debug_files" in labelled_files: - debug_files = labelled_files["debug_files"] - del labelled_files["debug_files"] - - return cls( - base_dir, - binary_file, - debug_files=debug_files, - labelled_files=labelled_files, - metadata=metadata, - immobile=immobile, - ) - - def __init__( - self, - base_dir, - binary_file, - debug_files=None, - labelled_files=None, - metadata=None, - immobile=False, - ): - labelled_files = {} if labelled_files is None else dict(labelled_files) - metadata = {} if metadata is None else dict(metadata) - labelled_files["binary_file"] = [binary_file] - if debug_files is not None: - labelled_files["debug_files"] = debug_files - - super(MicroBinary, self).__init__(base_dir, labelled_files, metadata, immobile=immobile) - - self.binary_file = binary_file - self.debug_files = debug_files diff --git a/python/tvm/micro/micro_library.py b/python/tvm/micro/micro_library.py deleted file mode 100644 index 74687ede1235..000000000000 --- a/python/tvm/micro/micro_library.py +++ /dev/null @@ -1,93 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines an Artifact subclass that describes a compiled static library.""" - -from tvm.contrib import utils -from . import artifact -from . import compiler - - -class MicroLibrary(artifact.Artifact): - """An Artifact that describes a compiled static library.""" - - ARTIFACT_TYPE = "micro_library" - - @classmethod - def from_unarchived(cls, base_dir, labelled_files, metadata, immobile): - library_files = labelled_files["library_files"] - del labelled_files["library_files"] - - debug_files = None - if "debug_files" in labelled_files: - debug_files = labelled_files["debug_files"] - del labelled_files["debug_files"] - - return cls( - base_dir, - library_files, - debug_files=debug_files, - labelled_files=labelled_files, - metadata=metadata, - immobile=immobile, - ) - - def __init__( - self, - base_dir, - library_files, - debug_files=None, - labelled_files=None, - metadata=None, - immobile=False, - ): - labelled_files = {} if labelled_files is None else dict(labelled_files) - metadata = {} if metadata is None else dict(metadata) - labelled_files["library_files"] = library_files - if debug_files is not None: - labelled_files["debug_files"] = debug_files - - super(MicroLibrary, self).__init__(base_dir, labelled_files, metadata, immobile=immobile) - - self.library_files = library_files - self.debug_file = debug_files - - -def create_micro_library(output, objects, options=None): - """Create a MicroLibrary using the default compiler options. - - Parameters - ---------- - output : str - Path to the output file, expected to end in .tar. - objects : List[str] - Paths to the source files to include in the library. - options : Optional[List[str]] - If given, additional command-line flags for the compiler. - """ - temp_dir = utils.tempdir() - comp = compiler.DefaultCompiler() - output = temp_dir.relpath("micro-library.o") - comp.library(output, objects, options=options) - - with open(output, "rb") as output_f: - elf_data = output_f.read() - - # TODO(areusch): Define a mechanism to determine compiler and linker flags for each lib - # enabled by the target str, and embed here. - micro_lib = MicroLibrary("", elf_data, {"target": comp.target.str()}) - micro_lib.save(output) diff --git a/tests/python/unittest/test_link_params.py b/tests/python/unittest/test_link_params.py index 51799bba61fd..ab7603ab1387 100644 --- a/tests/python/unittest/test_link_params.py +++ b/tests/python/unittest/test_link_params.py @@ -356,34 +356,21 @@ def test_crt_link_params(): main_func = mod["main"] target = "c --system-lib --runtime=c --link-params" with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): - graph_json, lib, params = tvm.relay.build(mod, target, params=param_init) - assert set(params.keys()) == {"p0", "p1"} # NOTE: op folded + factory = tvm.relay.build(mod, target, params=param_init) + assert set(factory.get_params().keys()) == {"p0", "p1"} # NOTE: op folded workspace = tvm.micro.Workspace() - compiler = tvm.micro.DefaultCompiler(target=target) - opts = tvm.micro.default_options( - os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") + template_project_dir = os.path.join( + tvm.micro.get_standalone_crt_dir(), "template", "host" ) - opts["bin_opts"]["ldflags"].append("-DTVM_HOST_USE_GRAPH_EXECUTOR_MODULE") - - micro_binary = tvm.micro.build_static_runtime( - workspace, - compiler, - lib, - compiler_options=opts, - extra_libs=[ - tvm.micro.get_standalone_crt_lib(m) - for m in ("memory", "graph_executor_module", "graph_executor") - ], + project = tvm.micro.generate_project( + template_project_dir, factory, workspace.relpath("project"), {"verbose": 1} ) - - flasher_kw = { - "debug": False, - } - flasher = compiler.flasher(**flasher_kw) - with tvm.micro.Session(binary=micro_binary, flasher=flasher) as sess: + project.build() + project.flash() + with tvm.micro.Session(project.transport()) as sess: graph_rt = tvm.micro.session.create_local_graph_executor( - graph_json, sess.get_system_lib(), sess.device + factory.get_graph_json(), sess.get_system_lib(), sess.device ) # NOTE: not setting params here. diff --git a/tests/python/unittest/test_micro_artifact.py b/tests/python/unittest/test_micro_artifact.py deleted file mode 100644 index fc180200720d..000000000000 --- a/tests/python/unittest/test_micro_artifact.py +++ /dev/null @@ -1,149 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Unit tests for the artifact module.""" - -import pytest -import json -import os -import shutil -import tvm - -from tvm.contrib import utils - -pytest.importorskip("tvm.micro") -from tvm.micro import artifact - -FILE_LIST = ["label1", "label2", "label12", "unlabelled"] - - -TEST_METADATA = {"foo": "bar"} - - -TEST_LABELS = {"label1": ["label1", "label12"], "label2": ["label2", "label12"]} - - -def build_artifact(artifact_path, immobile=False): - os.mkdir(artifact_path) - - for f in FILE_LIST: - with open(os.path.join(artifact_path, f), "w") as lib_f: - lib_f.write(f"{f}\n") - - sub_dir = os.path.join(artifact_path, "sub_dir") - os.mkdir(sub_dir) - os.symlink("label1", os.path.join(artifact_path, "rel_symlink")) - os.symlink("label2", os.path.join(artifact_path, "abs_symlink"), "label2") - os.symlink( - os.path.join(artifact_path, "sub_dir"), os.path.join(artifact_path, "abs_dir_symlink") - ) - - from tvm.micro import artifact - - art = artifact.Artifact(artifact_path, TEST_LABELS, TEST_METADATA, immobile=immobile) - - return art - - -@tvm.testing.requires_micro -def test_basic_functionality(): - temp_dir = utils.tempdir() - artifact_path = temp_dir.relpath("foo") - art = build_artifact(artifact_path) - - assert art.abspath("bar") == os.path.join(artifact_path, "bar") - - for label, paths in TEST_LABELS.items(): - assert art.label(label) == paths - assert art.label_abspath(label) == [os.path.join(artifact_path, p) for p in paths] - - -@tvm.testing.requires_micro -def test_archive(): - from tvm.micro import artifact - - temp_dir = utils.tempdir() - art = build_artifact(temp_dir.relpath("foo")) - - # Create archive - archive_path = art.archive(temp_dir.temp_dir) - assert archive_path == temp_dir.relpath("foo.tar") - - # Inspect created archive - unpack_dir = temp_dir.relpath("unpack") - os.mkdir(unpack_dir) - shutil.unpack_archive(archive_path, unpack_dir) - - for path in FILE_LIST: - with open(os.path.join(unpack_dir, "foo", path)) as f: - assert f.read() == f"{path}\n" - - with open(os.path.join(unpack_dir, "foo", "metadata.json")) as metadata_f: - metadata = json.load(metadata_f) - - assert metadata["version"] == 2 - assert metadata["labelled_files"] == TEST_LABELS - assert metadata["metadata"] == TEST_METADATA - - # Unarchive and verify basic functionality - unarchive_base_dir = temp_dir.relpath("unarchive") - unarch = artifact.Artifact.unarchive(archive_path, unarchive_base_dir) - - assert unarch.metadata == TEST_METADATA - assert unarch.labelled_files == TEST_LABELS - for f in FILE_LIST: - assert os.path.exists(os.path.join(unarchive_base_dir, f)) - - -@tvm.testing.requires_micro -def test_metadata_only(): - from tvm.micro import artifact - - temp_dir = utils.tempdir() - base_dir = temp_dir.relpath("foo") - art = build_artifact(base_dir) - - artifact_path = art.archive(temp_dir.relpath("foo.artifact"), metadata_only=True) - unarch_base_dir = temp_dir.relpath("bar") - unarch = artifact.Artifact.unarchive(artifact_path, unarch_base_dir) - assert unarch.base_dir == base_dir - - for p in unarch.label_abspath("label1") + unarch.label_abspath("label2"): - assert os.path.exists(p) - - os.unlink(art.abspath("label1")) - with open(art.abspath("label2"), "w+") as f: - f.write("changed line\n") - - try: - artifact.Artifact.unarchive(artifact_path, os.path.join(temp_dir.temp_dir, "bar2")) - assert False, "unarchive should raise error" - except artifact.ArchiveModifiedError as err: - assert str(err) == ( - "Files in metadata-only archive have been modified:\n" - " * label1: original file not found\n" - " * label2: sha256 mismatch: expected " - "6aa3c5668c8794c791400e19ecd7123949ded1616eafb0395acdd2d896354e83, got " - "ed87db21670a81819d65eccde87c5ae0243b2b61783bf77e9b27993be9a3eca0" - ) - - -if __name__ == "__main__": - test_basic_functionality() - test_archive() - test_metadata_only() - # TODO: tests for dir symlinks, symlinks out of bounds, loading malformed artifact tars. From 3483c10270ce09ea31ba721ccf72bb159101ac20 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Thu, 8 Jul 2021 11:54:54 -0700 Subject: [PATCH 15/47] Delete old Transport implementations and simplify module --- python/tvm/micro/__init__.py | 2 +- .../micro/{transport/base.py => transport.py} | 0 python/tvm/micro/transport/__init__.py | 27 ---- python/tvm/micro/transport/debug.py | 64 --------- python/tvm/micro/transport/file_descriptor.py | 119 --------------- python/tvm/micro/transport/serial.py | 135 ------------------ python/tvm/micro/transport/subprocess.py | 67 --------- python/tvm/micro/transport/wakeup.py | 79 ---------- 8 files changed, 1 insertion(+), 492 deletions(-) rename python/tvm/micro/{transport/base.py => transport.py} (100%) delete mode 100644 python/tvm/micro/transport/__init__.py delete mode 100644 python/tvm/micro/transport/debug.py delete mode 100644 python/tvm/micro/transport/file_descriptor.py delete mode 100644 python/tvm/micro/transport/serial.py delete mode 100644 python/tvm/micro/transport/subprocess.py delete mode 100644 python/tvm/micro/transport/wakeup.py diff --git a/python/tvm/micro/__init__.py b/python/tvm/micro/__init__.py index 3b8c27bcf985..ab5f749ee65b 100644 --- a/python/tvm/micro/__init__.py +++ b/python/tvm/micro/__init__.py @@ -26,4 +26,4 @@ Session, SessionTerminatedError, ) -from .transport import TransportLogger, DebugWrapperTransport, SubprocessTransport +from .transport import TransportLogger diff --git a/python/tvm/micro/transport/base.py b/python/tvm/micro/transport.py similarity index 100% rename from python/tvm/micro/transport/base.py rename to python/tvm/micro/transport.py diff --git a/python/tvm/micro/transport/__init__.py b/python/tvm/micro/transport/__init__.py deleted file mode 100644 index dffe9ae32792..000000000000 --- a/python/tvm/micro/transport/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines abstractions and implementations related to the microTVM RPC transport layer.""" - -from .base import IoTimeoutError -from .base import Transport -from .base import TransportClosedError -from .base import TransportLogger -from .base import TransportTimeouts -from .base import debug_transport_timeouts -from .debug import DebugWrapperTransport -from .subprocess import SubprocessTransport diff --git a/python/tvm/micro/transport/debug.py b/python/tvm/micro/transport/debug.py deleted file mode 100644 index 71e12c7ed391..000000000000 --- a/python/tvm/micro/transport/debug.py +++ /dev/null @@ -1,64 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines a wrapper Transport class that launches a debugger before opening.""" - -from .base import Transport, TransportTimeouts - - -class DebugWrapperTransport(Transport): - """A Transport wrapper class that launches a debugger before opening the transport. - - This is primiarly useful when debugging the other end of a SubprocessTransport. It allows you - to pipe data through the GDB process to drive the subprocess with a debugger attached. - """ - - def __init__(self, debugger, transport, disable_session_start_retry=False): - self.debugger = debugger - self.transport = transport - self.disable_session_start_retry = disable_session_start_retry - - def timeouts(self): - child_timeouts = self.transport.timeouts() - return TransportTimeouts( - session_start_retry_timeout_sec=( - 0 - if self.disable_session_start_retry - else child_timeouts.session_start_retry_timeout_sec - ), - session_start_timeout_sec=0, - session_established_timeout_sec=0, - ) - - def open(self): - self.debugger.start() - - try: - self.transport.open() - except Exception: - self.debugger.stop() - raise - - def write(self, data, timeout_sec): - return self.transport.write(data, timeout_sec) - - def read(self, n, timeout_sec): - return self.transport.read(n, timeout_sec) - - def close(self): - self.transport.close() - self.debugger.stop() diff --git a/python/tvm/micro/transport/file_descriptor.py b/python/tvm/micro/transport/file_descriptor.py deleted file mode 100644 index 58c4026f6704..000000000000 --- a/python/tvm/micro/transport/file_descriptor.py +++ /dev/null @@ -1,119 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines an implementation of Transport that uses file descriptors.""" - -import fcntl -import os -import select -import time -from . import base - - -class FdConfigurationError(Exception): - """Raised when specified file descriptors can't be placed in non-blocking mode.""" - - -class FdTransport(base.Transport): - """A Transport implementation that implements timeouts using non-blocking I/O.""" - - @classmethod - def _validate_configure_fd(cls, file_descriptor): - file_descriptor = ( - file_descriptor if isinstance(file_descriptor, int) else file_descriptor.fileno() - ) - flag = fcntl.fcntl(file_descriptor, fcntl.F_GETFL) - if flag & os.O_NONBLOCK != 0: - return file_descriptor - - fcntl.fcntl(file_descriptor, fcntl.F_SETFL, os.O_NONBLOCK | flag) - new_flag = fcntl.fcntl(file_descriptor, fcntl.F_GETFL) - if (new_flag & os.O_NONBLOCK) == 0: - raise FdConfigurationError( - f"Cannot set file descriptor {file_descriptor} to non-blocking" - ) - return file_descriptor - - def __init__(self, read_fd, write_fd, timeouts): - self.read_fd = self._validate_configure_fd(read_fd) - self.write_fd = self._validate_configure_fd(write_fd) - self._timeouts = timeouts - - def timeouts(self): - return self._timeouts - - def open(self): - pass - - def close(self): - if self.read_fd is not None: - os.close(self.read_fd) - self.read_fd = None - - if self.write_fd is not None: - os.close(self.write_fd) - self.write_fd = None - - def _await_ready(self, rlist, wlist, timeout_sec=None, end_time=None): - if end_time is None: - return True - - if timeout_sec is None: - timeout_sec = max(0, end_time - time.monotonic()) - rlist, wlist, xlist = select.select(rlist, wlist, rlist + wlist, timeout_sec) - if not rlist and not wlist and not xlist: - raise base.IoTimeoutError() - - return True - - def read(self, n, timeout_sec): - if self.read_fd is None: - raise base.TransportClosedError() - - end_time = None if timeout_sec is None else time.monotonic() + timeout_sec - - while True: - self._await_ready([self.read_fd], [], end_time=end_time) - try: - to_return = os.read(self.read_fd, n) - break - except BlockingIOError: - pass - - if not to_return: - self.close() - raise base.TransportClosedError() - - return to_return - - def write(self, data, timeout_sec): - if self.write_fd is None: - raise base.TransportClosedError() - - end_time = None if timeout_sec is None else time.monotonic() + timeout_sec - - data_len = len(data) - while data: - self._await_ready(end_time, [], [self.write_fd]) - num_written = os.write(self.write_fd, data) - if not num_written: - self.close() - raise base.TransportClosedError() - - data = data[num_written:] - - return data_len diff --git a/python/tvm/micro/transport/serial.py b/python/tvm/micro/transport/serial.py deleted file mode 100644 index dc107d68abc2..000000000000 --- a/python/tvm/micro/transport/serial.py +++ /dev/null @@ -1,135 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines a Transport implementation using pyserial.""" - -import atexit -import time -import serial -import serial.tools.list_ports -from .base import IoTimeoutError, Transport, TransportTimeouts - - -_DEFAULT_SERIAL_TIMEOUTS = TransportTimeouts( - session_start_retry_timeout_sec=5, - session_start_timeout_sec=10.0, - session_established_timeout_sec=30.0, -) - - -class SerialTransport(Transport): - """A Transport implementation using pySerial.""" - - _OPEN_PORTS = [] - - @classmethod - def close_atexit(cls): - """Close all serial ports before exit. - - Some USB-UART kernel drivers are particularly sensitive to being left open (i.e. require - unplugging and replugging of attached hardware or reboot of machine); try very hard to - close all serial ports at exit. - """ - for port in cls._OPEN_PORTS: - try: - port.close() - except Exception: # pylint: disable=broad-except - _LOG.warn("exception closing port", exc_info=True) - - cls._OPEN_PORTS = [] - - def __init__(self, grep=None, port_path=None, timeouts=None, **kw): - self._port_path = port_path - self._grep = grep - self._timeouts = timeouts if timeouts is not None else _DEFAULT_SERIAL_TIMEOUTS - self._kw = kw - if self._port_path is None and self._grep is None: - raise SerialPortNotFoundError("Must specify one of grep= or port_path=") - - def timeouts(self): - return self._timeouts - - def open(self): - if self._port_path is not None: - port_path = self._port_path - else: - ports = list(serial.tools.list_ports.grep(self._grep)) - if len(ports) != 1: - raise SerialPortNotFoundError( - f"grep expression should find 1 serial port; found {ports!r}" - ) - - port_path = ports[0].device - - self._port = serial.Serial(port_path, timeout=0.1, exclusive=True, **self._kw) - self._port.cancel_read() - self._port.reset_input_buffer() - self._port.reset_output_buffer() - self._OPEN_PORTS.append(self._port) - - def close(self): - if self._port is None: - return - - self._port.close() - self._OPEN_PORTS.remove(self._port) - self._port = None - - def read(self, n, timeout_sec): - if timeout_sec is None: - self._port.timeout = None - in_waiting = self._port.in_waiting - if in_waiting > 0: - return self._port.read(min(n, in_waiting)) - return self._port.read(1) - - end_time = time.monotonic() + timeout_sec - to_return = bytearray() - while True: - timeout_remaining = end_time - time.monotonic() - if timeout_sec != 0 and timeout_remaining < 0: - break - - # Read until *something* can be returned. If nothing is sent within 5 chars' time, stop. - # 5 is an arbitrary number. - self._port.timeout = 1 / self._port.baudrate * 5 - try: - data = self._port.read(n if timeout_sec != 0 else 1) - if not data and to_return: - break - - to_return.extend(data) - except serial.SerialTimeoutException: - if to_return: - break - - if not to_return: - raise IoTimeoutError() - - return to_return - - def write(self, data, timeout_sec): - self._port.write_timeout = timeout_sec - try: - to_return = self._port.write(data) - self._port.flush() - return to_return - except serial.SerialTimeoutException: - raise IoTimeoutError() - - -atexit.register(SerialTransport.close_atexit) diff --git a/python/tvm/micro/transport/subprocess.py b/python/tvm/micro/transport/subprocess.py deleted file mode 100644 index 4de1fa1266d3..000000000000 --- a/python/tvm/micro/transport/subprocess.py +++ /dev/null @@ -1,67 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines an implementation of Transport that uses subprocesses.""" - -import subprocess -from . import base -from . import file_descriptor - - -class SubprocessFdTransport(file_descriptor.FdTransport): - def timeouts(self): - raise NotImplementedError() - - -class SubprocessTransport(base.Transport): - """A Transport implementation that uses a subprocess's stdin/stdout as the channel.""" - - def __init__(self, args, max_startup_latency_sec=5.0, max_latency_sec=5.0, **kwargs): - self.max_startup_latency_sec = max_startup_latency_sec - self.max_latency_sec = max_latency_sec - self.args = args - self.kwargs = kwargs - self.popen = None - self.child_transport = None - - def timeouts(self): - return base.TransportTimeouts( - session_start_retry_timeout_sec=0, - session_start_timeout_sec=self.max_startup_latency_sec, - session_established_timeout_sec=self.max_latency_sec, - ) - - def open(self): - self.kwargs["stdout"] = subprocess.PIPE - self.kwargs["stdin"] = subprocess.PIPE - self.kwargs["bufsize"] = 0 - self.popen = subprocess.Popen(self.args, **self.kwargs) - self.child_transport = SubprocessFdTransport( - self.popen.stdout, self.popen.stdin, self.timeouts() - ) - - def write(self, data, timeout_sec): - return self.child_transport.write(data, timeout_sec) - - def read(self, n, timeout_sec): - return self.child_transport.read(n, timeout_sec) - - def close(self): - if self.child_transport is not None: - self.child_transport.close() - - self.popen.terminate() diff --git a/python/tvm/micro/transport/wakeup.py b/python/tvm/micro/transport/wakeup.py deleted file mode 100644 index 418f8bdbb27a..000000000000 --- a/python/tvm/micro/transport/wakeup.py +++ /dev/null @@ -1,79 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines an implementation of Transport that uses subprocesses.""" - -import logging -import time -from . import base - - -_LOG = logging.getLogger(__name__) - - -class WakeupTransport(base.Transport): - """A Transport implementation that waits for a "wakeup sequence" from the remote end.""" - - def __init__(self, child_transport, wakeup_sequence): - self.child_transport = child_transport - self.wakeup_sequence = bytes(wakeup_sequence) - self.wakeup_sequence_buffer = bytearray() - self.line_start_index = 0 - self.found_wakeup_sequence = False - - def open(self): - return self.child_transport.open() - - def close(self): - return self.child_transport.close() - - def timeouts(self): - return self.child_transport.timeouts() - - def _await_wakeup(self, end_time): - def _time_remaining(): - if end_time is None: - return None - return max(0, end_time - time.monotonic()) - - if not self.found_wakeup_sequence: - while self.wakeup_sequence not in self.wakeup_sequence_buffer: - x = self.child_transport.read(1, _time_remaining()) - self.wakeup_sequence_buffer.extend(x) - if x[0] in (b"\n", b"\xff"): - _LOG.debug("%s", self.wakeup_sequence_buffer[self.line_start_index : -1]) - self.line_start_index = len(self.wakeup_sequence_buffer) - - _LOG.info("remote side woke up!") - self.found_wakeup_sequence = True - time.sleep(0.2) - - return _time_remaining() - - def read(self, n, timeout_sec): - if not self.found_wakeup_sequence: - end_time = None if timeout_sec is None else time.monotonic() + timeout_sec - timeout_sec = self._await_wakeup(end_time) - - return self.child_transport.read(n, timeout_sec) - - def write(self, data, timeout_sec): - if not self.found_wakeup_sequence: - end_time = None if timeout_sec is None else time.monotonic() + timeout_sec - timeout_sec = self._await_wakeup(end_time) - - return self.child_transport.write(data, timeout_sec) From 1a1d44ea282769bf88f5a44f722835d3646d0000 Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Fri, 9 Jul 2021 16:39:55 -0700 Subject: [PATCH 16/47] lint --- .../template_project/microtvm_api_server.py | 99 +++++---- .../qemu-hack/qemu-system-xilinx-aarch64 | 0 python/tvm/micro/build.py | 1 - python/tvm/micro/model_library_format.py | 4 +- python/tvm/micro/project.py | 54 ++--- python/tvm/micro/project_api/client.py | 93 ++++++--- python/tvm/micro/project_api/server.py | 191 +++++++++++------- python/tvm/micro/transport.py | 9 +- .../crt/graph_executor/graph_executor.c | 11 +- src/runtime/crt/host/microtvm_api_server.py | 32 ++- .../crt/microtvm_rpc_common/framing.cc | 24 +-- src/runtime/micro/crt_config.h | 6 +- src/runtime/micro/micro_session.cc | 2 +- tests/lint/check_file_type.py | 35 +--- tests/micro/zephyr/test_zephyr.py | 17 +- tests/micro/zephyr/test_zephyr_aot.py | 30 ++- tests/python/relay/aot/aot_test_utils.py | 27 ++- tests/python/unittest/test_crt.py | 6 +- .../test_micro_model_library_format.py | 18 +- .../python/unittest/test_micro_project_api.py | 87 +++++--- 20 files changed, 442 insertions(+), 304 deletions(-) rename apps/microtvm/zephyr/{ => template_project}/qemu-hack/qemu-system-xilinx-aarch64 (100%) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index c48a3a22447a..3195ff759de5 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -40,7 +40,7 @@ def check_call(cmd_args, *args, **kwargs): - cwd_str = '' if 'cwd' not in kwargs else f" (in cwd: {kwargs['cwd']})" + cwd_str = "" if "cwd" not in kwargs else f" (in cwd: {kwargs['cwd']})" _LOG.info("run%s: %s", cwd_str, " ".join(shlex.quote(a) for a in cmd_args)) return subprocess.check_call(cmd_args, *args, **kwargs) @@ -55,7 +55,6 @@ def check_call(cmd_args, *args, **kwargs): class CMakeCache(collections.Mapping): - def __init__(self, path): self._path = path self._dict = None @@ -123,7 +122,9 @@ def _get_device_args(options): raise BoardError( f"Don't know how to find serial terminal for board {CMAKE_CACHE['BOARD']} with flash " - f"runner {flash_runner}") + f"runner {flash_runner}" + ) + # kwargs passed to usb.core.find to find attached boards for the openocd flash runner. BOARD_USB_FIND_KW = { @@ -132,6 +133,7 @@ def _get_device_args(options): "stm32f746g_disco": {"idVendor": 0x0483, "idProduct": 0x374B}, } + def openocd_serial(options): """Find the serial port to use for a board with OpenOCD flash strategy.""" if "openocd_serial" in options: @@ -163,24 +165,21 @@ def _get_nrf_device_args(options): nrfjprog_args = ["nrfjprog", "--ids"] nrfjprog_ids = subprocess.check_output(nrfjprog_args, encoding="utf-8") if not nrfjprog_ids.strip("\n"): - raise BoardAutodetectFailed( - f'No attached boards recognized by {" ".join(nrfjprog_args)}' - ) + raise BoardAutodetectFailed(f'No attached boards recognized by {" ".join(nrfjprog_args)}') boards = nrfjprog_ids.split("\n")[:-1] if len(boards) > 1: - if options['nrfjprog_snr'] is None: + if options["nrfjprog_snr"] is None: raise BoardError( - "Multiple boards connected; specify one with nrfjprog_snr=: " - f'{", ".join(boards)}' + "Multiple boards connected; specify one with nrfjprog_snr=: " f'{", ".join(boards)}' ) - if str(options['nrfjprog_snr']) not in boards: + if str(options["nrfjprog_snr"]) not in boards: raise BoardError( f"nrfjprog_snr ({options['nrfjprog_snr']}) not found in {nrfjprog_args}: {boards}" ) - return ["--snr", options['nrfjprog_snr']] + return ["--snr", options["nrfjprog_snr"]] if not boards: return [] @@ -197,7 +196,9 @@ def _get_nrf_device_args(options): PROJECT_OPTIONS = [ server.ProjectOption( - "extra_files", help="If given, during generate_project, uncompress the tarball at this path into the project dir"), + "extra_files", + help="If given, during generate_project, uncompress the tarball at this path into the project dir", + ), server.ProjectOption( "gdbserver_port", help=("If given, port number to use when running the local gdbserver") ), @@ -217,17 +218,19 @@ def _get_nrf_device_args(options): choices=tuple(PROJECT_TYPES), ), server.ProjectOption("verbose", help="Run build with verbose output"), - server.ProjectOption("west_cmd", - help=("Path to the west tool. If given, supersedes both the zephyr_base " - "option and ZEPHYR_BASE environment variable.")), - server.ProjectOption("zephyr_base", - help="Path to the zephyr base directory."), + server.ProjectOption( + "west_cmd", + help=( + "Path to the west tool. If given, supersedes both the zephyr_base " + "option and ZEPHYR_BASE environment variable." + ), + ), + server.ProjectOption("zephyr_base", help="Path to the zephyr base directory."), server.ProjectOption("zephyr_board", help="Name of the Zephyr board to build for"), ] class Handler(server.ProjectAPIHandler): - def __init__(self): super(Handler, self).__init__() self._proc = None @@ -236,8 +239,11 @@ def server_info_query(self): return server.ServerInfo( platform_name="zephyr", is_template=IS_TEMPLATE, - model_library_format_path="" if IS_TEMPLATE else (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH), - project_options=PROJECT_OPTIONS) + model_library_format_path="" + if IS_TEMPLATE + else (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH), + project_options=PROJECT_OPTIONS, + ) # These files and directories will be recursively copied into generated projects from the CRT. CRT_COPY_ITEMS = ("include", "Makefile", "src") @@ -251,31 +257,14 @@ def _create_prj_conf(self, project_dir, options): "CONFIG_UART_INTERRUPT_DRIVEN=y\n" "\n" ) - f.write( - "# For TVMPlatformAbort().\n" - "CONFIG_REBOOT=y\n" - "\n" - ) + f.write("# For TVMPlatformAbort().\n" "CONFIG_REBOOT=y\n" "\n") - if True: # options["project_type"] == "host_driven": - f.write( - "# For RPC server C++ bindings.\n" - "CONFIG_CPLUSPLUS=y\n" - "\n" - ) + if True: # options["project_type"] == "host_driven": + f.write("# For RPC server C++ bindings.\n" "CONFIG_CPLUSPLUS=y\n" "\n") - f.write( - "# For math routines\n" - "CONFIG_NEWLIB_LIBC=y\n" - "\n" - ) + f.write("# For math routines\n" "CONFIG_NEWLIB_LIBC=y\n" "\n") - - f.write( - "# For models with floating point.\n" - "CONFIG_FPU=y\n" - "\n" - ) + f.write("# For models with floating point.\n" "CONFIG_FPU=y\n" "\n") main_stack_size = None if self._is_qemu(options) and options["project_type"] == "host_driven": @@ -285,10 +274,7 @@ def _create_prj_conf(self, project_dir, options): if main_stack_size is not None: f.write(f"CONFIG_MAIN_STACK_SIZE={main_stack_size}\n") - f.write( - "# For random number generation.\n" - "CONFIG_TEST_RANDOM_GENERATOR=y\n" - ) + f.write("# For random number generation.\n" "CONFIG_TEST_RANDOM_GENERATOR=y\n") if self._is_qemu(options): f.write("CONFIG_TIMER_RANDOM_GENERATOR=y\n") @@ -352,7 +338,9 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Populate crt-config.h crt_config_dir = project_dir / "crt_config" crt_config_dir.mkdir() - shutil.copy2(API_SERVER_DIR / "crt_config" / "crt_config.h", crt_config_dir / "crt_config.h") + shutil.copy2( + API_SERVER_DIR / "crt_config" / "crt_config.h", crt_config_dir / "crt_config.h" + ) # Populate src/ src_dir = project_dir / "src" @@ -385,10 +373,12 @@ def build(self, options): @classmethod def _is_qemu(cls, options): return ( - "qemu" in options["zephyr_board"] or + "qemu" in options["zephyr_board"] + or # NOTE: mps2_an521 naming breaks the rule that "qemu" is included when qemu is the # launch method. - "mps2_an521" in options["zephyr_board"]) + "mps2_an521" in options["zephyr_board"] + ) def flash(self, options): if self._is_qemu(options): @@ -444,7 +434,6 @@ def _set_nonblock(fd): class ZephyrSerialTransport: - @classmethod def _lookup_baud_rate(cls, options): zephyr_base = options.get("zephyr_base", os.environ["ZEPHYR_BASE"]) @@ -483,8 +472,10 @@ def _find_openocd_serial_port(cls, options): serial_number = openocd_serial(options) ports = [p for p in serial.tools.list_ports.grep(serial_number)] if len(ports) != 1: - raise Exception(f"_find_openocd_serial_port: expected 1 port to match {serial_number}, " - f"found: {ports!r}") + raise Exception( + f"_find_openocd_serial_port: expected 1 port to match {serial_number}, " + f"found: {ports!r}" + ) return ports[0].device @@ -535,6 +526,7 @@ def write(self, data, timeout_sec): data = data[n:] bytes_written += n + class ZephyrQemuMakeResult(enum.Enum): QEMU_STARTED = "qemu_started" MAKE_FAILED = "make_failed" @@ -659,5 +651,6 @@ def _wait_for_qemu(self): raise ValueError(f"{item} not expected.") -if __name__ == '__main__': + +if __name__ == "__main__": server.main(Handler()) diff --git a/apps/microtvm/zephyr/qemu-hack/qemu-system-xilinx-aarch64 b/apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-xilinx-aarch64 similarity index 100% rename from apps/microtvm/zephyr/qemu-hack/qemu-system-xilinx-aarch64 rename to apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-xilinx-aarch64 diff --git a/python/tvm/micro/build.py b/python/tvm/micro/build.py index ae533c17ffa7..20f513d25d8e 100644 --- a/python/tvm/micro/build.py +++ b/python/tvm/micro/build.py @@ -25,7 +25,6 @@ from tvm.contrib import utils from .._ffi import libinfo -from . import model_library_format _LOG = logging.getLogger(__name__) diff --git a/python/tvm/micro/model_library_format.py b/python/tvm/micro/model_library_format.py index 738060be533d..239a7952c750 100644 --- a/python/tvm/micro/model_library_format.py +++ b/python/tvm/micro/model_library_format.py @@ -174,7 +174,9 @@ def _build_function_memory_map(function_metadata): # 2. BYOC operator implementations do not currently export useful FunctionInfo. if func_name == MAIN_FUNC_NAME_STR or not finfo.tir_primfuncs: continue - assert len(finfo.constant_sizes.items()) == num_targets, f"{func_name}: found {finfo.constant_sizes!r} vs {num_targets}" + assert ( + len(finfo.constant_sizes.items()) == num_targets + ), f"{func_name}: found {finfo.constant_sizes!r} vs {num_targets}" assert len(finfo.io_sizes.items()) == num_targets target = finfo.workspace_sizes.items()[i][0] workspace_size = finfo.workspace_sizes.items()[i][1] diff --git a/python/tvm/micro/project.py b/python/tvm/micro/project.py index 2d353d37a9a2..d870a702af23 100644 --- a/python/tvm/micro/project.py +++ b/python/tvm/micro/project.py @@ -8,9 +8,10 @@ class ProjectTransport(Transport): + """A Transport implementation that uses the Project API client.""" - def __init__(self, client, options): - self._client = client + def __init__(self, api_client, options): + self._api_client = api_client self._options = options self._timeouts = None @@ -19,18 +20,18 @@ def timeouts(self): return self._timeouts def open(self): - reply = self._client.open_transport(self._options) + reply = self._api_client.open_transport(self._options) self._timeouts = TransportTimeouts(**reply["timeouts"]) def close(self): - if not self._client.shutdown: - self._client.close_transport() + if not self._api_client.shutdown: + self._api_client.close_transport() def write(self, data, timeout_sec): - self._client.write_transport(data, timeout_sec) + self._api_client.write_transport(data, timeout_sec) def read(self, n, timeout_sec): - return self._client.read_transport(n, timeout_sec)["data"] + return self._api_client.read_transport(n, timeout_sec)["data"] class TemplateProjectError(Exception): @@ -44,56 +45,59 @@ class GeneratedProject: def from_directory(cls, project_dir, options): return cls(client.instantiate_from_dir(project_dir), options) - def __init__(self, client, options): - self._client = client + def __init__(self, api_client, options): + self._api_client = api_client self._options = options - self._info = self._client.server_info_query() - if self._info['is_template']: + self._info = self._api_client.server_info_query() + if self._info["is_template"]: raise TemplateProjectError() def build(self): - self._client.build(self._options) + self._api_client.build(self._options) def flash(self): - self._client.flash(self._options) + self._api_client.flash(self._options) def transport(self): - return ProjectTransport(self._client, self._options) + return ProjectTransport(self._api_client, self._options) class NotATemplateProjectError(Exception): - """Raised when the Project API server given to TemplateProject reports is_template=false.""" + """Raised when the API server given to TemplateProject reports is_template=false.""" class TemplateProject: + """Defines a glue interface to interact with a template project through the API Server.""" @classmethod def from_directory(cls, template_project_dir, options): return cls(client.instantiate_from_dir(template_project_dir), options) - def __init__(self, client, options): - self._client = client + def __init__(self, api_client, options): + self._api_client = api_client self._options = options - self._info = self._client.server_info_query() - if not self._info['is_template']: + self._info = self._api_client.server_info_query() + if not self._info["is_template"]: raise NotATemplateProjectError() def generate_project(self, graph_executor_factory, project_dir): """Generate a project given GraphRuntimeFactory.""" model_library_dir = utils.tempdir() - model_library_format_path = model_library_dir.relpath('model.tar') - export_model_library_format( - graph_executor_factory, model_library_format_path) + model_library_format_path = model_library_dir.relpath("model.tar") + export_model_library_format(graph_executor_factory, model_library_format_path) - self._client.generate_project( + self._api_client.generate_project( model_library_format_path=model_library_format_path, standalone_crt_dir=get_standalone_crt_dir(), project_dir=project_dir, - options=self._options) + options=self._options, + ) return GeneratedProject.from_directory(project_dir, self._options) -def generate_project(template_project_dir : str, graph_executor_factory, project_dir : str, options : dict = None): +def generate_project( + template_project_dir: str, graph_executor_factory, project_dir: str, options: dict = None +): template = TemplateProject.from_directory(template_project_dir, options) return template.generate_project(graph_executor_factory, project_dir) diff --git a/python/tvm/micro/project_api/client.py b/python/tvm/micro/project_api/client.py index b1f5e479e0c0..de204bf41136 100644 --- a/python/tvm/micro/project_api/client.py +++ b/python/tvm/micro/project_api/client.py @@ -33,23 +33,27 @@ class ProjectAPIServerNotFoundError(ProjectAPIErrorBase): class RPCError(ProjectAPIErrorBase): - def __init__(self, request, error): self.request = request self.error = error def __str__(self): - return (f"Calling project API method {self.request['method']}:" "\n" - f"{self.error}") + return f"Calling project API method {self.request['method']}:" "\n" f"{self.error}" class ProjectAPIClient: """A client for the Project API.""" - def __init__(self, read_file : typing.BinaryIO, write_file : typing.BinaryIO, - testonly_did_write_request : typing.Optional[typing.Callable] = None): - self.read_file = io.TextIOWrapper(read_file, encoding='UTF-8', errors='strict') - self.write_file = io.TextIOWrapper(write_file, encoding='UTF-8', errors='strict', write_through=True) + def __init__( + self, + read_file: typing.BinaryIO, + write_file: typing.BinaryIO, + testonly_did_write_request: typing.Optional[typing.Callable] = None, + ): + self.read_file = io.TextIOWrapper(read_file, encoding="UTF-8", errors="strict") + self.write_file = io.TextIOWrapper( + write_file, encoding="UTF-8", errors="strict", write_through=True + ) self.testonly_did_write_request = testonly_did_write_request self.next_request_id = 1 @@ -79,7 +83,7 @@ def _request_reply(self, method, params): request_str = json.dumps(request) self.write_file.write(request_str) _LOG.debug("send -> %s", request_str) - self.write_file.write('\n') + self.write_file.write("\n") if self.testonly_did_write_request: self.testonly_did_write_request() # Allow test to assert on server processing. reply_line = self.read_file.readline() @@ -93,11 +97,13 @@ def _request_reply(self, method, params): if reply.get("jsonrpc") != "2.0": raise MalformedReplyError( f"Server reply should include 'jsonrpc': '2.0'; " - f"saw jsonrpc={reply.get('jsonrpc')!r}") + f"saw jsonrpc={reply.get('jsonrpc')!r}" + ) if reply["id"] != request["id"]: raise MismatchedIdError( - f"Reply id ({reply['id']}) does not equal request id ({request['id']}") + f"Reply id ({reply['id']}) does not equal request id ({request['id']}" + ) if "error" in reply: raise server.JSONRPCError.from_json(f"calling method {method}", reply["error"]) @@ -109,42 +115,59 @@ def _request_reply(self, method, params): def server_info_query(self): return self._request_reply("server_info_query", {}) - def generate_project(self, model_library_format_path : str, standalone_crt_dir : str, project_dir : str, options : dict = None): - return self._request_reply("generate_project", {"model_library_format_path": model_library_format_path, - "standalone_crt_dir": standalone_crt_dir, - "project_dir": project_dir, - "options": (options if options is not None else {})}) - - def build(self, options : dict = None): + def generate_project( + self, + model_library_format_path: str, + standalone_crt_dir: str, + project_dir: str, + options: dict = None, + ): + return self._request_reply( + "generate_project", + { + "model_library_format_path": model_library_format_path, + "standalone_crt_dir": standalone_crt_dir, + "project_dir": project_dir, + "options": (options if options is not None else {}), + }, + ) + + def build(self, options: dict = None): return self._request_reply("build", {"options": (options if options is not None else {})}) - def flash(self, options : dict = None): + def flash(self, options: dict = None): return self._request_reply("flash", {"options": (options if options is not None else {})}) - def open_transport(self, options : dict = None): - return self._request_reply("open_transport", {"options": (options if options is not None else {})}) + def open_transport(self, options: dict = None): + return self._request_reply( + "open_transport", {"options": (options if options is not None else {})} + ) def close_transport(self): return self._request_reply("close_transport", {}) def read_transport(self, n, timeout_sec): reply = self._request_reply("read_transport", {"n": n, "timeout_sec": timeout_sec}) - reply['data'] = base64.b85decode(reply['data']) + reply["data"] = base64.b85decode(reply["data"]) return reply def write_transport(self, data, timeout_sec): - return self._request_reply("write_transport", {"data": str(base64.b85encode(data), 'utf-8'), - "timeout_sec": timeout_sec}) + return self._request_reply( + "write_transport", + {"data": str(base64.b85encode(data), "utf-8"), "timeout_sec": timeout_sec}, + ) # NOTE: windows support untested -SERVER_LAUNCH_SCRIPT_FILENAME = f"launch_microtvm_api_server.{'sh' if os.system != 'win32' else '.bat'}" +SERVER_LAUNCH_SCRIPT_FILENAME = ( + f"launch_microtvm_api_server.{'sh' if os.system != 'win32' else '.bat'}" +) SERVER_PYTHON_FILENAME = "microtvm_api_server.py" -def instantiate_from_dir(project_dir : str, debug : bool = False): +def instantiate_from_dir(project_dir: str, debug: bool = False): """Launch server located in project_dir, and instantiate a Project API Client connected to it.""" args = None @@ -158,20 +181,24 @@ def instantiate_from_dir(project_dir : str, debug : bool = False): if args is None: raise ProjectAPIServerNotFoundError( - f"No Project API server found in project directory: {project_dir}" "\n" - f"Tried: {SERVER_LAUNCH_SCRIPT_FILENAME}, {SERVER_PYTHON_FILENAME}") + f"No Project API server found in project directory: {project_dir}" + "\n" + f"Tried: {SERVER_LAUNCH_SCRIPT_FILENAME}, {SERVER_PYTHON_FILENAME}" + ) api_server_read_fd, tvm_write_fd = os.pipe() tvm_read_fd, api_server_write_fd = os.pipe() - args.extend(["--read-fd", str(api_server_read_fd), - "--write-fd", str(api_server_write_fd)]) + args.extend(["--read-fd", str(api_server_read_fd), "--write-fd", str(api_server_write_fd)]) if debug: - args.append("--debug") + args.append("--debug") - api_server_proc = subprocess.Popen(args, bufsize=0, pass_fds=(api_server_read_fd, api_server_write_fd), - cwd=project_dir) + api_server_proc = subprocess.Popen( + args, bufsize=0, pass_fds=(api_server_read_fd, api_server_write_fd), cwd=project_dir + ) os.close(api_server_read_fd) os.close(api_server_write_fd) - return ProjectAPIClient(os.fdopen(tvm_read_fd, 'rb', buffering=0), os.fdopen(tvm_write_fd, 'wb', buffering=0)) + return ProjectAPIClient( + os.fdopen(tvm_read_fd, "rb", buffering=0), os.fdopen(tvm_write_fd, "wb", buffering=0) + ) diff --git a/python/tvm/micro/project_api/server.py b/python/tvm/micro/project_api/server.py index aae1ba5ffe8a..789292a49243 100644 --- a/python/tvm/micro/project_api/server.py +++ b/python/tvm/micro/project_api/server.py @@ -27,6 +27,8 @@ _ProjectOption = collections.namedtuple("ProjectOption", ("name", "choices", "help")) + + class ProjectOption(_ProjectOption): def __new__(cls, name, **kw): """Override __new__ to force all options except name to be specified as kwargs.""" @@ -36,7 +38,9 @@ def __new__(cls, name, **kw): return super().__new__(cls, **kw) -ServerInfo = collections.namedtuple('ServerInfo', ('platform_name', 'is_template', 'model_library_format_path', 'project_options')) +ServerInfo = collections.namedtuple( + "ServerInfo", ("platform_name", "is_template", "model_library_format_path", "project_options") +) # Timeouts supported by the underlying C++ MicroSession. @@ -63,6 +67,7 @@ def __new__(cls, name, **kw): class ErrorCode(enum.IntEnum): """Enumerates error codes which can be returned. Includes JSON-RPC standard and custom codes.""" + # Custom (in reserved error code space). SERVER_ERROR = -32000 # A generic error was raised while processing the request. @@ -84,18 +89,19 @@ def __init__(self, code, message, data, client_context=None): self.client_context = client_context def to_json(self): - return {"code": self.code, - "message": self.message, - "data": self.data, + return { + "code": self.code, + "message": self.message, + "data": self.data, } def __str__(self): - data_str = '' + data_str = "" if self.data: if isinstance(self.data, dict) and self.data.get("traceback"): data_str = f'\n{self.data["traceback"]}' else: - data_str = f'\n{self.data!r}' + data_str = f"\n{self.data!r}" return f"JSON-RPC error # {self.code}: {self.message}" + data_str @classmethod @@ -108,16 +114,20 @@ def from_json(cls, client_context, json_error): if ErrorCode(json_error["code"]) == ErrorCode.SERVER_ERROR: found_server_error = True except ValueError: - ServerError.from_json(client_context, json_error) + ServerError.from_json(client_context, json_error) if found_server_error: return ServerError.from_json(client_context, json_error) - return cls(json_error["code"], json_error["message"], json_error.get("data", None), client_context=client_context) + return cls( + json_error["code"], + json_error["message"], + json_error.get("data", None), + client_context=client_context, + ) class ServerError(JSONRPCError): - @classmethod def from_exception(cls, exc, **kw): to_return = cls(**kw) @@ -150,13 +160,13 @@ def set_traceback(self, traceback): # # We want to place a comment on the first line of the outermost frame to indicate this is the # server-side stack frame. - first_frame_list = traceback_list[1].split('\n') + first_frame_list = traceback_list[1].split("\n") self.data["traceback"] = ( - traceback_list[0] + - f"{first_frame_list[0]} # <--- Outermost server-side stack frame\n" + - "\n".join(first_frame_list[1:]) + - "".join(traceback_list[2:]) - ) + traceback_list[0] + + f"{first_frame_list[0]} # <--- Outermost server-side stack frame\n" + + "\n".join(first_frame_list[1:]) + + "".join(traceback_list[2:]) + ) @classmethod def from_json(cls, client_context, json_error): @@ -164,9 +174,15 @@ def from_json(cls, client_context, json_error): for sub_cls in cls.__subclasses__(): if sub_cls.__name__ == json_error["message"]: - return sub_cls(message=json_error["message"], data=json_error.get("data"), client_context=client_context) + return sub_cls( + message=json_error["message"], + data=json_error.get("data"), + client_context=client_context, + ) - return cls(json_error["message"], data=json_error.get("data"), client_context=client_context) + return cls( + json_error["message"], data=json_error.get("data"), client_context=client_context + ) class TransportClosedError(ServerError): @@ -186,13 +202,18 @@ class IoTimeoutError(ServerError): class ProjectAPIHandler(metaclass=abc.ABCMeta): - @abc.abstractmethod def server_info_query(self) -> ServerInfo: raise NotImplementedError() @abc.abstractmethod - def generate_project(self, model_library_format_path : pathlib.Path, standalone_crt_dir : pathlib.Path, project_dir : pathlib.Path, options : dict): + def generate_project( + self, + model_library_format_path: pathlib.Path, + standalone_crt_dir: pathlib.Path, + project_dir: pathlib.Path, + options: dict, + ): """Generate a project from the given artifacts, copying ourselves to that project. Parameters @@ -211,7 +232,7 @@ def generate_project(self, model_library_format_path : pathlib.Path, standalone_ raise NotImplementedError() @abc.abstractmethod - def build(self, options : dict): + def build(self, options: dict): """Build the project, enabling the flash() call to made. Parameters @@ -222,7 +243,7 @@ def build(self, options : dict): raise NotImplementedError() @abc.abstractmethod - def flash(self, options : dict): + def flash(self, options: dict): """Program the project onto the device. Parameters @@ -233,7 +254,7 @@ def flash(self, options : dict): raise NotImplementedError() @abc.abstractmethod - def open_transport(self, options : dict) -> TransportTimeouts: + def open_transport(self, options: dict) -> TransportTimeouts: """Open resources needed for the transport layer. This function might e.g. open files or serial ports needed in write_transport or read_transport. @@ -260,7 +281,7 @@ def close_transport(self): raise NotImplementedError() @abc.abstractmethod - def read_transport(self, n : int, timeout_sec : typing.Union[float, type(None)]) -> bytes: + def read_transport(self, n: int, timeout_sec: typing.Union[float, type(None)]) -> bytes: """Read data from the transport. Parameters @@ -289,7 +310,7 @@ def read_transport(self, n : int, timeout_sec : typing.Union[float, type(None)]) raise NotImplementedError() @abc.abstractmethod - def write_transport(self, data : bytes, timeout_sec : float): + def write_transport(self, data: bytes, timeout_sec: float): """Write data to the transport. This function should either write all bytes in `data` or raise an exception. @@ -315,7 +336,6 @@ def write_transport(self, data : bytes, timeout_sec : float): raise NotImplementedError() - class ProjectAPIServer: """Base class for Project API Servers. @@ -329,8 +349,9 @@ class ProjectAPIServer: _PROTOCOL_VERSION = 1 - def __init__(self, read_file : typing.BinaryIO, write_file : typing.BinaryIO, - handler : ProjectAPIHandler): + def __init__( + self, read_file: typing.BinaryIO, write_file: typing.BinaryIO, handler: ProjectAPIHandler + ): """Initialize a new ProjectAPIServer. Parameters @@ -343,8 +364,10 @@ def __init__(self, read_file : typing.BinaryIO, write_file : typing.BinaryIO, A class which extends the abstract class ProjectAPIHandler and implements the server RPC functions. """ - self._read_file = io.TextIOWrapper(read_file, encoding='UTF-8', errors='strict') - self._write_file = io.TextIOWrapper(write_file, encoding='UTF-8', errors='strict', write_through=True) + self._read_file = io.TextIOWrapper(read_file, encoding="UTF-8", errors="strict") + self._write_file = io.TextIOWrapper( + write_file, encoding="UTF-8", errors="strict", write_through=True + ) self._handler = handler def serve_forever(self): @@ -368,14 +391,14 @@ def serve_one_request(self): """ try: line = self._read_file.readline() - _LOG.debug('read request <- %s', line) + _LOG.debug("read request <- %s", line) if not line: return False request = json.loads(line) except EOFError: - _LOG.error('EOF') + _LOG.error("EOF") return False except Exception as exc: @@ -399,25 +422,27 @@ def serve_one_request(self): message = f"calling method {request['method']}" exc = ServerError.from_exception(exc, message=message) - request_id = None if not isinstance(request, dict) else request.get('id') + request_id = None if not isinstance(request, dict) else request.get("id") self._reply_error(request_id, exc) return did_validate return True - VALID_METHOD_RE = re.compile('^[a-zA-Z0-9_]+$') + VALID_METHOD_RE = re.compile("^[a-zA-Z0-9_]+$") def _validate_request(self, request): if type(request) is not dict: - raise JSONRPCError(ErrorCode.INVALID_REQUEST, f"request: want dict; got {request!r}", None) + raise JSONRPCError( + ErrorCode.INVALID_REQUEST, f"request: want dict; got {request!r}", None + ) - jsonrpc = request.get('jsonrpc') + jsonrpc = request.get("jsonrpc") if jsonrpc != "2.0": raise JSONRPCError( ErrorCode.INVALID_REQUEST, f'request["jsonrpc"]: want "2.0"; got {jsonrpc!r}', None ) - method = request.get('method') + method = request.get("method") if type(method) != str: raise JSONRPCError( ErrorCode.INVALID_REQUEST, f'request["method"]: want str; got {method!r}', None @@ -427,16 +452,16 @@ def _validate_request(self, request): raise JSONRPCError( ErrorCode.INVALID_REQUEST, f'request["method"]: should match regex {self.VALID_METHOD_RE.pattern}; got {method!r}', - None + None, ) - params = request.get('params') + params = request.get("params") if type(params) != dict: raise JSONRPCError( ErrorCode.INVALID_REQUEST, f'request["params"]: want dict; got {type(params)}', None ) - request_id = request.get('id') + request_id = request.get("id") if type(request_id) not in (str, int, type(None)): raise JSONRPCError( ErrorCode.INVALID_REQUEST, @@ -445,72 +470,87 @@ def _validate_request(self, request): ) def _dispatch_request(self, request): - method = request['method'] + method = request["method"] interface_method = getattr(ProjectAPIHandler, method, None) if interface_method is None: raise JSONRPCError( - ErrorCode.METHOD_NOT_FOUND, f'{request["method"]}: no such method', None) + ErrorCode.METHOD_NOT_FOUND, f'{request["method"]}: no such method', None + ) has_preprocessing = True - dispatch_method = getattr(self, f'_dispatch_{method}', None) + dispatch_method = getattr(self, f"_dispatch_{method}", None) if dispatch_method is None: dispatch_method = getattr(self._handler, method) has_preprocessing = False - request_params = request['params'] + request_params = request["params"] params = {} for var_name, var_type in typing.get_type_hints(interface_method).items(): - if var_name == 'self' or var_name == 'return': + if var_name == "self" or var_name == "return": continue # NOTE: types can only be JSON-compatible types, so var_type is expected to be of type 'type'. if var_name not in request_params: - raise JSONRPCError(ErrorCode.INVALID_PARAMS, f'method {request["method"]}: parameter {var_name} not given', None) + raise JSONRPCError( + ErrorCode.INVALID_PARAMS, + f'method {request["method"]}: parameter {var_name} not given', + None, + ) param = request_params[var_name] if not has_preprocessing and not isinstance(param, var_type): raise JSONRPCError( ErrorCode.INVALID_PARAMS, - f'method {request["method"]}: parameter {var_name}: want {var_type!r}, got {type(param)!r}', None) + f'method {request["method"]}: parameter {var_name}: want {var_type!r}, got {type(param)!r}', + None, + ) params[var_name] = param - extra_params = [p for p in request['params'] if p not in params] + extra_params = [p for p in request["params"] if p not in params] if extra_params: - raise JSONRPCError(ErrorCode.INVALID_PARAMS, - f'{request["method"]}: extra parameters: {", ".join(extra_params)}', - None) + raise JSONRPCError( + ErrorCode.INVALID_PARAMS, + f'{request["method"]}: extra parameters: {", ".join(extra_params)}', + None, + ) return_value = dispatch_method(**params) - self._write_reply(request['id'], result=return_value) + self._write_reply(request["id"], result=return_value) def _write_reply(self, request_id, result=None, error=None): reply_dict = { - 'jsonrpc': "2.0", - 'id': request_id, + "jsonrpc": "2.0", + "id": request_id, } if error is not None: - assert result is None, f'Want either result= or error=, got result={result!r} and error={error!r})' + assert ( + result is None + ), f"Want either result= or error=, got result={result!r} and error={error!r})" reply_dict["error"] = error else: reply_dict["result"] = result reply_str = json.dumps(reply_dict) - _LOG.debug('write reply -> %r', reply_dict) + _LOG.debug("write reply -> %r", reply_dict) self._write_file.write(reply_str) - self._write_file.write('\n') + self._write_file.write("\n") def _reply_error(self, request_id, exception): self._write_reply(request_id, error=exception.to_json()) - def _dispatch_generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): - return self._handler.generate_project(pathlib.Path(model_library_format_path), - pathlib.Path(standalone_crt_dir), - pathlib.Path(project_dir), - options) + def _dispatch_generate_project( + self, model_library_format_path, standalone_crt_dir, project_dir, options + ): + return self._handler.generate_project( + pathlib.Path(model_library_format_path), + pathlib.Path(standalone_crt_dir), + pathlib.Path(project_dir), + options, + ) def _dispatch_server_info_query(self): query_reply = self._handler.server_info_query() @@ -527,7 +567,7 @@ def _dispatch_open_transport(self, options): def _dispatch_read_transport(self, n, timeout_sec): reply_data = self._handler.read_transport(n, timeout_sec) - return {'data': str(base64.b85encode(reply_data), 'utf-8')} + return {"data": str(base64.b85encode(reply_data), "utf-8")} def _dispatch_write_transport(self, data, timeout_sec): self._handler.write_transport(base64.b85decode(data), timeout_sec) @@ -646,7 +686,7 @@ def write_with_timeout(fd, data, timeout_sec): return num_written -def main(handler : ProjectAPIHandler, argv : typing.List[str] = None): +def main(handler: ProjectAPIHandler, argv: typing.List[str] = None): """Start a Project API server. Parameters @@ -660,16 +700,27 @@ def main(handler : ProjectAPIHandler, argv : typing.List[str] = None): argv = sys.argv[1:] parser = argparse.ArgumentParser(description="Generic TVM Project API server entry point") - parser.add_argument("--read-fd", type=int, required=True, help="Numeric file descriptor where RPC requests should be read.") - parser.add_argument("--write-fd", type=int, required=True, help="Numeric file descriptor where RPC replies should be written.") - parser.add_argument("--debug", action="store_true", help="When given, configure logging at DEBUG level.") + parser.add_argument( + "--read-fd", + type=int, + required=True, + help="Numeric file descriptor where RPC requests should be read.", + ) + parser.add_argument( + "--write-fd", + type=int, + required=True, + help="Numeric file descriptor where RPC replies should be written.", + ) + parser.add_argument( + "--debug", action="store_true", help="When given, configure logging at DEBUG level." + ) args = parser.parse_args() - logging.basicConfig(level='DEBUG' if args.debug else 'INFO', - stream=sys.stderr) + logging.basicConfig(level="DEBUG" if args.debug else "INFO", stream=sys.stderr) - read_file = os.fdopen(args.read_fd, 'rb', buffering=0) - write_file = os.fdopen(args.write_fd, 'wb', buffering=0) + read_file = os.fdopen(args.read_fd, "rb", buffering=0) + write_file = os.fdopen(args.write_fd, "wb", buffering=0) server = ProjectAPIServer(read_file, write_file, handler) server.serve_forever() diff --git a/python/tvm/micro/transport.py b/python/tvm/micro/transport.py index 6d4205a5ca5e..8e95ff7ea77a 100644 --- a/python/tvm/micro/transport.py +++ b/python/tvm/micro/transport.py @@ -18,12 +18,16 @@ """Defines abstractions and implementations of the RPC transport used with micro TVM.""" import abc -import collections import logging import string import typing -from .project_api.server import IoTimeoutError, TransportClosedError, TransportTimeouts +from .project_api.server import IoTimeoutError, TransportTimeouts +from .project_api.server import TransportClosedError + + +_ = TransportClosedError # work around pylint unused-import error + _LOG = logging.getLogger(__name__) @@ -271,5 +275,4 @@ def write(self, data, timeout_sec): ) - TransportContextManager = typing.ContextManager[Transport] diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index 8b4e20e31edc..0236c997c1e3 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -265,8 +265,8 @@ int TVMGraphExecutorGraphAttr_Load(TVMGraphExecutorGraphAttr* attr, JSONReader* break; } DLDevice dev = {kDLCPU, 0}; - tvm_crt_error_t err = - TVMPlatformMemoryAllocate(TVM_CRT_MAX_STRLEN_DLTYPE * num_items, dev, (void**)&attr->dltype); + tvm_crt_error_t err = TVMPlatformMemoryAllocate(TVM_CRT_MAX_STRLEN_DLTYPE * num_items, dev, + (void**)&attr->dltype); if (err != kTvmErrorNoError) { fprintf(stderr, "memory allocate error: %08x", err); return -1; @@ -792,8 +792,8 @@ int TVMGraphExecutor_LoadParams(TVMGraphExecutor* executor, const char* param_bl // read names char* names = NULL; DLDevice dev = {kDLCPU, 0}; - tvm_crt_error_t err = - TVMPlatformMemoryAllocate(TVM_CRT_MAX_STRLEN_FUNCTION_NAME * executor->nodes_count, dev, (void**)&names); + tvm_crt_error_t err = TVMPlatformMemoryAllocate( + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * executor->nodes_count, dev, (void**)&names); if (err != kTvmErrorNoError) { fprintf(stderr, "memory allocate error: %08x", err); status = -1; @@ -827,7 +827,8 @@ int TVMGraphExecutor_LoadParams(TVMGraphExecutor* executor, const char* param_bl } for (idx = 0; idx < size; idx++) { - int32_t in_idx = TVMGraphExecutor_GetInputIndex(executor, names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx); + int32_t in_idx = + TVMGraphExecutor_GetInputIndex(executor, names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx); CHECK_GT(in_idx, 0, "Found param for non-existent input: %s\n", names + TVM_CRT_MAX_STRLEN_FUNCTION_NAME * idx); uint32_t eid = TVMGraphExecutor_GetEntryId(executor, executor->input_nodes[in_idx], 0); diff --git a/src/runtime/crt/host/microtvm_api_server.py b/src/runtime/crt/host/microtvm_api_server.py index 225723d243ce..ed2a4103846e 100644 --- a/src/runtime/crt/host/microtvm_api_server.py +++ b/src/runtime/crt/host/microtvm_api_server.py @@ -31,8 +31,11 @@ def server_info_query(self): return server.ServerInfo( platform_name="host", is_template=IS_TEMPLATE, - model_library_format_path="" if IS_TEMPLATE else PROJECT_DIR / MODEL_LIBRARY_FORMAT_RELPATH, - project_options=[server.ProjectOption("verbose", help="Run make with verbose output")]) + model_library_format_path="" + if IS_TEMPLATE + else PROJECT_DIR / MODEL_LIBRARY_FORMAT_RELPATH, + project_options=[server.ProjectOption("verbose", help="Run make with verbose output")], + ) # These files and directories will be recursively copied into generated projects from the CRT. CRT_COPY_ITEMS = ("include", "Makefile", "src") @@ -76,13 +79,17 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Populate crt-config.h crt_config_dir = project_dir / "crt_config" crt_config_dir.mkdir() - shutil.copy2(os.path.join(os.path.dirname(__file__), "..", "crt_config-template.h"), - os.path.join(crt_config_dir, "crt_config.h")) + shutil.copy2( + os.path.join(os.path.dirname(__file__), "..", "crt_config-template.h"), + os.path.join(crt_config_dir, "crt_config.h"), + ) # Populate src/ src_dir = os.path.join(project_dir, "src") os.mkdir(src_dir) - shutil.copy2(os.path.join(os.path.dirname(__file__), "main.cc"), os.path.join(src_dir, "main.cc")) + shutil.copy2( + os.path.join(os.path.dirname(__file__), "main.cc"), os.path.join(src_dir, "main.cc") + ) def build(self, options): args = ["make"] @@ -103,12 +110,16 @@ def _set_nonblock(self, fd): assert (new_flag & os.O_NONBLOCK) != 0, "Cannot set file descriptor {fd} to non-blocking" def open_transport(self, options): - self._proc = subprocess.Popen([self.BUILD_TARGET], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0) + self._proc = subprocess.Popen( + [self.BUILD_TARGET], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0 + ) self._set_nonblock(self._proc.stdin.fileno()) self._set_nonblock(self._proc.stdout.fileno()) - return server.TransportTimeouts(session_start_retry_timeout_sec=0, - session_start_timeout_sec=0, - session_established_timeout_sec=0) + return server.TransportTimeouts( + session_start_retry_timeout_sec=0, + session_start_timeout_sec=0, + session_established_timeout_sec=0, + ) def close_transport(self): if self._proc is not None: @@ -168,6 +179,5 @@ def write_transport(self, data, timeout_sec): data = data[num_written:] - -if __name__ == '__main__': +if __name__ == "__main__": server.main(Handler()) diff --git a/src/runtime/crt/microtvm_rpc_common/framing.cc b/src/runtime/crt/microtvm_rpc_common/framing.cc index 87f152d6aaf0..47e4a33a718c 100644 --- a/src/runtime/crt/microtvm_rpc_common/framing.cc +++ b/src/runtime/crt/microtvm_rpc_common/framing.cc @@ -69,18 +69,18 @@ void Unframer::Reset() { size_t Unframer::BytesNeeded() { size_t bytes_needed = 0; switch (state_) { - case State::kFindPacketStart: - return 1; - case State::kFindPacketLength: - bytes_needed = PacketFieldSizeBytes::kPayloadLength; - break; - case State::kFindPacketCrc: - return num_payload_bytes_remaining_; - case State::kFindCrcEnd: - bytes_needed = PacketFieldSizeBytes::kCrc; - break; - default: - CHECK(false); + case State::kFindPacketStart: + return 1; + case State::kFindPacketLength: + bytes_needed = PacketFieldSizeBytes::kPayloadLength; + break; + case State::kFindPacketCrc: + return num_payload_bytes_remaining_; + case State::kFindCrcEnd: + bytes_needed = PacketFieldSizeBytes::kCrc; + break; + default: + CHECK(false); } return bytes_needed > num_buffer_bytes_valid_ ? bytes_needed - num_buffer_bytes_valid_ : 0; diff --git a/src/runtime/micro/crt_config.h b/src/runtime/micro/crt_config.h index ab6e277b150f..c3e8fea1ba08 100644 --- a/src/runtime/micro/crt_config.h +++ b/src/runtime/micro/crt_config.h @@ -21,8 +21,8 @@ * \file tvm/runtime/crt/host/crt_config.h * \brief CRT configuration for the host-linked CRT. */ -#ifndef TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ -#define TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ +#ifndef TVM_RUNTIME_MICRO_CRT_CONFIG_H_ +#define TVM_RUNTIME_MICRO_CRT_CONFIG_H_ /*! Log level of the CRT runtime */ #define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG @@ -53,4 +53,4 @@ // #define TVM_CRT_FRAMER_ENABLE_LOGS -#endif // TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ +#endif // TVM_RUNTIME_MICRO_CRT_CONFIG_H_ diff --git a/src/runtime/micro/micro_session.cc b/src/runtime/micro/micro_session.cc index f18177d4c4cc..2dcd928b24f8 100644 --- a/src/runtime/micro/micro_session.cc +++ b/src/runtime/micro/micro_session.cc @@ -37,10 +37,10 @@ #include #include "../../support/str_escape.h" -#include "crt_config.h" #include "../rpc/rpc_channel.h" #include "../rpc/rpc_endpoint.h" #include "../rpc/rpc_session.h" +#include "crt_config.h" namespace tvm { namespace runtime { diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py index f31630c2a705..01447ac6183f 100644 --- a/tests/lint/check_file_type.py +++ b/tests/lint/check_file_type.py @@ -80,6 +80,8 @@ "idl", # opencl file "cl", + # zephyr config file + "conf", } # List of file names allowed @@ -132,33 +134,12 @@ "tests/micro/zephyr/testdata/mnist-8.onnx", "tests/micro/zephyr/testdata/ic_sample_fp32_8.npy", # microTVM Zephyr runtime - "apps/microtvm/zephyr/qemu-hack/qemu-system-i386", - "apps/microtvm/zephyr/qemu-hack/qemu-system-arm", - "apps/microtvm/zephyr/qemu-hack/qemu-system-riscv32", - "apps/microtvm/zephyr/qemu-hack/qemu-system-riscv64", - "apps/microtvm/zephyr/qemu-hack/qemu-system-xilinx-aarch64", - "apps/microtvm/zephyr/host_driven/prj.conf", - "apps/microtvm/zephyr/host_driven/boards/qemu_x86.conf", - "apps/microtvm/zephyr/host_driven/boards/qemu_riscv32.conf", - "apps/microtvm/zephyr/host_driven/boards/qemu_riscv64.conf", - "apps/microtvm/zephyr/host_driven/boards/nrf5340dk_nrf5340_cpuapp.conf", - "apps/microtvm/zephyr/host_driven/boards/nucleo_f746zg.conf", - "apps/microtvm/zephyr/host_driven/boards/stm32f746g_disco.conf", - "apps/microtvm/zephyr/host_driven/boards/mps2_an521.conf", - "apps/microtvm/zephyr/host_driven/boards/nucleo_l4r5zi.conf", - "apps/microtvm/zephyr/host_driven/boards/qemu_cortex_r5.conf", - "apps/microtvm/zephyr/host_driven/qemu-hack", - "apps/microtvm/zephyr/aot_demo/prj.conf", - "apps/microtvm/zephyr/aot_demo/boards/qemu_x86.conf", - "apps/microtvm/zephyr/aot_demo/boards/qemu_riscv32.conf", - "apps/microtvm/zephyr/aot_demo/boards/qemu_riscv64.conf", - "apps/microtvm/zephyr/aot_demo/boards/nrf5340dk_nrf5340_cpuapp.conf", - "apps/microtvm/zephyr/aot_demo/boards/nucleo_f746zg.conf", - "apps/microtvm/zephyr/aot_demo/boards/stm32f746g_disco.conf", - "apps/microtvm/zephyr/aot_demo/boards/mps2_an521.conf", - "apps/microtvm/zephyr/aot_demo/boards/nucleo_l4r5zi.conf", - "apps/microtvm/zephyr/aot_demo/boards/qemu_cortex_r5.conf", - "apps/microtvm/zephyr/aot_demo/qemu-hack", + "apps/microtvm/zephyr/template_project/CMakeLists.txt.template", + "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-arm", + "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-xilinx-aarch64", + "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-i386", + "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-riscv32", + "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-riscv64", # microTVM Virtual Machines "apps/microtvm/reference-vm/zephyr/Vagrantfile", "apps/microtvm/reference-vm/zephyr/base-box/Vagrantfile.packer-template", diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 8b1271de4fed..0a3ef90e833f 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -72,12 +72,25 @@ def _make_session(model, target, zephyr_board, west_cmd, mod, build_config): workspace = tvm.micro.Workspace(debug=True, root=workspace_root) template_project_dir = ( - pathlib.Path(__file__).parent / ".." / ".." / ".." / "apps" / "microtvm" / "zephyr" / "template_project").resolve() + pathlib.Path(__file__).parent + / ".." + / ".." + / ".." + / "apps" + / "microtvm" + / "zephyr" + / "template_project" + ).resolve() project = tvm.micro.generate_project( str(template_project_dir), mod, workspace.relpath("project"), - {"project_type": "host_driven", "west_cmd": west_cmd, "verbose": 0, "zephyr_board": zephyr_board}, + { + "project_type": "host_driven", + "west_cmd": west_cmd, + "verbose": 0, + "zephyr_board": zephyr_board, + }, ) project.build() project.flash() diff --git a/tests/micro/zephyr/test_zephyr_aot.py b/tests/micro/zephyr/test_zephyr_aot.py index 29470c1b717a..10e62410a21a 100644 --- a/tests/micro/zephyr/test_zephyr_aot.py +++ b/tests/micro/zephyr/test_zephyr_aot.py @@ -74,7 +74,13 @@ def _build_project(model, target, zephyr_board, west_cmd, mod, build_config, ext str(template_project_dir), mod, project_dir, - {"extra_files_tar": extra_files_tar, "project_type": "aot_demo", "west_cmd": west_cmd, "verbose": 0, "zephyr_board": zephyr_board}, + { + "extra_files_tar": extra_files_tar, + "project_type": "aot_demo", + "west_cmd": west_cmd, + "verbose": 0, + "zephyr_board": zephyr_board, + }, ) project.build() return project, project_dir @@ -108,7 +114,7 @@ def _create_header_file(tensor_name, npy_data, output_path, tar_file): header_file.write("};\n\n") header_file_bytes = bytes(header_file.getvalue(), "utf-8") - raw_path = (pathlib.Path(output_path) / f"{tensor_name}.h") + raw_path = pathlib.Path(output_path) / f"{tensor_name}.h" ti = tarfile.TarInfo(name=str(raw_path)) ti.size = len(header_file_bytes) ti.mode = 0o644 @@ -193,7 +199,13 @@ def test_tflite(platform, west_cmd, skip_build, tvm_debug): ) project, _ = _build_project( - model, target, zephyr_board, west_cmd, lowered, build_config, extra_files_tar=temp_file.name + model, + target, + zephyr_board, + west_cmd, + lowered, + build_config, + extra_files_tar=temp_file.name, ) project.flash() @@ -239,10 +251,18 @@ def test_qemu_make_fail(platform, west_cmd, skip_build, tvm_debug): _create_header_file("output_data", np.zeros(shape=shape, dtype=dtype), "include", tf) project, project_dir = _build_project( - model, target, zephyr_board, west_cmd, lowered, build_config, extra_files_tar=temp_file.name + model, + target, + zephyr_board, + west_cmd, + lowered, + build_config, + extra_files_tar=temp_file.name, ) - file_path = pathlib.Path(project_dir) / "build" / "zephyr" / "CMakeFiles" / "run.dir" / "build.make" + file_path = ( + pathlib.Path(project_dir) / "build" / "zephyr" / "CMakeFiles" / "run.dir" / "build.make" + ) assert file_path.is_file(), f"[{file_path}] does not exist." # Remove a file to create make failure. diff --git a/tests/python/relay/aot/aot_test_utils.py b/tests/python/relay/aot/aot_test_utils.py index cf95843bc41b..fae8ebb42512 100644 --- a/tests/python/relay/aot/aot_test_utils.py +++ b/tests/python/relay/aot/aot_test_utils.py @@ -299,24 +299,28 @@ def compile_and_run( include_path = os.path.join(base_path, "include") os.mkdir(include_path) crt_root = tvm.micro.get_standalone_crt_dir() - shutil.copy2(os.path.join(crt_root, "template", "crt_config-template.h"), - os.path.join(include_path, "crt_config.h")) + shutil.copy2( + os.path.join(crt_root, "template", "crt_config-template.h"), + os.path.join(include_path, "crt_config.h"), + ) for i in range(len(input_list)): - create_header_file(f'{mangle_name(mod_name, "input_data")}{i}', - input_list[i], - os.path.join(base_path, "include")) + create_header_file( + f'{mangle_name(mod_name, "input_data")}{i}', + input_list[i], + os.path.join(base_path, "include"), + ) for i in range(len(output_list)): create_header_file( f'{mangle_name(mod_name,"output_data")}{i}', np.zeros(output_list[i].shape, output_list[i].dtype), - os.path.join(base_path, "include") + os.path.join(base_path, "include"), ) create_header_file( f'{mangle_name(mod_name, "expected_output_data")}{i}', output_list[i], - os.path.join(base_path, "include") + os.path.join(base_path, "include"), ) create_main( @@ -360,8 +364,10 @@ def compile_and_run_multiple_models( include_path = os.path.join(base_path, "include") os.mkdir(include_path) crt_root = tvm.micro.get_standalone_crt_dir() - shutil.copy2(os.path.join(crt_root, "template", "crt_config-template.h"), - os.path.join(include_path, "crt_config.h")) + shutil.copy2( + os.path.join(crt_root, "template", "crt_config-template.h"), + os.path.join(include_path, "crt_config.h"), + ) for mod_name, mod in mod_map.items(): @@ -402,7 +408,8 @@ def compile_and_run_multiple_models( f"make -f {makefile} build_dir=" + build_path + f" TVM_ROOT={file_dir}/../../../.." - + f" STANDALONE_CRT_DIR={tvm.micro.get_standalone_crt_dir()}") + + f" STANDALONE_CRT_DIR={tvm.micro.get_standalone_crt_dir()}" + ) compile_log_path = os.path.join(build_path, "test_compile.log") ret = subprocess_with_stdout_and_log(make_cmd, ".", compile_log_path, False) diff --git a/tests/python/unittest/test_crt.py b/tests/python/unittest/test_crt.py index cd91af9de425..a1d55a16433c 100644 --- a/tests/python/unittest/test_crt.py +++ b/tests/python/unittest/test_crt.py @@ -54,7 +54,9 @@ def _make_sess_from_op(workspace, op_name, sched, arg_bufs): def _make_session(workspace, mod): template_project_dir = os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") - project = tvm.micro.generate_project(template_project_dir, mod, workspace.relpath("project"), {"verbose": 1}) + project = tvm.micro.generate_project( + template_project_dir, mod, workspace.relpath("project"), {"verbose": 1} + ) project.build() project.flash() return tvm.micro.Session(project.transport()) @@ -219,4 +221,4 @@ def test_platform_timer(): if __name__ == "__main__": - sys.exit(pytest.main([__file__] + sys.argv[1:])) + sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/python/unittest/test_micro_model_library_format.py b/tests/python/unittest/test_micro_model_library_format.py index 1e9bada2a7a3..db073468622f 100644 --- a/tests/python/unittest/test_micro_model_library_format.py +++ b/tests/python/unittest/test_micro_model_library_format.py @@ -381,7 +381,6 @@ def test_export_byoc_c_module(): mod = tvm.relay.transform.PartitionGraph("mod_name")(mod) mod = tvm.relay.transform.InferType()(mod) - with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): factory = tvm.relay.build(mod, tvm.target.target.micro("host")) @@ -389,21 +388,24 @@ def test_export_byoc_c_module(): mlf_tar_path = temp_dir.relpath("lib.tar") from tvm import micro + micro.export_model_library_format(factory, mlf_tar_path) with tarfile.open(mlf_tar_path, "r:*") as tf: tar_members = [ti.name for ti in tf.getmembers()] print("tar members", tar_members) - assert './metadata.json' in tar_members + assert "./metadata.json" in tar_members with tf.extractfile("./metadata.json") as f: metadata = json.load(f) main_md = metadata["memory"]["functions"]["main"] - assert main_md == [{ - "constants_size_bytes": 0, - "device": 1, - "io_size_bytes": 4800, - "workspace_size_bytes": 800, - }] + assert main_md == [ + { + "constants_size_bytes": 0, + "device": 1, + "io_size_bytes": 4800, + "workspace_size_bytes": 800, + } + ] if __name__ == "__main__": diff --git a/tests/python/unittest/test_micro_project_api.py b/tests/python/unittest/test_micro_project_api.py index f00fc5244ec8..138fe36a9ae0 100644 --- a/tests/python/unittest/test_micro_project_api.py +++ b/tests/python/unittest/test_micro_project_api.py @@ -71,7 +71,6 @@ def write_transport(self, data, timeout_sec): class Transport: - def readable(self): return True @@ -103,21 +102,26 @@ def write(self, data): class ClientServerFixture: - def __init__(self, handler): self.handler = handler self.client_to_server = Transport() self.server_to_client = Transport() - self.server = project_api.server.ProjectAPIServer(self.client_to_server, self.server_to_client, handler) + self.server = project_api.server.ProjectAPIServer( + self.client_to_server, self.server_to_client, handler + ) self.client = project_api.client.ProjectAPIClient( - self.server_to_client, self.client_to_server, testonly_did_write_request=self._process_server_request + self.server_to_client, + self.client_to_server, + testonly_did_write_request=self._process_server_request, ) self.expect_failure = False def _process_server_request(self): - assert self.server.serve_one_request() == (not self.expect_failure), "Server failed to process request" + assert self.server.serve_one_request() == ( + not self.expect_failure + ), "Server failed to process request" def test_server_info_query(): @@ -138,7 +142,8 @@ def test_server_info_query(): platform_name="foo_bar", is_template=False, model_library_format_path=None, - project_options=[]) + project_options=[], + ) reply = fixture.client.server_info_query() expected_reply = dict(fixture.handler.TEST_SERVER_INFO._asdict()) expected_reply["protocol_version"] = 1 @@ -183,7 +188,9 @@ def test_open_transport(): with mock.patch.object(BaseTestHandler, "open_transport", return_value=timeouts) as patch: fixture = ClientServerFixture(BaseTestHandler()) - assert fixture.client.open_transport(options={"bar": "baz"}) == {"timeouts": dict(timeouts._asdict())} + assert fixture.client.open_transport(options={"bar": "baz"}) == { + "timeouts": dict(timeouts._asdict()) + } fixture.handler.open_transport.assert_called_once_with({"bar": "baz"}) @@ -242,7 +249,9 @@ class ProjectAPITestError(Exception): def test_method_raises_error(): - with mock.patch.object(BaseTestHandler, "close_transport", side_effect=ProjectAPITestError) as patch: + with mock.patch.object( + BaseTestHandler, "close_transport", side_effect=ProjectAPITestError + ) as patch: fixture = ClientServerFixture(BaseTestHandler()) with pytest.raises(project_api.server.ServerError) as exc_info: fixture.client.close_transport() @@ -264,14 +273,14 @@ def test_extra_param(): fixture = ClientServerFixture(BaseTestHandler()) # test one with has_preprocssing and one without - assert hasattr(fixture.server, '_dispatch_build') == False + assert hasattr(fixture.server, "_dispatch_build") == False with pytest.raises(project_api.server.JSONRPCError) as exc_info: fixture.client._request_reply("build", {"invalid_param_name": None, "options": {}}) assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS assert "build: extra parameters: invalid_param_name" in str(exc_info.value) - assert hasattr(fixture.server, '_dispatch_open_transport') == True + assert hasattr(fixture.server, "_dispatch_open_transport") == True with pytest.raises(project_api.server.JSONRPCError) as exc_info: fixture.client._request_reply("open_transport", {"invalid_param_name": None, "options": {}}) @@ -283,14 +292,14 @@ def test_missing_param(): fixture = ClientServerFixture(BaseTestHandler()) # test one with has_preprocssing and one without - assert hasattr(fixture.server, '_dispatch_build') == False + assert hasattr(fixture.server, "_dispatch_build") == False with pytest.raises(project_api.server.JSONRPCError) as exc_info: fixture.client._request_reply("build", {}) assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS assert "build: parameter options not given" in str(exc_info.value) - assert hasattr(fixture.server, '_dispatch_open_transport') == True + assert hasattr(fixture.server, "_dispatch_open_transport") == True with pytest.raises(project_api.server.JSONRPCError) as exc_info: fixture.client._request_reply("open_transport", {}) @@ -303,12 +312,14 @@ def test_incorrect_param_type(): # The error message given at the JSON-RPC server level doesn't make sense when preprocessing is # used. Only test without preprocessing here. - assert hasattr(fixture.server, '_dispatch_build') == False + assert hasattr(fixture.server, "_dispatch_build") == False with pytest.raises(project_api.server.JSONRPCError) as exc_info: fixture.client._request_reply("build", {"options": None}) assert exc_info.value.code == project_api.server.ErrorCode.INVALID_PARAMS - assert "build: parameter options: want , got " in str(exc_info.value) + assert "build: parameter options: want , got " in str( + exc_info.value + ) def test_invalid_request(): @@ -330,54 +341,66 @@ def _request_reply(request): # Parseable JSON with the wrong schema gets a reply. assert _request_reply(b"1") == { - "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, - "data": None, - "message": "request: want dict; got 1"}, + "error": { + "code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": "request: want dict; got 1", + }, "id": None, "jsonrpc": "2.0", } # Incorrect JSON-RPC spec version. assert _request_reply(b'{"jsonrpc": 1.0}') == { - "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, - "data": None, - "message": 'request["jsonrpc"]: want "2.0"; got 1.0'}, + "error": { + "code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["jsonrpc"]: want "2.0"; got 1.0', + }, "id": None, "jsonrpc": "2.0", } # Method not a str assert _request_reply(b'{"jsonrpc": "2.0", "method": 123}') == { - "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, - "data": None, - "message": 'request["method"]: want str; got 123'}, + "error": { + "code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["method"]: want str; got 123', + }, "id": None, "jsonrpc": "2.0", } # Method name has invalid characters assert _request_reply(b'{"jsonrpc": "2.0", "method": "bar!"}') == { - "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, - "data": None, - "message": 'request["method"]: should match regex ^[a-zA-Z0-9_]+$; got \'bar!\''}, + "error": { + "code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": "request[\"method\"]: should match regex ^[a-zA-Z0-9_]+$; got 'bar!'", + }, "id": None, "jsonrpc": "2.0", } # params not a dict assert _request_reply(b'{"jsonrpc": "2.0", "method": "bar", "params": 123}') == { - "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, - "data": None, - "message": 'request["params"]: want dict; got '}, + "error": { + "code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": "request[\"params\"]: want dict; got ", + }, "id": None, "jsonrpc": "2.0", } # id not valid assert _request_reply(b'{"jsonrpc": "2.0", "method": "bar", "params": {}, "id": {}}') == { - "error": {"code": project_api.server.ErrorCode.INVALID_REQUEST, - "data": None, - "message": 'request["id"]: want str, number, null; got {}'}, + "error": { + "code": project_api.server.ErrorCode.INVALID_REQUEST, + "data": None, + "message": 'request["id"]: want str, number, null; got {}', + }, "id": None, "jsonrpc": "2.0", } From 07c0f2bc27b80d06757e497fd02a2dadcac0216e Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Tue, 20 Jul 2021 09:04:41 -0700 Subject: [PATCH 17/47] ASF header --- .../template_project/CMakeLists.txt.template | 17 +++++++++++++++++ .../template_project/microtvm_api_server.py | 17 +++++++++++++++++ python/tvm/micro/project.py | 17 +++++++++++++++++ python/tvm/micro/project_api/client.py | 17 +++++++++++++++++ python/tvm/micro/project_api/server.py | 17 +++++++++++++++++ src/runtime/crt/host/microtvm_api_server.py | 17 +++++++++++++++++ 6 files changed, 102 insertions(+) diff --git a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template index 6bfea191cb99..9d4b16729cde 100644 --- a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template +++ b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.13.1) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index 3195ff759de5..5cf619b0a23a 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + import collections import enum import fcntl diff --git a/python/tvm/micro/project.py b/python/tvm/micro/project.py index d870a702af23..ac9ddc9d9636 100644 --- a/python/tvm/micro/project.py +++ b/python/tvm/micro/project.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + """Defines glue wrappers around the Project API which mate to TVM interfaces.""" from ..contrib import utils diff --git a/python/tvm/micro/project_api/client.py b/python/tvm/micro/project_api/client.py index de204bf41136..b96916c78096 100644 --- a/python/tvm/micro/project_api/client.py +++ b/python/tvm/micro/project_api/client.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + import base64 import io import json diff --git a/python/tvm/micro/project_api/server.py b/python/tvm/micro/project_api/server.py index 789292a49243..65bd0b7e01a3 100644 --- a/python/tvm/micro/project_api/server.py +++ b/python/tvm/micro/project_api/server.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + """Defines a basic Project API server template. This file is meant to be imported or copied into Project API servers, so it should not have any diff --git a/src/runtime/crt/host/microtvm_api_server.py b/src/runtime/crt/host/microtvm_api_server.py index ed2a4103846e..aa9f2745b20f 100644 --- a/src/runtime/crt/host/microtvm_api_server.py +++ b/src/runtime/crt/host/microtvm_api_server.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + import fcntl import os import os.path From a6a1c0a5ce052eac7986d5897a9314785afe078a Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 13:19:08 -0700 Subject: [PATCH 18/47] Remove warning from Zephyr API server --- apps/microtvm/zephyr/template_project/microtvm_api_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index 5cf619b0a23a..e81b98e07dfd 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -71,7 +71,7 @@ def check_call(cmd_args, *args, **kwargs): ) -class CMakeCache(collections.Mapping): +class CMakeCache(collections.abc.Mapping): def __init__(self, path): self._path = path self._dict = None @@ -90,7 +90,7 @@ def __len__(self): def _read_cmake_cache(self): """Read a CMakeCache.txt-like file and return a dictionary of values.""" - entries = collections.OrderedDict() + entries = collections.abc.OrderedDict() with open(self._path, encoding="utf-8") as f: for line in f: m = CACHE_ENTRY_RE.match(line.rstrip("\n")) From 80e2b37e9637f30654b1c717fb3a3b740311d472 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 13:19:27 -0700 Subject: [PATCH 19/47] Add Arduino target --- python/tvm/target/target.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index 106432cd44f7..75ac81c55ec6 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -286,6 +286,7 @@ def intel_graphics(model="unknown", options=None): "stm32f746xx": ["-mcpu=cortex-m7", "-march=armv7e-m"], "stm32l4r5zi": ["-mcpu=cortex-m4"], "zynq_mp_r5": ["-mcpu=cortex-r5"], + "cxd5602gg": ["-mcpu=cortex-m4f"], } From feb1b722faf977b27cc2b118a6fa7c2e56e68c52 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 13:19:59 -0700 Subject: [PATCH 20/47] ProjectAPI Arduino unit test --- tests/micro/arduino/test_arduino.py | 70 +++++++++++++++++++++ tests/micro/arduino/testdata/yes_no.tflite | Bin 0 -> 18712 bytes 2 files changed, 70 insertions(+) create mode 100644 tests/micro/arduino/test_arduino.py create mode 100644 tests/micro/arduino/testdata/yes_no.tflite diff --git a/tests/micro/arduino/test_arduino.py b/tests/micro/arduino/test_arduino.py new file mode 100644 index 000000000000..975e1b1852b6 --- /dev/null +++ b/tests/micro/arduino/test_arduino.py @@ -0,0 +1,70 @@ +import datetime +import os +import pathlib +import sys + +import pytest +import tflite +import tvm +from tvm import micro, relay + +import conftest + +PLATFORMS = conftest.PLATFORMS + +def _make_session(model, target, arduino_board, arduino_cmd, mod, build_config): + parent_dir = os.path.dirname(__file__) + filename = os.path.splitext(os.path.basename(__file__))[0] + prev_build = f"{os.path.join(parent_dir, 'archive')}_{filename}_{arduino_board}_last_build.micro" + workspace_root = os.path.join( + f"{os.path.join(parent_dir, 'workspace')}_{filename}_{arduino_board}", + datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S"), + ) + workspace_parent = os.path.dirname(workspace_root) + if not os.path.exists(workspace_parent): + os.makedirs(workspace_parent) + workspace = tvm.micro.Workspace(debug=True, root=workspace_root) + print("Outputing workspace root:") + print(workspace_root) + template_project_dir = ( + pathlib.Path(__file__).parent + / ".." + / ".." + / ".." + / "apps" + / "microtvm" + / "arduino" + / "template_project" + ).resolve() + project = tvm.micro.generate_project( + str(template_project_dir), + mod, + workspace.relpath("project"), + {"arduino_board": arduino_board, "arduino_cmd": arduino_cmd, "verbose": 0}, + ) + #project.build() + #project.flash() + #return tvm.micro.Session(project.transport()) + + +# This is bad, don't do this +TARGET = "c -keys=cpu -link-params=1 -mcpu=cortex-m33 -model=nrf5340dk -runtime=c -system-lib=1" + +def test_generate_yes_no_project(platform, arduino_cmd): + current_dir = os.path.dirname(__file__) + model, arduino_board = PLATFORMS[platform] + #target = tvm.target.target.micro(model, options=["-link-params=1"]) + build_config = {} + + with open(f"{current_dir}/testdata/yes_no.tflite", "rb") as f: + tflite_model_buf = f.read() + tflite_model = tflite.Model.GetRootAsModel(tflite_model_buf, 0) + mod, params = relay.frontend.from_tflite(tflite_model) + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + mod = relay.build(mod, TARGET, params=params) + + session = _make_session(model, TARGET, arduino_board, arduino_cmd, mod, build_config) + + +if __name__ == "__main__": + sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/micro/arduino/testdata/yes_no.tflite b/tests/micro/arduino/testdata/yes_no.tflite new file mode 100644 index 0000000000000000000000000000000000000000..4f533dac840504c65ac16ee7f6db8001d3b0d76a GIT binary patch literal 18712 zcmXWDXRvMQSsu7@j(hDK&)FxRn`38nE6}0=A%YPMCQM;dfL+K_%#6ps1~W5Nu5wLL zHf0zyq#1cY1_TlUMI^V>YN@+#-@ftOlf%w&<($_nVehK9zO(mUYn?i4ec$^%@AEwG zSpfjxAHDt~xAGx>6@Vsy00h7Q0#MA~SMyH*0M6q-{2&0_{s;gx^LRObkK_U7!Q}D% z4x;pZ>+)_@6)hKmFRT|MD;X%U}MD|MA!U#i#$__`MGUz~AMu zo?qC806@-Tl*ik7d?Sy``MK+PH1qiW?DOpvl(#ueaYncx7(~NVY!~nbwy-`H`T8|`0@kp;%7#06icuY?!ER|_g(~i z;ul6g)w=!zgBbk5-?Nv&uWab1^ybJ4fNacgElSKP&v#p1hO( z>eC;;`bOuQ;POUA*Rrqv{4YU#=U@G^F9hi)??26`-)mo1zG2fJdDmEc`e)>~fB&yf z4+{V05c|wqlh6Ft`uuYbzFhj}Uk7SGv-9N?-1xmGGWUo7{V&dK=hy!Em-_B6eC2P( z;vZCnVZXGXe>~DXi^yZ(G|B8ZsyLkKuzx|y* zyXXAokALdhe|vPt|4oE~U;Qs??N|TRj|HFgezWy2PiD8ib}*g&TFG<1n9i(!JitCp z-u=q`=imId+r_KZ&u+f|#ovAIa~~Z1_^*8IgB$0_{GS05#hjIU>;LN;#+Q(v0DtVv z*?iCT_ir>FX)yJzy-yC3->`r7y@MOy`t8au{50@)FTVTacW%Ca_?&^cU`W>;3Bw<<|=m3MX(FHy@Mw ze4D&LLwz=sI6`}>^0Q^pQlq4rE6PnC^FygbOhv7?kG*%(53jopWQVHt3Nh-N1~# zRwrgjp@c?e=BLWKEJcJzA)jFd=1T`k-fT={)QMukS+(j8ZkE+fn z9WdN7<=c4s1DFOhQW0Abn%i*mM+fu~-kq_`;&LnOf%TbAw$hgoBB;7KD>D zhOv4;^u`$qP76>Ag;82nV%~y!r3*+AHZ>MgHFqhJwukZHE)AZ=wo!o*JnqKlDGgf@ zvw_qQ7z`uNqmn@jEb&0GyB9QqQJdK zWmFfrVm`HdK9h>{K~lw8oUp$~Q60u^K{Uy&W)+$>l>^F$v2dKgMSfnlkHHCviEOeg zuY~|z@s1A~Aqb=CKBTIUg( zTvc_fBkM$F+7F8-!&8WfP8yHWAl-EU`h(tpL;U0Bn4j5R3G*S3k1D}2c>LIflCm)h zah0+Xb4~A4^+HG6A`M1bOB^^Qld_K!8-(vthJ%yneV>qmmdKx~)>aU7Lv|oVBHYd* z#o%Zjz+AyOkf1XF@r@!;I$X(VEO)@>N8`Q(}VGY^B?5Bdui z4ay~?SM=IVYD=?tOGSJGAEGqSEN$UlNQYrX>N^p$dhH-`lBYJ#C3GA*92ADNB#fnX zW^}_Z*hqC}s}>XD5KlK>Lm>p>ReNh;JK`oiMW48fkPgD~*MW$ogNgfH-vxO^kv?FL zv5Jbfz8Ve|riuWkk0M7-4OTx1SQzH0<9*P?ip|b!#wCnny<(w4*r*bBNn;swLCec@ zW)AMGmSA;W&*#NPsE>>eczWf*d_iNEsq0hH)`gwmmFelodueO!MGE7R%lM^c2n6Dy zxQwf?Q7#}43uhxvCudglp93{&w-WR(#~GL+0ra)g3)Ikz_B*QA1^{1>X9(|R2-%~! zk#3YPJ?&FkkkVZTq=^}rpIU~PoC8m2SHG%Tos74=(IxYmXdm+UK*^s)F56foQcVp$V#r!`0I31x_qTn z`mVp2;z+vv|C;^4&+P8soxm#-i>nQ(P_@Q~fpLzZrZX)cruahT!t7)UD_}y_!wD+P z!H(p)k(8Y%DJ76Q4&rRGOi&IX`|7#VZ$%KjhA)mNi{jf; zK0|xuz)fzRnokK^u51i+)nHTU3MlT4@aU#k#19~0d8q})mM+y#RsdZ`%kbXtEggf} zl-&8tML1mP!i~S>hE{89D17$V0w@ZJzQ{8Uo=NQ!H_&F&sJfKNV?(Ere%RblL3-za zL1-;$#zsW*7fR)M<{(SJIEt$E92MH(3Py@V%MV*l(@9}`bn9KFFJ)ul@>5+waL_3C zq)^1eS{VfJO+ap~p*CK*+){Oh9M;!qtNkEN3#)Z^?K`k7)OT&-%?z>WR^7i3c#@si zwQp(+3&jO#K6S#-tzUhx2NsfeQ@B4>B{kC)09UUd48lw-_v8$*yJFeGW#7bM$e~=Q z)etcsgBDin0iXn$D|g?~m6IMM;xqlRk|oU5Jw@gW9OSt4n9ei)RSD0N#_BxQ+Xsu= z`1hDSIlQ~WU)!Mdy|l3cNbTcz$=sJ**>&%tkFvf(&G{` zxpP{zkPt219`{FfFCcCrgw1CJc@2XSlvy9P9p=oht{fIMyFzRf5$p?&L-HTWw*S_t z9Tr!QyswPYDc(j(-wlc@OAxF+044)3ve>5<;_I-n@g)uIfi6;h3{tBg%7Hp%0k9v! z^m&>^3lef>Qvz^&UJ949s6&(|VIP3cjLmWDC^P-+)?y4Zc;NH>_8AWt*eZ!y@&v(e z8i5H`J5L6beJ|cFza^M4G2i7w=g)C@Ui&EB{Ij`TW-rC!|J^s54nL}X>!`Rn@u`X& z^}82l+TL3paD=PHlRb})9B|_xV$;lO-Awb`!pM-IfhKKv;YP%YB_w863p+sQI#_ox zP=Q3JTTe$LDxNJaD~dKV(WG>Ed`}I5lrweCLEzRU73Br#&HxcE9xh+o9KlxL6xVkz z(!U(1&8<5p+2=qvuGPiPe|8|g-to-O0Hy9o0?4ChP%ZJNS=7@!)v{_|xBz&amAJ{vtLdMW(^I2mn)BjoY`ac~pX1;>rc-G(oY zNLT-G`yrAiS+{ZR9pmd7Uv9sEw*TFP4ZE)n*Zy=k=g}=l{CzY;VKCc#>UjZbYim!= zP$JA=l~Or?tSloCrPc~5*5u`lrc8qw=n~{?Ag_(*`qgjt zwc^}$TBpN_cDVNacnZu# z;CfKBYiFK!%{H^GQu7;T#GrSG2KJ22C z#q^_Br^SeBr-_h=rY_Q8JX1;(^FbF!{WD0KksJg6M)SBbj6so?Or)%q6Zo z-Jeo&mVy`dMKcXN>0Sn7a}Og9EQJ^*j97|F#P(}bmXK%VUwQrAvH`6FFMf2t@Yhq@ zfAN<`%onxJZ0Eo8#kc!uxb@NZ+J7HeQi(OB4|KeS1Bc5u_1M+QAoT5vWR775ad1E4 z;-Xm}uuBpbCeqR=6_;sHd6K7Cgfh7EDY(MKr@?rhwWw6y`1}*F99L%T4~_=LlE`k2 zqa~7hNk`2Tzn3MKD5uo2UM;((YHRwZsQNeJYmxq`Hut?5MvK4nq2R0UDNE>|Tu#0_ z2P*t?>B{|HkDIJe$%RToYA3>;DCsgTBt_tmaOFxM07q3rY9lmfX$PbM<*Z* z+wNJhsTpUUbX3xyCsPz1kf!K-T}OH#CQP)r5ZH}tL;k>}MUMfns)oT7>DE~#z5G#o zeC|LSKl{esWWDWiKlk%`FlsKhfAb&9;R)lcyz%3GWx{Q4-@1eLN1Zr$p^7L9IGqqA zJPkNZCJOaq?IIYon?T_J^j0fU?Yt+K!3M_Yvnq^nWC&YlU_|ZO?(saG7ix3#0$7>? z)BoUovvb6R(d-q;qCwsOvj;Y#^4@=S?a|nmw^G(NL6G6Ufx7 z8?o5RV0eOy3|mIg4NDL(a#q(>GF$he2^JDCU?Tdm*o0S$trT`4G6N({Xw$8x;ZBph zUXr{RSRF2Ljx_uPR%=NXWs3;cDFmSlDc#H5aV#oH2|hbP(u~W(Xso$um!G9}as*Qs z4~-(4L{=2>K1#wKib~OPZCZ_h^lZL(I;(4-LADO#;~FfFErqfrI-f$pVwh$u)i|gu z=K!a!Yb35axZ?;F%!{EYm{OY-2$D3k<*P~4H~EMcV~tD^$Gjpz_pWN619;`8B8qf*|U!j?>Q>OPf^<&7-r1j7}}7U|R74OQphz zIEdNitQDIBsdel&t8F`kPx&mX(u(0jwh%07jt8LzCIHhl))fhdM+7aMI5+&jN%|Z!q%ZoCu1ANRASHynHYk{`hcnynD&aYG6Hu= zmV0EMC6`Qs@x3@GX|*Pp4M6KU7nNpBAS`Q(2-P`AvO##fgOlsQ&_BOoJFh7hxW-fT zO?C!M+&HO;qy;aG2uiRlC1s2$1=zvRQ$RSMr(Ewj_DS{HqQIgrKEvDQsuJ;3_ z#rX38BQG-AazJBYLWP)9{UxBS@}#zGYvyK9F$|p%T3IXKsiI;7?j|xE`rxH@gCy}x zUr~#Z>x)M_c8F^$fq7lkt$j9y?>YzI1kuqBf0p?j6L~HK5?|;?tl|z*BE<|*1x0Q52dFB)wHH?GU0ElAS!1qI)ff$mrwEwgiGJCXxoI7p-I2%u zPeb-KW?>_s7_TkuIkKGQiOO&jD1et|9@0ZZY_k9DfJR3-K;$LOzY7QOrqUL^=sp z3gBu|r{F;Zw9`*+^}QQZfUkR3A#9f%^^KrUs`TAws0cDx47DC5yZGG&VSrTxdd+GH>d782trim zZkBXJg2U7`$lb}ptw_tPv|Pj9b#E>0X<>IbxfzyAmDdcSZn+F2KI%g1!uA76s5l12 zB{z?a!HIdh^ONz&bj=p>L$ZopfOTXmo+L*J2QcNl(1^JlA7BUpDb8quV{mmL4MM?$ zWcX#*%{dRhw4Sge2xLIM-4JjKEx$hT34kOsFkXpev;iB1QMlf8OjMkP(8}V5@bK`K znvpB74yYaVMYH_^)J{!p!16{7n&wDtltz$i*m!+6z=C7D3w@HZV$0p2wn2}apxa*C z%qw-2r^uYjLJNEelJkNE^|RBQ%`a5sT1bXgi^ew=j1wYWdMhnzhzn*Ck%31KV%6KiV83nJ9WHX$>;VMe_K31IvJ)W&1 zpx$(gNr7RY+wLR@f&|!&gN#ZW&h68nBuyLMMnV#lUf~gYj^PdzJEUsh&%-ER=9O&K zzl}c*wg6_e`jH7#H?A4nOR7&S(tHUyKDz`35-LRoZDM(8UxU&S)~UQ<4j>KLEWb(^ zR1$21B{~pJp%t$@Az{NQG^1T2q{?nd$HasS+=5~y9Xceg`x(6yGQ7*;WDf1)OtW%WO`Q z3A`J$y)hBy_@x8^L>h6W?J$zC2z5o5#!7u)&i7l@gyu8TNs5`B4*Vj$#P1B2G}2AE zmbot8mp8mDz;;G>^Tcn3A2UIIBEo6~>oJG(L`?CsMRtBhk=IRd)LULDTv^8YEa(ug zE;%V+n$U*qqLy7H3iBul2&tKG*N{=OS_Uo_Lepil3KeyHaovqmTtSpTV5>lPttP7?#v zLJO{96sIpkmdLPm9;SFMBW}e27ey#un@tO1$z$s7HnB8wt`3%!P&YLLTGPM}hqhgE zoM8KW#eL^|b(G5NWp!Q9TLtbC<|iPbCHzw9Ul`-^kceIPP+yBLIoZ^n?~s?6jG&dB z;>*sm>I&VUvX&>b@LJklCa{h-$*|AzRXEjUT7dEz-doUU+QB#+=z}1RlAc!b3Mh=U z5TSsf08!mHL)G2QSOdeE5w^I>bmRm~g^_0cN)4s?H6=@X#WKJ)X(y_x zS!PukG<4I{_ess?biikP7cYzcBFn%PoXAN{Oks(Vz5zA>CMOHpePk~489X4350VI zhk&4xo+pInQW-xrd1xU7azZXTub&M(!tv&}qHFG#TBW7v?Or8Xc2o+xw=>L5!T@$` z``H2{x`v589y3}2Ci1M?z+bq%I>@1gyW_1>aaK>zEaRv;8I=&&OMy6F!z~U|;3~v4bU6~pvcImfM!bDEP|z(JO}cMCNWB}m7_rQWl)B6 zcV$_|Z(0v@XiuDMbHi2jLHlTJ6AJ?aoY45`>Nkd(Ux+8Evsa=3DVDAM`f&B`E<7LR zTEW#x<%0*maSGt9%vdL}sT1M|35k8yy=D0^ct3ZhA&%U^Pcf$}}kF zOo(UTI_A?kp|p~Kh;wAy<}9x!^g*gLw3RTjp(-9}BZBR78{bQqCar_bJ<(~9(XliP zgrcq|orm@);wVu$jVE3#_305Js;k95K8gtV(w56YITZDaHJ;A%cmZ~*R^&h&3*d2e zgVuuy0j?KAW!xcCLaomvuBh8KG|Ggo44Wf<}8G6~2F zsZ4`W21K|-*rNgvg)y3(vsoCP(WLGK_${=83M-9fLbBgCM$}rsk*`B+&RSyvRcnY^>8Drvh<>}EihBn3*i#e$`PUp z8hJU9AU@RQEJ#RjnuLW95gcPrljc@k=@X|%MjI~}Li8@d!>!XbB%m^ad+!Ab#N{43 zD<1a4eP21rT26&;o+`N$L~^E23MYL@T;qZpry5SJn1PfvcP~z+qM1b7KnaFcaIYpw zE}14PMaPMYTqP|bf!lH$BQ*i$>H~fSg;)&rwTp6PVh9$7oJ3PUpA{?>Zsi(;SkYNl zy*M*U`yQZOovbI%5S<{DF5V{}(bdVT)OZjO(7sgqCcycFdonlCO5*z$0o)a$U`+Hy zAr1lm`j{xr3In7@hqo*_^*8+0Z4EfjF2S+DJWOg7W9BOcYF=!zI{ly&$y;tP9z@CE zwmdiQkr2j&DEN$mBBXMbE)7yBWI%7zQ*0pLmmvq6!O5xlRz~k{AanhQAiuj-a3#^I z>bKKWSwNN3Mq`mKQuFNunX@c;Z?BKEGhW;0pZS~nw`R^u#_WX|F$N*Ttx=Dbbjjn^ zT>H?jtZfv*gCyuQM5^jIeD5kaJ{g$Ba2FUX5-$Gs9l{Od+@(XiV7j0QbhJ1~REx>w zzT6cfNC^)6%$}0swray~RWCojb$AjgLUZ^WwMboUMY-uY*1d#!$O`9pRnYetWSKij z-hO{X*Ww*l#x3R2JcQ&+x_M=$00l{@zoMf^ED7LiCI$uF^{fgkXT@Nnm%-zNSX`yi z>G{Z6ylC6goE=oJQzxib&b+XrjUM$=RaiX`yDOGH0GqYf?d?7@fROoNO*_w&Z(1&y0gPBFF)iq zQw?#7q;xw=IgAs6U3!ul^}9PeD0)~dY|_>2;DT3fc(F`t%#-T@l4_&q@y&6rqE7Mz zLXUc(U9_v-k*h3XEOwM73ODGC&Plv73aHeH@OMSN-%ND>J7VSQA1_V7g~1R%RMPW2 z^*n`wr}XTVD{Dzn)R zQt>Y2!scC^!KVAt@Hz=Nz1;EiJnNz|3XIpa)CUf$#wKmXy%9#&(eJsl^OS+fbWm+X z9%kf^v4OK7rRaZEuk1gU9Lo2M(%ZKTMW2^g^X_@VMc=O=n1w#3)o%;-tfK;tMpFsj zDl8WK!Y-;Q8QnN(G^d*5)iLn05+Na8lP|IHVa#{pE7p6*RGJHf=g`kRlIDBb`S2YN`9 zlI9qxy|dA^&ZB6>U2dcf;Z!)J8K`m}D`dNplA_g(6;fIppxCXAjM5jC{`HWh5P+MP zS6FDK;Io&Fqp+`3W1SqL>0_F6Hj?Oio5;1}MLceoom}o*yuG?LzjSgUkJIM{uWy9% z$f}t?f&tM|5WHP~UR76T&umIrPlGRh!)gK7aH4N0TU%*2fmROD4cW*jev-bP^zV6H zm{@gqFJhwN6?i-%>G}lRp6}D%O8B8zqhVv1_C7)4j&i@U#gWAb) zf8)Hw6~Fm~;ds66vxlCURUKT1HlDSwUQAIi4pmE+?nJ}eo~*B(q5;YR8mg^F^^ zu1(Wd$Jo`0dFoUr;5BtRN-x9S3W^V~HmY2M7e2>yvK4#QZ_3>^nmG5a&##Y)nR#0A zH~Tawz6BX~1qfA)QdsdlWA-#5qAkafupG_@2b^5UEtVUH#uv%vo3|J5E>5`h^N**e zJque&FS*s?2eooqFZrU~J-B~XIxPm!-`^WrY~)(?`HEtTS-# zD!HG#lM2Y7Oi>nXohCxe4SK1b{SDR_U6;Q1oselC{j}KM57n0U+(JjS^|m3*mbJ%K z=UrcRI%f8_PbM686|eMi@8t?!CT6Xt#O-7ALDcC3S57YOqmc*P3TRN;_VIO}huJxn zRs+RhdY8EXiymyvc4`60y}O}YUxKM{+6LM}0L1Tdh=k}+ddN4tzJFdAXcW{tGq~N2 z(h{=uH{-3-kMQ$1XDhefdsUDv7`lu+k5R$6`6|zB46q4oAEfT|S_psneM6)!$L(JB z)>+FELcE=0%UlzmZ5htGlBiN?KIo2vv(`-{+yK;s=GH(-UXAXC3AE&&ql>MxYES506a&5HMQ@L%B6y?j9U60MHAqKu=n+;th-FNO_2yI_ z!mA^spj%4$E|f^(#j=JAOhEW{D`}N&ijW!dl^(4MkK3h>E<+1_$2{6tQRm5nGP8wI zSg=o{Gidfi0L>DKJRPL^&Bl?lxc|P;G+u)C-n^FZNKc5kWpwO zs*U^emj<0@$|R}2v5Fm@hk7GDC%oc2eCr{w$QsWG_Qin$qZCrEJcjFP)0I2k-C$)7lgUHU$k*Y{Dx=6{c!YrkE5;VswRRbhm$ zeaFWhc_}xgpf7wCh^l~`H?5-r=9%^9G zECT%YQD&c#VUu_aQ}*a&<@y2>2b*5G8|3KHjap2Z!UOSqg{UDE&3JbFVZ$$zr(FF~ zXPSb&WN=%y99X6!#63%}A{)jAbq|y|PCiig%(NVSlhxjTTU8{X=KqZ`1T4lq|6XaU zxKna%XA02Q7!BqV!U_VEy6?axB-1xq%u&f!oSWzaPuHrX`5FH(E;poJ1DDU#_ zVdL0axB=%g=sWvZVc72C_Sb za1T*8cF5Fn_NY=$c%{e`T%P}zlq_6GJ?)(gCQ`H38!>sd^U7v`Ra~aMMx>`nMZ9o$ z5GK?HL^qgn9pE>MzHQaYUFuO_pqJWh&B4&=tGk$9DoQJja{;~O6G(;ST~OKHXiCu{ zvYJ6hk1Bp9c=S2~u#88QB_GWV!m3~7l(dmnB%!d{497IV2n|?cQA$U|5|C(6)y}=; zke5iB7_8$Z3SyWk%vZ%X6w0W!E!Vx8Lu)%n=ojPY?*->af%B&jm z))uhw|ZIp`K4DkGX z#V4ynzTX8`=?*Cjs$#C+6bp&y&R6MaI zqH^0RxHu9o2r+FzAx@~CUiMj2*v0AXJ<((08wScd7c~paqDC z=`H*o$w@Xqh7OZe2{t?rVbf|Xu|n6 zBq#SPUe;8ASD#gKe}mDi$lO0tosE!CrJLL#$*W^bu%l5#o=X`N$P_S7PV@R21ATJ9 z6pz!DA(^{!`)FnXL}VnnRg%cS7M)Ob6bCgWHnAz>GI0bl%SLV}Yk9NSWog~8 zOzB5^Xj9H9gp!KOxd)Sjna+%JjvfiI$n+&F&@!N*zg8g+dQ(bv-ElS@AZjyKbyvi|x+GS3s$O+ zk&rlNUIwky` z{A1js%^&gCmKiY=F9*BEoSyR~`Bv_|0l}NY4{wRkRh%4lKVhI-@P>2wB^xNya#~@t zX}XT7`aEo!wcN7!1cVl-Y12;v_5-$Ha{Uf4Eg~6t5+7l~64b)TiKF5maG)dxJqwVh zCSZuNJ{Kt*0oBMXf(6|)^qe~O!C6q*AZ2DMD>}n>)VwkPkp&V55;B1%2jHI0 zq17=leo`)^+|#%-_eIQl=4U!W9r=k<$gsvFP0v-gYWSE1Es$cYPRKwVPU9mjrWn{A zh2Uh-SLDQ{addl3IbnMuHRM5*PO=b zi=P^6n;Cp?qpTT3?$d$|lgQA5sU9ox!eb0_G6C)jEW2V?*W>^g;d@c}2{-{UJ%z1Y zlSI8eDCc+ej+HJUuE8UY-YI(6X)p&f!o+e_2d&59G71YTZg18M_u0!xpM2@*uIed< z$Lcrv_c%aUk8bGpnS*S+v$atcuaegvU%oxjSDn>&Uwh$PFId8?QwlidUudDb%Y*<8IJoeZ(eHXFId2O`p#K|KnF zX;}4ES2BwnXL1F=ic7Ov?#ODtou9A`zsJ0Kc)bk!^Ju$&m%11)iRK48IiIY6JI0EH z$QA0&cj~YA4qhg=A9q)th1+4aXsz{doHq^QIBd*Ipp{fKb=_7<)?RRTng8(K3cnJ^mp^dhMf;RC{Z){ z;$z!KNiOS73E9@bnrJPTO8u>cH* zHx#qQg%4LPGG^$GgL(mwAaNN7E`pGWY)@a}m6b@B5x_UE1V{8w$+tFr%A_|gcF@Pg zfmt})j%6Z&UYzV~umVZlbShUC(Vckv(ayE^F&;6k+fAE5<3!>8xlWc-D9f!pOUAd! z+zWj~C!@MnBo1Sz4?i@-lM*iY@xXA-vnja9-M>^-P6N{7AR`XSD4tuTJVZfG5X3!I zWhb?v5#6g$J?~W9Ps2Os6YOKb9V*J?sC=}>?`anpeYBDJOPg95cbiL_WorHQjd`?^ zXX-oKe}`>Pqxo)KD=~_Ja8y zp&!^0wZB6`Kpd+M8nq3>g5)N{(TF=O5?f$8o`MbC&FraK%^ef_xJ_g&X-a=Y+Xe#N z0!m4}`)#M^lM@aNTo9B_J8Xmybm?UKwnD$Cl!q_ee4AdGdeEKqc-HidkZGA{R^UN- zp~KTsf)c%k-)}3Y;xef($-n|;x%2%zZxI4Ur0GvhHXQH>VM=~rWUyG6+Y-)5m%OuJ z9-g#ZHyF9>k|m~76K)+Hj=aU`=_aIx&&PG`*tnqUy>j^!2|X3sfSibMbEbl@hYjb& z)0L||=lY2BU;cro#M&V_x^sP$;w}oJSuVpRFv$3fPznt{sDYJ_(mD8CC%SkP@bStV}Gy&4C(jE79*Y5tna_!;G+h(wn_bxoYo10%i zI;}*&l1Lb+mA8Q{$ut#uZVu2bQXB<%U#Xx>|HSkc-8lp#Q_~OPa5h`cS;Y6fJlimm z$p@2!uMu7dT7`T|CleYcpm={)%r|B2n4@s*b`Nd;=)9Kok%$w~PH9gLra`=sVJ7JA zR@OzU-idB5Z(Re3%lf9harelmKa+!-Td}PVL#A8_gK?)5W(f<;bIeK}LyIw$!IDk| z^Gt~AmdD$njSAEm>FHw}51|9!10&AI`4ldoq~xC_TfFS$khkDRi_v`E%TD)~xfQoD ze;dn}_3Oc0y25n)r%X{q$6LgAwTm;v7X^J9!7opXjO{k4HSZ3%sbplc?+e;sZO`SB^Cvptt>HrfN|$+P;N3kU1oT5t93rn10AApEa1bu zIm*i*nr)~qnl&p3S^+ZFS`!j9TEGH+;ANIkQJ%hEY&!7AUC;bb-nOg6Ds3AZ!Xp>Z zgt#<5u@*wtyd(xx?EGYTX&T-yr6(Ln(2jR<{ep?1-V6fM#k}AEW%`j z33ZDNsb@}IG~-UnjkE(5Ya@kikHdUbuY+C!i!}j>4#F~quZ2a?CU2pq676YrKZ@zHi0iE@~TLWV9tda6&SGb$u!u%tUop_&H#wxl(MR@` z7@!*Qge-g5x%k&RlAytZC^T{}!i_yg@i_hEj;9{SK=yumO1d4e!Z3KDVL0LM}=!&g#FPD!nZ z(9SfYAaee8nVpu3#v^&E>$lKjlO~=A@#GLY(#xI9>m6ENf#6UV)*1zS#zTo&-t6{p zZ?Y_R>on*ro^*MsMCg$sTJ!9js`9zJIhqx!pzZp4XT9VA%>bmtXJ7%6b8q+}$h@c| zkHIAx2e<|k^5=9ip@kyk0 zL^h<7w10+H9!kZ6W;gnW8$oD_Npuj_v7OC@ZC#v|I(iV2=+MDqn05+_h^SL1rDGJG z?9ib=1n*uFqQ0LuJL4LG2cP`rz4x2<`}=-x7KHcZZu@IpXjcjSnb*Db51O~?U+jC- z5B7EUHFu8{g!fSLGVSKxJ2I`h&ps-dAyK~w9<(lcA`-lWnSYi(4Q((dz<zWLI z@{&X?TSU)2dCQ6mr6NGAhzYYIZg^G=?+=K;r$GLr{0n8!Ew~6PCW42K20hUdlf|-* zDn~r9C_uts4Si^-yk-}AAh0jc#TfnAgcr29)f7oaKGw4~w1J;QR3k~^LLO4sTQCH$ z0Y9_@CI-APC)lnPozCd<}Nc&{2P+B{@64KhcuAnzUC|65jIOCYSn8C&4<&=EnU} z`o5RDJ6iHP#yvN)nFI2Q9w@2WS7#6E!Cth9i2IcIm3b4(H1hI(V5{CD)%Lw#ZHY|MdXop*f=Z-8}o*?Zx`s-(#NmR zZ#C5~oM<$i>A ziFrGlmHBz0!_h9C60(PRhY6Ivt!gyC6AiVBmM>h5sS^!s Date: Wed, 14 Jul 2021 13:20:16 -0700 Subject: [PATCH 21/47] Arduino template_project --- .../arduino/template_project/project.ino | 12 +++ .../arduino/template_project/src/crt_config.h | 56 +++++++++++++ .../template_project/src/implementation.c | 78 +++++++++++++++++ .../arduino/template_project/src/model.cpp | 83 +++++++++++++++++++ .../arduino/template_project/src/model.h | 20 +++++ .../arduino/template_project/src/parameters.h | 19 +++++ 6 files changed, 268 insertions(+) create mode 100644 apps/microtvm/arduino/template_project/project.ino create mode 100644 apps/microtvm/arduino/template_project/src/crt_config.h create mode 100644 apps/microtvm/arduino/template_project/src/implementation.c create mode 100644 apps/microtvm/arduino/template_project/src/model.cpp create mode 100644 apps/microtvm/arduino/template_project/src/model.h create mode 100644 apps/microtvm/arduino/template_project/src/parameters.h diff --git a/apps/microtvm/arduino/template_project/project.ino b/apps/microtvm/arduino/template_project/project.ino new file mode 100644 index 000000000000..4920e8b36749 --- /dev/null +++ b/apps/microtvm/arduino/template_project/project.ino @@ -0,0 +1,12 @@ +#include "src/model.h" + +static Model model; + +void setup() { + model = Model(); + //model.inference(input_data, output_data); +} + +void loop() { + // put your main code here, to run repeatedly: +} diff --git a/apps/microtvm/arduino/template_project/src/crt_config.h b/apps/microtvm/arduino/template_project/src/crt_config.h new file mode 100644 index 000000000000..b81a74eb4ae6 --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/crt_config.h @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \file tvm/runtime/crt/host/crt_config.h + * \brief CRT configuration for the host-linked CRT. + */ +#ifndef TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ +#define TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ + +/*! Log level of the CRT runtime */ +#define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG + +/*! Support low-level debugging in MISRA-C runtime */ +#define TVM_CRT_DEBUG 0 + +/*! Maximum supported dimension in NDArray */ +#define TVM_CRT_MAX_NDIM 6 +/*! Maximum supported arguments in generated functions */ +#define TVM_CRT_MAX_ARGS 10 +/*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ +#define TVM_CRT_STRLEN_DLTYPE 10 +/*! Maximum supported string length in function names */ +#define TVM_CRT_STRLEN_NAME 80 + +/*! Maximum number of registered modules. */ +#define TVM_CRT_MAX_REGISTERED_MODULES 2 + +/*! Size of the global function registry, in bytes. */ +#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 512 + +/*! Maximum packet size, in bytes, including the length header. */ +#define TVM_CRT_MAX_PACKET_SIZE_BYTES 8 * 1024 + +/*! \brief Maximum length of a PackedFunc function name. */ +#define TVM_CRT_MAX_FUNCTION_NAME_LENGTH_BYTES 30 + +// #define TVM_CRT_FRAMER_ENABLE_LOGS + +#endif // TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c new file mode 100644 index 000000000000..11cb5ec8090c --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -0,0 +1,78 @@ +#ifndef IMPLEMENTATION +#define IMPLEMENTATION + +#include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" +#include "Arduino.h" + +size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, + va_list args) { + return 0; +} + +// Blink code for debugging purposes +void TVMPlatformAbort(tvm_crt_error_t error) { + //pinMode(LED_BUILTIN, OUTPUT); + for (;;) { + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(250); + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(750); + } +} + +// Heap for use by TVMPlatformMemoryAllocate. + +// Called by TVM to allocate memory. +tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, void** out_ptr) { + if (num_bytes == 0) { + num_bytes = sizeof(int); + } + *out_ptr = malloc(num_bytes); + return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; +} + +// Called by TVM to deallocate memory. +tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { + free(ptr); + return kTvmErrorNoError; +} + +unsigned long g_utvm_start_time; + +#define MILLIS_TIL_EXPIRY 200 + +int g_utvm_timer_running = 0; + +tvm_crt_error_t TVMPlatformTimerStart() { + if (g_utvm_timer_running) { + return kTvmErrorPlatformTimerBadState; + } + g_utvm_timer_running = 1; + g_utvm_start_time = micros(); + return kTvmErrorNoError; +} + +tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { + if (!g_utvm_timer_running) { + return kTvmErrorPlatformTimerBadState; + } + g_utvm_timer_running = 0; + unsigned long g_utvm_stop_time = micros() - g_utvm_start_time; + *elapsed_time_seconds = ((double) g_utvm_stop_time) / 1e6; + return kTvmErrorNoError; +} + +unsigned int random_seed = 0; +tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { + for (size_t i = 0; i < num_bytes; ++i) { + buffer[i] = (uint8_t)4; // Chosen by fair die roll + // Guaranteed to be random + } + return kTvmErrorNoError; +} + +#endif diff --git a/apps/microtvm/arduino/template_project/src/model.cpp b/apps/microtvm/arduino/template_project/src/model.cpp new file mode 100644 index 000000000000..1142dde24cb7 --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/model.cpp @@ -0,0 +1,83 @@ +#include "model.h" +#include "parameters.h" +#include "standalone_crt/include/tvm/runtime/crt/logging.h" +#include "standalone_crt/include/tvm/runtime/crt/crt.h" +#include "standalone_crt/include/tvm/runtime/crt/packed_func.h" +#include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" +#include "standalone_crt/include/dlpack/dlpack.h" + +// Model +#include "model/graph_json.c" +#include "Arduino.h" + +Model::Model() +{ + tvm_crt_error_t ret = TVMInitializeRuntime(); + + + TVMPackedFunc pf; + TVMArgs args = TVMArgs_Create(NULL, NULL, 0); + TVMPackedFunc_InitGlobalFunc(&pf, "runtime.SystemLib", &args); + TVMPackedFunc_Call(&pf); + + TVMModuleHandle mod_syslib = TVMArgs_AsModuleHandle(&pf.ret_value, 0); + + // Create device + int64_t device_type = kDLCPU; + int64_t device_id = 0; + + DLDevice dev; + dev.device_type = (DLDeviceType)device_type; + dev.device_id = device_id; + + graph_runtime = NULL; + TVMGraphExecutor_Create(graph_json, mod_syslib, &dev, &graph_runtime); +} + + +void Model::inference(void *input_data, void *output_data) { + // Reformat input data into tensor + DLTensor input_data_tensor = { + input_data, + HARDWARE_DEVICE, + INPUT_DATA_DIMENSION, + INPUT_DATA_TYPE, + INPUT_DATA_SHAPE, + NULL, + 0, + }; + + // Run inputs through the model + TVMGraphExecutor_SetInput(graph_runtime, INPUT_LAYER, (DLTensor*) &input_data_tensor); + TVMGraphExecutor_Run(graph_runtime); + + // Prepare our output tensor + DLTensor output_data_tensor = { + output_data, + HARDWARE_DEVICE, + OUTPUT_DATA_DIMENSION, + OUTPUT_DATA_TYPE, + OUTPUT_DATA_SHAPE, + NULL, + 0, + }; + + // Populate output tensor + TVMGraphExecutor_GetOutput(graph_runtime, 0, &output_data_tensor); +} + +int Model::infer_category(void *input_data) { + int8_t output_data[10] = {0}; + Model::inference(input_data, output_data); + int best = -1; + int maximum = -1000; + //Serial.println("Output tensor:"); + for (int i = 0; i < 10; i++) { + //Serial.println(output_data[i]); + if (output_data[i] > maximum) { + maximum = output_data[i]; + best = i; + } + } + return best; +} diff --git a/apps/microtvm/arduino/template_project/src/model.h b/apps/microtvm/arduino/template_project/src/model.h new file mode 100644 index 000000000000..a2ebc1047061 --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/model.h @@ -0,0 +1,20 @@ +#ifndef Model_h +#define Model_h + +#include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" + + +class Model +{ + + public: + Model(); + void inference(void *input_data, void *output_data); + int infer_category(void *input_data); + + private: + TVMGraphExecutor* graph_runtime; +}; + +#endif + diff --git a/apps/microtvm/arduino/template_project/src/parameters.h b/apps/microtvm/arduino/template_project/src/parameters.h new file mode 100644 index 000000000000..f9f6d78f21eb --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/parameters.h @@ -0,0 +1,19 @@ +#ifndef Parameters_h +#define Parameters_h + +#include "standalone_crt/include/dlpack/dlpack.h" + +// Some Arduinos (like the Spresense) have multiple CPUs, +// so this could be expaneded at some point +static const DLDevice HARDWARE_DEVICE = {kDLCPU, 0}; + +static const int INPUT_DATA_DIMENSION = $input_data_dimension; +static const int64_t INPUT_DATA_SHAPE[] = $input_data_shape; +static const DLDataType INPUT_DATA_TYPE = $input_data_type; +static const char* INPUT_LAYER = $input_layer_name; + +static const int OUTPUT_DATA_DIMENSION = $output_data_dimension; +static const int64_t OUTPUT_DATA_SHAPE[] = $output_data_shape; +static const DLDataType OUTPUT_DATA_TYPE = $output_data_type; + +#endif From c1961b33efba6b5886dbcf0afc14c20d01e27201 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 13:20:27 -0700 Subject: [PATCH 22/47] Arduino uTVM API server --- .../template_project/microtvm_api_server.py | 499 ++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 apps/microtvm/arduino/template_project/microtvm_api_server.py diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py new file mode 100644 index 000000000000..8a1e4e3f87cd --- /dev/null +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -0,0 +1,499 @@ +import collections +import glob +import json +import logging +import os +import os.path +import pathlib +import re +import shlex +import shutil +import subprocess +import sys +import tarfile +from string import Template +import tempfile +import time + +import serial +import serial.tools.list_ports + +from tvm.micro.project_api import server + + +_LOG = logging.getLogger(__name__) + + +API_SERVER_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) + + +BUILD_DIR = API_SERVER_DIR / "build" + + +MODEL_LIBRARY_FORMAT_RELPATH = "src/model/model.tar" + + +IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() + + +def check_call(cmd_args, *args, **kwargs): + cwd_str = "" if "cwd" not in kwargs else f" (in cwd: {kwargs['cwd']})" + _LOG.info("run%s: %s", cwd_str, " ".join(shlex.quote(a) for a in cmd_args)) + return subprocess.check_call(cmd_args, *args, **kwargs) + + +CACHE_ENTRY_RE = re.compile(r"(?P[^:]+):(?P[^=]+)=(?P.*)") + + +CMAKE_BOOL_MAP = dict( + [(k, True) for k in ("1", "ON", "YES", "TRUE", "Y")] + + [(k, False) for k in ("0", "OFF", "NO", "FALSE", "N", "IGNORE", "NOTFOUND", "")] +) + + +class CMakeCache(collections.abc.Mapping): + def __init__(self, path): + self._path = path + self._dict = None + + def __iter__(self): + return iter(self._dict) + + def __getitem__(self, key): + if self._dict is None: + self._dict = self._read_cmake_cache() + + return self._dict[key] + + def __len__(self): + return len(self._dict) + + def _read_cmake_cache(self): + """Read a CMakeCache.txt-like file and return a dictionary of values.""" + entries = collections.abc.OrderedDict() + with open(self._path, encoding="utf-8") as f: + for line in f: + m = CACHE_ENTRY_RE.match(line.rstrip("\n")) + if not m: + continue + + if m.group("type") == "BOOL": + value = CMAKE_BOOL_MAP[m.group("value").upper()] + else: + value = m.group("value") + + entries[m.group("name")] = value + + return entries + + +CMAKE_CACHE = CMakeCache(BUILD_DIR / "CMakeCache.txt") + + +class BoardError(Exception): + """Raised when an attached board cannot be opened (i.e. missing /dev nodes, etc).""" + + +class BoardAutodetectFailed(Exception): + """Raised when no attached hardware is found matching the board= given to ZephyrCompiler.""" + + +'''def _get_flash_runner(): + flash_runner = CMAKE_CACHE.get("ZEPHYR_BOARD_FLASH_RUNNER") + if flash_runner is not None: + return flash_runner + + with open(CMAKE_CACHE["ZEPHYR_RUNNERS_YAML"]) as f: + doc = yaml.load(f, Loader=yaml.FullLoader) + return doc["flash-runner"] + + +def _get_device_args(options, cmake_entries): + flash_runner = _get_flash_runner() + + if flash_runner == "nrfjprog": + return _get_nrf_device_args(options) + + if flash_runner == "openocd": + return _get_openocd_device_args(options) + + raise BoardError( + f"Don't know how to find serial terminal for board {CMAKE_CACHE['BOARD']} with flash " + f"runner {flash_runner}" + )''' + + +# kwargs passed to usb.core.find to find attached boards for the openocd flash runner. +BOARD_USB_FIND_KW = { + "nucleo_l4r5zi": {"idVendor": 0x0483, "idProduct": 0x374B}, + "nucleo_f746zg": {"idVendor": 0x0483, "idProduct": 0x374B}, + "stm32f746g_disco": {"idVendor": 0x0483, "idProduct": 0x374B}, +} + + +def openocd_serial(options): + """Find the serial port to use for a board with OpenOCD flash strategy.""" + if "openocd_serial" in options: + return options["openocd_serial"] + + import usb # pylint: disable=import-outside-toplevel + + find_kw = BOARD_USB_FIND_KW[CMAKE_CACHE["BOARD"]] + boards = usb.core.find(find_all=True, **find_kw) + serials = [] + for b in boards: + serials.append(b.serial_number) + + if len(serials) == 0: + raise BoardAutodetectFailed(f"No attached USB devices matching: {find_kw!r}") + serials.sort() + + autodetected_openocd_serial = serials[0] + _LOG.debug("zephyr openocd driver: autodetected serial %s", serials[0]) + + return autodetected_openocd_serial + + +def _get_openocd_device_args(options): + return ["--serial", openocd_serial(options)] + + +def _get_nrf_device_args(options): + nrfjprog_args = ["nrfjprog", "--ids"] + nrfjprog_ids = subprocess.check_output(nrfjprog_args, encoding="utf-8") + if not nrfjprog_ids.strip("\n"): + raise BoardAutodetectFailed(f'No attached boards recognized by {" ".join(nrfjprog_args)}') + + boards = nrfjprog_ids.split("\n")[:-1] + if len(boards) > 1: + if options["nrfjprog_snr"] is None: + raise BoardError( + "Multiple boards connected; specify one with nrfjprog_snr=: " f'{", ".join(boards)}' + ) + + if str(options["nrfjprog_snr"]) not in boards: + raise BoardError( + f"nrfjprog_snr ({options['nrfjprog_snr']}) not found in {nrfjprog_args}: {boards}" + ) + + return ["--snr", options["nrfjprog_snr"]] + + if not boards: + return [] + + return ["--snr", boards[0]] + + +PROJECT_OPTIONS = [ + server.ProjectOption( + "gdbserver_port", help=("If given, port number to use when running the " "local gdbserver") + ), + server.ProjectOption( + "openocd_serial", + help=("When used with OpenOCD targets, serial # of the " "attached board to use"), + ), + server.ProjectOption( + "nrfjprog_snr", + help=( + "When used with nRF targets, serial # of the " "attached board to use, from nrfjprog" + ), + ), + server.ProjectOption("verbose", help="Run build with verbose output"), + server.ProjectOption( + "west_cmd", + help=( + "Path to the west tool. If given, supersedes both the zephyr_base " + "option and ZEPHYR_BASE environment variable." + ), + ), + server.ProjectOption("zephyr_base", help="Path to the zephyr base directory."), + server.ProjectOption("zephyr_board", help="Name of the Zephyr board to build for"), +] + + +class Handler(server.ProjectAPIHandler): + def __init__(self): + super(Handler, self).__init__() + self._proc = None + + def server_info_query(self): + return server.ServerInfo( + platform_name="arduino", + is_template=IS_TEMPLATE, + model_library_format_path="" + if IS_TEMPLATE + else (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH), + project_options=PROJECT_OPTIONS, + ) + + CRT_COPY_ITEMS = ("include", "src") + def _copy_standalone_crt(self, source_dir, standalone_crt_dir): + # Copy over the standalone_crt directory + output_crt_dir = source_dir / "standalone_crt" + output_crt_dir.mkdir() + for item in self.CRT_COPY_ITEMS: + src_path = os.path.join(standalone_crt_dir, item) + dst_path = output_crt_dir / item + if os.path.isdir(src_path): + shutil.copytree(src_path, dst_path) + else: + shutil.copy2(src_path, dst_path) + + GRAPH_JSON_TEMPLATE = 'static const char* graph_json = "{}";\n' + def _compile_graph_json(self, model_dir, obj): + graph_json = json.dumps(obj).replace('"', '\\"') + output = self.GRAPH_JSON_TEMPLATE.format(graph_json) + graph_json_path = model_dir / "graph_json.c" + with open(graph_json_path, "w") as out_file: + out_file.write(output) + + + def _disassemble_mlf(self, mlf_tar_path, source_dir): + mlf_unpacking_dir = tempfile.TemporaryDirectory() + print(mlf_tar_path) + with tarfile.open(mlf_tar_path, 'r:') as tar: + tar.extractall(mlf_unpacking_dir.name) + print("Unpacked tar") + + # Copy C files + # TODO are the defaultlib0.c the same? + model_dir = source_dir / "model" + model_dir.mkdir() + for source, dest in [ + ("codegen/host/src/default_lib0.c", "default_lib0.c"), + ("codegen/host/src/default_lib1.c", "default_lib1.c"), + ]: + shutil.copy( + os.path.join(mlf_unpacking_dir.name, source), + model_dir / dest + ) + + # Load graph.json, serialize to c format, and extact parameters + with open( + os.path.join(mlf_unpacking_dir.name, + "runtime-config/graph/graph.json") + ) as f: + graph_data = json.load(f) + self._compile_graph_json(model_dir, graph_data) + + mlf_unpacking_dir.cleanup() + return graph_data + + def _print_c_array(self, l): + c_arr_str = str(l) + return "{" + c_arr_str[1:-1] + "}" + + + def _print_c_str(self, s): + return '"{}"'.format(s) + + + DL_DATA_TYPE_REFERENCE = { + "uint8": "{kDLUInt, 8, 0}", + "uint16": "{kDLUInt, 16, 0}", + "uint32": "{kDLUInt, 32, 0}", + "uint64": "{kDLUInt, 64, 0}", + "int8": "{kDLInt, 8, 0}", + "int16": "{kDLInt, 16, 0}", + "int32": "{kDLInt, 32, 0}", + "int64": "{kDLInt, 64, 0}", + "float16": "{kDLFloat, 16, 0}", + "float32": "{kDLFloat, 32, 0}", + "float64": "{kDLFloat, 64, 0}", + } + def _populate_parameters_file(self, graph, source_dir): + graph_types = graph["attrs"]["dltype"] + graph_shapes = graph["attrs"]["shape"] + assert(graph_types[0] == "list_str") + assert(graph_shapes[0] == "list_shape") + + template_values = { + "input_data_dimension": len(graph_shapes[1][0]), + "input_data_shape": self._print_c_array(graph_shapes[1][0]), + "input_data_type": self.DL_DATA_TYPE_REFERENCE[graph_types[1][0]], + "output_data_dimension": len(graph_shapes[1][-1]), + "output_data_shape": self._print_c_array(graph_shapes[1][-1]), + "output_data_type": self.DL_DATA_TYPE_REFERENCE[graph_types[1][-1]], + "input_layer_name": self._print_c_str(graph["nodes"][0]["name"]), + } + + # Apply template values + with open(source_dir / "parameters.h", 'r') as f: + template_params = Template(f.read()) + + parameters_h = template_params.substitute(template_values) + + with open(source_dir / "parameters.h", "w") as f: + f.write(parameters_h) + + C_STD_LIBS = ["assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", + "float.h", "inttypes.h", "iso646.h", "limits.h", "locale.h", + "math.h", "setjmp.h", "signal.h", "stdalign.h", "stdarg.h", + "stdatomic.h", "stdbool.h", "stddef.h", "stdint.h", "stdio.h", + "stdlib.h", "stdnoreturn.h", "string.h", "tgmath.h", "threads.h", + "time.h", "uchar.h", "wchar.h", "wctype.h"] + + # This list is LONG, TODO find a better way + CPP_STD_LIBS = ["utility"] + + POSSIBLE_BASE_PATHS = ["src/standalone_crt/include/"] + # /home/.../project/src/standalone_crt/include/tvm/runtime/crt/graph_executor/load_json.c + # tvm/runtime/crt/platform.h + def _find_modified_include_path(self, project_dir, file_path, import_path): + # If the import is a C standard library, don't modify it + if import_path in self.C_STD_LIBS or import_path in self.CPP_STD_LIBS: + return import_path + + # If the import already works, don't modify it + print("Checking existance!") + if (file_path / import_path).exists(): + print("Import path succeeded!") + return import_path + + relative_path = file_path.relative_to(project_dir) + print(relative_path) + up_dirs_path = "../" * str(relative_path).count("/") + + for base_path in self.POSSIBLE_BASE_PATHS: + full_potential_path = project_dir / base_path / import_path + print(full_potential_path) + if full_potential_path.exists(): + new_include = up_dirs_path + base_path + import_path + print(f"Replaced {import_path} with {new_include}") + return new_include + + print(f"Error! Could not find {import_path}") + print(file_path) + + # Arduino only supports imports relative to the top-level project, + # so we need to adjust each import to meet this convention + def _convert_imports(self, project_dir, source_dir): + for ext in ("c", "h"): + for filename in source_dir.rglob(f"*.{ext}"): + with filename.open() as file: + lines = file.readlines() + + for i in range(len(lines)): + # Check if line has an include + result = re.search(r"#include\s*<([^>]*)>", lines[i]) + if not result: + continue + print(lines[i]) + print(result.groups()[0]) + new_include = self._find_modified_include_path( + project_dir, filename, result.groups()[0] + ) + + lines[i] = f'#include "{new_include}"' + + with filename.open("w") as file: + file.writelines(lines) + + print(filename) + print("---") + + + def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): + # Copy template folder to project_dir, creating project/ and src/ + # directories in the process. Also copies this file, microtvm_api_server.py, + # in case TVM needs to call it from the new location + shutil.copytree(API_SERVER_DIR, project_dir, dirs_exist_ok=True) + + # Reference key directories with pathlib + project_dir = pathlib.Path(project_dir) + source_dir = project_dir / "src" + + # Copy standalone_crt into src folder + self._copy_standalone_crt(source_dir, standalone_crt_dir) + + # Unpack the MLF and copy the relevant files + graph = self._disassemble_mlf(model_library_format_path, source_dir) + + # Populate our parameters file + self._populate_parameters_file(graph, source_dir) + + # Recursively change imports + self._convert_imports(project_dir, source_dir) + + def build(self, options): + BUILD_DIR.mkdir() + + cmake_args = ["cmake", ".."] + if options.get("verbose"): + cmake_args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE") + + if options.get("zephyr_base"): + cmake_args.append(f"-DZEPHYR_BASE:STRING={options['zephyr_base']}") + + cmake_args.append(f"-DBOARD:STRING={options['zephyr_board']}") + + check_call(cmake_args, cwd=BUILD_DIR) + + args = ["make", "-j2"] + if options.get("verbose"): + args.append("VERBOSE=1") + check_call(args, cwd=BUILD_DIR) + + @classmethod + def _is_qemu(cls, options): + return "qemu" in options["zephyr_board"] + + def flash(self, options): + if self._is_qemu(options): + return # NOTE: qemu requires no flash step--it is launched from open_transport. + + zephyr_board = options["zephyr_board"] + + # The nRF5340DK requires an additional `nrfjprog --recover` before each flash cycle. + # This is because readback protection is enabled by default when this device is flashed. + # Otherwise, flashing may fail with an error such as the following: + # ERROR: The operation attempted is unavailable due to readback protection in + # ERROR: your device. Please use --recover to unlock the device. + if zephyr_board.startswith("nrf5340dk") and _get_flash_runner() == "nrfjprog": + recover_args = ["nrfjprog", "--recover"] + recover_args.extend(_get_nrf_device_args(options)) + self._subprocess_env.run(recover_args, cwd=build_dir) + + check_call(["make", "flash"], cwd=API_SERVER_DIR / "build") + + def _open_qemu_transport(self, options): + zephyr_board = options["zephyr_board"] + # For Zephyr boards that run emulated by default but don't have the prefix "qemu_" in their + # board names, a suffix "-qemu" is added by users of µTVM when specifying the board name to + # inform that the QEMU transporter must be used just like for the boards with the prefix. + # Zephyr does not recognize the suffix, so we trim it off before passing it. + if "-qemu" in zephyr_board: + zephyr_board = zephyr_board.replace("-qemu", "") + + return ZephyrQemuTransport(options) + + def open_transport(self, options): + if self._is_qemu(options): + transport = self._open_qemu_transport(options) + else: + transport = ZephyrSerialTransport(options) + + to_return = transport.open() + self._transport = transport + return to_return + + def close_transport(self): + if self._transport is not None: + self._transport.close() + self._transport = None + + def read_transport(self, n, timeout_sec): + if self._transport is None: + raise server.TransportClosedError() + + return self._transport.read(n, timeout_sec) + + def write_transport(self, data, timeout_sec): + if self._transport is None: + raise server.TransportClosedError() + + return self._transport.write(data, timeout_sec) + + +if __name__ == "__main__": + server.main(Handler()) From ceadb9ac755145b757ef025b38c8ad91137b1199 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 17:02:40 -0700 Subject: [PATCH 23/47] Move crt_config to dedicated folder --- .../src/{ => standalone_crt/crt_config}/crt_config.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename apps/microtvm/arduino/template_project/src/{ => standalone_crt/crt_config}/crt_config.h (90%) diff --git a/apps/microtvm/arduino/template_project/src/crt_config.h b/apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h similarity index 90% rename from apps/microtvm/arduino/template_project/src/crt_config.h rename to apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h index b81a74eb4ae6..c3e8fea1ba08 100644 --- a/apps/microtvm/arduino/template_project/src/crt_config.h +++ b/apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h @@ -21,8 +21,8 @@ * \file tvm/runtime/crt/host/crt_config.h * \brief CRT configuration for the host-linked CRT. */ -#ifndef TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ -#define TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ +#ifndef TVM_RUNTIME_MICRO_CRT_CONFIG_H_ +#define TVM_RUNTIME_MICRO_CRT_CONFIG_H_ /*! Log level of the CRT runtime */ #define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG @@ -35,9 +35,9 @@ /*! Maximum supported arguments in generated functions */ #define TVM_CRT_MAX_ARGS 10 /*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ -#define TVM_CRT_STRLEN_DLTYPE 10 +#define TVM_CRT_MAX_STRLEN_DLTYPE 10 /*! Maximum supported string length in function names */ -#define TVM_CRT_STRLEN_NAME 80 +#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 /*! Maximum number of registered modules. */ #define TVM_CRT_MAX_REGISTERED_MODULES 2 @@ -53,4 +53,4 @@ // #define TVM_CRT_FRAMER_ENABLE_LOGS -#endif // TVM_RUNTIME_CRT_HOST_CRT_CONFIG_H_ +#endif // TVM_RUNTIME_MICRO_CRT_CONFIG_H_ From bb4812bd6cf350a88688995b7646aeea9fc0555d Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 17:09:04 -0700 Subject: [PATCH 24/47] Kind-of-working conftest --- tests/micro/arduino/conftest.py | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/micro/arduino/conftest.py diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py new file mode 100644 index 000000000000..638550946637 --- /dev/null +++ b/tests/micro/arduino/conftest.py @@ -0,0 +1,45 @@ +import pytest + +import tvm.target.target + +# The models that should pass this configuration. Maps a short, identifying platform string to +# (model, zephyr_board). +PLATFORMS = { + "spresense_main": ("cxd5602gg", "spresense"), +} + + +def pytest_addoption(parser): + parser.addoption( + "--microtvm-platforms", + default="spresense_main", + choices=PLATFORMS.keys(), + help=( + "Specify a comma-separated list of test models (i.e. as passed to tvm.target.micro()) " + "for microTVM tests." + ), + ) + parser.addoption( + "--arduino-cmd", default="arduino-cli", help="Path to `arduino-cli` command for flashing device." + ) + parser.addoption( + "--skip-build", + action="store_true", + help="If set true, reuses build from the previous test run. Otherwise, build from the scratch.", + ) + parser.addoption( + "--tvm-debug", + action="store_true", + default=False, + help="If set true, enable a debug session while the test is running. Before running the test, in a separate shell, you should run: ", + ) + + +def pytest_generate_tests(metafunc): + if "platform" in metafunc.fixturenames: + metafunc.parametrize("platform", metafunc.config.getoption("microtvm_platforms").split(",")) + + +@pytest.fixture +def arduino_cmd(request): + return request.config.getoption("--arduino-cmd") From 6f533f1399543bd17862ccd4a14e784cf411b9a1 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 17:22:52 -0700 Subject: [PATCH 25/47] Fully functional project generation --- .../template_project/microtvm_api_server.py | 58 +++++++++---------- .../template_project/src/implementation.c | 13 +++++ 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 8a1e4e3f87cd..f13877e3c604 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -1,5 +1,5 @@ import collections -import glob +import functools import json import logging import os @@ -230,7 +230,6 @@ def server_info_query(self): def _copy_standalone_crt(self, source_dir, standalone_crt_dir): # Copy over the standalone_crt directory output_crt_dir = source_dir / "standalone_crt" - output_crt_dir.mkdir() for item in self.CRT_COPY_ITEMS: src_path = os.path.join(standalone_crt_dir, item) dst_path = output_crt_dir / item @@ -239,6 +238,20 @@ def _copy_standalone_crt(self, source_dir, standalone_crt_dir): else: shutil.copy2(src_path, dst_path) + UNUSED_COMPONENTS = [ + "include/dmlc", + "src/support", + "src/runtime/minrpc", + "src/runtime/crt/aot_executor", + "src/runtime/crt/microtvm_rpc_common", + "src/runtime/crt/microtvm_rpc_server", + "src/runtime/crt/tab", + ] + def _remove_unused_components(self, source_dir): + for component in self.UNUSED_COMPONENTS: + shutil.rmtree(source_dir / "standalone_crt" / component) + + GRAPH_JSON_TEMPLATE = 'static const char* graph_json = "{}";\n' def _compile_graph_json(self, model_dir, obj): graph_json = json.dumps(obj).replace('"', '\\"') @@ -326,44 +339,29 @@ def _populate_parameters_file(self, graph, source_dir): with open(source_dir / "parameters.h", "w") as f: f.write(parameters_h) - C_STD_LIBS = ["assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", - "float.h", "inttypes.h", "iso646.h", "limits.h", "locale.h", - "math.h", "setjmp.h", "signal.h", "stdalign.h", "stdarg.h", - "stdatomic.h", "stdbool.h", "stddef.h", "stdint.h", "stdio.h", - "stdlib.h", "stdnoreturn.h", "string.h", "tgmath.h", "threads.h", - "time.h", "uchar.h", "wchar.h", "wctype.h"] - - # This list is LONG, TODO find a better way - CPP_STD_LIBS = ["utility"] - POSSIBLE_BASE_PATHS = ["src/standalone_crt/include/"] - # /home/.../project/src/standalone_crt/include/tvm/runtime/crt/graph_executor/load_json.c - # tvm/runtime/crt/platform.h + POSSIBLE_BASE_PATHS = [ + "src/standalone_crt/include/", + "src/standalone_crt/crt_config/" + ] def _find_modified_include_path(self, project_dir, file_path, import_path): - # If the import is a C standard library, don't modify it - if import_path in self.C_STD_LIBS or import_path in self.CPP_STD_LIBS: - return import_path # If the import already works, don't modify it - print("Checking existance!") - if (file_path / import_path).exists(): - print("Import path succeeded!") + if (file_path.parents[0] / import_path).exists(): return import_path relative_path = file_path.relative_to(project_dir) - print(relative_path) up_dirs_path = "../" * str(relative_path).count("/") for base_path in self.POSSIBLE_BASE_PATHS: full_potential_path = project_dir / base_path / import_path - print(full_potential_path) if full_potential_path.exists(): new_include = up_dirs_path + base_path + import_path - print(f"Replaced {import_path} with {new_include}") return new_include - print(f"Error! Could not find {import_path}") - print(file_path) + # If we can't find the file, just leave it untouched + # It's probably a standard C/C++ header + return import_path # Arduino only supports imports relative to the top-level project, # so we need to adjust each import to meet this convention @@ -375,23 +373,18 @@ def _convert_imports(self, project_dir, source_dir): for i in range(len(lines)): # Check if line has an include - result = re.search(r"#include\s*<([^>]*)>", lines[i]) + result = re.search(r"#include\s*[<\"]([^>]*)[>\"]", lines[i]) if not result: continue - print(lines[i]) - print(result.groups()[0]) new_include = self._find_modified_include_path( project_dir, filename, result.groups()[0] ) - lines[i] = f'#include "{new_include}"' + lines[i] = f'#include "{new_include}"\n' with filename.open("w") as file: file.writelines(lines) - print(filename) - print("---") - def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): # Copy template folder to project_dir, creating project/ and src/ @@ -405,6 +398,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Copy standalone_crt into src folder self._copy_standalone_crt(source_dir, standalone_crt_dir) + self._remove_unused_components(source_dir) # Unpack the MLF and copy the relevant files graph = self._disassemble_mlf(model_library_format_path, source_dir) diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c index 11cb5ec8090c..60c9e7cd14b2 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -2,6 +2,9 @@ #define IMPLEMENTATION #include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" +#include "standalone_crt/include/tvm/runtime/c_runtime_api.h" +#include "standalone_crt/include/tvm/runtime/crt/logging.h" +#include "standalone_crt/include/tvm/runtime/crt/stack_allocator.h" #include "Arduino.h" size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, @@ -9,6 +12,16 @@ size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const return 0; } +void TVMLogf(const char* msg, ...) { + /*char buffer[256]; + int size; + va_list args; + va_start(args, msg); + size = vsprintf(buffer, msg, args); + va_end(args); + TVMPlatformWriteSerial(buffer, (uint32_t)size);*/ +} + // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { //pinMode(LED_BUILTIN, OUTPUT); From 1f24b940eafdd5d1699c9febbaeca26ba2786a99 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Thu, 15 Jul 2021 10:05:53 -0700 Subject: [PATCH 26/47] Stub unused functions and fix template flagging --- .../template_project/microtvm_api_server.py | 260 ++---------------- 1 file changed, 20 insertions(+), 240 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index f13877e3c604..fd9f75c14f9d 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -20,75 +20,15 @@ from tvm.micro.project_api import server - -_LOG = logging.getLogger(__name__) - - -API_SERVER_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) - - -BUILD_DIR = API_SERVER_DIR / "build" - - MODEL_LIBRARY_FORMAT_RELPATH = "src/model/model.tar" - +API_SERVER_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() +MODEL_LIBRARY_FORMAT_PATH = "" if IS_TEMPLATE else API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH +_LOG = logging.getLogger(__name__) - -def check_call(cmd_args, *args, **kwargs): - cwd_str = "" if "cwd" not in kwargs else f" (in cwd: {kwargs['cwd']})" - _LOG.info("run%s: %s", cwd_str, " ".join(shlex.quote(a) for a in cmd_args)) - return subprocess.check_call(cmd_args, *args, **kwargs) - - -CACHE_ENTRY_RE = re.compile(r"(?P[^:]+):(?P[^=]+)=(?P.*)") - - -CMAKE_BOOL_MAP = dict( - [(k, True) for k in ("1", "ON", "YES", "TRUE", "Y")] - + [(k, False) for k in ("0", "OFF", "NO", "FALSE", "N", "IGNORE", "NOTFOUND", "")] -) - - -class CMakeCache(collections.abc.Mapping): - def __init__(self, path): - self._path = path - self._dict = None - - def __iter__(self): - return iter(self._dict) - - def __getitem__(self, key): - if self._dict is None: - self._dict = self._read_cmake_cache() - - return self._dict[key] - - def __len__(self): - return len(self._dict) - - def _read_cmake_cache(self): - """Read a CMakeCache.txt-like file and return a dictionary of values.""" - entries = collections.abc.OrderedDict() - with open(self._path, encoding="utf-8") as f: - for line in f: - m = CACHE_ENTRY_RE.match(line.rstrip("\n")) - if not m: - continue - - if m.group("type") == "BOOL": - value = CMAKE_BOOL_MAP[m.group("value").upper()] - else: - value = m.group("value") - - entries[m.group("name")] = value - - return entries - - -CMAKE_CACHE = CMakeCache(BUILD_DIR / "CMakeCache.txt") - +print("Checking if I'm a template:") +print("Yes!" if IS_TEMPLATE else "No!") class BoardError(Exception): """Raised when an attached board cannot be opened (i.e. missing /dev nodes, etc).""" @@ -98,116 +38,10 @@ class BoardAutodetectFailed(Exception): """Raised when no attached hardware is found matching the board= given to ZephyrCompiler.""" -'''def _get_flash_runner(): - flash_runner = CMAKE_CACHE.get("ZEPHYR_BOARD_FLASH_RUNNER") - if flash_runner is not None: - return flash_runner - - with open(CMAKE_CACHE["ZEPHYR_RUNNERS_YAML"]) as f: - doc = yaml.load(f, Loader=yaml.FullLoader) - return doc["flash-runner"] - - -def _get_device_args(options, cmake_entries): - flash_runner = _get_flash_runner() - - if flash_runner == "nrfjprog": - return _get_nrf_device_args(options) - - if flash_runner == "openocd": - return _get_openocd_device_args(options) - - raise BoardError( - f"Don't know how to find serial terminal for board {CMAKE_CACHE['BOARD']} with flash " - f"runner {flash_runner}" - )''' - - -# kwargs passed to usb.core.find to find attached boards for the openocd flash runner. -BOARD_USB_FIND_KW = { - "nucleo_l4r5zi": {"idVendor": 0x0483, "idProduct": 0x374B}, - "nucleo_f746zg": {"idVendor": 0x0483, "idProduct": 0x374B}, - "stm32f746g_disco": {"idVendor": 0x0483, "idProduct": 0x374B}, -} - - -def openocd_serial(options): - """Find the serial port to use for a board with OpenOCD flash strategy.""" - if "openocd_serial" in options: - return options["openocd_serial"] - - import usb # pylint: disable=import-outside-toplevel - - find_kw = BOARD_USB_FIND_KW[CMAKE_CACHE["BOARD"]] - boards = usb.core.find(find_all=True, **find_kw) - serials = [] - for b in boards: - serials.append(b.serial_number) - - if len(serials) == 0: - raise BoardAutodetectFailed(f"No attached USB devices matching: {find_kw!r}") - serials.sort() - - autodetected_openocd_serial = serials[0] - _LOG.debug("zephyr openocd driver: autodetected serial %s", serials[0]) - - return autodetected_openocd_serial - - -def _get_openocd_device_args(options): - return ["--serial", openocd_serial(options)] - - -def _get_nrf_device_args(options): - nrfjprog_args = ["nrfjprog", "--ids"] - nrfjprog_ids = subprocess.check_output(nrfjprog_args, encoding="utf-8") - if not nrfjprog_ids.strip("\n"): - raise BoardAutodetectFailed(f'No attached boards recognized by {" ".join(nrfjprog_args)}') - - boards = nrfjprog_ids.split("\n")[:-1] - if len(boards) > 1: - if options["nrfjprog_snr"] is None: - raise BoardError( - "Multiple boards connected; specify one with nrfjprog_snr=: " f'{", ".join(boards)}' - ) - - if str(options["nrfjprog_snr"]) not in boards: - raise BoardError( - f"nrfjprog_snr ({options['nrfjprog_snr']}) not found in {nrfjprog_args}: {boards}" - ) - - return ["--snr", options["nrfjprog_snr"]] - - if not boards: - return [] - - return ["--snr", boards[0]] - - PROJECT_OPTIONS = [ - server.ProjectOption( - "gdbserver_port", help=("If given, port number to use when running the " "local gdbserver") - ), - server.ProjectOption( - "openocd_serial", - help=("When used with OpenOCD targets, serial # of the " "attached board to use"), - ), - server.ProjectOption( - "nrfjprog_snr", - help=( - "When used with nRF targets, serial # of the " "attached board to use, from nrfjprog" - ), - ), server.ProjectOption("verbose", help="Run build with verbose output"), - server.ProjectOption( - "west_cmd", - help=( - "Path to the west tool. If given, supersedes both the zephyr_base " - "option and ZEPHYR_BASE environment variable." - ), - ), - server.ProjectOption("zephyr_base", help="Path to the zephyr base directory."), - server.ProjectOption("zephyr_board", help="Name of the Zephyr board to build for"), + server.ProjectOption("arduino_cmd", help="Path to the arduino-cli tool."), + server.ProjectOption("arduino_board", help="Name of the Arduino board to build for"), ] @@ -220,9 +54,7 @@ def server_info_query(self): return server.ServerInfo( platform_name="arduino", is_template=IS_TEMPLATE, - model_library_format_path="" - if IS_TEMPLATE - else (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH), + model_library_format_path=MODEL_LIBRARY_FORMAT_PATH, project_options=PROJECT_OPTIONS, ) @@ -403,6 +235,10 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Unpack the MLF and copy the relevant files graph = self._disassemble_mlf(model_library_format_path, source_dir) + # Copy the MLF tarball to the model directory for use as a flag + # Very ugly - TODO ask Andrew why this is + shutil.copy2(model_library_format_path, source_dir / "model") + # Populate our parameters file self._populate_parameters_file(graph, source_dir) @@ -410,83 +246,27 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec self._convert_imports(project_dir, source_dir) def build(self, options): - BUILD_DIR.mkdir() + raise NotImplementedError - cmake_args = ["cmake", ".."] - if options.get("verbose"): - cmake_args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE") - - if options.get("zephyr_base"): - cmake_args.append(f"-DZEPHYR_BASE:STRING={options['zephyr_base']}") - - cmake_args.append(f"-DBOARD:STRING={options['zephyr_board']}") - - check_call(cmake_args, cwd=BUILD_DIR) - - args = ["make", "-j2"] - if options.get("verbose"): - args.append("VERBOSE=1") - check_call(args, cwd=BUILD_DIR) - - @classmethod - def _is_qemu(cls, options): - return "qemu" in options["zephyr_board"] def flash(self, options): - if self._is_qemu(options): - return # NOTE: qemu requires no flash step--it is launched from open_transport. - - zephyr_board = options["zephyr_board"] - - # The nRF5340DK requires an additional `nrfjprog --recover` before each flash cycle. - # This is because readback protection is enabled by default when this device is flashed. - # Otherwise, flashing may fail with an error such as the following: - # ERROR: The operation attempted is unavailable due to readback protection in - # ERROR: your device. Please use --recover to unlock the device. - if zephyr_board.startswith("nrf5340dk") and _get_flash_runner() == "nrfjprog": - recover_args = ["nrfjprog", "--recover"] - recover_args.extend(_get_nrf_device_args(options)) - self._subprocess_env.run(recover_args, cwd=build_dir) - - check_call(["make", "flash"], cwd=API_SERVER_DIR / "build") - - def _open_qemu_transport(self, options): - zephyr_board = options["zephyr_board"] - # For Zephyr boards that run emulated by default but don't have the prefix "qemu_" in their - # board names, a suffix "-qemu" is added by users of µTVM when specifying the board name to - # inform that the QEMU transporter must be used just like for the boards with the prefix. - # Zephyr does not recognize the suffix, so we trim it off before passing it. - if "-qemu" in zephyr_board: - zephyr_board = zephyr_board.replace("-qemu", "") - - return ZephyrQemuTransport(options) + raise NotImplementedError + def open_transport(self, options): - if self._is_qemu(options): - transport = self._open_qemu_transport(options) - else: - transport = ZephyrSerialTransport(options) + raise NotImplementedError - to_return = transport.open() - self._transport = transport - return to_return def close_transport(self): - if self._transport is not None: - self._transport.close() - self._transport = None + raise NotImplementedError + def read_transport(self, n, timeout_sec): - if self._transport is None: - raise server.TransportClosedError() + raise NotImplementedError - return self._transport.read(n, timeout_sec) def write_transport(self, data, timeout_sec): - if self._transport is None: - raise server.TransportClosedError() - - return self._transport.write(data, timeout_sec) + raise NotImplementedError if __name__ == "__main__": From 25120dce9445d3b051d49f6bdb5d089b7f031491 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Thu, 15 Jul 2021 14:20:37 -0700 Subject: [PATCH 27/47] Arduino project compilation unit test --- .../template_project/microtvm_api_server.py | 41 +++++++++- tests/micro/arduino/conftest.py | 4 +- tests/micro/arduino/test_arduino.py | 74 ++++++++++++++++--- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index fd9f75c14f9d..4a514547e752 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -23,6 +23,7 @@ MODEL_LIBRARY_FORMAT_RELPATH = "src/model/model.tar" API_SERVER_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) +BUILD_DIR = API_SERVER_DIR / "build" IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() MODEL_LIBRARY_FORMAT_PATH = "" if IS_TEMPLATE else API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH _LOG = logging.getLogger(__name__) @@ -44,6 +45,19 @@ class BoardAutodetectFailed(Exception): server.ProjectOption("arduino_board", help="Name of the Arduino board to build for"), ] +BOARD_PROPERTIES = { + "spresense": { + "package": "SPRESENSE", + "architecture": "spresense", + "board": "spresense", + }, + "nano33ble_sense": { + "package": "arduino", + "architecture": "mbed_nano", + "board": "nano33ble", + } +} + class Handler(server.ProjectAPIHandler): def __init__(self): @@ -235,8 +249,6 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Unpack the MLF and copy the relevant files graph = self._disassemble_mlf(model_library_format_path, source_dir) - # Copy the MLF tarball to the model directory for use as a flag - # Very ugly - TODO ask Andrew why this is shutil.copy2(model_library_format_path, source_dir / "model") # Populate our parameters file @@ -245,8 +257,31 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Recursively change imports self._convert_imports(project_dir, source_dir) + + def _get_fqbn(self, options): + o = BOARD_PROPERTIES[options['arduino_board']] + print(o['package']) + return f"{o['package']}:{o['architecture']}:{o['board']}" + + def build(self, options): - raise NotImplementedError + BUILD_DIR.mkdir() + print(BUILD_DIR) + + compile_cmd = [ + options['arduino_cmd'], "compile", + "--fqbn", self._get_fqbn(options), + "--build-path", BUILD_DIR.resolve() + ] + + if options.get("verbose"): + compile_cmd.append("--verbose") + + # Specify project to compile + compile_cmd.append("./project/") + print(compile_cmd) + print(API_SERVER_DIR) + subprocess.check_call(compile_cmd) def flash(self, options): diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 638550946637..6e6f6ce4178a 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -5,14 +5,14 @@ # The models that should pass this configuration. Maps a short, identifying platform string to # (model, zephyr_board). PLATFORMS = { - "spresense_main": ("cxd5602gg", "spresense"), + "spresense": ("cxd5602gg", "spresense"), } def pytest_addoption(parser): parser.addoption( "--microtvm-platforms", - default="spresense_main", + default="spresense", choices=PLATFORMS.keys(), help=( "Specify a comma-separated list of test models (i.e. as passed to tvm.target.micro()) " diff --git a/tests/micro/arduino/test_arduino.py b/tests/micro/arduino/test_arduino.py index 975e1b1852b6..bcfc2866e48a 100644 --- a/tests/micro/arduino/test_arduino.py +++ b/tests/micro/arduino/test_arduino.py @@ -12,7 +12,7 @@ PLATFORMS = conftest.PLATFORMS -def _make_session(model, target, arduino_board, arduino_cmd, mod, build_config): +def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_config): parent_dir = os.path.dirname(__file__) filename = os.path.splitext(os.path.basename(__file__))[0] prev_build = f"{os.path.join(parent_dir, 'archive')}_{filename}_{arduino_board}_last_build.micro" @@ -24,8 +24,7 @@ def _make_session(model, target, arduino_board, arduino_cmd, mod, build_config): if not os.path.exists(workspace_parent): os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) - print("Outputing workspace root:") - print(workspace_root) + template_project_dir = ( pathlib.Path(__file__).parent / ".." @@ -42,15 +41,12 @@ def _make_session(model, target, arduino_board, arduino_cmd, mod, build_config): workspace.relpath("project"), {"arduino_board": arduino_board, "arduino_cmd": arduino_cmd, "verbose": 0}, ) - #project.build() - #project.flash() - #return tvm.micro.Session(project.transport()) + return (workspace, project) # This is bad, don't do this TARGET = "c -keys=cpu -link-params=1 -mcpu=cortex-m33 -model=nrf5340dk -runtime=c -system-lib=1" - -def test_generate_yes_no_project(platform, arduino_cmd): +def _generate_yes_no_project(platform, arduino_cmd): current_dir = os.path.dirname(__file__) model, arduino_board = PLATFORMS[platform] #target = tvm.target.target.micro(model, options=["-link-params=1"]) @@ -63,7 +59,67 @@ def test_generate_yes_no_project(platform, arduino_cmd): with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): mod = relay.build(mod, TARGET, params=params) - session = _make_session(model, TARGET, arduino_board, arduino_cmd, mod, build_config) + return _generate_project(model, TARGET, arduino_board, arduino_cmd, mod, build_config) + #project.build() + #project.flash() + #return tvm.micro.Session(project.transport()) + + +def test_generate_yes_no_project(platform, arduino_cmd): + workspace, project = _generate_yes_no_project(platform, arduino_cmd) + + # Ensure top-level directory structure looks good + assert(os.listdir(workspace.path) == ['project']) + + project_dir = pathlib.Path(workspace.path) / "project" + assert(set([ + 'microtvm_api_server.py', 'project.ino', 'src'] + ).issubset(os.listdir(project_dir))) + + source_dir = project_dir / "src" + assert(set(os.listdir(source_dir)) == set([ + 'model', 'standalone_crt', 'implementation.c', + 'model.cpp', 'model.h', 'parameters.h' + ])) + + + # Ensure model was connected and graph_json compiled + model_dir = source_dir / "model" + assert(set(os.listdir(model_dir)) == set([ + 'default_lib0.c', 'default_lib1.c', 'graph_json.c', 'model.tar' + ])) + with (model_dir / "graph_json.c").open() as f: + graph_json_c = f.read() + assert("static const char* graph_json" in graph_json_c) + + + # Ensure parameters.h was templated with correct information + # for our yes/no model + with (source_dir / "parameters.h").open() as f: + parameters_h = f.read() + assert("INPUT_DATA_SHAPE[] = {1, 1960};" in parameters_h) + + + # Check one file to ensure imports were rerouted + runtime_c_path = source_dir / "standalone_crt" / "src" / "runtime" + load_json_path = runtime_c_path / "crt" / "graph_executor" / "load_json.c" + assert(load_json_path.exists()) + + with (load_json_path).open() as f: + load_json_c = f.read() + assert('#include "stdlib.h"' in load_json_c) + assert('include/tvm/runtime/crt/platform.h' in load_json_c) + + +def test_compile_yes_no_project(platform, arduino_cmd): + workspace, project = _generate_yes_no_project(platform, arduino_cmd) + project.build() + + # Make sure build_dir is not empty + build_dir = pathlib.Path(workspace.path) / "project" / "build" + assert(build_dir.exists()) + first_build_file = next(build_dir.iterdir(), None) + assert(first_build_file is not None) if __name__ == "__main__": From 8c538e3598a8c176689b3f9311d1c73efe1b076a Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Thu, 15 Jul 2021 16:34:29 -0700 Subject: [PATCH 28/47] Arduino board flashing functionality --- .../template_project/microtvm_api_server.py | 67 ++++++++++++++++--- tests/micro/arduino/conftest.py | 15 ++--- tests/micro/arduino/test_arduino.py | 9 +++ 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 4a514547e752..77c3cf72a2bd 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -28,21 +28,21 @@ MODEL_LIBRARY_FORMAT_PATH = "" if IS_TEMPLATE else API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH _LOG = logging.getLogger(__name__) -print("Checking if I'm a template:") -print("Yes!" if IS_TEMPLATE else "No!") - -class BoardError(Exception): - """Raised when an attached board cannot be opened (i.e. missing /dev nodes, etc).""" +class InvalidPortException(Exception): + """Raised when the given port could not be opened""" +class SketchUploadException(Exception): + """Raised when a sketch cannot be uploaded for an unknown reason.""" class BoardAutodetectFailed(Exception): - """Raised when no attached hardware is found matching the board= given to ZephyrCompiler.""" + """Raised when no attached hardware is found matching the requested board""" PROJECT_OPTIONS = [ server.ProjectOption("verbose", help="Run build with verbose output"), server.ProjectOption("arduino_cmd", help="Path to the arduino-cli tool."), server.ProjectOption("arduino_board", help="Name of the Arduino board to build for"), + server.ProjectOption("port", help="Port to use for connecting to hardware") ] BOARD_PROPERTIES = { @@ -63,6 +63,7 @@ class Handler(server.ProjectAPIHandler): def __init__(self): super(Handler, self).__init__() self._proc = None + self.port = None def server_info_query(self): return server.ServerInfo( @@ -260,7 +261,6 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec def _get_fqbn(self, options): o = BOARD_PROPERTIES[options['arduino_board']] - print(o['package']) return f"{o['package']}:{o['architecture']}:{o['board']}" @@ -269,7 +269,7 @@ def build(self, options): print(BUILD_DIR) compile_cmd = [ - options['arduino_cmd'], "compile", + options['arduino_cmd'], "compile", "./project/", "--fqbn", self._get_fqbn(options), "--build-path", BUILD_DIR.resolve() ] @@ -278,14 +278,59 @@ def build(self, options): compile_cmd.append("--verbose") # Specify project to compile - compile_cmd.append("./project/") print(compile_cmd) print(API_SERVER_DIR) - subprocess.check_call(compile_cmd) + output = subprocess.check_call(compile_cmd) + assert(output == 0) + + # We run the command `arduino-cli board list`, which produces + # outputs of the form: + ''' + Port Type Board Name FQBN Core + /dev/ttyS4 Serial Port Unknown + /dev/ttyUSB0 Serial Port (USB) Spresense SPRESENSE:spresense:spresense SPRESENSE:spresense + ''' + def _auto_detect_port(self, options): + list_cmd = [options['arduino_cmd'], "board", "list"] + list_cmd_output = subprocess.check_output(list_cmd).decode('utf-8') + # Remove header and new lines at bottom + port_options = list_cmd_output.split("\n")[1:-2] + + # Select the first compatible board + fqbn = self._get_fqbn(options) + for port_option in port_options: + if fqbn in port_option: + return port_option.split(" ")[0] + + # If no compatible boards, raise an error + raise BoardAutodetectFailed + + + def _get_arduino_port(self, options): + if not self.port: + if 'port' in options and options['port']: + self.port = options['port'] + else: + self.port = self._auto_detect_port(options) + + return self.port def flash(self, options): - raise NotImplementedError + port = self._get_arduino_port(options) + + upload_cmd = [ + options["arduino_cmd"], "upload", "./project", + "--fqbn", self._get_fqbn(options), + "--input-dir", BUILD_DIR.resolve(), + "--port", port, + ] + output = subprocess.check_call(upload_cmd) + + if output == 2: + raise InvalidPortException + elif output > 0: + raise SketchUploadException def open_transport(self, options): diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 6e6f6ce4178a..8c59b8c35bbd 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -23,17 +23,8 @@ def pytest_addoption(parser): "--arduino-cmd", default="arduino-cli", help="Path to `arduino-cli` command for flashing device." ) parser.addoption( - "--skip-build", - action="store_true", - help="If set true, reuses build from the previous test run. Otherwise, build from the scratch.", + "--run-hardware-tests", action="store_true", help="Run tests that require physical hardware." ) - parser.addoption( - "--tvm-debug", - action="store_true", - default=False, - help="If set true, enable a debug session while the test is running. Before running the test, in a separate shell, you should run: ", - ) - def pytest_generate_tests(metafunc): if "platform" in metafunc.fixturenames: @@ -43,3 +34,7 @@ def pytest_generate_tests(metafunc): @pytest.fixture def arduino_cmd(request): return request.config.getoption("--arduino-cmd") + +@pytest.fixture +def run_hardware_tests(request): + return request.config.getoption("--run-hardware-tests") diff --git a/tests/micro/arduino/test_arduino.py b/tests/micro/arduino/test_arduino.py index bcfc2866e48a..44d58f22b14c 100644 --- a/tests/micro/arduino/test_arduino.py +++ b/tests/micro/arduino/test_arduino.py @@ -122,5 +122,14 @@ def test_compile_yes_no_project(platform, arduino_cmd): assert(first_build_file is not None) +def test_upload_yes_no_project(platform, arduino_cmd, run_hardware_tests): + if not run_hardware_tests: + pytest.skip("skipping hardware tests") + + workspace, project = _generate_yes_no_project(platform, arduino_cmd) + project.build() + project.flash() + + if __name__ == "__main__": sys.exit(pytest.main([__file__] + sys.argv[1:])) From ad441413bd1bc7e7c417bc2ed2cb4439904b1a8c Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Thu, 15 Jul 2021 17:45:40 -0700 Subject: [PATCH 29/47] C runtime bugfixes --- include/tvm/runtime/crt/packed_func.h | 4 ++-- src/runtime/crt/graph_executor/graph_executor.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/tvm/runtime/crt/packed_func.h b/include/tvm/runtime/crt/packed_func.h index 0c39fe1a65b8..20f27eb3093c 100644 --- a/include/tvm/runtime/crt/packed_func.h +++ b/include/tvm/runtime/crt/packed_func.h @@ -62,11 +62,11 @@ void TVMPackedFunc_SetArgs(TVMPackedFunc* pf, const TVMArgs* args); inline TVMModuleHandle TVMArgs_AsModuleHandle(const TVMArgs* args, size_t index) { if (index >= args->values_count) { - TVMPlatformAbort(-1); + TVMPlatformAbort((tvm_crt_error_t) -1); } if (args->tcodes[index] != kTVMModuleHandle) { - TVMPlatformAbort(-1); + TVMPlatformAbort((tvm_crt_error_t) -1); } return args->values[index].v_handle; diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index 0236c997c1e3..fe09f1c20ee6 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -130,7 +130,7 @@ int TVMGraphExecutorNode_Load(TVMGraphExecutorNode* node, JSONReader* reader) { } bitmask |= 2; } else if (!strcmp(key, "inputs")) { - size_t count = node->inputs_count; + size_t count = 0; reader->BeginArray(reader); size_t num_inputs = 0; if (reader->ArrayLength(reader, &num_inputs) != 0) { @@ -1092,6 +1092,8 @@ int TVMGraphExecutor_SetupOpExecs(TVMGraphExecutor* executor) { TVMGraphExecutor_CreateTVMOp(executor, &(inode->param), args, args_count, inode->inputs_count, &pf); executor->op_execs[nid] = pf; + } else { + memset(&executor->op_execs[nid], 0, sizeof(TVMPackedFunc)); } } return status; From c134e3dba4bf5a7494684219f43e61179a922f73 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Thu, 15 Jul 2021 17:46:11 -0700 Subject: [PATCH 30/47] Redesign unit tests to mimic workflow --- .../template_project/microtvm_api_server.py | 2 +- tests/micro/arduino/conftest.py | 21 +++--- ...st_arduino.py => test_arduino_workflow.py} | 65 ++++++++++++------- 3 files changed, 53 insertions(+), 35 deletions(-) rename tests/micro/arduino/{test_arduino.py => test_arduino_workflow.py} (71%) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 77c3cf72a2bd..f92466feb758 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -51,7 +51,7 @@ class BoardAutodetectFailed(Exception): "architecture": "spresense", "board": "spresense", }, - "nano33ble_sense": { + "nano33ble": { "package": "arduino", "architecture": "mbed_nano", "board": "nano33ble", diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 8c59b8c35bbd..bc820029ee07 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -6,18 +6,15 @@ # (model, zephyr_board). PLATFORMS = { "spresense": ("cxd5602gg", "spresense"), + "nano33ble": ("nRF52840", "nano33ble"), } - def pytest_addoption(parser): parser.addoption( - "--microtvm-platforms", + "--platform", default="spresense", choices=PLATFORMS.keys(), - help=( - "Specify a comma-separated list of test models (i.e. as passed to tvm.target.micro()) " - "for microTVM tests." - ), + help="Target platform for microTVM tests.", ) parser.addoption( "--arduino-cmd", default="arduino-cli", help="Path to `arduino-cli` command for flashing device." @@ -26,15 +23,15 @@ def pytest_addoption(parser): "--run-hardware-tests", action="store_true", help="Run tests that require physical hardware." ) -def pytest_generate_tests(metafunc): - if "platform" in metafunc.fixturenames: - metafunc.parametrize("platform", metafunc.config.getoption("microtvm_platforms").split(",")) - +# TODO re-add parameterization +@pytest.fixture(scope="session") +def platform(request): + return request.config.getoption("--platform") -@pytest.fixture +@pytest.fixture(scope="session") def arduino_cmd(request): return request.config.getoption("--arduino-cmd") -@pytest.fixture +@pytest.fixture(scope="session") def run_hardware_tests(request): return request.config.getoption("--run-hardware-tests") diff --git a/tests/micro/arduino/test_arduino.py b/tests/micro/arduino/test_arduino_workflow.py similarity index 71% rename from tests/micro/arduino/test_arduino.py rename to tests/micro/arduino/test_arduino_workflow.py index 44d58f22b14c..86d208459e6e 100644 --- a/tests/micro/arduino/test_arduino.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -10,6 +10,17 @@ import conftest +''' +This unit test simulates a simple user workflow, where we: +1. Generate a base sketch using a simple audio model +2. Modify the .ino file, much like a user would +3. Compile the sketch for the target platform +-- If physical hardware is present -- +4. Upload the sketch to a connected board +5. Open a serial connection to the board +6. Use serial connection to ensure model behaves correctly +''' + PLATFORMS = conftest.PLATFORMS def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_config): @@ -46,7 +57,9 @@ def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_conf # This is bad, don't do this TARGET = "c -keys=cpu -link-params=1 -mcpu=cortex-m33 -model=nrf5340dk -runtime=c -system-lib=1" -def _generate_yes_no_project(platform, arduino_cmd): + +@pytest.fixture(scope="module") +def yes_no_project(platform, arduino_cmd): current_dir = os.path.dirname(__file__) model, arduino_board = PLATFORMS[platform] #target = tvm.target.target.micro(model, options=["-link-params=1"]) @@ -60,18 +73,22 @@ def _generate_yes_no_project(platform, arduino_cmd): mod = relay.build(mod, TARGET, params=params) return _generate_project(model, TARGET, arduino_board, arduino_cmd, mod, build_config) - #project.build() - #project.flash() #return tvm.micro.Session(project.transport()) -def test_generate_yes_no_project(platform, arduino_cmd): - workspace, project = _generate_yes_no_project(platform, arduino_cmd) +@pytest.fixture(scope="module") +def project(yes_no_project): + workspace, project = yes_no_project + return project + - # Ensure top-level directory structure looks good - assert(os.listdir(workspace.path) == ['project']) +@pytest.fixture(scope="module") +def project_dir(yes_no_project): + workspace, project = yes_no_project + return pathlib.Path(workspace.path) / "project" - project_dir = pathlib.Path(workspace.path) / "project" + +def test_project_folder_structure(project_dir): assert(set([ 'microtvm_api_server.py', 'project.ino', 'src'] ).issubset(os.listdir(project_dir))) @@ -83,8 +100,8 @@ def test_generate_yes_no_project(platform, arduino_cmd): ])) - # Ensure model was connected and graph_json compiled - model_dir = source_dir / "model" +def test_project_model_integrity(project_dir): + model_dir = project_dir / "src" / "model" assert(set(os.listdir(model_dir)) == set([ 'default_lib0.c', 'default_lib1.c', 'graph_json.c', 'model.tar' ])) @@ -93,15 +110,17 @@ def test_generate_yes_no_project(platform, arduino_cmd): assert("static const char* graph_json" in graph_json_c) +def test_parameter_header_templating(project_dir): # Ensure parameters.h was templated with correct information # for our yes/no model - with (source_dir / "parameters.h").open() as f: + with (project_dir / "src" / "parameters.h").open() as f: parameters_h = f.read() assert("INPUT_DATA_SHAPE[] = {1, 1960};" in parameters_h) +def test_import_rerouting(project_dir): # Check one file to ensure imports were rerouted - runtime_c_path = source_dir / "standalone_crt" / "src" / "runtime" + runtime_c_path = project_dir / "src" / "standalone_crt" / "src" / "runtime" load_json_path = runtime_c_path / "crt" / "graph_executor" / "load_json.c" assert(load_json_path.exists()) @@ -111,25 +130,27 @@ def test_generate_yes_no_project(platform, arduino_cmd): assert('include/tvm/runtime/crt/platform.h' in load_json_c) -def test_compile_yes_no_project(platform, arduino_cmd): - workspace, project = _generate_yes_no_project(platform, arduino_cmd) +@pytest.fixture(scope="module") +def do_compile(project): project.build() - # Make sure build_dir is not empty - build_dir = pathlib.Path(workspace.path) / "project" / "build" + +def test_compile_yes_no_project(project_dir, project, do_compile): + build_dir = project_dir / "build" assert(build_dir.exists()) first_build_file = next(build_dir.iterdir(), None) assert(first_build_file is not None) -def test_upload_yes_no_project(platform, arduino_cmd, run_hardware_tests): - if not run_hardware_tests: - pytest.skip("skipping hardware tests") - - workspace, project = _generate_yes_no_project(platform, arduino_cmd) - project.build() +@pytest.fixture(scope="module") +def do_upload(project): project.flash() +def test_upload_yes_no_project(project_dir, project, do_compile, do_upload): + pass + # Test upload + + if __name__ == "__main__": sys.exit(pytest.main([__file__] + sys.argv[1:])) From 626018ad58b8323e6f13b81aff90a2d9220a89ee Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 14:08:51 -0700 Subject: [PATCH 31/47] Suppress graph_executor.c warning --- src/runtime/crt/graph_executor/graph_executor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index fe09f1c20ee6..74bae9251017 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -101,7 +101,8 @@ void TVMGraphExecutorNode_LoadAttrs(TVMGraphExecutorNode* node, JSONReader* read param->flatten_data = strtoul(value, 0, 10); bitmask |= 8; } else { - fprintf(stderr, "do not support key %s", key); + // TODO determine if suppressing these warnings is OK + //fprintf(stderr, "do not support key %s", key); } } if (bitmask != (1 | 2 | 4 | 8)) { From 9e969c9f4d96c16b33a3fd26d94cd6b39b7be32c Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 14:09:02 -0700 Subject: [PATCH 32/47] Sample test project --- tests/micro/arduino/testdata/no.c | 1962 ++++++++++++++++++++++ tests/micro/arduino/testdata/project.ino | 38 + tests/micro/arduino/testdata/silence.c | 1962 ++++++++++++++++++++++ tests/micro/arduino/testdata/unknown.c | 1962 ++++++++++++++++++++++ tests/micro/arduino/testdata/yes.c | 1962 ++++++++++++++++++++++ 5 files changed, 7886 insertions(+) create mode 100644 tests/micro/arduino/testdata/no.c create mode 100644 tests/micro/arduino/testdata/project.ino create mode 100644 tests/micro/arduino/testdata/silence.c create mode 100644 tests/micro/arduino/testdata/unknown.c create mode 100644 tests/micro/arduino/testdata/yes.c diff --git a/tests/micro/arduino/testdata/no.c b/tests/micro/arduino/testdata/no.c new file mode 100644 index 000000000000..0bce1b37c373 --- /dev/null +++ b/tests/micro/arduino/testdata/no.c @@ -0,0 +1,1962 @@ +static const char input_no[1960] = { + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xcf, + 0xe4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xdb, + 0xe4, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x2f, + 0x1e, + 0x7, + 0xe4, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x52, + 0x41, + 0x4b, + 0x3a, + 0x20, + 0xf6, + 0xcf, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x62, + 0x53, + 0x5d, + 0x51, + 0x4a, + 0xf9, + 0xe4, + 0xb4, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xc5, + 0x80, + 0xcf, + 0x80, + 0x41, + 0x49, + 0x6a, + 0x5d, + 0x75, + 0x62, + 0x75, + 0x63, + 0x7a, + 0x65, + 0x7b, + 0x64, + 0x78, + 0x62, + 0x75, + 0x5d, + 0x71, + 0x5b, + 0x37, + 0xd, + 0x3, + 0xf6, + 0xec, + 0xd6, + 0x32, + 0x2a, + 0x1a, + 0xf6, + 0x42, + 0x4b, + 0x3f, + 0xe0, + 0xe4, + 0xcf, + 0xf3, + 0xef, + 0xf3, + 0xfb, + 0x3, + 0x0, + 0x6d, + 0x56, + 0x6e, + 0x57, + 0x69, + 0x55, + 0x72, + 0x5d, + 0x66, + 0x52, + 0x6e, + 0x5d, + 0x6f, + 0x46, + 0x64, + 0x52, + 0x62, + 0x42, + 0x4e, + 0x29, + 0x32, + 0xe, + 0x25, + 0x35, + 0x56, + 0x49, + 0x4d, + 0x42, + 0x5d, + 0x57, + 0x61, + 0x34, + 0x1c, + 0x5, + 0x20, + 0x17, + 0x17, + 0x17, + 0x24, + 0x20, + 0x76, + 0x65, + 0x7a, + 0x63, + 0x7b, + 0x65, + 0x7b, + 0x5d, + 0x70, + 0x53, + 0x73, + 0x61, + 0x70, + 0x53, + 0x66, + 0x57, + 0x63, + 0x52, + 0x5c, + 0x3a, + 0x54, + 0x4d, + 0x6b, + 0x5f, + 0x78, + 0x66, + 0x7a, + 0x64, + 0x7b, + 0x64, + 0x75, + 0x56, + 0x5a, + 0x46, + 0x4b, + 0x3d, + 0x46, + 0x3e, + 0x4e, + 0x3f, + 0x68, + 0x58, + 0x6e, + 0x57, + 0x6d, + 0x5f, + 0x76, + 0x5a, + 0x6e, + 0x57, + 0x75, + 0x5d, + 0x67, + 0x53, + 0x68, + 0x50, + 0x67, + 0x53, + 0x6c, + 0x59, + 0x68, + 0x5a, + 0x6a, + 0x53, + 0x65, + 0x5a, + 0x74, + 0x56, + 0x6d, + 0x5c, + 0x6b, + 0x4a, + 0x50, + 0x46, + 0x58, + 0x48, + 0x66, + 0x56, + 0x59, + 0x46, + 0x5e, + 0x43, + 0x61, + 0x44, + 0x61, + 0x50, + 0x6e, + 0x55, + 0x67, + 0x5a, + 0x63, + 0x4e, + 0x5f, + 0x3b, + 0x63, + 0x52, + 0x5e, + 0x4e, + 0x67, + 0x4d, + 0x62, + 0x51, + 0x6a, + 0x4e, + 0x62, + 0x48, + 0x69, + 0x55, + 0x66, + 0x50, + 0x62, + 0x50, + 0x59, + 0x40, + 0x4c, + 0x41, + 0x6c, + 0x55, + 0x5a, + 0x3f, + 0x58, + 0x3c, + 0x5b, + 0x28, + 0x50, + 0x3d, + 0x62, + 0x4b, + 0x5b, + 0x55, + 0x62, + 0x43, + 0x5d, + 0x3c, + 0x50, + 0x37, + 0x55, + 0x2d, + 0x55, + 0x49, + 0x59, + 0x48, + 0x53, + 0x3e, + 0x53, + 0x46, + 0x64, + 0x53, + 0x61, + 0x3f, + 0x5e, + 0x2e, + 0x4d, + 0x39, + 0x4e, + 0x41, + 0x61, + 0x4a, + 0x53, + 0x36, + 0x52, + 0x35, + 0x55, + 0x2a, + 0x4f, + 0x3a, + 0x5a, + 0x3e, + 0x55, + 0x4f, + 0x5e, + 0x37, + 0x4d, + 0x34, + 0x4c, + 0x37, + 0x4e, + 0x28, + 0x50, + 0x36, + 0x53, + 0x39, + 0x49, + 0x2b, + 0x4f, + 0x39, + 0x5c, + 0x47, + 0x51, + 0x35, + 0x5d, + 0x1b, + 0x3f, + 0x2b, + 0x46, + 0x3b, + 0x5d, + 0x44, + 0x5a, + 0x35, + 0x4d, + 0x35, + 0x4e, + 0x30, + 0x4b, + 0x3f, + 0x57, + 0x35, + 0x59, + 0x3f, + 0x45, + 0xd, + 0x2b, + 0x4, + 0x45, + 0x26, + 0x48, + 0x36, + 0x47, + 0x26, + 0x44, + 0x39, + 0x50, + 0x2e, + 0x46, + 0x2f, + 0x55, + 0x43, + 0x4c, + 0x23, + 0x52, + 0x2f, + 0x3f, + 0x25, + 0x43, + 0x2d, + 0x3b, + 0xf9, + 0x4d, + 0x29, + 0x44, + 0x1b, + 0x35, + 0x38, + 0x48, + 0x3a, + 0x46, + 0x3c, + 0x5d, + 0x29, + 0x43, + 0x5, + 0x4a, + 0xd, + 0x26, + 0xb4, + 0x28, + 0xcf, + 0x3c, + 0x13, + 0x25, + 0x2, + 0x32, + 0xf9, + 0x2f, + 0x1e, + 0x4d, + 0x19, + 0x3a, + 0x2, + 0x3c, + 0x7, + 0x3c, + 0x12, + 0x3c, + 0x10, + 0xdb, + 0x80, + 0x37, + 0x24, + 0x42, + 0x21, + 0x3a, + 0x30, + 0x4a, + 0x28, + 0x32, + 0x31, + 0x48, + 0xe7, + 0x2d, + 0x80, + 0x19, + 0xf9, + 0x2d, + 0xf3, + 0x32, + 0x2, + 0x24, + 0xb4, + 0x14, + 0x80, + 0x22, + 0xb4, + 0x35, + 0x3, + 0x40, + 0xf, + 0x30, + 0x80, + 0x26, + 0x80, + 0x26, + 0xcf, + 0x21, + 0x80, + 0x80, + 0x80, + 0xf5, + 0xef, + 0x28, + 0x80, + 0x4b, + 0x34, + 0x3c, + 0xdb, + 0x34, + 0x12, + 0x44, + 0xe0, + 0x26, + 0x80, + 0x1d, + 0x80, + 0xd6, + 0x80, + 0x21, + 0xe4, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xf6, + 0x11, + 0x2b, + 0xff, + 0x3e, + 0x16, + 0x1f, + 0x80, + 0x21, + 0xf6, + 0x14, + 0xd6, + 0x27, + 0xcf, + 0x80, + 0x80, + 0x0, + 0xec, + 0x48, + 0xd6, + 0x3b, + 0x0, + 0x36, + 0x1d, + 0x28, + 0xcf, + 0x2d, + 0xef, + 0x25, + 0x80, + 0xcf, + 0x80, + 0xf5, + 0x80, + 0xa, + 0x80, + 0x11, + 0x80, + 0x80, + 0x80, + 0xf8, + 0xe4, + 0x10, + 0xea, + 0x2a, + 0xf1, + 0x21, + 0x80, + 0xcf, + 0x80, + 0x3, + 0xe7, + 0x1a, + 0xb4, + 0x80, + 0x80, + 0xe0, + 0xdb, + 0x31, + 0xe0, + 0x32, + 0xc, + 0x30, + 0x80, + 0x0, + 0xc5, + 0x34, + 0x80, + 0x2, + 0x80, + 0xf1, + 0x80, + 0xcf, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x2, + 0x80, + 0x14, + 0x80, + 0xd6, + 0x80, + 0x80, + 0x80, + 0xfb, + 0xdb, + 0x8, + 0x80, + 0x80, + 0x80, + 0xe4, + 0xe7, + 0x28, + 0xc5, + 0x1e, + 0xdb, + 0x2a, + 0xb4, + 0x80, + 0x80, + 0x30, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf8, + 0xb4, + 0x17, + 0x80, + 0xcf, + 0x80, + 0x80, + 0x80, + 0x0, + 0xcf, + 0x12, + 0x80, + 0x80, + 0x80, + 0xdb, + 0xb4, + 0xe4, + 0x80, + 0x21, + 0xb4, + 0x2a, + 0x80, + 0x80, + 0x80, + 0x13, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf3, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xfd, + 0x80, + 0x80, + 0x80, + 0xe0, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xe4, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80 +}; diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino new file mode 100644 index 000000000000..7cebce26836c --- /dev/null +++ b/tests/micro/arduino/testdata/project.ino @@ -0,0 +1,38 @@ +#include "src/model.h" +#include "src/data/yes.c" +#include "src/data/no.c" +#include "src/data/unknown.c" +#include "src/data/silence.c" + +static Model model; + +void performInference(int8_t input_data[1960], String data_name) { + int8_t output_data[4]; + uint64_t start_time = micros(); + model.inference(input_data, output_data); + uint64_t end_time = micros(); + + Serial.print(data_name); + Serial.print(","); + Serial.print(end_time - start_time); + Serial.print(","); + for (int i = 0; i < 4; i++) { + Serial.print(output_data[i]); + Serial.print(","); + } + Serial.println(); +} + +void setup() { + model = Model(); + Serial.begin(115200); + Serial.println(); + Serial.println("category,runtime,yes,no,silence,unknown"); + performInference(input_yes, "yes"); + performInference(input_no, "no"); + performInference(input_silence, "silence"); + performInference(input_unknown, "unknown"); + Serial.end(); +} + +void loop() {} diff --git a/tests/micro/arduino/testdata/silence.c b/tests/micro/arduino/testdata/silence.c new file mode 100644 index 000000000000..478cb4d1bdb5 --- /dev/null +++ b/tests/micro/arduino/testdata/silence.c @@ -0,0 +1,1962 @@ +static const char input_silence[1960] = { + 0x23, + 0x17, + 0xe0, + 0x3, + 0x9, + 0xe7, + 0xe7, + 0xdb, + 0xcf, + 0xc5, + 0xe0, + 0xdb, + 0xc5, + 0xcf, + 0xef, + 0xcf, + 0xcf, + 0xdb, + 0xef, + 0xdb, + 0xe7, + 0xc5, + 0x5, + 0x3, + 0xfc, + 0xe7, + 0xf6, + 0xdb, + 0xcf, + 0xe7, + 0x9, + 0xef, + 0xef, + 0xdb, + 0xcf, + 0xe7, + 0xe0, + 0xe7, + 0xe0, + 0xc5, + 0xff, + 0xe0, + 0x4, + 0xcf, + 0xdb, + 0xb4, + 0x80, + 0xdb, + 0xef, + 0x80, + 0xc5, + 0xe4, + 0x9, + 0xe4, + 0xcf, + 0xc5, + 0xdb, + 0xcf, + 0xdb, + 0xcf, + 0xf5, + 0xdb, + 0xe7, + 0xcf, + 0xef, + 0xe4, + 0xe7, + 0xe4, + 0xe7, + 0xdb, + 0xdb, + 0xcf, + 0xc5, + 0xdb, + 0xcf, + 0xcf, + 0xcf, + 0xb4, + 0xcf, + 0xcf, + 0x13, + 0xef, + 0xf5, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xcf, + 0xcf, + 0x80, + 0x80, + 0xcf, + 0xf5, + 0xcf, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xcf, + 0xf9, + 0xdb, + 0xcf, + 0x80, + 0x80, + 0xcf, + 0xe7, + 0xdb, + 0xfb, + 0xe4, + 0xdb, + 0xcf, + 0xe7, + 0xcf, + 0xe7, + 0xb4, + 0xdb, + 0xe4, + 0xcf, + 0xb4, + 0xfb, + 0x0, + 0x6, + 0xd6, + 0xec, + 0xb4, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf3, + 0xb4, + 0xdb, + 0xdb, + 0xc5, + 0xb4, + 0xc5, + 0x80, + 0xcf, + 0xb4, + 0xdb, + 0xb4, + 0xb4, + 0x80, + 0xcf, + 0x80, + 0xdb, + 0xb4, + 0xb4, + 0x80, + 0xc5, + 0x80, + 0xdb, + 0xcf, + 0xdb, + 0xcf, + 0xcf, + 0xb4, + 0xff, + 0xcf, + 0xdb, + 0x80, + 0xb4, + 0x80, + 0x80, + 0xd6, + 0xcf, + 0xcf, + 0x80, + 0xcf, + 0xcf, + 0xcf, + 0xe4, + 0xcf, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xdb, + 0x80, + 0xb4, + 0x80, + 0xdb, + 0x80, + 0xb4, + 0x80, + 0xb4, + 0xb4, + 0xdb, + 0xcf, + 0xec, + 0xe0, + 0xcf, + 0xe0, + 0xe4, + 0xd6, + 0xdb, + 0x80, + 0xef, + 0xf6, + 0xea, + 0xd6, + 0xb4, + 0xd6, + 0xec, + 0xc5, + 0xec, + 0xcf, + 0xc5, + 0x80, + 0xdb, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xdb, + 0xcf, + 0xdb, + 0xd6, + 0xe4, + 0xc5, + 0xdb, + 0xb4, + 0xcf, + 0xc5, + 0xcf, + 0xd6, + 0xe4, + 0xc5, + 0xf3, + 0xe0, + 0xec, + 0xe0, + 0xfd, + 0xe7, + 0xcf, + 0xb4, + 0x24, + 0x1a, + 0x0, + 0xf1, + 0x19, + 0xe0, + 0xec, + 0xe0, + 0xb4, + 0xcf, + 0xdb, + 0xd6, + 0xb4, + 0xb4, + 0xb4, + 0x80, + 0xdb, + 0x80, + 0xdb, + 0xc5, + 0xf1, + 0xe7, + 0xea, + 0xf8, + 0xec, + 0xc5, + 0xe4, + 0xe0, + 0xec, + 0xc5, + 0xcf, + 0xb4, + 0xe4, + 0xd6, + 0xe4, + 0xdb, + 0xf1, + 0xdb, + 0xdb, + 0xc5, + 0x22, + 0xea, + 0xe7, + 0x80, + 0xea, + 0xf3, + 0xec, + 0xfb, + 0xec, + 0xe0, + 0xdb, + 0xb4, + 0xe4, + 0xe0, + 0xec, + 0xd6, + 0xf3, + 0xb4, + 0xb4, + 0x80, + 0xd6, + 0xd6, + 0xe4, + 0xdb, + 0xcf, + 0xb4, + 0xdb, + 0xdb, + 0xf1, + 0xe4, + 0xcf, + 0xb4, + 0xe4, + 0xcf, + 0xe4, + 0xea, + 0xea, + 0xe4, + 0xe4, + 0xd6, + 0xef, + 0xb4, + 0xc5, + 0xc5, + 0xd6, + 0xc5, + 0xe4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xcf, + 0xc5, + 0x0, + 0xdb, + 0xb4, + 0xb4, + 0xdb, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xc5, + 0xcf, + 0xb4, + 0xcf, + 0xcf, + 0xe0, + 0xcf, + 0xcf, + 0x80, + 0xb4, + 0x80, + 0xec, + 0xd6, + 0xe0, + 0xc5, + 0xb4, + 0xb4, + 0xcf, + 0x80, + 0xcf, + 0xb4, + 0xcf, + 0x80, + 0xd6, + 0xc5, + 0x80, + 0x80, + 0xdb, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xcf, + 0x80, + 0x80, + 0x80, + 0xcf, + 0xb4, + 0xd6, + 0xb4, + 0xd6, + 0xb4, + 0xf1, + 0xc5, + 0xc5, + 0x80, + 0xb4, + 0x80, + 0x11, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xcf, + 0xb4, + 0x80, + 0xe4, + 0xb4, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0xcf, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xb4, + 0xd6, + 0xc5, + 0xb4, + 0x80, + 0xc5, + 0x80, + 0xb4, + 0x80, + 0xcf, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xc5, + 0xe4, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xef, + 0x80, + 0xc5, + 0xb4, + 0xc5, + 0xc5, + 0xc5, + 0xcf, + 0xd6, + 0xc5, + 0xf5, + 0xb4, + 0xcf, + 0x80, + 0xe4, + 0xc5, + 0xb4, + 0xe0, + 0xd6, + 0xb4, + 0xcf, + 0x80, + 0xb4, + 0xc5, + 0xcf, + 0x80, + 0xe0, + 0xc5, + 0xd6, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xb4, + 0xc5, + 0x80, + 0xd6, + 0xb4, + 0xe0, + 0xb4, + 0xb4, + 0xc5, + 0xc5, + 0xb4, + 0xc5, + 0x80, + 0xc5, + 0xc5, + 0xd6, + 0x80, + 0x80, + 0x80, + 0xf8, + 0x80, + 0x80, + 0xb4, + 0xd6, + 0x80, + 0xd6, + 0xb4, + 0xb4, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xcf, + 0xcf, + 0xe7, + 0x80, + 0xb4, + 0x80, + 0xc5, + 0x80, + 0xc5, + 0x80, + 0xb4, + 0x80, + 0xb4, + 0xb4, + 0xc5, + 0x80, + 0xb4, + 0x80, + 0xc5, + 0x80, + 0xe0, + 0x80, + 0xef, + 0x80, + 0xcf, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xb4, + 0xfd, + 0xb4, + 0x80, + 0xb4, + 0xe0, + 0x80, + 0xcf, + 0xb4, + 0xb4, + 0x80, + 0xe7, + 0xb4, + 0xe7, + 0xb4, + 0xb4, + 0xd6, + 0xb4, + 0x80, + 0xe0, + 0xc5, + 0x80, + 0x80, + 0xc5, + 0xc5, + 0xd6, + 0x80, + 0xc5, + 0x80, + 0xdb, + 0xc5, + 0xea, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xb4, + 0x80, + 0xe0, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0xd6, + 0x80, + 0xb4, + 0x80, + 0xb4, + 0x80, + 0x80, + 0xb4, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xe7, + 0xb4, + 0xc5, + 0x80, + 0xd6, + 0x80, + 0xe7, + 0xc5, + 0xdb, + 0x80, + 0xdb, + 0xcf, + 0xe0, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xb4, + 0xdb, + 0x80, + 0xef, + 0xc5, + 0x80, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xd6, + 0x80, + 0xc5, + 0xb4, + 0xdb, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xe0, + 0x80, + 0x80, + 0xb4, + 0xf6, + 0xdb, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xc5, + 0x80, + 0xb4, + 0xb4, + 0xd6, + 0xb4, + 0xd6, + 0x80, + 0x80, + 0xb4, + 0xd6, + 0xb4, + 0x80, + 0x80, + 0xdb, + 0xb4, + 0xf3, + 0xb4, + 0xdb, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x1d, + 0xcf, + 0x16, + 0x12, + 0x17, + 0xc, + 0x23, + 0x2, + 0x1, + 0xc5, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xd6, + 0xc5, + 0xb4, + 0xc5, + 0xdb, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xb4, + 0xdb, + 0xc5, + 0xe4, + 0x80, + 0xdb, + 0x80, + 0xc5, + 0xb4, + 0x80, + 0x80, + 0x78, + 0x64, + 0x7a, + 0x64, + 0x76, + 0x60, + 0x67, + 0x55, + 0x5a, + 0x3a, + 0x37, + 0x24, + 0xf6, + 0xc5, + 0x14, + 0x17, + 0x1e, + 0x18, + 0x31, + 0x39, + 0x44, + 0x43, + 0x49, + 0x3e, + 0x39, + 0x23, + 0x18, + 0x17, + 0x42, + 0x41, + 0x40, + 0x34, + 0x39, + 0x34, + 0x37, + 0x30, + 0x38, + 0x23, + 0x22, + 0x9, + 0x75, + 0x63, + 0x73, + 0x63, + 0x77, + 0x58, + 0x73, + 0x5f, + 0x64, + 0x4d, + 0x57, + 0x41, + 0x58, + 0x46, + 0x36, + 0x32, + 0x45, + 0x51, + 0x64, + 0x56, + 0x72, + 0x61, + 0x67, + 0x57, + 0x60, + 0x52, + 0x49, + 0x4e, + 0x61, + 0x53, + 0x62, + 0x57, + 0x67, + 0x50, + 0x66, + 0x56, + 0x63, + 0x52, + 0x5e, + 0x3d, + 0x6b, + 0x5a, + 0x70, + 0x5d, + 0x72, + 0x50, + 0x6c, + 0x56, + 0x67, + 0x5a, + 0x69, + 0x49, + 0x5a, + 0x4f, + 0x56, + 0x50, + 0x61, + 0x50, + 0x6c, + 0x5d, + 0x71, + 0x5d, + 0x6e, + 0x56, + 0x6c, + 0x58, + 0x69, + 0x55, + 0x6c, + 0x57, + 0x65, + 0x57, + 0x6c, + 0x56, + 0x68, + 0x4c, + 0x61, + 0x58, + 0x66, + 0x44, + 0x68, + 0x52, + 0x6b, + 0x56, + 0x6c, + 0x60, + 0x6e, + 0x52, + 0x72, + 0x4e, + 0x5b, + 0x4d, + 0x56, + 0x4e, + 0x68, + 0x51, + 0x69, + 0x5a, + 0x6a, + 0x5a, + 0x72, + 0x54, + 0x6f, + 0x5d, + 0x75, + 0x5f, + 0x67, + 0x57, + 0x65, + 0x48, + 0x5c, + 0x4c, + 0x66, + 0x52, + 0x68, + 0x52, + 0x63, + 0x53, + 0x64, + 0x44, + 0x5f, + 0x44, + 0x60, + 0x49, + 0x69, + 0x60, + 0x71, + 0x51, + 0x6c, + 0x59, + 0x6c, + 0x53, + 0x62, + 0x4b, + 0x5c, + 0x4e, + 0x61, + 0x4c, + 0x6a, + 0x5c, + 0x69, + 0x4b, + 0x6b, + 0x56, + 0x6b, + 0x40, + 0x5d, + 0x43, + 0x6c, + 0x55, + 0x60, + 0x3f, + 0x5f, + 0x4d, + 0x69, + 0x52, + 0x64, + 0x4d, + 0x64, + 0x41, + 0x59, + 0x3b, + 0x55, + 0x35, + 0x67, + 0x55, + 0x71, + 0x5a, + 0x69, + 0x58, + 0x65, + 0x48, + 0x5e, + 0x4e, + 0x6a, + 0x55, + 0x69, + 0x55, + 0x73, + 0x5c, + 0x68, + 0x35, + 0x64, + 0x57, + 0x6a, + 0x43, + 0x57, + 0x42, + 0x63, + 0x4c, + 0x71, + 0x57, + 0x60, + 0x43, + 0x5a, + 0x44, + 0x5c, + 0x3e, + 0x5d, + 0x3e, + 0x57, + 0x31, + 0x46, + 0x7, + 0x56, + 0x4b, + 0x73, + 0x52, + 0x64, + 0x4b, + 0x5b, + 0x4a, + 0x66, + 0x4f, + 0x69, + 0x4d, + 0x69, + 0x56, + 0x6e, + 0x3e, + 0x4b, + 0x37, + 0x5c, + 0x44, + 0x56, + 0x24, + 0x4f, + 0x2a, + 0x46, + 0x3b, + 0x61, + 0x4e, + 0x61, + 0x43, + 0x5d, + 0x45, + 0x5e, + 0x44, + 0x50, + 0x3c, + 0x56, + 0x2d, + 0x45, + 0x4, + 0x50, + 0x40, + 0x64, + 0x57, + 0x69, + 0x4d, + 0x64, + 0x50, + 0x62, + 0x4e, + 0x67, + 0x4e, + 0x62, + 0x56, + 0x67, + 0x3c, + 0x48, + 0x23, + 0x58, + 0x43, + 0x53, + 0x28, + 0x3b, + 0xcf, + 0x48, + 0x48, + 0x5c, + 0x40, + 0x4d, + 0x37, + 0x4e, + 0x3c, + 0x56, + 0x20, + 0x3d, + 0x11, + 0x37, + 0xc5, + 0x4a, + 0xd6, + 0x2d, + 0x2b, + 0x57, + 0x4e, + 0x5a, + 0x44, + 0x60, + 0x43, + 0x5a, + 0x3f, + 0x5c, + 0x41, + 0x67, + 0x50, + 0x60, + 0x2f, + 0x36, + 0x1c, + 0x54, + 0x3e, + 0x4f, + 0xc, + 0x2d, + 0x80, + 0x36, + 0x22, + 0x50, + 0x41, + 0x5f, + 0x3e, + 0x50, + 0x3f, + 0x5f, + 0x3d, + 0x46, + 0x19, + 0x41, + 0xfd, + 0x33, + 0xd6, + 0x25, + 0x2, + 0x40, + 0x2f, + 0x59, + 0x3a, + 0x4f, + 0x3d, + 0x47, + 0x23, + 0x52, + 0x32, + 0x5c, + 0x3e, + 0x45, + 0xcf, + 0xd, + 0xdb, + 0x42, + 0x2a, + 0x3f, + 0x80, + 0x15, + 0x80, + 0xe4, + 0xb4, + 0x36, + 0x28, + 0x49, + 0x39, + 0x52, + 0x3a, + 0x5a, + 0x39, + 0x52, + 0xb, + 0x26, + 0x80, + 0x27, + 0xc5, + 0x2f, + 0xf6, + 0x45, + 0x24, + 0x40, + 0x29, + 0x52, + 0x33, + 0x43, + 0xfc, + 0x33, + 0x1d, + 0x44, + 0x17, + 0x2e, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x24, + 0x80, + 0xb4, + 0x80, + 0x34, + 0x32, + 0x4c, + 0x32, + 0x4b, + 0x30, + 0x54, + 0x3f, + 0x51, + 0x30, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xe4, + 0x80, + 0x1, + 0x80, + 0x26, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xfd, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x29, + 0xe0, + 0xe0, + 0xc5, + 0x27, + 0x80, + 0x1b, + 0x7, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x23, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf9, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xd6, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xf5, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xe0, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x1d, + 0xe4, + 0x11, + 0xb4, + 0x32, + 0xa, + 0x6, + 0x80, + 0x80, + 0x80, + 0xd6, + 0x80, + 0x1c, + 0xd, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x15, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf8, + 0xcf, + 0x10, + 0x80, + 0x17, + 0x80, + 0x1e, + 0x80, + 0xff, + 0xec, + 0x25, + 0x80, + 0x1c, + 0x23, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x11, + 0xb4, + 0x2, + 0x80, + 0x30, + 0x8, + 0x15, + 0x80, + 0x6, + 0x20, + 0x36, + 0xf8, + 0x2e, + 0x18, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf3, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xd, + 0x4, + 0xa, + 0xea, + 0x37, + 0x24, + 0x2a, + 0xc, + 0x39, + 0x26, + 0x43, + 0x5, + 0x2d, + 0x1f, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x14, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x7, + 0xcf, + 0xf, + 0xef, + 0x32, + 0xd, + 0x2a, + 0x14, + 0x37, + 0x1, + 0x32, + 0x0, + 0x38, + 0x10, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x1c, + 0x80, + 0x80, + 0x80, + 0x28, + 0xdb, + 0xe4, + 0xe0, + 0xb4, + 0x80, + 0x16, + 0xcf, + 0x1b, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80 +}; diff --git a/tests/micro/arduino/testdata/unknown.c b/tests/micro/arduino/testdata/unknown.c new file mode 100644 index 000000000000..edfe9be3ebca --- /dev/null +++ b/tests/micro/arduino/testdata/unknown.c @@ -0,0 +1,1962 @@ +static const char input_unknown[1960] = { + 0x78, + 0x66, + 0x7a, + 0x63, + 0x78, + 0x62, + 0x6d, + 0x52, + 0x58, + 0x19, + 0x0, + 0xcf, + 0x80, + 0x80, + 0x80, + 0x80, + 0xcf, + 0xc5, + 0xc5, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xc5, + 0xe7, + 0xe0, + 0x80, + 0x80, + 0xc5, + 0x80, + 0xcf, + 0xc5, + 0xc5, + 0x80, + 0xc5, + 0xcf, + 0xe7, + 0xe0, + 0xdb, + 0x72, + 0x4b, + 0x65, + 0x60, + 0x70, + 0x50, + 0x73, + 0x59, + 0x60, + 0x4f, + 0x4d, + 0x3c, + 0x11, + 0xff, + 0xc5, + 0xc5, + 0xdb, + 0xdb, + 0xcf, + 0xec, + 0xe7, + 0xcf, + 0xcf, + 0x2, + 0x31, + 0x4d, + 0x4c, + 0xe7, + 0xdb, + 0xc5, + 0x80, + 0xcf, + 0xef, + 0xe4, + 0x4, + 0xff, + 0xf5, + 0xec, + 0xef, + 0x5, + 0x6c, + 0x4b, + 0x56, + 0x54, + 0x6a, + 0x47, + 0x6f, + 0x5b, + 0x63, + 0x55, + 0x4c, + 0x41, + 0x2d, + 0x22, + 0x20, + 0x3a, + 0x4e, + 0xf1, + 0xcf, + 0xfc, + 0x19, + 0xf3, + 0xe7, + 0x2d, + 0x48, + 0x4e, + 0x5b, + 0x80, + 0xcf, + 0xcf, + 0x80, + 0x80, + 0x80, + 0xdb, + 0x3, + 0xfb, + 0xf5, + 0xea, + 0x0, + 0xf5, + 0x62, + 0x40, + 0x46, + 0x47, + 0x62, + 0x41, + 0x68, + 0x53, + 0x5f, + 0x51, + 0x57, + 0x4e, + 0x5b, + 0x51, + 0x58, + 0x4b, + 0x62, + 0x2b, + 0xef, + 0x44, + 0x5d, + 0x41, + 0x49, + 0x5c, + 0x62, + 0x56, + 0x58, + 0x2f, + 0xc5, + 0xb4, + 0xcf, + 0xcf, + 0xc5, + 0xe0, + 0xf9, + 0xe7, + 0x7, + 0xf5, + 0xa, + 0xfc, + 0x5b, + 0x39, + 0x35, + 0x3d, + 0x5c, + 0x37, + 0x5d, + 0x49, + 0x57, + 0x49, + 0x63, + 0x57, + 0x61, + 0x55, + 0x5e, + 0x4d, + 0x64, + 0x4b, + 0x63, + 0x58, + 0x5c, + 0x49, + 0x5f, + 0x57, + 0x6a, + 0x56, + 0x68, + 0x41, + 0x15, + 0xf1, + 0x7, + 0xf1, + 0xf9, + 0xef, + 0xfd, + 0xfb, + 0xc, + 0xf6, + 0x5, + 0xef, + 0x5a, + 0x40, + 0x4a, + 0x44, + 0x69, + 0x57, + 0x55, + 0x50, + 0x63, + 0x49, + 0x67, + 0x5a, + 0x72, + 0x60, + 0x70, + 0x5a, + 0x71, + 0x61, + 0x77, + 0x63, + 0x75, + 0x5e, + 0x71, + 0x52, + 0x6f, + 0x5f, + 0x78, + 0x64, + 0x78, + 0x5d, + 0x56, + 0x57, + 0x56, + 0x28, + 0x39, + 0x3b, + 0x58, + 0x49, + 0x3d, + 0x33, + 0x58, + 0x3f, + 0x2a, + 0x50, + 0x6c, + 0x53, + 0x6a, + 0x5b, + 0x69, + 0x57, + 0x6e, + 0x5e, + 0x73, + 0x60, + 0x74, + 0x5a, + 0x75, + 0x61, + 0x76, + 0x60, + 0x75, + 0x59, + 0x6e, + 0x4c, + 0x6b, + 0x4c, + 0x6b, + 0x58, + 0x74, + 0x61, + 0x6e, + 0x36, + 0x49, + 0x41, + 0x5b, + 0x5d, + 0x6e, + 0x57, + 0x5e, + 0x44, + 0x50, + 0x30, + 0x3a, + 0x46, + 0x5f, + 0x3c, + 0x64, + 0x4e, + 0x5d, + 0x53, + 0x69, + 0x55, + 0x6a, + 0x57, + 0x69, + 0x52, + 0x71, + 0x5a, + 0x6b, + 0x47, + 0x5f, + 0x4d, + 0x61, + 0x43, + 0x5b, + 0x37, + 0x59, + 0x3e, + 0x57, + 0x3f, + 0x53, + 0xe, + 0x44, + 0x47, + 0x5c, + 0x43, + 0x62, + 0x51, + 0x5d, + 0x3f, + 0x4a, + 0x2a, + 0x39, + 0x3f, + 0x59, + 0x37, + 0x5c, + 0x40, + 0x58, + 0x50, + 0x65, + 0x4e, + 0x65, + 0x52, + 0x67, + 0x54, + 0x6f, + 0x52, + 0x59, + 0x3b, + 0x57, + 0x48, + 0x61, + 0x49, + 0x54, + 0xf8, + 0x3e, + 0x2d, + 0x4e, + 0x3e, + 0x50, + 0xc, + 0x3e, + 0x53, + 0x67, + 0x2d, + 0x4c, + 0x3b, + 0x4f, + 0x2a, + 0x43, + 0x14, + 0x46, + 0x37, + 0x50, + 0x23, + 0x58, + 0x36, + 0x57, + 0x48, + 0x63, + 0x46, + 0x67, + 0x4e, + 0x65, + 0x55, + 0x6d, + 0x4c, + 0x55, + 0x35, + 0x41, + 0x3b, + 0x58, + 0x3f, + 0x53, + 0x2f, + 0x44, + 0x25, + 0x48, + 0x37, + 0x58, + 0xe4, + 0x4d, + 0x48, + 0x53, + 0x2b, + 0x41, + 0x28, + 0x4a, + 0x2d, + 0x3d, + 0x5, + 0x44, + 0x29, + 0x44, + 0x1c, + 0x5c, + 0x3b, + 0x53, + 0x35, + 0x5a, + 0x3b, + 0x60, + 0x45, + 0x61, + 0x50, + 0x64, + 0x3a, + 0x43, + 0x1f, + 0x35, + 0x23, + 0x4d, + 0x4a, + 0x5e, + 0x3c, + 0x4d, + 0x30, + 0x51, + 0x2e, + 0x51, + 0xf3, + 0x4d, + 0x3e, + 0x50, + 0x1a, + 0x34, + 0xfc, + 0x44, + 0x27, + 0x37, + 0xf8, + 0x3a, + 0x9, + 0x32, + 0x33, + 0x5d, + 0x37, + 0x57, + 0x35, + 0x5d, + 0x3b, + 0x58, + 0x31, + 0x60, + 0x45, + 0x50, + 0xff, + 0x3a, + 0xe0, + 0x24, + 0x3, + 0x24, + 0x3a, + 0x4f, + 0xe, + 0x32, + 0x1d, + 0x46, + 0x2d, + 0x45, + 0x4, + 0x56, + 0x3d, + 0x50, + 0x7, + 0xa, + 0x80, + 0x3a, + 0x1f, + 0x31, + 0xe0, + 0x43, + 0x3, + 0x26, + 0x3a, + 0x5b, + 0x34, + 0x56, + 0x30, + 0x58, + 0x2e, + 0x53, + 0x1f, + 0x61, + 0x3f, + 0x3f, + 0x80, + 0x2f, + 0xe4, + 0x2f, + 0x14, + 0x30, + 0x1e, + 0x50, + 0xe0, + 0x22, + 0x0, + 0x4b, + 0x2d, + 0x39, + 0xdb, + 0x56, + 0x3e, + 0x46, + 0x34, + 0x2d, + 0x80, + 0x29, + 0x5, + 0x2f, + 0xc5, + 0x46, + 0xfb, + 0x1c, + 0x3a, + 0x56, + 0x26, + 0x53, + 0x2b, + 0x4e, + 0x8, + 0x53, + 0x25, + 0x65, + 0x3a, + 0xf, + 0x80, + 0xf5, + 0x80, + 0xb, + 0xd6, + 0x1e, + 0x7, + 0x55, + 0xd6, + 0x6, + 0x80, + 0x2c, + 0x0, + 0x11, + 0xe4, + 0x3e, + 0x26, + 0x41, + 0x25, + 0x2c, + 0x80, + 0x1d, + 0x2, + 0x2a, + 0xd6, + 0x45, + 0xec, + 0x4, + 0x3c, + 0x54, + 0x20, + 0x4d, + 0x12, + 0x49, + 0xf6, + 0x57, + 0x32, + 0x61, + 0x23, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb, + 0xe7, + 0x3b, + 0x80, + 0xc5, + 0x80, + 0xc5, + 0x80, + 0xcf, + 0xdb, + 0x14, + 0x1d, + 0x3d, + 0x36, + 0x3f, + 0x80, + 0x19, + 0xfc, + 0x1f, + 0x80, + 0x40, + 0xea, + 0x8, + 0x3c, + 0x52, + 0x22, + 0x3a, + 0xf8, + 0x49, + 0x3, + 0x58, + 0x21, + 0x3c, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0xf6, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x37, + 0x2d, + 0x3b, + 0x1b, + 0x31, + 0x80, + 0x16, + 0xf5, + 0xf3, + 0x80, + 0x3e, + 0xcf, + 0xec, + 0x3b, + 0x4e, + 0x12, + 0x4, + 0x80, + 0x4f, + 0x26, + 0x5a, + 0x1a, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xfc, + 0xb4, + 0x2c, + 0x0, + 0x1b, + 0x2a, + 0x2f, + 0x80, + 0xc, + 0xdb, + 0xd6, + 0x80, + 0x44, + 0xfd, + 0x11, + 0x33, + 0x44, + 0xd6, + 0x8, + 0x80, + 0x4e, + 0xe, + 0x26, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x1, + 0x80, + 0xe7, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x14, + 0xdb, + 0xf8, + 0x80, + 0x48, + 0x0, + 0x7, + 0xe7, + 0x18, + 0x80, + 0xef, + 0x80, + 0x36, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xdb, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x17, + 0x80, + 0x80, + 0x80, + 0x48, + 0x6, + 0x10, + 0x80, + 0xf1, + 0x80, + 0x24, + 0x80, + 0x7, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x3c, + 0xf1, + 0x7, + 0x80, + 0xc5, + 0x80, + 0x33, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xe0, + 0x80, + 0x26, + 0x80, + 0xcf, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xf6, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80 +}; diff --git a/tests/micro/arduino/testdata/yes.c b/tests/micro/arduino/testdata/yes.c new file mode 100644 index 000000000000..d2f70077b033 --- /dev/null +++ b/tests/micro/arduino/testdata/yes.c @@ -0,0 +1,1962 @@ +static const char input_yes[1960] = { + 0x7c, + 0x66, + 0x79, + 0x65, + 0x7d, + 0x67, + 0x7c, + 0x67, + 0x7c, + 0x66, + 0x7c, + 0x67, + 0x7c, + 0x67, + 0x7d, + 0x66, + 0x7c, + 0x67, + 0x7d, + 0x66, + 0x7c, + 0x67, + 0x7d, + 0x66, + 0x7c, + 0x67, + 0x7d, + 0x67, + 0x7d, + 0x67, + 0x7d, + 0x67, + 0x7d, + 0x67, + 0x7d, + 0x67, + 0x7d, + 0x67, + 0x7d, + 0x67, + 0x52, + 0x57, + 0x78, + 0x5a, + 0x67, + 0x53, + 0x6f, + 0x4b, + 0x6d, + 0x5c, + 0x71, + 0x52, + 0x66, + 0x4d, + 0x6e, + 0x56, + 0x73, + 0x50, + 0x5f, + 0x54, + 0x6d, + 0x55, + 0x6a, + 0x5b, + 0x6f, + 0x57, + 0x68, + 0x50, + 0x71, + 0x58, + 0x6d, + 0x57, + 0x69, + 0x55, + 0x6a, + 0x55, + 0x6c, + 0x59, + 0x6c, + 0x5a, + 0x5b, + 0x3c, + 0x54, + 0x44, + 0x58, + 0x4f, + 0x66, + 0x30, + 0x58, + 0x50, + 0x61, + 0x3d, + 0x67, + 0x36, + 0x5b, + 0x4d, + 0x64, + 0x51, + 0x6a, + 0x4d, + 0x60, + 0x4b, + 0x61, + 0x53, + 0x69, + 0x54, + 0x60, + 0x47, + 0x5c, + 0x4d, + 0x63, + 0x45, + 0x64, + 0x4d, + 0x63, + 0x4b, + 0x67, + 0x50, + 0x68, + 0x4d, + 0x64, + 0x4b, + 0x64, + 0x4e, + 0x5f, + 0x3d, + 0x53, + 0x42, + 0x59, + 0x39, + 0x57, + 0x43, + 0x5e, + 0x3a, + 0x44, + 0x3b, + 0x56, + 0x3c, + 0x5c, + 0x46, + 0x66, + 0x4c, + 0x61, + 0x3e, + 0x5d, + 0x49, + 0x55, + 0x48, + 0x5d, + 0x45, + 0x5a, + 0x48, + 0x5f, + 0x41, + 0x59, + 0x49, + 0x5a, + 0x46, + 0x5d, + 0x3b, + 0x51, + 0x3d, + 0x4c, + 0x44, + 0x57, + 0x37, + 0x54, + 0x43, + 0x4f, + 0xa, + 0x32, + 0x28, + 0x5b, + 0x3a, + 0x5e, + 0x47, + 0x4d, + 0x2b, + 0x57, + 0x4a, + 0x5d, + 0x34, + 0x52, + 0x3e, + 0x50, + 0x38, + 0x54, + 0x30, + 0x53, + 0x41, + 0x57, + 0x39, + 0x5c, + 0x3c, + 0x53, + 0x41, + 0x5a, + 0x1e, + 0x4e, + 0x41, + 0x4d, + 0x2c, + 0x3e, + 0x18, + 0x4c, + 0x1c, + 0x36, + 0x11, + 0x4b, + 0x32, + 0x52, + 0x2f, + 0x50, + 0x2d, + 0x4e, + 0x20, + 0x50, + 0x3c, + 0x4a, + 0x16, + 0x44, + 0x22, + 0x48, + 0x29, + 0x4d, + 0x34, + 0x4e, + 0x2c, + 0x52, + 0x2e, + 0x46, + 0x35, + 0x4b, + 0x14, + 0x50, + 0x33, + 0x53, + 0x3e, + 0x50, + 0x2d, + 0x4a, + 0x0, + 0x4b, + 0x3a, + 0x47, + 0x16, + 0x45, + 0x32, + 0x45, + 0x10, + 0x42, + 0x23, + 0x49, + 0x39, + 0x41, + 0x10, + 0x48, + 0x32, + 0x4e, + 0x30, + 0x40, + 0x34, + 0x46, + 0x39, + 0x54, + 0xf5, + 0x49, + 0x38, + 0x53, + 0x2c, + 0x4a, + 0x37, + 0x51, + 0x2c, + 0x46, + 0x2f, + 0x4c, + 0x2a, + 0x4d, + 0x2b, + 0x3d, + 0x2f, + 0x4e, + 0x20, + 0x1e, + 0x7, + 0x41, + 0x8, + 0x39, + 0xd, + 0x46, + 0x20, + 0x3b, + 0x2a, + 0x3f, + 0x20, + 0x40, + 0xe, + 0x4e, + 0x2e, + 0x3e, + 0x21, + 0x4f, + 0x16, + 0x2e, + 0x35, + 0x54, + 0x32, + 0x41, + 0x1c, + 0x48, + 0x2a, + 0x44, + 0xc, + 0x48, + 0x21, + 0x41, + 0x19, + 0x48, + 0x2a, + 0x3d, + 0x21, + 0x44, + 0xb4, + 0x41, + 0x14, + 0x3e, + 0x2b, + 0x45, + 0x23, + 0x50, + 0x28, + 0x3e, + 0x1f, + 0x43, + 0x26, + 0x46, + 0x1b, + 0x48, + 0x12, + 0x44, + 0x2d, + 0x47, + 0x22, + 0x3c, + 0x32, + 0x48, + 0x26, + 0x2f, + 0x21, + 0x45, + 0x17, + 0x43, + 0x22, + 0x43, + 0x1d, + 0x44, + 0x28, + 0x4d, + 0x14, + 0x56, + 0x23, + 0x40, + 0x2c, + 0x34, + 0x80, + 0x44, + 0xf, + 0x37, + 0x16, + 0x49, + 0x21, + 0x34, + 0x1e, + 0x3f, + 0x22, + 0x2b, + 0x16, + 0x34, + 0x28, + 0x43, + 0x2d, + 0x43, + 0x11, + 0x49, + 0x1a, + 0x46, + 0x20, + 0x46, + 0x21, + 0x3d, + 0x17, + 0x3d, + 0x28, + 0x3e, + 0xf5, + 0x33, + 0x15, + 0x39, + 0x20, + 0x4d, + 0x2d, + 0x36, + 0x80, + 0x1a, + 0xdb, + 0x3e, + 0x17, + 0x3b, + 0x1f, + 0x40, + 0x17, + 0x2b, + 0xcf, + 0x39, + 0x2d, + 0x4d, + 0x2b, + 0x35, + 0xf6, + 0x44, + 0x29, + 0x3d, + 0x24, + 0x30, + 0x17, + 0x3b, + 0x28, + 0x44, + 0xd, + 0x38, + 0x20, + 0x3b, + 0xf3, + 0x45, + 0x19, + 0x4c, + 0x24, + 0x37, + 0x15, + 0xf3, + 0xb4, + 0x3c, + 0x28, + 0x36, + 0xf3, + 0x44, + 0x1b, + 0x48, + 0x25, + 0x1d, + 0xd6, + 0x25, + 0xcf, + 0x3a, + 0x9, + 0x3f, + 0xfc, + 0x31, + 0xf1, + 0x41, + 0x24, + 0x44, + 0x17, + 0x45, + 0x20, + 0x42, + 0x2, + 0x33, + 0xb4, + 0x31, + 0x1b, + 0x43, + 0x18, + 0x2c, + 0x14, + 0x44, + 0xa, + 0x43, + 0x7, + 0x4, + 0x80, + 0x2b, + 0xf3, + 0x49, + 0x2a, + 0x47, + 0xea, + 0x3b, + 0xec, + 0x30, + 0xfb, + 0x3c, + 0x18, + 0x35, + 0xff, + 0x14, + 0x18, + 0x39, + 0x7, + 0x3c, + 0x5, + 0xa, + 0xf, + 0x35, + 0x12, + 0x3a, + 0x0, + 0x2d, + 0xc, + 0x46, + 0x13, + 0x3e, + 0x23, + 0x3f, + 0x18, + 0x3a, + 0x16, + 0x35, + 0xf5, + 0x3a, + 0x1b, + 0x4e, + 0x2d, + 0x3c, + 0xef, + 0x3c, + 0xfc, + 0x2e, + 0xa, + 0x32, + 0xb4, + 0x23, + 0xfb, + 0x3e, + 0x16, + 0x40, + 0xe, + 0x24, + 0x3, + 0x44, + 0x24, + 0x3b, + 0xa, + 0x19, + 0x80, + 0x28, + 0x1a, + 0x3b, + 0xfb, + 0x2a, + 0xf, + 0x31, + 0x4, + 0x3a, + 0x4, + 0x2d, + 0xec, + 0x29, + 0xa, + 0x25, + 0xb4, + 0x20, + 0xb4, + 0x35, + 0x1b, + 0x31, + 0xb4, + 0x7, + 0xc, + 0x4b, + 0x1b, + 0x1c, + 0x80, + 0x28, + 0xd6, + 0x23, + 0x16, + 0x2d, + 0xf8, + 0x35, + 0xf6, + 0x45, + 0x11, + 0x1d, + 0xc5, + 0x2a, + 0xf6, + 0x37, + 0xea, + 0x36, + 0x11, + 0x3f, + 0x7, + 0x36, + 0x11, + 0x2e, + 0xf1, + 0x3b, + 0x11, + 0x16, + 0x2a, + 0x3a, + 0x6, + 0x37, + 0xcf, + 0x18, + 0x80, + 0x30, + 0xd6, + 0x14, + 0xf1, + 0x16, + 0xfc, + 0x28, + 0xe4, + 0x3d, + 0xe0, + 0x2d, + 0x80, + 0x26, + 0xec, + 0x3d, + 0xf8, + 0x36, + 0xcf, + 0x11, + 0xef, + 0x2c, + 0x16, + 0x2d, + 0xff, + 0x35, + 0x12, + 0x3e, + 0xa, + 0x35, + 0xd, + 0x2f, + 0xf9, + 0x3f, + 0x2d, + 0x40, + 0x80, + 0xe7, + 0x6, + 0x2a, + 0x80, + 0x34, + 0x4, + 0x5, + 0x1d, + 0x3d, + 0x12, + 0x1e, + 0xa, + 0x3f, + 0x26, + 0x2b, + 0xfb, + 0x2b, + 0x80, + 0x26, + 0x80, + 0x1e, + 0x15, + 0x24, + 0xdb, + 0x2a, + 0xd6, + 0x2b, + 0x80, + 0x6, + 0xdb, + 0x26, + 0xfd, + 0x37, + 0xec, + 0x2a, + 0xec, + 0x2, + 0x1c, + 0x3c, + 0xe7, + 0x11, + 0x80, + 0xf3, + 0xfd, + 0x3a, + 0x1, + 0x28, + 0x17, + 0x3a, + 0xdb, + 0xf6, + 0x80, + 0x2, + 0xd6, + 0x21, + 0xcf, + 0x2a, + 0xdb, + 0xf, + 0x80, + 0x2b, + 0x17, + 0x24, + 0xcf, + 0x2e, + 0xcf, + 0x30, + 0xf8, + 0xa, + 0xf1, + 0x26, + 0xe7, + 0x2d, + 0xf5, + 0x31, + 0xef, + 0x25, + 0x80, + 0x1, + 0xfb, + 0xd6, + 0x80, + 0x19, + 0x1c, + 0x37, + 0xfb, + 0x39, + 0x11, + 0x2c, + 0x80, + 0x23, + 0x18, + 0x33, + 0xf8, + 0x2e, + 0xd, + 0x34, + 0xcf, + 0x2b, + 0xf1, + 0x21, + 0x80, + 0x29, + 0x80, + 0x1f, + 0xe4, + 0xe, + 0xb, + 0x25, + 0xc5, + 0x1f, + 0xc5, + 0x21, + 0x0, + 0x19, + 0x80, + 0xef, + 0x80, + 0xb, + 0xe4, + 0x1c, + 0xcf, + 0x33, + 0x16, + 0x3e, + 0x7, + 0x21, + 0xf5, + 0x2f, + 0x0, + 0x2e, + 0xef, + 0x23, + 0x6, + 0x3d, + 0xe7, + 0x23, + 0xe7, + 0x26, + 0xd6, + 0x40, + 0xfd, + 0x30, + 0x80, + 0xa, + 0xf5, + 0x35, + 0x0, + 0x32, + 0xf8, + 0x20, + 0xcf, + 0x2d, + 0xef, + 0x32, + 0x13, + 0x3c, + 0x1c, + 0x0, + 0xfc, + 0x26, + 0xe0, + 0x26, + 0xd6, + 0xec, + 0x80, + 0x16, + 0xf3, + 0xb4, + 0xf1, + 0x31, + 0xcf, + 0x1f, + 0x80, + 0x7, + 0xf6, + 0x19, + 0xfd, + 0xe7, + 0x80, + 0x1, + 0x80, + 0x1c, + 0x2, + 0x2f, + 0x80, + 0x2f, + 0x80, + 0x26, + 0x4, + 0x1c, + 0xb4, + 0x4, + 0xdb, + 0x1e, + 0xcf, + 0x2a, + 0x80, + 0xdb, + 0x80, + 0x1a, + 0xea, + 0x31, + 0xa, + 0x18, + 0x23, + 0x39, + 0xf8, + 0x36, + 0x22, + 0x25, + 0xc5, + 0x1f, + 0x80, + 0x26, + 0xef, + 0x34, + 0x80, + 0x19, + 0xe7, + 0x2d, + 0xe0, + 0x17, + 0xe4, + 0x2f, + 0x17, + 0x34, + 0x7, + 0x31, + 0xef, + 0x25, + 0xe0, + 0x1e, + 0xf8, + 0x1d, + 0xdb, + 0xfd, + 0xb, + 0x11, + 0x80, + 0x11, + 0x80, + 0xe7, + 0xcf, + 0x32, + 0x80, + 0xc, + 0xdb, + 0xa, + 0x80, + 0xf9, + 0x80, + 0x14, + 0x14, + 0x35, + 0x80, + 0x2c, + 0xf9, + 0x1f, + 0xdb, + 0x1b, + 0xea, + 0x11, + 0x80, + 0x26, + 0xc5, + 0xb, + 0xb4, + 0xb, + 0x80, + 0x7, + 0xef, + 0x22, + 0x6, + 0x20, + 0xe0, + 0x0, + 0x80, + 0x1a, + 0x1c, + 0x25, + 0xfb, + 0x2f, + 0x80, + 0x80, + 0xea, + 0x31, + 0x19, + 0x3c, + 0xf, + 0x23, + 0x80, + 0x16, + 0x0, + 0x38, + 0xf1, + 0x21, + 0xea, + 0x2c, + 0x80, + 0x1e, + 0xec, + 0x2a, + 0xe4, + 0x7, + 0x80, + 0xf8, + 0x80, + 0x9, + 0xd6, + 0x20, + 0xc5, + 0x18, + 0x80, + 0x0, + 0x14, + 0x2a, + 0xcf, + 0x1d, + 0x80, + 0xc, + 0xe4, + 0x1c, + 0xa, + 0x3a, + 0x24, + 0x1b, + 0x80, + 0xf8, + 0x80, + 0x8, + 0x80, + 0x9, + 0x80, + 0x20, + 0xdb, + 0x20, + 0xd6, + 0x2d, + 0x19, + 0x1a, + 0xd6, + 0x25, + 0x80, + 0xb4, + 0x80, + 0x38, + 0x12, + 0x17, + 0xec, + 0x14, + 0x80, + 0x20, + 0xb4, + 0x13, + 0xdb, + 0xb, + 0x80, + 0xfc, + 0x15, + 0x2f, + 0x0, + 0xdb, + 0x80, + 0xf5, + 0x0, + 0x8, + 0xcf, + 0xf8, + 0xe4, + 0xc, + 0x13, + 0x34, + 0x80, + 0x17, + 0x80, + 0xe7, + 0x80, + 0x11, + 0xcf, + 0x2f, + 0xf6, + 0x5, + 0xdb, + 0x27, + 0x6, + 0xf1, + 0x80, + 0x11, + 0xc5, + 0x24, + 0x80, + 0x11, + 0xea, + 0xa, + 0x80, + 0x23, + 0x1, + 0x16, + 0xf3, + 0xfb, + 0x80, + 0x15, + 0x13, + 0x33, + 0x6, + 0xfc, + 0x80, + 0xd6, + 0x80, + 0x10, + 0x80, + 0x1a, + 0xf5, + 0x11, + 0x80, + 0x9, + 0xc5, + 0xf, + 0xcf, + 0xef, + 0xc5, + 0x1b, + 0xf9, + 0x8, + 0x80, + 0x20, + 0xc5, + 0x1c, + 0xdb, + 0x1f, + 0x80, + 0x1e, + 0xf3, + 0x12, + 0xea, + 0x26, + 0xcf, + 0x16, + 0xcf, + 0x2, + 0xd6, + 0x7, + 0x80, + 0x24, + 0x80, + 0xf9, + 0xcf, + 0x1a, + 0xb4, + 0x26, + 0xc5, + 0xfb, + 0x80, + 0xfc, + 0xc5, + 0xef, + 0xcf, + 0x28, + 0x80, + 0x19, + 0xcf, + 0x28, + 0xea, + 0x2c, + 0xc5, + 0x2f, + 0xc, + 0x1, + 0xec, + 0x2d, + 0xb4, + 0x14, + 0x80, + 0xc, + 0xec, + 0xf5, + 0xdb, + 0x0, + 0xc5, + 0x20, + 0x80, + 0x21, + 0x1, + 0x0, + 0x80, + 0xa, + 0x80, + 0x29, + 0x80, + 0xdb, + 0x7, + 0xf, + 0xb4, + 0x23, + 0xfb, + 0x27, + 0xdb, + 0x22, + 0xec, + 0x21, + 0x80, + 0xd6, + 0xb4, + 0x15, + 0xd6, + 0x11, + 0x80, + 0x1f, + 0xc5, + 0x1a, + 0xb4, + 0x7, + 0xe0, + 0x21, + 0xcf, + 0x14, + 0x16, + 0x2a, + 0x80, + 0x80, + 0x80, + 0xa, + 0xe7, + 0x6, + 0x80, + 0xb4, + 0x80, + 0xf, + 0x80, + 0xfc, + 0xe4, + 0x13, + 0x80, + 0x19, + 0xb4, + 0xd, + 0xb4, + 0xdb, + 0xc5, + 0x18, + 0x80, + 0x21, + 0xb4, + 0x2d, + 0xc5, + 0xf1, + 0xdb, + 0xf, + 0x80, + 0x23, + 0xd6, + 0x28, + 0x80, + 0xea, + 0xd6, + 0xe7, + 0xcf, + 0x11, + 0xe4, + 0xec, + 0x2, + 0x20, + 0xb4, + 0x29, + 0xdb, + 0x6, + 0x80, + 0xef, + 0x80, + 0xe0, + 0x80, + 0x4, + 0xc5, + 0x32, + 0xb4, + 0x2f, + 0x80, + 0x7, + 0xb4, + 0xe0, + 0x80, + 0xf5, + 0x80, + 0x5, + 0xb4, + 0x8, + 0xcf, + 0x1f, + 0xf6, + 0x28, + 0xdb, + 0x1b, + 0xff, + 0x12, + 0x80, + 0x2a, + 0xff, + 0x2f, + 0xfc, + 0xcf, + 0x80, + 0xc, + 0xf1, + 0x21, + 0x80, + 0x2, + 0x1, + 0x2d, + 0xf8, + 0xf9, + 0xf3, + 0x25, + 0x80, + 0xdb, + 0x80, + 0xd6, + 0x80, + 0xc, + 0xe4, + 0x1b, + 0xc5, + 0xe0, + 0xec, + 0xec, + 0x80, + 0x6, + 0xb4, + 0xf5, + 0xcf, + 0xc, + 0x80, + 0x1, + 0xf6, + 0x1d, + 0x80, + 0xe7, + 0x80, + 0xf3, + 0x80, + 0xc5, + 0x80, + 0xf6, + 0x80, + 0x1b, + 0xcf, + 0x11, + 0x80, + 0xd6, + 0x80, + 0x80, + 0x80, + 0xdb, + 0x80, + 0xec, + 0x80, + 0x19, + 0xe0, + 0x2, + 0x80, + 0x19, + 0xef, + 0x16, + 0x80, + 0xd6, + 0x80, + 0xe7, + 0x80, + 0x11, + 0xd6, + 0xfc, + 0x80, + 0xa, + 0xd6, + 0x17, + 0xe7, + 0xe4, + 0x80, + 0xb4, + 0xb4, + 0x1d, + 0xb4, + 0xf, + 0x80, + 0x32, + 0xfb, + 0x1b, + 0xdb, + 0x25, + 0xec, + 0xf5, + 0x80, + 0xd6, + 0xef, + 0x23, + 0xec, + 0x14, + 0x80, + 0xe0, + 0xdb, + 0xf9, + 0x80, + 0xcf, + 0x80, + 0xff, + 0xb4, + 0xd, + 0x80, + 0xe4, + 0x80, + 0x0, + 0xc5, + 0x1f, + 0xdb, + 0x23, + 0xe0, + 0x1, + 0x80, + 0x80, + 0x80, + 0xcf, + 0x80, + 0xb4, + 0x80, + 0xe0, + 0xf6, + 0x1d, + 0xcf, + 0xdb, + 0x80, + 0xdb, + 0x80, + 0x80, + 0xb4, + 0xb, + 0x80, + 0x80, + 0x80, + 0x1d, + 0x80, + 0x4, + 0xe4, + 0xf5, + 0x80, + 0x80, + 0x80, + 0x4, + 0x80, + 0xe4, + 0x80, + 0xfc, + 0x80, + 0xd6, + 0x80, + 0xf9, + 0x80, + 0x80, + 0xb4, + 0xc, + 0x80, + 0x26, + 0xf9, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xf1, + 0x80, + 0x80, + 0x80, + 0xf3, + 0xb4, + 0x0, + 0x80, + 0x2, + 0xcf, + 0xb4, + 0xea, + 0x14, + 0x80, + 0x18, + 0x80, + 0xcf, + 0x80, + 0xd, + 0x80, + 0xe0, + 0x80, + 0x16, + 0x80, + 0xf8, + 0xc5, + 0x11, + 0xb4, + 0xf8, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xe4, + 0xe, + 0x1c, + 0x80, + 0xfc, + 0xb4, + 0x2a, + 0x6, + 0x31, + 0x10, + 0x1c, + 0x80, + 0xfd, + 0xfc, + 0xc, + 0xe7, + 0xea, + 0x80, + 0xe7, + 0xd6, + 0xd, + 0xb4, + 0x22, + 0xf1, + 0x7, + 0xb4, + 0x1d, + 0xf6, + 0x11, + 0xd6, + 0x28, + 0x80, + 0xc5, + 0xb4, + 0x1f, + 0xe0, + 0x80, + 0x80, + 0x80, + 0x80, + 0xfb, + 0xe7, + 0xc, + 0x80, + 0xdb, + 0x80, + 0xcf, + 0x80, + 0x80, + 0x80, + 0xd6, + 0xc5, + 0xf, + 0x80, + 0x80, + 0xb4, + 0x1b, + 0x80, + 0x0, + 0xdb, + 0xf5, + 0x80, + 0x80, + 0x80, + 0x15, + 0xec, + 0xf, + 0x80, + 0xd6, + 0x80, + 0x80, + 0xb4, + 0xc, + 0xd6, + 0xd6, + 0x80, + 0xd6, + 0xd6, + 0x9, + 0x80, + 0x80, + 0x80, + 0x3, + 0xc5, + 0x9, + 0x80, + 0x80, + 0x80, + 0xe4, + 0x80, + 0xf3, + 0x80, + 0x10, + 0xea, + 0xb4, + 0x80, + 0xdb, + 0xf3, + 0xa, + 0x80, + 0xc5, + 0x80, + 0xef, + 0x80, + 0xc5, + 0x80, + 0xec, + 0x80, + 0xff, + 0x80, + 0xa, + 0xc5, + 0xf1, + 0x80, + 0xb4, + 0x80, + 0xe0, + 0x80, + 0xfb, + 0x80, + 0xf8, + 0x80, + 0x3, + 0x80, + 0xc, + 0xcf, + 0x80, + 0xd6, + 0xe0, + 0x80, + 0x80, + 0xb4, + 0xcf, + 0xc5, + 0x28, + 0xd6, + 0x17, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xec, + 0x14, + 0x80, + 0xf3, + 0x80, + 0xf8, + 0x80, + 0xf3, + 0x80, + 0xcf, + 0x80, + 0xf8, + 0xe0, + 0xea, + 0x80, + 0xc5, + 0x0, + 0x35, + 0xea, + 0x3, + 0x80, + 0x80, + 0x80, + 0x17, + 0xf, + 0x16, + 0x80, + 0x19, + 0xd6, + 0x80, + 0x80, + 0x80, + 0x80, + 0xe0, + 0x80, + 0xfd, + 0x80, + 0x4, + 0xfc, + 0x1e, + 0x80, + 0xef, + 0x80, + 0xef, + 0xf1, + 0x1f, + 0x80, + 0xfc, + 0x80, + 0xe7, + 0x80, + 0xff, + 0x80, + 0xf8, + 0x80, + 0x80, + 0x80, + 0x17, + 0x80, + 0xcf, + 0xfb, + 0x1c, + 0x0, + 0x26, + 0x11, + 0x16, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xcf, + 0xf3, + 0x80, + 0x14, + 0xb4, + 0xdb, + 0x5, + 0x19, + 0x80, + 0xd6, + 0x80, + 0xf5, + 0x80, + 0x17, + 0xc5, + 0x0, + 0xc5, + 0xcf, + 0xc5, + 0x4, + 0x80, + 0x5, + 0x80, + 0xa, + 0x80, + 0x19, + 0xd6, + 0x28, + 0x5, + 0xea, + 0x80, + 0x80, + 0x80, + 0x80, + 0xec, + 0xd, + 0x80, + 0x80, + 0x80, + 0x2, + 0x80, + 0xf1, + 0x80, + 0x80, + 0x80, + 0xd6, + 0x80, + 0xd6, + 0x80, + 0xdb, + 0x80, + 0xf3, + 0x80, + 0xff, + 0x80, + 0x80, + 0xc5, + 0x20, + 0x80, + 0xea, + 0x80, + 0xb4, + 0x80, + 0x22, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xc5, + 0x80, + 0x15, + 0x80, + 0x24, + 0xc5, + 0xfc, + 0x80, + 0xb, + 0xe4, + 0xcf, + 0x80, + 0x80, + 0x80, + 0xe7, + 0xa, + 0x1, + 0xdb, + 0x12, + 0x80, + 0xf5, + 0x80, + 0x80, + 0x80, + 0xa, + 0xd6, + 0xfd, + 0xf5, + 0xfc, + 0xcf, + 0xe, + 0x80, + 0xd6, + 0x80, + 0x80, + 0x80, + 0xef, + 0x80, + 0xfd, + 0xc5, + 0x12, + 0xea, + 0x20, + 0x80, + 0xe0, + 0xdb, + 0xc5, + 0xd6, + 0x1a, + 0x80, + 0x80, + 0xd6, + 0x14, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0xc5, + 0xb4, + 0xe4, + 0xb4, + 0xf6, + 0x3, + 0xfc, + 0x80, + 0x80, + 0x80, + 0xfb, + 0x80, + 0x0, + 0xe4, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x5, + 0xb4, + 0x80, + 0x80, + 0x19, + 0xd6, + 0xe0, + 0x80, + 0x80, + 0x80, + 0xb4, + 0xc5, + 0xb4, + 0x80, + 0xfb, + 0x4, + 0x13, + 0x80, + 0xf, + 0xc5, + 0x2, + 0xec, + 0xb4, + 0xb4, + 0xef, + 0x80, + 0xe0, + 0x80, + 0xcf, + 0xf5, + 0x1, + 0x80, + 0xe4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x1, + 0x1c, + 0x80, + 0x80, + 0x80, + 0xc5, + 0xcf, + 0xc, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xfb, + 0xc5, + 0xcf, + 0xdb, + 0xcf, + 0x80, + 0xd6, + 0x80, + 0xea, + 0x80, + 0x80, + 0x80, + 0xd6, + 0x80, + 0x80, + 0xcf, + 0xf9, + 0xdb, + 0xf8, + 0x80, + 0xdb, + 0xb4, + 0xff, + 0xe0, + 0xb4, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xea, + 0x80, + 0xc5, + 0x80, + 0x80, + 0x80, + 0xe0, + 0xec, + 0xf5, + 0x80, + 0x80, + 0x80, + 0x17, + 0x80, + 0xcf, + 0x80, + 0xf8, + 0xf6, + 0xe7, + 0x80, + 0xd6, + 0x80, + 0xcf, + 0x80, + 0x80, + 0x80, + 0xb4, + 0x80, + 0xe4, + 0x80, + 0xf8, + 0x80, + 0x80, + 0x80, + 0xdb, + 0x80, + 0xfb, + 0x80, + 0x80, + 0x80, + 0xf3, + 0x80, + 0x11, + 0xc5, + 0x80, + 0x80, + 0xb4, + 0x80, + 0x80, + 0x80, + 0xd6, + 0x80, + 0xec, + 0xb4, + 0x14, + 0xb4, + 0xf3, + 0x80, + 0xf9, + 0x80, + 0x8, + 0x80, + 0x80, + 0x80, + 0xe7, + 0x80, + 0x80, + 0xc5, + 0xf1, + 0x80, + 0xf3, + 0x80 +}; From 62e3f63cc9cc35f41194c117dbca0dc305a137aa Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 16:24:28 -0700 Subject: [PATCH 33/47] Runtime tests when hardware is present --- .../template_project/microtvm_api_server.py | 47 +++++--- tests/micro/arduino/test_arduino_workflow.py | 106 ++++++++++++++++-- 2 files changed, 132 insertions(+), 21 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index f92466feb758..92968e020ac5 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -63,7 +63,8 @@ class Handler(server.ProjectAPIHandler): def __init__(self): super(Handler, self).__init__() self._proc = None - self.port = None + self._port = None + self._serial = None def server_info_query(self): return server.ServerInfo( @@ -110,10 +111,8 @@ def _compile_graph_json(self, model_dir, obj): def _disassemble_mlf(self, mlf_tar_path, source_dir): mlf_unpacking_dir = tempfile.TemporaryDirectory() - print(mlf_tar_path) with tarfile.open(mlf_tar_path, 'r:') as tar: tar.extractall(mlf_unpacking_dir.name) - print("Unpacked tar") # Copy C files # TODO are the defaultlib0.c the same? @@ -278,8 +277,6 @@ def build(self, options): compile_cmd.append("--verbose") # Specify project to compile - print(compile_cmd) - print(API_SERVER_DIR) output = subprocess.check_call(compile_cmd) assert(output == 0) @@ -307,13 +304,17 @@ def _auto_detect_port(self, options): def _get_arduino_port(self, options): - if not self.port: + if not self._port: if 'port' in options and options['port']: - self.port = options['port'] + self._port = options['port'] else: - self.port = self._auto_detect_port(options) + self._port = self._auto_detect_port(options) - return self.port + return self._port + + + def _get_baudrate(self, options): + return 115200 def flash(self, options): @@ -334,19 +335,39 @@ def flash(self, options): def open_transport(self, options): - raise NotImplementedError + # Zephyr example doesn't throw an error in this case + if self._serial is not None: + return + + port = self._get_arduino_port(options) + baudrate = self._get_baudrate(options) + self._serial = serial.Serial(port, baudrate=baudrate, timeout=5) + return server.TransportTimeouts( + session_start_retry_timeout_sec=2.0, + session_start_timeout_sec=5.0, + session_established_timeout_sec=5.0, + ) def close_transport(self): - raise NotImplementedError + if self._serial is None: + return + self._serial.close() + self._serial = None def read_transport(self, n, timeout_sec): - raise NotImplementedError + # It's hard to set timeout_sec, so we just throw it away + # TODO fix this + if self._serial is None: + raise server.TransportClosedError() + return self._serial.read(n) def write_transport(self, data, timeout_sec): - raise NotImplementedError + if self._serial is None: + raise server.TransportClosedError() + return self._serial.write(data, timeout_sec) if __name__ == "__main__": diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 86d208459e6e..976a53a2eb36 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -1,6 +1,7 @@ import datetime import os import pathlib +import shutil import sys import pytest @@ -130,26 +131,115 @@ def test_import_rerouting(project_dir): assert('include/tvm/runtime/crt/platform.h' in load_json_c) +# Build on top of the generated project by replacing the +# top-level .ino fileand adding data input files, much +# like a user would @pytest.fixture(scope="module") -def do_compile(project): - project.build() +def modified_project(project_dir, project): + testdata_dir = pathlib.Path(os.path.dirname(__file__)) / "testdata" + shutil.copy2(testdata_dir / "project.ino", project_dir / "project.ino") -def test_compile_yes_no_project(project_dir, project, do_compile): + project_data_dir = project_dir / "src" / "data" + project_data_dir.mkdir() + for sample in ["yes.c", "no.c", "silence.c", "unknown.c"]: + shutil.copy2(testdata_dir / sample, project_data_dir / sample) + + return project + + +@pytest.fixture(scope="module") +def compiled_project(modified_project): + modified_project.build() + return modified_project + + +def test_compile_yes_no_project(project_dir, project, compiled_project): build_dir = project_dir / "build" assert(build_dir.exists()) first_build_file = next(build_dir.iterdir(), None) assert(first_build_file is not None) +'''------------------------------------------------------------ +If we're not running on real hardware, no further tests are run +------------------------------------------------------------''' + + @pytest.fixture(scope="module") -def do_upload(project): - project.flash() +def uploaded_project(compiled_project, run_hardware_tests): + if not run_hardware_tests: + pytest.skip() + + compiled_project.flash() + return compiled_project -def test_upload_yes_no_project(project_dir, project, do_compile, do_upload): - pass - # Test upload +''' Sample serial output: + +category,runtime,yes,no,silence,unknown +yes,56762,115,-123,-125,-123, +no,56762,-128,4,-123,-9, +silence,56792,-128,-118,107,-117, +unknown,56792,-128,-125,-128,125, +''' +SERIAL_OUTPUT_HEADERS = "category,runtime,yes,no,silence,unknown" +@pytest.fixture(scope="module") +def serial_output(uploaded_project): + transport = uploaded_project.transport() + transport.open() + out = transport.read(2048, -1) + out_str = out.decode('utf-8') + out_lines = out_str.split("\r\n") + + assert(SERIAL_OUTPUT_HEADERS in out_lines) + headers_index = out_lines.index(SERIAL_OUTPUT_HEADERS) + data_lines = out_lines[headers_index + 1:headers_index + 5] + split_lines = [line.split(",") for line in data_lines] + + return [ + [line[0]] + + list(map(int, line[1:6])) + for line in split_lines + ] + + +TENSORFLOW_EVALUATIONS = { + "yes": [115,-123,-125,-123], + "no": [-128,4,-123,-9], + "silence": [-128,-118,107,-117], + "unknown": [-128,-125,-128,125], +} +MAX_PREDICTION_DIFFERENCE = 2 +@pytest.mark.requires_hardware +def test_project_inference_correctness(serial_output): + predictions = {line[0]: line[2:] for line in serial_output} + + for sample, prediction in predictions.items(): + # Due to rounding issues, we don't get the *exact* same + # values as Tensorflow gives, but they're pretty close + + reference_prediction = TENSORFLOW_EVALUATIONS[sample] + deltas = [prediction[i] - reference_prediction[i] for i in range(4)] + assert max(deltas) < MAX_PREDICTION_DIFFERENCE + + +MAX_INFERENCE_TIME_US = 200 * 1000 +MAX_INFERENCE_TIME_RANGE_US = 1000 +@pytest.mark.requires_hardware +def test_project_inference_runtime(serial_output): + runtimes_us = [line[1] for line in serial_output] + + # Inference time will vary based on architecture + # and clock speed. However, anything more than 200 ms + # is way too long. Each inference takes ~60 ms on the + # Sony spresense, running at 156 MHz + assert max(runtimes_us) < MAX_INFERENCE_TIME_US + + # Clock speeds should be consistent for each input. On + # the Sony spresense, they vary by <100 us. + range_runtimes_us = max(runtimes_us) - min(runtimes_us) + assert range_runtimes_us < MAX_INFERENCE_TIME_RANGE_US if __name__ == "__main__": From e7b829f19d142ebcb64558a916d31d594cffd11d Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 16:26:51 -0700 Subject: [PATCH 34/47] Remove unused pytest mark --- tests/micro/arduino/test_arduino_workflow.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 976a53a2eb36..b2c867fb9587 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -211,7 +211,6 @@ def serial_output(uploaded_project): "unknown": [-128,-125,-128,125], } MAX_PREDICTION_DIFFERENCE = 2 -@pytest.mark.requires_hardware def test_project_inference_correctness(serial_output): predictions = {line[0]: line[2:] for line in serial_output} @@ -226,7 +225,6 @@ def test_project_inference_correctness(serial_output): MAX_INFERENCE_TIME_US = 200 * 1000 MAX_INFERENCE_TIME_RANGE_US = 1000 -@pytest.mark.requires_hardware def test_project_inference_runtime(serial_output): runtimes_us = [line[1] for line in serial_output] From 7e6741d4990a80edfc7157df428046a1f9866c09 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 16:38:48 -0700 Subject: [PATCH 35/47] Linting --- .../template_project/microtvm_api_server.py | 94 +- .../template_project/src/implementation.c | 12 +- .../arduino/template_project/src/model.h | 18 +- include/tvm/runtime/crt/packed_func.h | 4 +- .../crt/graph_executor/graph_executor.c | 2 +- tests/micro/arduino/conftest.py | 12 +- tests/micro/arduino/test_arduino_workflow.py | 79 +- tests/micro/arduino/testdata/no.c | 2084 +---------------- tests/micro/arduino/testdata/silence.c | 2084 +---------------- tests/micro/arduino/testdata/unknown.c | 2084 +---------------- tests/micro/arduino/testdata/yes.c | 2084 +---------------- 11 files changed, 603 insertions(+), 7954 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 92968e020ac5..dda1bc062d8f 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -28,12 +28,15 @@ MODEL_LIBRARY_FORMAT_PATH = "" if IS_TEMPLATE else API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH _LOG = logging.getLogger(__name__) + class InvalidPortException(Exception): """Raised when the given port could not be opened""" + class SketchUploadException(Exception): """Raised when a sketch cannot be uploaded for an unknown reason.""" + class BoardAutodetectFailed(Exception): """Raised when no attached hardware is found matching the requested board""" @@ -42,7 +45,7 @@ class BoardAutodetectFailed(Exception): server.ProjectOption("verbose", help="Run build with verbose output"), server.ProjectOption("arduino_cmd", help="Path to the arduino-cli tool."), server.ProjectOption("arduino_board", help="Name of the Arduino board to build for"), - server.ProjectOption("port", help="Port to use for connecting to hardware") + server.ProjectOption("port", help="Port to use for connecting to hardware"), ] BOARD_PROPERTIES = { @@ -55,7 +58,7 @@ class BoardAutodetectFailed(Exception): "package": "arduino", "architecture": "mbed_nano", "board": "nano33ble", - } + }, } @@ -75,6 +78,7 @@ def server_info_query(self): ) CRT_COPY_ITEMS = ("include", "src") + def _copy_standalone_crt(self, source_dir, standalone_crt_dir): # Copy over the standalone_crt directory output_crt_dir = source_dir / "standalone_crt" @@ -95,12 +99,13 @@ def _copy_standalone_crt(self, source_dir, standalone_crt_dir): "src/runtime/crt/microtvm_rpc_server", "src/runtime/crt/tab", ] + def _remove_unused_components(self, source_dir): for component in self.UNUSED_COMPONENTS: shutil.rmtree(source_dir / "standalone_crt" / component) - GRAPH_JSON_TEMPLATE = 'static const char* graph_json = "{}";\n' + def _compile_graph_json(self, model_dir, obj): graph_json = json.dumps(obj).replace('"', '\\"') output = self.GRAPH_JSON_TEMPLATE.format(graph_json) @@ -108,10 +113,9 @@ def _compile_graph_json(self, model_dir, obj): with open(graph_json_path, "w") as out_file: out_file.write(output) - def _disassemble_mlf(self, mlf_tar_path, source_dir): mlf_unpacking_dir = tempfile.TemporaryDirectory() - with tarfile.open(mlf_tar_path, 'r:') as tar: + with tarfile.open(mlf_tar_path, "r:") as tar: tar.extractall(mlf_unpacking_dir.name) # Copy C files @@ -119,19 +123,13 @@ def _disassemble_mlf(self, mlf_tar_path, source_dir): model_dir = source_dir / "model" model_dir.mkdir() for source, dest in [ - ("codegen/host/src/default_lib0.c", "default_lib0.c"), - ("codegen/host/src/default_lib1.c", "default_lib1.c"), - ]: - shutil.copy( - os.path.join(mlf_unpacking_dir.name, source), - model_dir / dest - ) + ("codegen/host/src/default_lib0.c", "default_lib0.c"), + ("codegen/host/src/default_lib1.c", "default_lib1.c"), + ]: + shutil.copy(os.path.join(mlf_unpacking_dir.name, source), model_dir / dest) # Load graph.json, serialize to c format, and extact parameters - with open( - os.path.join(mlf_unpacking_dir.name, - "runtime-config/graph/graph.json") - ) as f: + with open(os.path.join(mlf_unpacking_dir.name, "runtime-config/graph/graph.json")) as f: graph_data = json.load(f) self._compile_graph_json(model_dir, graph_data) @@ -142,11 +140,9 @@ def _print_c_array(self, l): c_arr_str = str(l) return "{" + c_arr_str[1:-1] + "}" - def _print_c_str(self, s): return '"{}"'.format(s) - DL_DATA_TYPE_REFERENCE = { "uint8": "{kDLUInt, 8, 0}", "uint16": "{kDLUInt, 16, 0}", @@ -160,11 +156,12 @@ def _print_c_str(self, s): "float32": "{kDLFloat, 32, 0}", "float64": "{kDLFloat, 64, 0}", } + def _populate_parameters_file(self, graph, source_dir): graph_types = graph["attrs"]["dltype"] graph_shapes = graph["attrs"]["shape"] - assert(graph_types[0] == "list_str") - assert(graph_shapes[0] == "list_shape") + assert graph_types[0] == "list_str" + assert graph_shapes[0] == "list_shape" template_values = { "input_data_dimension": len(graph_shapes[1][0]), @@ -177,7 +174,7 @@ def _populate_parameters_file(self, graph, source_dir): } # Apply template values - with open(source_dir / "parameters.h", 'r') as f: + with open(source_dir / "parameters.h", "r") as f: template_params = Template(f.read()) parameters_h = template_params.substitute(template_values) @@ -185,11 +182,8 @@ def _populate_parameters_file(self, graph, source_dir): with open(source_dir / "parameters.h", "w") as f: f.write(parameters_h) + POSSIBLE_BASE_PATHS = ["src/standalone_crt/include/", "src/standalone_crt/crt_config/"] - POSSIBLE_BASE_PATHS = [ - "src/standalone_crt/include/", - "src/standalone_crt/crt_config/" - ] def _find_modified_include_path(self, project_dir, file_path, import_path): # If the import already works, don't modify it @@ -231,7 +225,6 @@ def _convert_imports(self, project_dir, source_dir): with filename.open("w") as file: file.writelines(lines) - def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): # Copy template folder to project_dir, creating project/ and src/ # directories in the process. Also copies this file, microtvm_api_server.py, @@ -257,20 +250,22 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Recursively change imports self._convert_imports(project_dir, source_dir) - def _get_fqbn(self, options): - o = BOARD_PROPERTIES[options['arduino_board']] + o = BOARD_PROPERTIES[options["arduino_board"]] return f"{o['package']}:{o['architecture']}:{o['board']}" - def build(self, options): BUILD_DIR.mkdir() print(BUILD_DIR) compile_cmd = [ - options['arduino_cmd'], "compile", "./project/", - "--fqbn", self._get_fqbn(options), - "--build-path", BUILD_DIR.resolve() + options["arduino_cmd"], + "compile", + "./project/", + "--fqbn", + self._get_fqbn(options), + "--build-path", + BUILD_DIR.resolve(), ] if options.get("verbose"): @@ -278,18 +273,19 @@ def build(self, options): # Specify project to compile output = subprocess.check_call(compile_cmd) - assert(output == 0) + assert output == 0 # We run the command `arduino-cli board list`, which produces # outputs of the form: - ''' + """ Port Type Board Name FQBN Core /dev/ttyS4 Serial Port Unknown /dev/ttyUSB0 Serial Port (USB) Spresense SPRESENSE:spresense:spresense SPRESENSE:spresense - ''' + """ + def _auto_detect_port(self, options): - list_cmd = [options['arduino_cmd'], "board", "list"] - list_cmd_output = subprocess.check_output(list_cmd).decode('utf-8') + list_cmd = [options["arduino_cmd"], "board", "list"] + list_cmd_output = subprocess.check_output(list_cmd).decode("utf-8") # Remove header and new lines at bottom port_options = list_cmd_output.split("\n")[1:-2] @@ -302,29 +298,31 @@ def _auto_detect_port(self, options): # If no compatible boards, raise an error raise BoardAutodetectFailed - def _get_arduino_port(self, options): if not self._port: - if 'port' in options and options['port']: - self._port = options['port'] + if "port" in options and options["port"]: + self._port = options["port"] else: self._port = self._auto_detect_port(options) return self._port - def _get_baudrate(self, options): return 115200 - def flash(self, options): port = self._get_arduino_port(options) upload_cmd = [ - options["arduino_cmd"], "upload", "./project", - "--fqbn", self._get_fqbn(options), - "--input-dir", BUILD_DIR.resolve(), - "--port", port, + options["arduino_cmd"], + "upload", + "./project", + "--fqbn", + self._get_fqbn(options), + "--input-dir", + BUILD_DIR.resolve(), + "--port", + port, ] output = subprocess.check_call(upload_cmd) @@ -333,7 +331,6 @@ def flash(self, options): elif output > 0: raise SketchUploadException - def open_transport(self, options): # Zephyr example doesn't throw an error in this case if self._serial is not None: @@ -348,14 +345,12 @@ def open_transport(self, options): session_established_timeout_sec=5.0, ) - def close_transport(self): if self._serial is None: return self._serial.close() self._serial = None - def read_transport(self, n, timeout_sec): # It's hard to set timeout_sec, so we just throw it away # TODO fix this @@ -363,7 +358,6 @@ def read_transport(self, n, timeout_sec): raise server.TransportClosedError() return self._serial.read(n) - def write_transport(self, data, timeout_sec): if self._serial is None: raise server.TransportClosedError() diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c index 60c9e7cd14b2..a28d370b5984 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -1,11 +1,11 @@ #ifndef IMPLEMENTATION #define IMPLEMENTATION -#include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" +#include "Arduino.h" #include "standalone_crt/include/tvm/runtime/c_runtime_api.h" +#include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" #include "standalone_crt/include/tvm/runtime/crt/logging.h" #include "standalone_crt/include/tvm/runtime/crt/stack_allocator.h" -#include "Arduino.h" size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, va_list args) { @@ -24,7 +24,7 @@ void TVMLogf(const char* msg, ...) { // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { - //pinMode(LED_BUILTIN, OUTPUT); + // pinMode(LED_BUILTIN, OUTPUT); for (;;) { digitalWrite(LED_BUILTIN, HIGH); delay(250); @@ -75,15 +75,15 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { } g_utvm_timer_running = 0; unsigned long g_utvm_stop_time = micros() - g_utvm_start_time; - *elapsed_time_seconds = ((double) g_utvm_stop_time) / 1e6; + *elapsed_time_seconds = ((double)g_utvm_stop_time) / 1e6; return kTvmErrorNoError; } unsigned int random_seed = 0; tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { for (size_t i = 0; i < num_bytes; ++i) { - buffer[i] = (uint8_t)4; // Chosen by fair die roll - // Guaranteed to be random + buffer[i] = (uint8_t)4; // Chosen by fair die roll + // Guaranteed to be random } return kTvmErrorNoError; } diff --git a/apps/microtvm/arduino/template_project/src/model.h b/apps/microtvm/arduino/template_project/src/model.h index a2ebc1047061..61f2cfbe994c 100644 --- a/apps/microtvm/arduino/template_project/src/model.h +++ b/apps/microtvm/arduino/template_project/src/model.h @@ -3,18 +3,14 @@ #include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" +class Model { + public: + Model(); + void inference(void* input_data, void* output_data); + int infer_category(void* input_data); -class Model -{ - - public: - Model(); - void inference(void *input_data, void *output_data); - int infer_category(void *input_data); - - private: - TVMGraphExecutor* graph_runtime; + private: + TVMGraphExecutor* graph_runtime; }; #endif - diff --git a/include/tvm/runtime/crt/packed_func.h b/include/tvm/runtime/crt/packed_func.h index 20f27eb3093c..83d961baf203 100644 --- a/include/tvm/runtime/crt/packed_func.h +++ b/include/tvm/runtime/crt/packed_func.h @@ -62,11 +62,11 @@ void TVMPackedFunc_SetArgs(TVMPackedFunc* pf, const TVMArgs* args); inline TVMModuleHandle TVMArgs_AsModuleHandle(const TVMArgs* args, size_t index) { if (index >= args->values_count) { - TVMPlatformAbort((tvm_crt_error_t) -1); + TVMPlatformAbort((tvm_crt_error_t)-1); } if (args->tcodes[index] != kTVMModuleHandle) { - TVMPlatformAbort((tvm_crt_error_t) -1); + TVMPlatformAbort((tvm_crt_error_t)-1); } return args->values[index].v_handle; diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index 74bae9251017..9bd14949805d 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -102,7 +102,7 @@ void TVMGraphExecutorNode_LoadAttrs(TVMGraphExecutorNode* node, JSONReader* read bitmask |= 8; } else { // TODO determine if suppressing these warnings is OK - //fprintf(stderr, "do not support key %s", key); + // fprintf(stderr, "do not support key %s", key); } } if (bitmask != (1 | 2 | 4 | 8)) { diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index bc820029ee07..ff1c92f739a5 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -9,6 +9,7 @@ "nano33ble": ("nRF52840", "nano33ble"), } + def pytest_addoption(parser): parser.addoption( "--platform", @@ -17,21 +18,28 @@ def pytest_addoption(parser): help="Target platform for microTVM tests.", ) parser.addoption( - "--arduino-cmd", default="arduino-cli", help="Path to `arduino-cli` command for flashing device." + "--arduino-cmd", + default="arduino-cli", + help="Path to `arduino-cli` command for flashing device.", ) parser.addoption( - "--run-hardware-tests", action="store_true", help="Run tests that require physical hardware." + "--run-hardware-tests", + action="store_true", + help="Run tests that require physical hardware.", ) + # TODO re-add parameterization @pytest.fixture(scope="session") def platform(request): return request.config.getoption("--platform") + @pytest.fixture(scope="session") def arduino_cmd(request): return request.config.getoption("--arduino-cmd") + @pytest.fixture(scope="session") def run_hardware_tests(request): return request.config.getoption("--run-hardware-tests") diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index b2c867fb9587..afd50065a10b 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -11,7 +11,7 @@ import conftest -''' +""" This unit test simulates a simple user workflow, where we: 1. Generate a base sketch using a simple audio model 2. Modify the .ino file, much like a user would @@ -20,14 +20,17 @@ 4. Upload the sketch to a connected board 5. Open a serial connection to the board 6. Use serial connection to ensure model behaves correctly -''' +""" PLATFORMS = conftest.PLATFORMS + def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_config): parent_dir = os.path.dirname(__file__) filename = os.path.splitext(os.path.basename(__file__))[0] - prev_build = f"{os.path.join(parent_dir, 'archive')}_{filename}_{arduino_board}_last_build.micro" + prev_build = ( + f"{os.path.join(parent_dir, 'archive')}_{filename}_{arduino_board}_last_build.micro" + ) workspace_root = os.path.join( f"{os.path.join(parent_dir, 'workspace')}_{filename}_{arduino_board}", datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S"), @@ -59,11 +62,12 @@ def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_conf # This is bad, don't do this TARGET = "c -keys=cpu -link-params=1 -mcpu=cortex-m33 -model=nrf5340dk -runtime=c -system-lib=1" + @pytest.fixture(scope="module") def yes_no_project(platform, arduino_cmd): current_dir = os.path.dirname(__file__) model, arduino_board = PLATFORMS[platform] - #target = tvm.target.target.micro(model, options=["-link-params=1"]) + # target = tvm.target.target.micro(model, options=["-link-params=1"]) build_config = {} with open(f"{current_dir}/testdata/yes_no.tflite", "rb") as f: @@ -74,7 +78,7 @@ def yes_no_project(platform, arduino_cmd): mod = relay.build(mod, TARGET, params=params) return _generate_project(model, TARGET, arduino_board, arduino_cmd, mod, build_config) - #return tvm.micro.Session(project.transport()) + # return tvm.micro.Session(project.transport()) @pytest.fixture(scope="module") @@ -90,25 +94,22 @@ def project_dir(yes_no_project): def test_project_folder_structure(project_dir): - assert(set([ - 'microtvm_api_server.py', 'project.ino', 'src'] - ).issubset(os.listdir(project_dir))) + assert set(["microtvm_api_server.py", "project.ino", "src"]).issubset(os.listdir(project_dir)) source_dir = project_dir / "src" - assert(set(os.listdir(source_dir)) == set([ - 'model', 'standalone_crt', 'implementation.c', - 'model.cpp', 'model.h', 'parameters.h' - ])) + assert set(os.listdir(source_dir)) == set( + ["model", "standalone_crt", "implementation.c", "model.cpp", "model.h", "parameters.h"] + ) def test_project_model_integrity(project_dir): model_dir = project_dir / "src" / "model" - assert(set(os.listdir(model_dir)) == set([ - 'default_lib0.c', 'default_lib1.c', 'graph_json.c', 'model.tar' - ])) + assert set(os.listdir(model_dir)) == set( + ["default_lib0.c", "default_lib1.c", "graph_json.c", "model.tar"] + ) with (model_dir / "graph_json.c").open() as f: graph_json_c = f.read() - assert("static const char* graph_json" in graph_json_c) + assert "static const char* graph_json" in graph_json_c def test_parameter_header_templating(project_dir): @@ -116,19 +117,19 @@ def test_parameter_header_templating(project_dir): # for our yes/no model with (project_dir / "src" / "parameters.h").open() as f: parameters_h = f.read() - assert("INPUT_DATA_SHAPE[] = {1, 1960};" in parameters_h) + assert "INPUT_DATA_SHAPE[] = {1, 1960};" in parameters_h def test_import_rerouting(project_dir): # Check one file to ensure imports were rerouted runtime_c_path = project_dir / "src" / "standalone_crt" / "src" / "runtime" load_json_path = runtime_c_path / "crt" / "graph_executor" / "load_json.c" - assert(load_json_path.exists()) + assert load_json_path.exists() with (load_json_path).open() as f: load_json_c = f.read() - assert('#include "stdlib.h"' in load_json_c) - assert('include/tvm/runtime/crt/platform.h' in load_json_c) + assert '#include "stdlib.h"' in load_json_c + assert "include/tvm/runtime/crt/platform.h" in load_json_c # Build on top of the generated project by replacing the @@ -156,14 +157,14 @@ def compiled_project(modified_project): def test_compile_yes_no_project(project_dir, project, compiled_project): build_dir = project_dir / "build" - assert(build_dir.exists()) + assert build_dir.exists() first_build_file = next(build_dir.iterdir(), None) - assert(first_build_file is not None) + assert first_build_file is not None -'''------------------------------------------------------------ +"""------------------------------------------------------------ If we're not running on real hardware, no further tests are run -------------------------------------------------------------''' +------------------------------------------------------------""" @pytest.fixture(scope="module") @@ -175,42 +176,42 @@ def uploaded_project(compiled_project, run_hardware_tests): return compiled_project -''' Sample serial output: +""" Sample serial output: category,runtime,yes,no,silence,unknown yes,56762,115,-123,-125,-123, no,56762,-128,4,-123,-9, silence,56792,-128,-118,107,-117, unknown,56792,-128,-125,-128,125, -''' +""" SERIAL_OUTPUT_HEADERS = "category,runtime,yes,no,silence,unknown" + + @pytest.fixture(scope="module") def serial_output(uploaded_project): transport = uploaded_project.transport() transport.open() out = transport.read(2048, -1) - out_str = out.decode('utf-8') + out_str = out.decode("utf-8") out_lines = out_str.split("\r\n") - assert(SERIAL_OUTPUT_HEADERS in out_lines) + assert SERIAL_OUTPUT_HEADERS in out_lines headers_index = out_lines.index(SERIAL_OUTPUT_HEADERS) - data_lines = out_lines[headers_index + 1:headers_index + 5] + data_lines = out_lines[headers_index + 1 : headers_index + 5] split_lines = [line.split(",") for line in data_lines] - return [ - [line[0]] + - list(map(int, line[1:6])) - for line in split_lines - ] + return [[line[0]] + list(map(int, line[1:6])) for line in split_lines] TENSORFLOW_EVALUATIONS = { - "yes": [115,-123,-125,-123], - "no": [-128,4,-123,-9], - "silence": [-128,-118,107,-117], - "unknown": [-128,-125,-128,125], + "yes": [115, -123, -125, -123], + "no": [-128, 4, -123, -9], + "silence": [-128, -118, 107, -117], + "unknown": [-128, -125, -128, 125], } MAX_PREDICTION_DIFFERENCE = 2 + + def test_project_inference_correctness(serial_output): predictions = {line[0]: line[2:] for line in serial_output} @@ -225,6 +226,8 @@ def test_project_inference_correctness(serial_output): MAX_INFERENCE_TIME_US = 200 * 1000 MAX_INFERENCE_TIME_RANGE_US = 1000 + + def test_project_inference_runtime(serial_output): runtimes_us = [line[1] for line in serial_output] diff --git a/tests/micro/arduino/testdata/no.c b/tests/micro/arduino/testdata/no.c index 0bce1b37c373..ee2f07a8d99a 100644 --- a/tests/micro/arduino/testdata/no.c +++ b/tests/micro/arduino/testdata/no.c @@ -1,1962 +1,124 @@ static const char input_no[1960] = { - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xcf, - 0xe4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xdb, - 0xe4, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x2f, - 0x1e, - 0x7, - 0xe4, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x52, - 0x41, - 0x4b, - 0x3a, - 0x20, - 0xf6, - 0xcf, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x62, - 0x53, - 0x5d, - 0x51, - 0x4a, - 0xf9, - 0xe4, - 0xb4, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xc5, - 0x80, - 0xcf, - 0x80, - 0x41, - 0x49, - 0x6a, - 0x5d, - 0x75, - 0x62, - 0x75, - 0x63, - 0x7a, - 0x65, - 0x7b, - 0x64, - 0x78, - 0x62, - 0x75, - 0x5d, - 0x71, - 0x5b, - 0x37, - 0xd, - 0x3, - 0xf6, - 0xec, - 0xd6, - 0x32, - 0x2a, - 0x1a, - 0xf6, - 0x42, - 0x4b, - 0x3f, - 0xe0, - 0xe4, - 0xcf, - 0xf3, - 0xef, - 0xf3, - 0xfb, - 0x3, - 0x0, - 0x6d, - 0x56, - 0x6e, - 0x57, - 0x69, - 0x55, - 0x72, - 0x5d, - 0x66, - 0x52, - 0x6e, - 0x5d, - 0x6f, - 0x46, - 0x64, - 0x52, - 0x62, - 0x42, - 0x4e, - 0x29, - 0x32, - 0xe, - 0x25, - 0x35, - 0x56, - 0x49, - 0x4d, - 0x42, - 0x5d, - 0x57, - 0x61, - 0x34, - 0x1c, - 0x5, - 0x20, - 0x17, - 0x17, - 0x17, - 0x24, - 0x20, - 0x76, - 0x65, - 0x7a, - 0x63, - 0x7b, - 0x65, - 0x7b, - 0x5d, - 0x70, - 0x53, - 0x73, - 0x61, - 0x70, - 0x53, - 0x66, - 0x57, - 0x63, - 0x52, - 0x5c, - 0x3a, - 0x54, - 0x4d, - 0x6b, - 0x5f, - 0x78, - 0x66, - 0x7a, - 0x64, - 0x7b, - 0x64, - 0x75, - 0x56, - 0x5a, - 0x46, - 0x4b, - 0x3d, - 0x46, - 0x3e, - 0x4e, - 0x3f, - 0x68, - 0x58, - 0x6e, - 0x57, - 0x6d, - 0x5f, - 0x76, - 0x5a, - 0x6e, - 0x57, - 0x75, - 0x5d, - 0x67, - 0x53, - 0x68, - 0x50, - 0x67, - 0x53, - 0x6c, - 0x59, - 0x68, - 0x5a, - 0x6a, - 0x53, - 0x65, - 0x5a, - 0x74, - 0x56, - 0x6d, - 0x5c, - 0x6b, - 0x4a, - 0x50, - 0x46, - 0x58, - 0x48, - 0x66, - 0x56, - 0x59, - 0x46, - 0x5e, - 0x43, - 0x61, - 0x44, - 0x61, - 0x50, - 0x6e, - 0x55, - 0x67, - 0x5a, - 0x63, - 0x4e, - 0x5f, - 0x3b, - 0x63, - 0x52, - 0x5e, - 0x4e, - 0x67, - 0x4d, - 0x62, - 0x51, - 0x6a, - 0x4e, - 0x62, - 0x48, - 0x69, - 0x55, - 0x66, - 0x50, - 0x62, - 0x50, - 0x59, - 0x40, - 0x4c, - 0x41, - 0x6c, - 0x55, - 0x5a, - 0x3f, - 0x58, - 0x3c, - 0x5b, - 0x28, - 0x50, - 0x3d, - 0x62, - 0x4b, - 0x5b, - 0x55, - 0x62, - 0x43, - 0x5d, - 0x3c, - 0x50, - 0x37, - 0x55, - 0x2d, - 0x55, - 0x49, - 0x59, - 0x48, - 0x53, - 0x3e, - 0x53, - 0x46, - 0x64, - 0x53, - 0x61, - 0x3f, - 0x5e, - 0x2e, - 0x4d, - 0x39, - 0x4e, - 0x41, - 0x61, - 0x4a, - 0x53, - 0x36, - 0x52, - 0x35, - 0x55, - 0x2a, - 0x4f, - 0x3a, - 0x5a, - 0x3e, - 0x55, - 0x4f, - 0x5e, - 0x37, - 0x4d, - 0x34, - 0x4c, - 0x37, - 0x4e, - 0x28, - 0x50, - 0x36, - 0x53, - 0x39, - 0x49, - 0x2b, - 0x4f, - 0x39, - 0x5c, - 0x47, - 0x51, - 0x35, - 0x5d, - 0x1b, - 0x3f, - 0x2b, - 0x46, - 0x3b, - 0x5d, - 0x44, - 0x5a, - 0x35, - 0x4d, - 0x35, - 0x4e, - 0x30, - 0x4b, - 0x3f, - 0x57, - 0x35, - 0x59, - 0x3f, - 0x45, - 0xd, - 0x2b, - 0x4, - 0x45, - 0x26, - 0x48, - 0x36, - 0x47, - 0x26, - 0x44, - 0x39, - 0x50, - 0x2e, - 0x46, - 0x2f, - 0x55, - 0x43, - 0x4c, - 0x23, - 0x52, - 0x2f, - 0x3f, - 0x25, - 0x43, - 0x2d, - 0x3b, - 0xf9, - 0x4d, - 0x29, - 0x44, - 0x1b, - 0x35, - 0x38, - 0x48, - 0x3a, - 0x46, - 0x3c, - 0x5d, - 0x29, - 0x43, - 0x5, - 0x4a, - 0xd, - 0x26, - 0xb4, - 0x28, - 0xcf, - 0x3c, - 0x13, - 0x25, - 0x2, - 0x32, - 0xf9, - 0x2f, - 0x1e, - 0x4d, - 0x19, - 0x3a, - 0x2, - 0x3c, - 0x7, - 0x3c, - 0x12, - 0x3c, - 0x10, - 0xdb, - 0x80, - 0x37, - 0x24, - 0x42, - 0x21, - 0x3a, - 0x30, - 0x4a, - 0x28, - 0x32, - 0x31, - 0x48, - 0xe7, - 0x2d, - 0x80, - 0x19, - 0xf9, - 0x2d, - 0xf3, - 0x32, - 0x2, - 0x24, - 0xb4, - 0x14, - 0x80, - 0x22, - 0xb4, - 0x35, - 0x3, - 0x40, - 0xf, - 0x30, - 0x80, - 0x26, - 0x80, - 0x26, - 0xcf, - 0x21, - 0x80, - 0x80, - 0x80, - 0xf5, - 0xef, - 0x28, - 0x80, - 0x4b, - 0x34, - 0x3c, - 0xdb, - 0x34, - 0x12, - 0x44, - 0xe0, - 0x26, - 0x80, - 0x1d, - 0x80, - 0xd6, - 0x80, - 0x21, - 0xe4, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xf6, - 0x11, - 0x2b, - 0xff, - 0x3e, - 0x16, - 0x1f, - 0x80, - 0x21, - 0xf6, - 0x14, - 0xd6, - 0x27, - 0xcf, - 0x80, - 0x80, - 0x0, - 0xec, - 0x48, - 0xd6, - 0x3b, - 0x0, - 0x36, - 0x1d, - 0x28, - 0xcf, - 0x2d, - 0xef, - 0x25, - 0x80, - 0xcf, - 0x80, - 0xf5, - 0x80, - 0xa, - 0x80, - 0x11, - 0x80, - 0x80, - 0x80, - 0xf8, - 0xe4, - 0x10, - 0xea, - 0x2a, - 0xf1, - 0x21, - 0x80, - 0xcf, - 0x80, - 0x3, - 0xe7, - 0x1a, - 0xb4, - 0x80, - 0x80, - 0xe0, - 0xdb, - 0x31, - 0xe0, - 0x32, - 0xc, - 0x30, - 0x80, - 0x0, - 0xc5, - 0x34, - 0x80, - 0x2, - 0x80, - 0xf1, - 0x80, - 0xcf, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x2, - 0x80, - 0x14, - 0x80, - 0xd6, - 0x80, - 0x80, - 0x80, - 0xfb, - 0xdb, - 0x8, - 0x80, - 0x80, - 0x80, - 0xe4, - 0xe7, - 0x28, - 0xc5, - 0x1e, - 0xdb, - 0x2a, - 0xb4, - 0x80, - 0x80, - 0x30, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf8, - 0xb4, - 0x17, - 0x80, - 0xcf, - 0x80, - 0x80, - 0x80, - 0x0, - 0xcf, - 0x12, - 0x80, - 0x80, - 0x80, - 0xdb, - 0xb4, - 0xe4, - 0x80, - 0x21, - 0xb4, - 0x2a, - 0x80, - 0x80, - 0x80, - 0x13, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf3, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xfd, - 0x80, - 0x80, - 0x80, - 0xe0, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xe4, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80 -}; + 0x80, 0x80, 0x80, 0xc5, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc5, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc5, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb4, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xcf, 0xe4, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0xdb, 0xe4, 0xc5, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x2f, 0x1e, 0x7, 0xe4, 0xc5, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x52, 0x41, 0x4b, 0x3a, 0x20, 0xf6, 0xcf, 0xb4, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0xb4, 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xc5, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0x80, 0x80, + 0x62, 0x53, 0x5d, 0x51, 0x4a, 0xf9, 0xe4, 0xb4, 0xc5, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xc5, 0x80, 0x80, 0x80, 0xc5, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0xb4, 0xc5, 0x80, 0xcf, 0x80, 0x41, 0x49, 0x6a, 0x5d, 0x75, 0x62, 0x75, 0x63, + 0x7a, 0x65, 0x7b, 0x64, 0x78, 0x62, 0x75, 0x5d, 0x71, 0x5b, 0x37, 0xd, 0x3, 0xf6, 0xec, 0xd6, + 0x32, 0x2a, 0x1a, 0xf6, 0x42, 0x4b, 0x3f, 0xe0, 0xe4, 0xcf, 0xf3, 0xef, 0xf3, 0xfb, 0x3, 0x0, + 0x6d, 0x56, 0x6e, 0x57, 0x69, 0x55, 0x72, 0x5d, 0x66, 0x52, 0x6e, 0x5d, 0x6f, 0x46, 0x64, 0x52, + 0x62, 0x42, 0x4e, 0x29, 0x32, 0xe, 0x25, 0x35, 0x56, 0x49, 0x4d, 0x42, 0x5d, 0x57, 0x61, 0x34, + 0x1c, 0x5, 0x20, 0x17, 0x17, 0x17, 0x24, 0x20, 0x76, 0x65, 0x7a, 0x63, 0x7b, 0x65, 0x7b, 0x5d, + 0x70, 0x53, 0x73, 0x61, 0x70, 0x53, 0x66, 0x57, 0x63, 0x52, 0x5c, 0x3a, 0x54, 0x4d, 0x6b, 0x5f, + 0x78, 0x66, 0x7a, 0x64, 0x7b, 0x64, 0x75, 0x56, 0x5a, 0x46, 0x4b, 0x3d, 0x46, 0x3e, 0x4e, 0x3f, + 0x68, 0x58, 0x6e, 0x57, 0x6d, 0x5f, 0x76, 0x5a, 0x6e, 0x57, 0x75, 0x5d, 0x67, 0x53, 0x68, 0x50, + 0x67, 0x53, 0x6c, 0x59, 0x68, 0x5a, 0x6a, 0x53, 0x65, 0x5a, 0x74, 0x56, 0x6d, 0x5c, 0x6b, 0x4a, + 0x50, 0x46, 0x58, 0x48, 0x66, 0x56, 0x59, 0x46, 0x5e, 0x43, 0x61, 0x44, 0x61, 0x50, 0x6e, 0x55, + 0x67, 0x5a, 0x63, 0x4e, 0x5f, 0x3b, 0x63, 0x52, 0x5e, 0x4e, 0x67, 0x4d, 0x62, 0x51, 0x6a, 0x4e, + 0x62, 0x48, 0x69, 0x55, 0x66, 0x50, 0x62, 0x50, 0x59, 0x40, 0x4c, 0x41, 0x6c, 0x55, 0x5a, 0x3f, + 0x58, 0x3c, 0x5b, 0x28, 0x50, 0x3d, 0x62, 0x4b, 0x5b, 0x55, 0x62, 0x43, 0x5d, 0x3c, 0x50, 0x37, + 0x55, 0x2d, 0x55, 0x49, 0x59, 0x48, 0x53, 0x3e, 0x53, 0x46, 0x64, 0x53, 0x61, 0x3f, 0x5e, 0x2e, + 0x4d, 0x39, 0x4e, 0x41, 0x61, 0x4a, 0x53, 0x36, 0x52, 0x35, 0x55, 0x2a, 0x4f, 0x3a, 0x5a, 0x3e, + 0x55, 0x4f, 0x5e, 0x37, 0x4d, 0x34, 0x4c, 0x37, 0x4e, 0x28, 0x50, 0x36, 0x53, 0x39, 0x49, 0x2b, + 0x4f, 0x39, 0x5c, 0x47, 0x51, 0x35, 0x5d, 0x1b, 0x3f, 0x2b, 0x46, 0x3b, 0x5d, 0x44, 0x5a, 0x35, + 0x4d, 0x35, 0x4e, 0x30, 0x4b, 0x3f, 0x57, 0x35, 0x59, 0x3f, 0x45, 0xd, 0x2b, 0x4, 0x45, 0x26, + 0x48, 0x36, 0x47, 0x26, 0x44, 0x39, 0x50, 0x2e, 0x46, 0x2f, 0x55, 0x43, 0x4c, 0x23, 0x52, 0x2f, + 0x3f, 0x25, 0x43, 0x2d, 0x3b, 0xf9, 0x4d, 0x29, 0x44, 0x1b, 0x35, 0x38, 0x48, 0x3a, 0x46, 0x3c, + 0x5d, 0x29, 0x43, 0x5, 0x4a, 0xd, 0x26, 0xb4, 0x28, 0xcf, 0x3c, 0x13, 0x25, 0x2, 0x32, 0xf9, + 0x2f, 0x1e, 0x4d, 0x19, 0x3a, 0x2, 0x3c, 0x7, 0x3c, 0x12, 0x3c, 0x10, 0xdb, 0x80, 0x37, 0x24, + 0x42, 0x21, 0x3a, 0x30, 0x4a, 0x28, 0x32, 0x31, 0x48, 0xe7, 0x2d, 0x80, 0x19, 0xf9, 0x2d, 0xf3, + 0x32, 0x2, 0x24, 0xb4, 0x14, 0x80, 0x22, 0xb4, 0x35, 0x3, 0x40, 0xf, 0x30, 0x80, 0x26, 0x80, + 0x26, 0xcf, 0x21, 0x80, 0x80, 0x80, 0xf5, 0xef, 0x28, 0x80, 0x4b, 0x34, 0x3c, 0xdb, 0x34, 0x12, + 0x44, 0xe0, 0x26, 0x80, 0x1d, 0x80, 0xd6, 0x80, 0x21, 0xe4, 0x80, 0x80, 0xb4, 0x80, 0xf6, 0x11, + 0x2b, 0xff, 0x3e, 0x16, 0x1f, 0x80, 0x21, 0xf6, 0x14, 0xd6, 0x27, 0xcf, 0x80, 0x80, 0x0, 0xec, + 0x48, 0xd6, 0x3b, 0x0, 0x36, 0x1d, 0x28, 0xcf, 0x2d, 0xef, 0x25, 0x80, 0xcf, 0x80, 0xf5, 0x80, + 0xa, 0x80, 0x11, 0x80, 0x80, 0x80, 0xf8, 0xe4, 0x10, 0xea, 0x2a, 0xf1, 0x21, 0x80, 0xcf, 0x80, + 0x3, 0xe7, 0x1a, 0xb4, 0x80, 0x80, 0xe0, 0xdb, 0x31, 0xe0, 0x32, 0xc, 0x30, 0x80, 0x0, 0xc5, + 0x34, 0x80, 0x2, 0x80, 0xf1, 0x80, 0xcf, 0x80, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x2, 0x80, 0x14, 0x80, 0xd6, 0x80, 0x80, 0x80, 0xfb, 0xdb, 0x8, 0x80, 0x80, 0x80, 0xe4, 0xe7, + 0x28, 0xc5, 0x1e, 0xdb, 0x2a, 0xb4, 0x80, 0x80, 0x30, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf8, 0xb4, 0x17, 0x80, 0xcf, 0x80, 0x80, 0x80, + 0x0, 0xcf, 0x12, 0x80, 0x80, 0x80, 0xdb, 0xb4, 0xe4, 0x80, 0x21, 0xb4, 0x2a, 0x80, 0x80, 0x80, + 0x13, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xf3, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xfd, 0x80, 0x80, 0x80, 0xe0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe4, 0x80, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; diff --git a/tests/micro/arduino/testdata/silence.c b/tests/micro/arduino/testdata/silence.c index 478cb4d1bdb5..bf756399157a 100644 --- a/tests/micro/arduino/testdata/silence.c +++ b/tests/micro/arduino/testdata/silence.c @@ -1,1962 +1,124 @@ static const char input_silence[1960] = { - 0x23, - 0x17, - 0xe0, - 0x3, - 0x9, - 0xe7, - 0xe7, - 0xdb, - 0xcf, - 0xc5, - 0xe0, - 0xdb, - 0xc5, - 0xcf, - 0xef, - 0xcf, - 0xcf, - 0xdb, - 0xef, - 0xdb, - 0xe7, - 0xc5, - 0x5, - 0x3, - 0xfc, - 0xe7, - 0xf6, - 0xdb, - 0xcf, - 0xe7, - 0x9, - 0xef, - 0xef, - 0xdb, - 0xcf, - 0xe7, - 0xe0, - 0xe7, - 0xe0, - 0xc5, - 0xff, - 0xe0, - 0x4, - 0xcf, - 0xdb, - 0xb4, - 0x80, - 0xdb, - 0xef, - 0x80, - 0xc5, - 0xe4, - 0x9, - 0xe4, - 0xcf, - 0xc5, - 0xdb, - 0xcf, - 0xdb, - 0xcf, - 0xf5, - 0xdb, - 0xe7, - 0xcf, - 0xef, - 0xe4, - 0xe7, - 0xe4, - 0xe7, - 0xdb, - 0xdb, - 0xcf, - 0xc5, - 0xdb, - 0xcf, - 0xcf, - 0xcf, - 0xb4, - 0xcf, - 0xcf, - 0x13, - 0xef, - 0xf5, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xcf, - 0xcf, - 0x80, - 0x80, - 0xcf, - 0xf5, - 0xcf, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xcf, - 0xf9, - 0xdb, - 0xcf, - 0x80, - 0x80, - 0xcf, - 0xe7, - 0xdb, - 0xfb, - 0xe4, - 0xdb, - 0xcf, - 0xe7, - 0xcf, - 0xe7, - 0xb4, - 0xdb, - 0xe4, - 0xcf, - 0xb4, - 0xfb, - 0x0, - 0x6, - 0xd6, - 0xec, - 0xb4, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf3, - 0xb4, - 0xdb, - 0xdb, - 0xc5, - 0xb4, - 0xc5, - 0x80, - 0xcf, - 0xb4, - 0xdb, - 0xb4, - 0xb4, - 0x80, - 0xcf, - 0x80, - 0xdb, - 0xb4, - 0xb4, - 0x80, - 0xc5, - 0x80, - 0xdb, - 0xcf, - 0xdb, - 0xcf, - 0xcf, - 0xb4, - 0xff, - 0xcf, - 0xdb, - 0x80, - 0xb4, - 0x80, - 0x80, - 0xd6, - 0xcf, - 0xcf, - 0x80, - 0xcf, - 0xcf, - 0xcf, - 0xe4, - 0xcf, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xdb, - 0x80, - 0xb4, - 0x80, - 0xdb, - 0x80, - 0xb4, - 0x80, - 0xb4, - 0xb4, - 0xdb, - 0xcf, - 0xec, - 0xe0, - 0xcf, - 0xe0, - 0xe4, - 0xd6, - 0xdb, - 0x80, - 0xef, - 0xf6, - 0xea, - 0xd6, - 0xb4, - 0xd6, - 0xec, - 0xc5, - 0xec, - 0xcf, - 0xc5, - 0x80, - 0xdb, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xdb, - 0xcf, - 0xdb, - 0xd6, - 0xe4, - 0xc5, - 0xdb, - 0xb4, - 0xcf, - 0xc5, - 0xcf, - 0xd6, - 0xe4, - 0xc5, - 0xf3, - 0xe0, - 0xec, - 0xe0, - 0xfd, - 0xe7, - 0xcf, - 0xb4, - 0x24, - 0x1a, - 0x0, - 0xf1, - 0x19, - 0xe0, - 0xec, - 0xe0, - 0xb4, - 0xcf, - 0xdb, - 0xd6, - 0xb4, - 0xb4, - 0xb4, - 0x80, - 0xdb, - 0x80, - 0xdb, - 0xc5, - 0xf1, - 0xe7, - 0xea, - 0xf8, - 0xec, - 0xc5, - 0xe4, - 0xe0, - 0xec, - 0xc5, - 0xcf, - 0xb4, - 0xe4, - 0xd6, - 0xe4, - 0xdb, - 0xf1, - 0xdb, - 0xdb, - 0xc5, - 0x22, - 0xea, - 0xe7, - 0x80, - 0xea, - 0xf3, - 0xec, - 0xfb, - 0xec, - 0xe0, - 0xdb, - 0xb4, - 0xe4, - 0xe0, - 0xec, - 0xd6, - 0xf3, - 0xb4, - 0xb4, - 0x80, - 0xd6, - 0xd6, - 0xe4, - 0xdb, - 0xcf, - 0xb4, - 0xdb, - 0xdb, - 0xf1, - 0xe4, - 0xcf, - 0xb4, - 0xe4, - 0xcf, - 0xe4, - 0xea, - 0xea, - 0xe4, - 0xe4, - 0xd6, - 0xef, - 0xb4, - 0xc5, - 0xc5, - 0xd6, - 0xc5, - 0xe4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xcf, - 0xc5, - 0x0, - 0xdb, - 0xb4, - 0xb4, - 0xdb, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xc5, - 0xcf, - 0xb4, - 0xcf, - 0xcf, - 0xe0, - 0xcf, - 0xcf, - 0x80, - 0xb4, - 0x80, - 0xec, - 0xd6, - 0xe0, - 0xc5, - 0xb4, - 0xb4, - 0xcf, - 0x80, - 0xcf, - 0xb4, - 0xcf, - 0x80, - 0xd6, - 0xc5, - 0x80, - 0x80, - 0xdb, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xcf, - 0x80, - 0x80, - 0x80, - 0xcf, - 0xb4, - 0xd6, - 0xb4, - 0xd6, - 0xb4, - 0xf1, - 0xc5, - 0xc5, - 0x80, - 0xb4, - 0x80, - 0x11, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xcf, - 0xb4, - 0x80, - 0xe4, - 0xb4, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0xcf, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xb4, - 0xd6, - 0xc5, - 0xb4, - 0x80, - 0xc5, - 0x80, - 0xb4, - 0x80, - 0xcf, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xc5, - 0xe4, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xef, - 0x80, - 0xc5, - 0xb4, - 0xc5, - 0xc5, - 0xc5, - 0xcf, - 0xd6, - 0xc5, - 0xf5, - 0xb4, - 0xcf, - 0x80, - 0xe4, - 0xc5, - 0xb4, - 0xe0, - 0xd6, - 0xb4, - 0xcf, - 0x80, - 0xb4, - 0xc5, - 0xcf, - 0x80, - 0xe0, - 0xc5, - 0xd6, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xb4, - 0xc5, - 0x80, - 0xd6, - 0xb4, - 0xe0, - 0xb4, - 0xb4, - 0xc5, - 0xc5, - 0xb4, - 0xc5, - 0x80, - 0xc5, - 0xc5, - 0xd6, - 0x80, - 0x80, - 0x80, - 0xf8, - 0x80, - 0x80, - 0xb4, - 0xd6, - 0x80, - 0xd6, - 0xb4, - 0xb4, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xcf, - 0xcf, - 0xe7, - 0x80, - 0xb4, - 0x80, - 0xc5, - 0x80, - 0xc5, - 0x80, - 0xb4, - 0x80, - 0xb4, - 0xb4, - 0xc5, - 0x80, - 0xb4, - 0x80, - 0xc5, - 0x80, - 0xe0, - 0x80, - 0xef, - 0x80, - 0xcf, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xb4, - 0xfd, - 0xb4, - 0x80, - 0xb4, - 0xe0, - 0x80, - 0xcf, - 0xb4, - 0xb4, - 0x80, - 0xe7, - 0xb4, - 0xe7, - 0xb4, - 0xb4, - 0xd6, - 0xb4, - 0x80, - 0xe0, - 0xc5, - 0x80, - 0x80, - 0xc5, - 0xc5, - 0xd6, - 0x80, - 0xc5, - 0x80, - 0xdb, - 0xc5, - 0xea, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xb4, - 0x80, - 0xe0, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0xd6, - 0x80, - 0xb4, - 0x80, - 0xb4, - 0x80, - 0x80, - 0xb4, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xe7, - 0xb4, - 0xc5, - 0x80, - 0xd6, - 0x80, - 0xe7, - 0xc5, - 0xdb, - 0x80, - 0xdb, - 0xcf, - 0xe0, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xb4, - 0xdb, - 0x80, - 0xef, - 0xc5, - 0x80, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xd6, - 0x80, - 0xc5, - 0xb4, - 0xdb, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xe0, - 0x80, - 0x80, - 0xb4, - 0xf6, - 0xdb, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xc5, - 0x80, - 0xb4, - 0xb4, - 0xd6, - 0xb4, - 0xd6, - 0x80, - 0x80, - 0xb4, - 0xd6, - 0xb4, - 0x80, - 0x80, - 0xdb, - 0xb4, - 0xf3, - 0xb4, - 0xdb, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x1d, - 0xcf, - 0x16, - 0x12, - 0x17, - 0xc, - 0x23, - 0x2, - 0x1, - 0xc5, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xd6, - 0xc5, - 0xb4, - 0xc5, - 0xdb, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xb4, - 0xdb, - 0xc5, - 0xe4, - 0x80, - 0xdb, - 0x80, - 0xc5, - 0xb4, - 0x80, - 0x80, - 0x78, - 0x64, - 0x7a, - 0x64, - 0x76, - 0x60, - 0x67, - 0x55, - 0x5a, - 0x3a, - 0x37, - 0x24, - 0xf6, - 0xc5, - 0x14, - 0x17, - 0x1e, - 0x18, - 0x31, - 0x39, - 0x44, - 0x43, - 0x49, - 0x3e, - 0x39, - 0x23, - 0x18, - 0x17, - 0x42, - 0x41, - 0x40, - 0x34, - 0x39, - 0x34, - 0x37, - 0x30, - 0x38, - 0x23, - 0x22, - 0x9, - 0x75, - 0x63, - 0x73, - 0x63, - 0x77, - 0x58, - 0x73, - 0x5f, - 0x64, - 0x4d, - 0x57, - 0x41, - 0x58, - 0x46, - 0x36, - 0x32, - 0x45, - 0x51, - 0x64, - 0x56, - 0x72, - 0x61, - 0x67, - 0x57, - 0x60, - 0x52, - 0x49, - 0x4e, - 0x61, - 0x53, - 0x62, - 0x57, - 0x67, - 0x50, - 0x66, - 0x56, - 0x63, - 0x52, - 0x5e, - 0x3d, - 0x6b, - 0x5a, - 0x70, - 0x5d, - 0x72, - 0x50, - 0x6c, - 0x56, - 0x67, - 0x5a, - 0x69, - 0x49, - 0x5a, - 0x4f, - 0x56, - 0x50, - 0x61, - 0x50, - 0x6c, - 0x5d, - 0x71, - 0x5d, - 0x6e, - 0x56, - 0x6c, - 0x58, - 0x69, - 0x55, - 0x6c, - 0x57, - 0x65, - 0x57, - 0x6c, - 0x56, - 0x68, - 0x4c, - 0x61, - 0x58, - 0x66, - 0x44, - 0x68, - 0x52, - 0x6b, - 0x56, - 0x6c, - 0x60, - 0x6e, - 0x52, - 0x72, - 0x4e, - 0x5b, - 0x4d, - 0x56, - 0x4e, - 0x68, - 0x51, - 0x69, - 0x5a, - 0x6a, - 0x5a, - 0x72, - 0x54, - 0x6f, - 0x5d, - 0x75, - 0x5f, - 0x67, - 0x57, - 0x65, - 0x48, - 0x5c, - 0x4c, - 0x66, - 0x52, - 0x68, - 0x52, - 0x63, - 0x53, - 0x64, - 0x44, - 0x5f, - 0x44, - 0x60, - 0x49, - 0x69, - 0x60, - 0x71, - 0x51, - 0x6c, - 0x59, - 0x6c, - 0x53, - 0x62, - 0x4b, - 0x5c, - 0x4e, - 0x61, - 0x4c, - 0x6a, - 0x5c, - 0x69, - 0x4b, - 0x6b, - 0x56, - 0x6b, - 0x40, - 0x5d, - 0x43, - 0x6c, - 0x55, - 0x60, - 0x3f, - 0x5f, - 0x4d, - 0x69, - 0x52, - 0x64, - 0x4d, - 0x64, - 0x41, - 0x59, - 0x3b, - 0x55, - 0x35, - 0x67, - 0x55, - 0x71, - 0x5a, - 0x69, - 0x58, - 0x65, - 0x48, - 0x5e, - 0x4e, - 0x6a, - 0x55, - 0x69, - 0x55, - 0x73, - 0x5c, - 0x68, - 0x35, - 0x64, - 0x57, - 0x6a, - 0x43, - 0x57, - 0x42, - 0x63, - 0x4c, - 0x71, - 0x57, - 0x60, - 0x43, - 0x5a, - 0x44, - 0x5c, - 0x3e, - 0x5d, - 0x3e, - 0x57, - 0x31, - 0x46, - 0x7, - 0x56, - 0x4b, - 0x73, - 0x52, - 0x64, - 0x4b, - 0x5b, - 0x4a, - 0x66, - 0x4f, - 0x69, - 0x4d, - 0x69, - 0x56, - 0x6e, - 0x3e, - 0x4b, - 0x37, - 0x5c, - 0x44, - 0x56, - 0x24, - 0x4f, - 0x2a, - 0x46, - 0x3b, - 0x61, - 0x4e, - 0x61, - 0x43, - 0x5d, - 0x45, - 0x5e, - 0x44, - 0x50, - 0x3c, - 0x56, - 0x2d, - 0x45, - 0x4, - 0x50, - 0x40, - 0x64, - 0x57, - 0x69, - 0x4d, - 0x64, - 0x50, - 0x62, - 0x4e, - 0x67, - 0x4e, - 0x62, - 0x56, - 0x67, - 0x3c, - 0x48, - 0x23, - 0x58, - 0x43, - 0x53, - 0x28, - 0x3b, - 0xcf, - 0x48, - 0x48, - 0x5c, - 0x40, - 0x4d, - 0x37, - 0x4e, - 0x3c, - 0x56, - 0x20, - 0x3d, - 0x11, - 0x37, - 0xc5, - 0x4a, - 0xd6, - 0x2d, - 0x2b, - 0x57, - 0x4e, - 0x5a, - 0x44, - 0x60, - 0x43, - 0x5a, - 0x3f, - 0x5c, - 0x41, - 0x67, - 0x50, - 0x60, - 0x2f, - 0x36, - 0x1c, - 0x54, - 0x3e, - 0x4f, - 0xc, - 0x2d, - 0x80, - 0x36, - 0x22, - 0x50, - 0x41, - 0x5f, - 0x3e, - 0x50, - 0x3f, - 0x5f, - 0x3d, - 0x46, - 0x19, - 0x41, - 0xfd, - 0x33, - 0xd6, - 0x25, - 0x2, - 0x40, - 0x2f, - 0x59, - 0x3a, - 0x4f, - 0x3d, - 0x47, - 0x23, - 0x52, - 0x32, - 0x5c, - 0x3e, - 0x45, - 0xcf, - 0xd, - 0xdb, - 0x42, - 0x2a, - 0x3f, - 0x80, - 0x15, - 0x80, - 0xe4, - 0xb4, - 0x36, - 0x28, - 0x49, - 0x39, - 0x52, - 0x3a, - 0x5a, - 0x39, - 0x52, - 0xb, - 0x26, - 0x80, - 0x27, - 0xc5, - 0x2f, - 0xf6, - 0x45, - 0x24, - 0x40, - 0x29, - 0x52, - 0x33, - 0x43, - 0xfc, - 0x33, - 0x1d, - 0x44, - 0x17, - 0x2e, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x24, - 0x80, - 0xb4, - 0x80, - 0x34, - 0x32, - 0x4c, - 0x32, - 0x4b, - 0x30, - 0x54, - 0x3f, - 0x51, - 0x30, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xe4, - 0x80, - 0x1, - 0x80, - 0x26, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xfd, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x29, - 0xe0, - 0xe0, - 0xc5, - 0x27, - 0x80, - 0x1b, - 0x7, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x23, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf9, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xd6, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xf5, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xe0, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x1d, - 0xe4, - 0x11, - 0xb4, - 0x32, - 0xa, - 0x6, - 0x80, - 0x80, - 0x80, - 0xd6, - 0x80, - 0x1c, - 0xd, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x15, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf8, - 0xcf, - 0x10, - 0x80, - 0x17, - 0x80, - 0x1e, - 0x80, - 0xff, - 0xec, - 0x25, - 0x80, - 0x1c, - 0x23, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x11, - 0xb4, - 0x2, - 0x80, - 0x30, - 0x8, - 0x15, - 0x80, - 0x6, - 0x20, - 0x36, - 0xf8, - 0x2e, - 0x18, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf3, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xd, - 0x4, - 0xa, - 0xea, - 0x37, - 0x24, - 0x2a, - 0xc, - 0x39, - 0x26, - 0x43, - 0x5, - 0x2d, - 0x1f, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x14, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x7, - 0xcf, - 0xf, - 0xef, - 0x32, - 0xd, - 0x2a, - 0x14, - 0x37, - 0x1, - 0x32, - 0x0, - 0x38, - 0x10, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x1c, - 0x80, - 0x80, - 0x80, - 0x28, - 0xdb, - 0xe4, - 0xe0, - 0xb4, - 0x80, - 0x16, - 0xcf, - 0x1b, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80 -}; + 0x23, 0x17, 0xe0, 0x3, 0x9, 0xe7, 0xe7, 0xdb, 0xcf, 0xc5, 0xe0, 0xdb, 0xc5, 0xcf, 0xef, 0xcf, + 0xcf, 0xdb, 0xef, 0xdb, 0xe7, 0xc5, 0x5, 0x3, 0xfc, 0xe7, 0xf6, 0xdb, 0xcf, 0xe7, 0x9, 0xef, + 0xef, 0xdb, 0xcf, 0xe7, 0xe0, 0xe7, 0xe0, 0xc5, 0xff, 0xe0, 0x4, 0xcf, 0xdb, 0xb4, 0x80, 0xdb, + 0xef, 0x80, 0xc5, 0xe4, 0x9, 0xe4, 0xcf, 0xc5, 0xdb, 0xcf, 0xdb, 0xcf, 0xf5, 0xdb, 0xe7, 0xcf, + 0xef, 0xe4, 0xe7, 0xe4, 0xe7, 0xdb, 0xdb, 0xcf, 0xc5, 0xdb, 0xcf, 0xcf, 0xcf, 0xb4, 0xcf, 0xcf, + 0x13, 0xef, 0xf5, 0x80, 0x80, 0x80, 0xc5, 0xcf, 0xcf, 0x80, 0x80, 0xcf, 0xf5, 0xcf, 0x80, 0x80, + 0x80, 0x80, 0x80, 0xcf, 0xf9, 0xdb, 0xcf, 0x80, 0x80, 0xcf, 0xe7, 0xdb, 0xfb, 0xe4, 0xdb, 0xcf, + 0xe7, 0xcf, 0xe7, 0xb4, 0xdb, 0xe4, 0xcf, 0xb4, 0xfb, 0x0, 0x6, 0xd6, 0xec, 0xb4, 0x80, 0xb4, + 0x80, 0x80, 0x80, 0x80, 0xf3, 0xb4, 0xdb, 0xdb, 0xc5, 0xb4, 0xc5, 0x80, 0xcf, 0xb4, 0xdb, 0xb4, + 0xb4, 0x80, 0xcf, 0x80, 0xdb, 0xb4, 0xb4, 0x80, 0xc5, 0x80, 0xdb, 0xcf, 0xdb, 0xcf, 0xcf, 0xb4, + 0xff, 0xcf, 0xdb, 0x80, 0xb4, 0x80, 0x80, 0xd6, 0xcf, 0xcf, 0x80, 0xcf, 0xcf, 0xcf, 0xe4, 0xcf, + 0xc5, 0x80, 0x80, 0x80, 0xdb, 0x80, 0xb4, 0x80, 0xdb, 0x80, 0xb4, 0x80, 0xb4, 0xb4, 0xdb, 0xcf, + 0xec, 0xe0, 0xcf, 0xe0, 0xe4, 0xd6, 0xdb, 0x80, 0xef, 0xf6, 0xea, 0xd6, 0xb4, 0xd6, 0xec, 0xc5, + 0xec, 0xcf, 0xc5, 0x80, 0xdb, 0x80, 0x80, 0x80, 0x80, 0xb4, 0xdb, 0xcf, 0xdb, 0xd6, 0xe4, 0xc5, + 0xdb, 0xb4, 0xcf, 0xc5, 0xcf, 0xd6, 0xe4, 0xc5, 0xf3, 0xe0, 0xec, 0xe0, 0xfd, 0xe7, 0xcf, 0xb4, + 0x24, 0x1a, 0x0, 0xf1, 0x19, 0xe0, 0xec, 0xe0, 0xb4, 0xcf, 0xdb, 0xd6, 0xb4, 0xb4, 0xb4, 0x80, + 0xdb, 0x80, 0xdb, 0xc5, 0xf1, 0xe7, 0xea, 0xf8, 0xec, 0xc5, 0xe4, 0xe0, 0xec, 0xc5, 0xcf, 0xb4, + 0xe4, 0xd6, 0xe4, 0xdb, 0xf1, 0xdb, 0xdb, 0xc5, 0x22, 0xea, 0xe7, 0x80, 0xea, 0xf3, 0xec, 0xfb, + 0xec, 0xe0, 0xdb, 0xb4, 0xe4, 0xe0, 0xec, 0xd6, 0xf3, 0xb4, 0xb4, 0x80, 0xd6, 0xd6, 0xe4, 0xdb, + 0xcf, 0xb4, 0xdb, 0xdb, 0xf1, 0xe4, 0xcf, 0xb4, 0xe4, 0xcf, 0xe4, 0xea, 0xea, 0xe4, 0xe4, 0xd6, + 0xef, 0xb4, 0xc5, 0xc5, 0xd6, 0xc5, 0xe4, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xcf, 0xc5, 0x0, 0xdb, + 0xb4, 0xb4, 0xdb, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0xc5, 0xcf, 0xb4, + 0xcf, 0xcf, 0xe0, 0xcf, 0xcf, 0x80, 0xb4, 0x80, 0xec, 0xd6, 0xe0, 0xc5, 0xb4, 0xb4, 0xcf, 0x80, + 0xcf, 0xb4, 0xcf, 0x80, 0xd6, 0xc5, 0x80, 0x80, 0xdb, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xcf, 0x80, 0x80, 0x80, 0xcf, 0xb4, 0xd6, 0xb4, 0xd6, 0xb4, 0xf1, 0xc5, 0xc5, 0x80, 0xb4, 0x80, + 0x11, 0xc5, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xc5, 0xcf, 0xb4, 0x80, + 0xe4, 0xb4, 0x80, 0xb4, 0x80, 0x80, 0x80, 0x80, 0xcf, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0xb4, + 0xd6, 0xc5, 0xb4, 0x80, 0xc5, 0x80, 0xb4, 0x80, 0xcf, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xb4, 0xc5, 0xe4, 0xc5, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xef, 0x80, 0xc5, 0xb4, 0xc5, 0xc5, 0xc5, 0xcf, 0xd6, 0xc5, 0xf5, 0xb4, 0xcf, 0x80, + 0xe4, 0xc5, 0xb4, 0xe0, 0xd6, 0xb4, 0xcf, 0x80, 0xb4, 0xc5, 0xcf, 0x80, 0xe0, 0xc5, 0xd6, 0x80, + 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0xb4, 0xc5, 0x80, 0xd6, 0xb4, 0xe0, 0xb4, 0xb4, 0xc5, + 0xc5, 0xb4, 0xc5, 0x80, 0xc5, 0xc5, 0xd6, 0x80, 0x80, 0x80, 0xf8, 0x80, 0x80, 0xb4, 0xd6, 0x80, + 0xd6, 0xb4, 0xb4, 0x80, 0xb4, 0x80, 0x80, 0x80, 0x80, 0xb4, 0xcf, 0xcf, 0xe7, 0x80, 0xb4, 0x80, + 0xc5, 0x80, 0xc5, 0x80, 0xb4, 0x80, 0xb4, 0xb4, 0xc5, 0x80, 0xb4, 0x80, 0xc5, 0x80, 0xe0, 0x80, + 0xef, 0x80, 0xcf, 0x80, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb4, 0xb4, 0xfd, 0xb4, 0x80, 0xb4, + 0xe0, 0x80, 0xcf, 0xb4, 0xb4, 0x80, 0xe7, 0xb4, 0xe7, 0xb4, 0xb4, 0xd6, 0xb4, 0x80, 0xe0, 0xc5, + 0x80, 0x80, 0xc5, 0xc5, 0xd6, 0x80, 0xc5, 0x80, 0xdb, 0xc5, 0xea, 0x80, 0x80, 0x80, 0xb4, 0x80, + 0xb4, 0x80, 0xe0, 0x80, 0x80, 0x80, 0xc5, 0xb4, 0x80, 0x80, 0xd6, 0x80, 0xb4, 0x80, 0xb4, 0x80, + 0x80, 0xb4, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xe7, 0xb4, 0xc5, 0x80, 0xd6, 0x80, + 0xe7, 0xc5, 0xdb, 0x80, 0xdb, 0xcf, 0xe0, 0x80, 0x80, 0x80, 0xc5, 0xb4, 0xdb, 0x80, 0xef, 0xc5, + 0x80, 0x80, 0x80, 0x80, 0xc5, 0xb4, 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xd6, 0x80, + 0xc5, 0xb4, 0xdb, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xe0, 0x80, 0x80, 0xb4, 0xf6, 0xdb, 0xc5, 0x80, + 0x80, 0x80, 0xc5, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xc5, 0x80, 0xb4, 0xb4, 0xd6, 0xb4, 0xd6, 0x80, + 0x80, 0xb4, 0xd6, 0xb4, 0x80, 0x80, 0xdb, 0xb4, 0xf3, 0xb4, 0xdb, 0x80, 0x80, 0x80, 0xc5, 0x80, + 0x1d, 0xcf, 0x16, 0x12, 0x17, 0xc, 0x23, 0x2, 0x1, 0xc5, 0xc5, 0xb4, 0x80, 0x80, 0x80, 0x80, + 0x80, 0xc5, 0xd6, 0xc5, 0xb4, 0xc5, 0xdb, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb4, 0xb4, 0xdb, 0xc5, + 0xe4, 0x80, 0xdb, 0x80, 0xc5, 0xb4, 0x80, 0x80, 0x78, 0x64, 0x7a, 0x64, 0x76, 0x60, 0x67, 0x55, + 0x5a, 0x3a, 0x37, 0x24, 0xf6, 0xc5, 0x14, 0x17, 0x1e, 0x18, 0x31, 0x39, 0x44, 0x43, 0x49, 0x3e, + 0x39, 0x23, 0x18, 0x17, 0x42, 0x41, 0x40, 0x34, 0x39, 0x34, 0x37, 0x30, 0x38, 0x23, 0x22, 0x9, + 0x75, 0x63, 0x73, 0x63, 0x77, 0x58, 0x73, 0x5f, 0x64, 0x4d, 0x57, 0x41, 0x58, 0x46, 0x36, 0x32, + 0x45, 0x51, 0x64, 0x56, 0x72, 0x61, 0x67, 0x57, 0x60, 0x52, 0x49, 0x4e, 0x61, 0x53, 0x62, 0x57, + 0x67, 0x50, 0x66, 0x56, 0x63, 0x52, 0x5e, 0x3d, 0x6b, 0x5a, 0x70, 0x5d, 0x72, 0x50, 0x6c, 0x56, + 0x67, 0x5a, 0x69, 0x49, 0x5a, 0x4f, 0x56, 0x50, 0x61, 0x50, 0x6c, 0x5d, 0x71, 0x5d, 0x6e, 0x56, + 0x6c, 0x58, 0x69, 0x55, 0x6c, 0x57, 0x65, 0x57, 0x6c, 0x56, 0x68, 0x4c, 0x61, 0x58, 0x66, 0x44, + 0x68, 0x52, 0x6b, 0x56, 0x6c, 0x60, 0x6e, 0x52, 0x72, 0x4e, 0x5b, 0x4d, 0x56, 0x4e, 0x68, 0x51, + 0x69, 0x5a, 0x6a, 0x5a, 0x72, 0x54, 0x6f, 0x5d, 0x75, 0x5f, 0x67, 0x57, 0x65, 0x48, 0x5c, 0x4c, + 0x66, 0x52, 0x68, 0x52, 0x63, 0x53, 0x64, 0x44, 0x5f, 0x44, 0x60, 0x49, 0x69, 0x60, 0x71, 0x51, + 0x6c, 0x59, 0x6c, 0x53, 0x62, 0x4b, 0x5c, 0x4e, 0x61, 0x4c, 0x6a, 0x5c, 0x69, 0x4b, 0x6b, 0x56, + 0x6b, 0x40, 0x5d, 0x43, 0x6c, 0x55, 0x60, 0x3f, 0x5f, 0x4d, 0x69, 0x52, 0x64, 0x4d, 0x64, 0x41, + 0x59, 0x3b, 0x55, 0x35, 0x67, 0x55, 0x71, 0x5a, 0x69, 0x58, 0x65, 0x48, 0x5e, 0x4e, 0x6a, 0x55, + 0x69, 0x55, 0x73, 0x5c, 0x68, 0x35, 0x64, 0x57, 0x6a, 0x43, 0x57, 0x42, 0x63, 0x4c, 0x71, 0x57, + 0x60, 0x43, 0x5a, 0x44, 0x5c, 0x3e, 0x5d, 0x3e, 0x57, 0x31, 0x46, 0x7, 0x56, 0x4b, 0x73, 0x52, + 0x64, 0x4b, 0x5b, 0x4a, 0x66, 0x4f, 0x69, 0x4d, 0x69, 0x56, 0x6e, 0x3e, 0x4b, 0x37, 0x5c, 0x44, + 0x56, 0x24, 0x4f, 0x2a, 0x46, 0x3b, 0x61, 0x4e, 0x61, 0x43, 0x5d, 0x45, 0x5e, 0x44, 0x50, 0x3c, + 0x56, 0x2d, 0x45, 0x4, 0x50, 0x40, 0x64, 0x57, 0x69, 0x4d, 0x64, 0x50, 0x62, 0x4e, 0x67, 0x4e, + 0x62, 0x56, 0x67, 0x3c, 0x48, 0x23, 0x58, 0x43, 0x53, 0x28, 0x3b, 0xcf, 0x48, 0x48, 0x5c, 0x40, + 0x4d, 0x37, 0x4e, 0x3c, 0x56, 0x20, 0x3d, 0x11, 0x37, 0xc5, 0x4a, 0xd6, 0x2d, 0x2b, 0x57, 0x4e, + 0x5a, 0x44, 0x60, 0x43, 0x5a, 0x3f, 0x5c, 0x41, 0x67, 0x50, 0x60, 0x2f, 0x36, 0x1c, 0x54, 0x3e, + 0x4f, 0xc, 0x2d, 0x80, 0x36, 0x22, 0x50, 0x41, 0x5f, 0x3e, 0x50, 0x3f, 0x5f, 0x3d, 0x46, 0x19, + 0x41, 0xfd, 0x33, 0xd6, 0x25, 0x2, 0x40, 0x2f, 0x59, 0x3a, 0x4f, 0x3d, 0x47, 0x23, 0x52, 0x32, + 0x5c, 0x3e, 0x45, 0xcf, 0xd, 0xdb, 0x42, 0x2a, 0x3f, 0x80, 0x15, 0x80, 0xe4, 0xb4, 0x36, 0x28, + 0x49, 0x39, 0x52, 0x3a, 0x5a, 0x39, 0x52, 0xb, 0x26, 0x80, 0x27, 0xc5, 0x2f, 0xf6, 0x45, 0x24, + 0x40, 0x29, 0x52, 0x33, 0x43, 0xfc, 0x33, 0x1d, 0x44, 0x17, 0x2e, 0x80, 0x80, 0x80, 0xb4, 0x80, + 0x80, 0x80, 0x24, 0x80, 0xb4, 0x80, 0x34, 0x32, 0x4c, 0x32, 0x4b, 0x30, 0x54, 0x3f, 0x51, 0x30, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe4, 0x80, 0x1, 0x80, 0x26, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfd, 0x80, 0x80, 0x80, 0xb4, 0x80, + 0x29, 0xe0, 0xe0, 0xc5, 0x27, 0x80, 0x1b, 0x7, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x23, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xf9, 0x80, 0x80, 0x80, 0x80, 0x80, 0xd6, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xf5, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xe0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1d, 0xe4, 0x11, 0xb4, 0x32, 0xa, + 0x6, 0x80, 0x80, 0x80, 0xd6, 0x80, 0x1c, 0xd, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x15, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xf8, 0xcf, 0x10, 0x80, 0x17, 0x80, 0x1e, 0x80, 0xff, 0xec, 0x25, 0x80, 0x1c, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x11, 0xb4, 0x2, 0x80, 0x30, 0x8, + 0x15, 0x80, 0x6, 0x20, 0x36, 0xf8, 0x2e, 0x18, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0xf3, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xd, 0x4, 0xa, 0xea, 0x37, 0x24, 0x2a, 0xc, 0x39, 0x26, 0x43, 0x5, 0x2d, 0x1f, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x14, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7, 0xcf, 0xf, 0xef, 0x32, 0xd, + 0x2a, 0x14, 0x37, 0x1, 0x32, 0x0, 0x38, 0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x1c, 0x80, 0x80, 0x80, 0x28, 0xdb, 0xe4, 0xe0, 0xb4, 0x80, 0x16, 0xcf, 0x1b, 0xb4, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb4, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; diff --git a/tests/micro/arduino/testdata/unknown.c b/tests/micro/arduino/testdata/unknown.c index edfe9be3ebca..2aef2dbcb62e 100644 --- a/tests/micro/arduino/testdata/unknown.c +++ b/tests/micro/arduino/testdata/unknown.c @@ -1,1962 +1,124 @@ static const char input_unknown[1960] = { - 0x78, - 0x66, - 0x7a, - 0x63, - 0x78, - 0x62, - 0x6d, - 0x52, - 0x58, - 0x19, - 0x0, - 0xcf, - 0x80, - 0x80, - 0x80, - 0x80, - 0xcf, - 0xc5, - 0xc5, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xc5, - 0xe7, - 0xe0, - 0x80, - 0x80, - 0xc5, - 0x80, - 0xcf, - 0xc5, - 0xc5, - 0x80, - 0xc5, - 0xcf, - 0xe7, - 0xe0, - 0xdb, - 0x72, - 0x4b, - 0x65, - 0x60, - 0x70, - 0x50, - 0x73, - 0x59, - 0x60, - 0x4f, - 0x4d, - 0x3c, - 0x11, - 0xff, - 0xc5, - 0xc5, - 0xdb, - 0xdb, - 0xcf, - 0xec, - 0xe7, - 0xcf, - 0xcf, - 0x2, - 0x31, - 0x4d, - 0x4c, - 0xe7, - 0xdb, - 0xc5, - 0x80, - 0xcf, - 0xef, - 0xe4, - 0x4, - 0xff, - 0xf5, - 0xec, - 0xef, - 0x5, - 0x6c, - 0x4b, - 0x56, - 0x54, - 0x6a, - 0x47, - 0x6f, - 0x5b, - 0x63, - 0x55, - 0x4c, - 0x41, - 0x2d, - 0x22, - 0x20, - 0x3a, - 0x4e, - 0xf1, - 0xcf, - 0xfc, - 0x19, - 0xf3, - 0xe7, - 0x2d, - 0x48, - 0x4e, - 0x5b, - 0x80, - 0xcf, - 0xcf, - 0x80, - 0x80, - 0x80, - 0xdb, - 0x3, - 0xfb, - 0xf5, - 0xea, - 0x0, - 0xf5, - 0x62, - 0x40, - 0x46, - 0x47, - 0x62, - 0x41, - 0x68, - 0x53, - 0x5f, - 0x51, - 0x57, - 0x4e, - 0x5b, - 0x51, - 0x58, - 0x4b, - 0x62, - 0x2b, - 0xef, - 0x44, - 0x5d, - 0x41, - 0x49, - 0x5c, - 0x62, - 0x56, - 0x58, - 0x2f, - 0xc5, - 0xb4, - 0xcf, - 0xcf, - 0xc5, - 0xe0, - 0xf9, - 0xe7, - 0x7, - 0xf5, - 0xa, - 0xfc, - 0x5b, - 0x39, - 0x35, - 0x3d, - 0x5c, - 0x37, - 0x5d, - 0x49, - 0x57, - 0x49, - 0x63, - 0x57, - 0x61, - 0x55, - 0x5e, - 0x4d, - 0x64, - 0x4b, - 0x63, - 0x58, - 0x5c, - 0x49, - 0x5f, - 0x57, - 0x6a, - 0x56, - 0x68, - 0x41, - 0x15, - 0xf1, - 0x7, - 0xf1, - 0xf9, - 0xef, - 0xfd, - 0xfb, - 0xc, - 0xf6, - 0x5, - 0xef, - 0x5a, - 0x40, - 0x4a, - 0x44, - 0x69, - 0x57, - 0x55, - 0x50, - 0x63, - 0x49, - 0x67, - 0x5a, - 0x72, - 0x60, - 0x70, - 0x5a, - 0x71, - 0x61, - 0x77, - 0x63, - 0x75, - 0x5e, - 0x71, - 0x52, - 0x6f, - 0x5f, - 0x78, - 0x64, - 0x78, - 0x5d, - 0x56, - 0x57, - 0x56, - 0x28, - 0x39, - 0x3b, - 0x58, - 0x49, - 0x3d, - 0x33, - 0x58, - 0x3f, - 0x2a, - 0x50, - 0x6c, - 0x53, - 0x6a, - 0x5b, - 0x69, - 0x57, - 0x6e, - 0x5e, - 0x73, - 0x60, - 0x74, - 0x5a, - 0x75, - 0x61, - 0x76, - 0x60, - 0x75, - 0x59, - 0x6e, - 0x4c, - 0x6b, - 0x4c, - 0x6b, - 0x58, - 0x74, - 0x61, - 0x6e, - 0x36, - 0x49, - 0x41, - 0x5b, - 0x5d, - 0x6e, - 0x57, - 0x5e, - 0x44, - 0x50, - 0x30, - 0x3a, - 0x46, - 0x5f, - 0x3c, - 0x64, - 0x4e, - 0x5d, - 0x53, - 0x69, - 0x55, - 0x6a, - 0x57, - 0x69, - 0x52, - 0x71, - 0x5a, - 0x6b, - 0x47, - 0x5f, - 0x4d, - 0x61, - 0x43, - 0x5b, - 0x37, - 0x59, - 0x3e, - 0x57, - 0x3f, - 0x53, - 0xe, - 0x44, - 0x47, - 0x5c, - 0x43, - 0x62, - 0x51, - 0x5d, - 0x3f, - 0x4a, - 0x2a, - 0x39, - 0x3f, - 0x59, - 0x37, - 0x5c, - 0x40, - 0x58, - 0x50, - 0x65, - 0x4e, - 0x65, - 0x52, - 0x67, - 0x54, - 0x6f, - 0x52, - 0x59, - 0x3b, - 0x57, - 0x48, - 0x61, - 0x49, - 0x54, - 0xf8, - 0x3e, - 0x2d, - 0x4e, - 0x3e, - 0x50, - 0xc, - 0x3e, - 0x53, - 0x67, - 0x2d, - 0x4c, - 0x3b, - 0x4f, - 0x2a, - 0x43, - 0x14, - 0x46, - 0x37, - 0x50, - 0x23, - 0x58, - 0x36, - 0x57, - 0x48, - 0x63, - 0x46, - 0x67, - 0x4e, - 0x65, - 0x55, - 0x6d, - 0x4c, - 0x55, - 0x35, - 0x41, - 0x3b, - 0x58, - 0x3f, - 0x53, - 0x2f, - 0x44, - 0x25, - 0x48, - 0x37, - 0x58, - 0xe4, - 0x4d, - 0x48, - 0x53, - 0x2b, - 0x41, - 0x28, - 0x4a, - 0x2d, - 0x3d, - 0x5, - 0x44, - 0x29, - 0x44, - 0x1c, - 0x5c, - 0x3b, - 0x53, - 0x35, - 0x5a, - 0x3b, - 0x60, - 0x45, - 0x61, - 0x50, - 0x64, - 0x3a, - 0x43, - 0x1f, - 0x35, - 0x23, - 0x4d, - 0x4a, - 0x5e, - 0x3c, - 0x4d, - 0x30, - 0x51, - 0x2e, - 0x51, - 0xf3, - 0x4d, - 0x3e, - 0x50, - 0x1a, - 0x34, - 0xfc, - 0x44, - 0x27, - 0x37, - 0xf8, - 0x3a, - 0x9, - 0x32, - 0x33, - 0x5d, - 0x37, - 0x57, - 0x35, - 0x5d, - 0x3b, - 0x58, - 0x31, - 0x60, - 0x45, - 0x50, - 0xff, - 0x3a, - 0xe0, - 0x24, - 0x3, - 0x24, - 0x3a, - 0x4f, - 0xe, - 0x32, - 0x1d, - 0x46, - 0x2d, - 0x45, - 0x4, - 0x56, - 0x3d, - 0x50, - 0x7, - 0xa, - 0x80, - 0x3a, - 0x1f, - 0x31, - 0xe0, - 0x43, - 0x3, - 0x26, - 0x3a, - 0x5b, - 0x34, - 0x56, - 0x30, - 0x58, - 0x2e, - 0x53, - 0x1f, - 0x61, - 0x3f, - 0x3f, - 0x80, - 0x2f, - 0xe4, - 0x2f, - 0x14, - 0x30, - 0x1e, - 0x50, - 0xe0, - 0x22, - 0x0, - 0x4b, - 0x2d, - 0x39, - 0xdb, - 0x56, - 0x3e, - 0x46, - 0x34, - 0x2d, - 0x80, - 0x29, - 0x5, - 0x2f, - 0xc5, - 0x46, - 0xfb, - 0x1c, - 0x3a, - 0x56, - 0x26, - 0x53, - 0x2b, - 0x4e, - 0x8, - 0x53, - 0x25, - 0x65, - 0x3a, - 0xf, - 0x80, - 0xf5, - 0x80, - 0xb, - 0xd6, - 0x1e, - 0x7, - 0x55, - 0xd6, - 0x6, - 0x80, - 0x2c, - 0x0, - 0x11, - 0xe4, - 0x3e, - 0x26, - 0x41, - 0x25, - 0x2c, - 0x80, - 0x1d, - 0x2, - 0x2a, - 0xd6, - 0x45, - 0xec, - 0x4, - 0x3c, - 0x54, - 0x20, - 0x4d, - 0x12, - 0x49, - 0xf6, - 0x57, - 0x32, - 0x61, - 0x23, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb, - 0xe7, - 0x3b, - 0x80, - 0xc5, - 0x80, - 0xc5, - 0x80, - 0xcf, - 0xdb, - 0x14, - 0x1d, - 0x3d, - 0x36, - 0x3f, - 0x80, - 0x19, - 0xfc, - 0x1f, - 0x80, - 0x40, - 0xea, - 0x8, - 0x3c, - 0x52, - 0x22, - 0x3a, - 0xf8, - 0x49, - 0x3, - 0x58, - 0x21, - 0x3c, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0xf6, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x37, - 0x2d, - 0x3b, - 0x1b, - 0x31, - 0x80, - 0x16, - 0xf5, - 0xf3, - 0x80, - 0x3e, - 0xcf, - 0xec, - 0x3b, - 0x4e, - 0x12, - 0x4, - 0x80, - 0x4f, - 0x26, - 0x5a, - 0x1a, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xfc, - 0xb4, - 0x2c, - 0x0, - 0x1b, - 0x2a, - 0x2f, - 0x80, - 0xc, - 0xdb, - 0xd6, - 0x80, - 0x44, - 0xfd, - 0x11, - 0x33, - 0x44, - 0xd6, - 0x8, - 0x80, - 0x4e, - 0xe, - 0x26, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x1, - 0x80, - 0xe7, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x14, - 0xdb, - 0xf8, - 0x80, - 0x48, - 0x0, - 0x7, - 0xe7, - 0x18, - 0x80, - 0xef, - 0x80, - 0x36, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xdb, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x17, - 0x80, - 0x80, - 0x80, - 0x48, - 0x6, - 0x10, - 0x80, - 0xf1, - 0x80, - 0x24, - 0x80, - 0x7, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x3c, - 0xf1, - 0x7, - 0x80, - 0xc5, - 0x80, - 0x33, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xe0, - 0x80, - 0x26, - 0x80, - 0xcf, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xf6, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80 -}; + 0x78, 0x66, 0x7a, 0x63, 0x78, 0x62, 0x6d, 0x52, 0x58, 0x19, 0x0, 0xcf, 0x80, 0x80, 0x80, 0x80, + 0xcf, 0xc5, 0xc5, 0xc5, 0x80, 0x80, 0x80, 0xc5, 0xc5, 0xe7, 0xe0, 0x80, 0x80, 0xc5, 0x80, 0xcf, + 0xc5, 0xc5, 0x80, 0xc5, 0xcf, 0xe7, 0xe0, 0xdb, 0x72, 0x4b, 0x65, 0x60, 0x70, 0x50, 0x73, 0x59, + 0x60, 0x4f, 0x4d, 0x3c, 0x11, 0xff, 0xc5, 0xc5, 0xdb, 0xdb, 0xcf, 0xec, 0xe7, 0xcf, 0xcf, 0x2, + 0x31, 0x4d, 0x4c, 0xe7, 0xdb, 0xc5, 0x80, 0xcf, 0xef, 0xe4, 0x4, 0xff, 0xf5, 0xec, 0xef, 0x5, + 0x6c, 0x4b, 0x56, 0x54, 0x6a, 0x47, 0x6f, 0x5b, 0x63, 0x55, 0x4c, 0x41, 0x2d, 0x22, 0x20, 0x3a, + 0x4e, 0xf1, 0xcf, 0xfc, 0x19, 0xf3, 0xe7, 0x2d, 0x48, 0x4e, 0x5b, 0x80, 0xcf, 0xcf, 0x80, 0x80, + 0x80, 0xdb, 0x3, 0xfb, 0xf5, 0xea, 0x0, 0xf5, 0x62, 0x40, 0x46, 0x47, 0x62, 0x41, 0x68, 0x53, + 0x5f, 0x51, 0x57, 0x4e, 0x5b, 0x51, 0x58, 0x4b, 0x62, 0x2b, 0xef, 0x44, 0x5d, 0x41, 0x49, 0x5c, + 0x62, 0x56, 0x58, 0x2f, 0xc5, 0xb4, 0xcf, 0xcf, 0xc5, 0xe0, 0xf9, 0xe7, 0x7, 0xf5, 0xa, 0xfc, + 0x5b, 0x39, 0x35, 0x3d, 0x5c, 0x37, 0x5d, 0x49, 0x57, 0x49, 0x63, 0x57, 0x61, 0x55, 0x5e, 0x4d, + 0x64, 0x4b, 0x63, 0x58, 0x5c, 0x49, 0x5f, 0x57, 0x6a, 0x56, 0x68, 0x41, 0x15, 0xf1, 0x7, 0xf1, + 0xf9, 0xef, 0xfd, 0xfb, 0xc, 0xf6, 0x5, 0xef, 0x5a, 0x40, 0x4a, 0x44, 0x69, 0x57, 0x55, 0x50, + 0x63, 0x49, 0x67, 0x5a, 0x72, 0x60, 0x70, 0x5a, 0x71, 0x61, 0x77, 0x63, 0x75, 0x5e, 0x71, 0x52, + 0x6f, 0x5f, 0x78, 0x64, 0x78, 0x5d, 0x56, 0x57, 0x56, 0x28, 0x39, 0x3b, 0x58, 0x49, 0x3d, 0x33, + 0x58, 0x3f, 0x2a, 0x50, 0x6c, 0x53, 0x6a, 0x5b, 0x69, 0x57, 0x6e, 0x5e, 0x73, 0x60, 0x74, 0x5a, + 0x75, 0x61, 0x76, 0x60, 0x75, 0x59, 0x6e, 0x4c, 0x6b, 0x4c, 0x6b, 0x58, 0x74, 0x61, 0x6e, 0x36, + 0x49, 0x41, 0x5b, 0x5d, 0x6e, 0x57, 0x5e, 0x44, 0x50, 0x30, 0x3a, 0x46, 0x5f, 0x3c, 0x64, 0x4e, + 0x5d, 0x53, 0x69, 0x55, 0x6a, 0x57, 0x69, 0x52, 0x71, 0x5a, 0x6b, 0x47, 0x5f, 0x4d, 0x61, 0x43, + 0x5b, 0x37, 0x59, 0x3e, 0x57, 0x3f, 0x53, 0xe, 0x44, 0x47, 0x5c, 0x43, 0x62, 0x51, 0x5d, 0x3f, + 0x4a, 0x2a, 0x39, 0x3f, 0x59, 0x37, 0x5c, 0x40, 0x58, 0x50, 0x65, 0x4e, 0x65, 0x52, 0x67, 0x54, + 0x6f, 0x52, 0x59, 0x3b, 0x57, 0x48, 0x61, 0x49, 0x54, 0xf8, 0x3e, 0x2d, 0x4e, 0x3e, 0x50, 0xc, + 0x3e, 0x53, 0x67, 0x2d, 0x4c, 0x3b, 0x4f, 0x2a, 0x43, 0x14, 0x46, 0x37, 0x50, 0x23, 0x58, 0x36, + 0x57, 0x48, 0x63, 0x46, 0x67, 0x4e, 0x65, 0x55, 0x6d, 0x4c, 0x55, 0x35, 0x41, 0x3b, 0x58, 0x3f, + 0x53, 0x2f, 0x44, 0x25, 0x48, 0x37, 0x58, 0xe4, 0x4d, 0x48, 0x53, 0x2b, 0x41, 0x28, 0x4a, 0x2d, + 0x3d, 0x5, 0x44, 0x29, 0x44, 0x1c, 0x5c, 0x3b, 0x53, 0x35, 0x5a, 0x3b, 0x60, 0x45, 0x61, 0x50, + 0x64, 0x3a, 0x43, 0x1f, 0x35, 0x23, 0x4d, 0x4a, 0x5e, 0x3c, 0x4d, 0x30, 0x51, 0x2e, 0x51, 0xf3, + 0x4d, 0x3e, 0x50, 0x1a, 0x34, 0xfc, 0x44, 0x27, 0x37, 0xf8, 0x3a, 0x9, 0x32, 0x33, 0x5d, 0x37, + 0x57, 0x35, 0x5d, 0x3b, 0x58, 0x31, 0x60, 0x45, 0x50, 0xff, 0x3a, 0xe0, 0x24, 0x3, 0x24, 0x3a, + 0x4f, 0xe, 0x32, 0x1d, 0x46, 0x2d, 0x45, 0x4, 0x56, 0x3d, 0x50, 0x7, 0xa, 0x80, 0x3a, 0x1f, + 0x31, 0xe0, 0x43, 0x3, 0x26, 0x3a, 0x5b, 0x34, 0x56, 0x30, 0x58, 0x2e, 0x53, 0x1f, 0x61, 0x3f, + 0x3f, 0x80, 0x2f, 0xe4, 0x2f, 0x14, 0x30, 0x1e, 0x50, 0xe0, 0x22, 0x0, 0x4b, 0x2d, 0x39, 0xdb, + 0x56, 0x3e, 0x46, 0x34, 0x2d, 0x80, 0x29, 0x5, 0x2f, 0xc5, 0x46, 0xfb, 0x1c, 0x3a, 0x56, 0x26, + 0x53, 0x2b, 0x4e, 0x8, 0x53, 0x25, 0x65, 0x3a, 0xf, 0x80, 0xf5, 0x80, 0xb, 0xd6, 0x1e, 0x7, + 0x55, 0xd6, 0x6, 0x80, 0x2c, 0x0, 0x11, 0xe4, 0x3e, 0x26, 0x41, 0x25, 0x2c, 0x80, 0x1d, 0x2, + 0x2a, 0xd6, 0x45, 0xec, 0x4, 0x3c, 0x54, 0x20, 0x4d, 0x12, 0x49, 0xf6, 0x57, 0x32, 0x61, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xb, 0xe7, 0x3b, 0x80, 0xc5, 0x80, 0xc5, 0x80, 0xcf, 0xdb, + 0x14, 0x1d, 0x3d, 0x36, 0x3f, 0x80, 0x19, 0xfc, 0x1f, 0x80, 0x40, 0xea, 0x8, 0x3c, 0x52, 0x22, + 0x3a, 0xf8, 0x49, 0x3, 0x58, 0x21, 0x3c, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc5, 0x80, + 0xf6, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x37, 0x2d, 0x3b, 0x1b, 0x31, 0x80, 0x16, 0xf5, + 0xf3, 0x80, 0x3e, 0xcf, 0xec, 0x3b, 0x4e, 0x12, 0x4, 0x80, 0x4f, 0x26, 0x5a, 0x1a, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfc, 0xb4, + 0x2c, 0x0, 0x1b, 0x2a, 0x2f, 0x80, 0xc, 0xdb, 0xd6, 0x80, 0x44, 0xfd, 0x11, 0x33, 0x44, 0xd6, + 0x8, 0x80, 0x4e, 0xe, 0x26, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0x80, 0xe7, 0x80, 0x80, 0x80, 0x80, 0x80, 0x14, 0xdb, + 0xf8, 0x80, 0x48, 0x0, 0x7, 0xe7, 0x18, 0x80, 0xef, 0x80, 0x36, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xdb, 0x80, 0x80, 0x80, 0x80, 0x80, 0x17, 0x80, 0x80, 0x80, 0x48, 0x6, 0x10, 0x80, 0xf1, 0x80, + 0x24, 0x80, 0x7, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x3c, 0xf1, 0x7, 0x80, 0xc5, 0x80, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0x80, 0x26, 0x80, 0xcf, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xf6, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; diff --git a/tests/micro/arduino/testdata/yes.c b/tests/micro/arduino/testdata/yes.c index d2f70077b033..9b500624bff0 100644 --- a/tests/micro/arduino/testdata/yes.c +++ b/tests/micro/arduino/testdata/yes.c @@ -1,1962 +1,124 @@ static const char input_yes[1960] = { - 0x7c, - 0x66, - 0x79, - 0x65, - 0x7d, - 0x67, - 0x7c, - 0x67, - 0x7c, - 0x66, - 0x7c, - 0x67, - 0x7c, - 0x67, - 0x7d, - 0x66, - 0x7c, - 0x67, - 0x7d, - 0x66, - 0x7c, - 0x67, - 0x7d, - 0x66, - 0x7c, - 0x67, - 0x7d, - 0x67, - 0x7d, - 0x67, - 0x7d, - 0x67, - 0x7d, - 0x67, - 0x7d, - 0x67, - 0x7d, - 0x67, - 0x7d, - 0x67, - 0x52, - 0x57, - 0x78, - 0x5a, - 0x67, - 0x53, - 0x6f, - 0x4b, - 0x6d, - 0x5c, - 0x71, - 0x52, - 0x66, - 0x4d, - 0x6e, - 0x56, - 0x73, - 0x50, - 0x5f, - 0x54, - 0x6d, - 0x55, - 0x6a, - 0x5b, - 0x6f, - 0x57, - 0x68, - 0x50, - 0x71, - 0x58, - 0x6d, - 0x57, - 0x69, - 0x55, - 0x6a, - 0x55, - 0x6c, - 0x59, - 0x6c, - 0x5a, - 0x5b, - 0x3c, - 0x54, - 0x44, - 0x58, - 0x4f, - 0x66, - 0x30, - 0x58, - 0x50, - 0x61, - 0x3d, - 0x67, - 0x36, - 0x5b, - 0x4d, - 0x64, - 0x51, - 0x6a, - 0x4d, - 0x60, - 0x4b, - 0x61, - 0x53, - 0x69, - 0x54, - 0x60, - 0x47, - 0x5c, - 0x4d, - 0x63, - 0x45, - 0x64, - 0x4d, - 0x63, - 0x4b, - 0x67, - 0x50, - 0x68, - 0x4d, - 0x64, - 0x4b, - 0x64, - 0x4e, - 0x5f, - 0x3d, - 0x53, - 0x42, - 0x59, - 0x39, - 0x57, - 0x43, - 0x5e, - 0x3a, - 0x44, - 0x3b, - 0x56, - 0x3c, - 0x5c, - 0x46, - 0x66, - 0x4c, - 0x61, - 0x3e, - 0x5d, - 0x49, - 0x55, - 0x48, - 0x5d, - 0x45, - 0x5a, - 0x48, - 0x5f, - 0x41, - 0x59, - 0x49, - 0x5a, - 0x46, - 0x5d, - 0x3b, - 0x51, - 0x3d, - 0x4c, - 0x44, - 0x57, - 0x37, - 0x54, - 0x43, - 0x4f, - 0xa, - 0x32, - 0x28, - 0x5b, - 0x3a, - 0x5e, - 0x47, - 0x4d, - 0x2b, - 0x57, - 0x4a, - 0x5d, - 0x34, - 0x52, - 0x3e, - 0x50, - 0x38, - 0x54, - 0x30, - 0x53, - 0x41, - 0x57, - 0x39, - 0x5c, - 0x3c, - 0x53, - 0x41, - 0x5a, - 0x1e, - 0x4e, - 0x41, - 0x4d, - 0x2c, - 0x3e, - 0x18, - 0x4c, - 0x1c, - 0x36, - 0x11, - 0x4b, - 0x32, - 0x52, - 0x2f, - 0x50, - 0x2d, - 0x4e, - 0x20, - 0x50, - 0x3c, - 0x4a, - 0x16, - 0x44, - 0x22, - 0x48, - 0x29, - 0x4d, - 0x34, - 0x4e, - 0x2c, - 0x52, - 0x2e, - 0x46, - 0x35, - 0x4b, - 0x14, - 0x50, - 0x33, - 0x53, - 0x3e, - 0x50, - 0x2d, - 0x4a, - 0x0, - 0x4b, - 0x3a, - 0x47, - 0x16, - 0x45, - 0x32, - 0x45, - 0x10, - 0x42, - 0x23, - 0x49, - 0x39, - 0x41, - 0x10, - 0x48, - 0x32, - 0x4e, - 0x30, - 0x40, - 0x34, - 0x46, - 0x39, - 0x54, - 0xf5, - 0x49, - 0x38, - 0x53, - 0x2c, - 0x4a, - 0x37, - 0x51, - 0x2c, - 0x46, - 0x2f, - 0x4c, - 0x2a, - 0x4d, - 0x2b, - 0x3d, - 0x2f, - 0x4e, - 0x20, - 0x1e, - 0x7, - 0x41, - 0x8, - 0x39, - 0xd, - 0x46, - 0x20, - 0x3b, - 0x2a, - 0x3f, - 0x20, - 0x40, - 0xe, - 0x4e, - 0x2e, - 0x3e, - 0x21, - 0x4f, - 0x16, - 0x2e, - 0x35, - 0x54, - 0x32, - 0x41, - 0x1c, - 0x48, - 0x2a, - 0x44, - 0xc, - 0x48, - 0x21, - 0x41, - 0x19, - 0x48, - 0x2a, - 0x3d, - 0x21, - 0x44, - 0xb4, - 0x41, - 0x14, - 0x3e, - 0x2b, - 0x45, - 0x23, - 0x50, - 0x28, - 0x3e, - 0x1f, - 0x43, - 0x26, - 0x46, - 0x1b, - 0x48, - 0x12, - 0x44, - 0x2d, - 0x47, - 0x22, - 0x3c, - 0x32, - 0x48, - 0x26, - 0x2f, - 0x21, - 0x45, - 0x17, - 0x43, - 0x22, - 0x43, - 0x1d, - 0x44, - 0x28, - 0x4d, - 0x14, - 0x56, - 0x23, - 0x40, - 0x2c, - 0x34, - 0x80, - 0x44, - 0xf, - 0x37, - 0x16, - 0x49, - 0x21, - 0x34, - 0x1e, - 0x3f, - 0x22, - 0x2b, - 0x16, - 0x34, - 0x28, - 0x43, - 0x2d, - 0x43, - 0x11, - 0x49, - 0x1a, - 0x46, - 0x20, - 0x46, - 0x21, - 0x3d, - 0x17, - 0x3d, - 0x28, - 0x3e, - 0xf5, - 0x33, - 0x15, - 0x39, - 0x20, - 0x4d, - 0x2d, - 0x36, - 0x80, - 0x1a, - 0xdb, - 0x3e, - 0x17, - 0x3b, - 0x1f, - 0x40, - 0x17, - 0x2b, - 0xcf, - 0x39, - 0x2d, - 0x4d, - 0x2b, - 0x35, - 0xf6, - 0x44, - 0x29, - 0x3d, - 0x24, - 0x30, - 0x17, - 0x3b, - 0x28, - 0x44, - 0xd, - 0x38, - 0x20, - 0x3b, - 0xf3, - 0x45, - 0x19, - 0x4c, - 0x24, - 0x37, - 0x15, - 0xf3, - 0xb4, - 0x3c, - 0x28, - 0x36, - 0xf3, - 0x44, - 0x1b, - 0x48, - 0x25, - 0x1d, - 0xd6, - 0x25, - 0xcf, - 0x3a, - 0x9, - 0x3f, - 0xfc, - 0x31, - 0xf1, - 0x41, - 0x24, - 0x44, - 0x17, - 0x45, - 0x20, - 0x42, - 0x2, - 0x33, - 0xb4, - 0x31, - 0x1b, - 0x43, - 0x18, - 0x2c, - 0x14, - 0x44, - 0xa, - 0x43, - 0x7, - 0x4, - 0x80, - 0x2b, - 0xf3, - 0x49, - 0x2a, - 0x47, - 0xea, - 0x3b, - 0xec, - 0x30, - 0xfb, - 0x3c, - 0x18, - 0x35, - 0xff, - 0x14, - 0x18, - 0x39, - 0x7, - 0x3c, - 0x5, - 0xa, - 0xf, - 0x35, - 0x12, - 0x3a, - 0x0, - 0x2d, - 0xc, - 0x46, - 0x13, - 0x3e, - 0x23, - 0x3f, - 0x18, - 0x3a, - 0x16, - 0x35, - 0xf5, - 0x3a, - 0x1b, - 0x4e, - 0x2d, - 0x3c, - 0xef, - 0x3c, - 0xfc, - 0x2e, - 0xa, - 0x32, - 0xb4, - 0x23, - 0xfb, - 0x3e, - 0x16, - 0x40, - 0xe, - 0x24, - 0x3, - 0x44, - 0x24, - 0x3b, - 0xa, - 0x19, - 0x80, - 0x28, - 0x1a, - 0x3b, - 0xfb, - 0x2a, - 0xf, - 0x31, - 0x4, - 0x3a, - 0x4, - 0x2d, - 0xec, - 0x29, - 0xa, - 0x25, - 0xb4, - 0x20, - 0xb4, - 0x35, - 0x1b, - 0x31, - 0xb4, - 0x7, - 0xc, - 0x4b, - 0x1b, - 0x1c, - 0x80, - 0x28, - 0xd6, - 0x23, - 0x16, - 0x2d, - 0xf8, - 0x35, - 0xf6, - 0x45, - 0x11, - 0x1d, - 0xc5, - 0x2a, - 0xf6, - 0x37, - 0xea, - 0x36, - 0x11, - 0x3f, - 0x7, - 0x36, - 0x11, - 0x2e, - 0xf1, - 0x3b, - 0x11, - 0x16, - 0x2a, - 0x3a, - 0x6, - 0x37, - 0xcf, - 0x18, - 0x80, - 0x30, - 0xd6, - 0x14, - 0xf1, - 0x16, - 0xfc, - 0x28, - 0xe4, - 0x3d, - 0xe0, - 0x2d, - 0x80, - 0x26, - 0xec, - 0x3d, - 0xf8, - 0x36, - 0xcf, - 0x11, - 0xef, - 0x2c, - 0x16, - 0x2d, - 0xff, - 0x35, - 0x12, - 0x3e, - 0xa, - 0x35, - 0xd, - 0x2f, - 0xf9, - 0x3f, - 0x2d, - 0x40, - 0x80, - 0xe7, - 0x6, - 0x2a, - 0x80, - 0x34, - 0x4, - 0x5, - 0x1d, - 0x3d, - 0x12, - 0x1e, - 0xa, - 0x3f, - 0x26, - 0x2b, - 0xfb, - 0x2b, - 0x80, - 0x26, - 0x80, - 0x1e, - 0x15, - 0x24, - 0xdb, - 0x2a, - 0xd6, - 0x2b, - 0x80, - 0x6, - 0xdb, - 0x26, - 0xfd, - 0x37, - 0xec, - 0x2a, - 0xec, - 0x2, - 0x1c, - 0x3c, - 0xe7, - 0x11, - 0x80, - 0xf3, - 0xfd, - 0x3a, - 0x1, - 0x28, - 0x17, - 0x3a, - 0xdb, - 0xf6, - 0x80, - 0x2, - 0xd6, - 0x21, - 0xcf, - 0x2a, - 0xdb, - 0xf, - 0x80, - 0x2b, - 0x17, - 0x24, - 0xcf, - 0x2e, - 0xcf, - 0x30, - 0xf8, - 0xa, - 0xf1, - 0x26, - 0xe7, - 0x2d, - 0xf5, - 0x31, - 0xef, - 0x25, - 0x80, - 0x1, - 0xfb, - 0xd6, - 0x80, - 0x19, - 0x1c, - 0x37, - 0xfb, - 0x39, - 0x11, - 0x2c, - 0x80, - 0x23, - 0x18, - 0x33, - 0xf8, - 0x2e, - 0xd, - 0x34, - 0xcf, - 0x2b, - 0xf1, - 0x21, - 0x80, - 0x29, - 0x80, - 0x1f, - 0xe4, - 0xe, - 0xb, - 0x25, - 0xc5, - 0x1f, - 0xc5, - 0x21, - 0x0, - 0x19, - 0x80, - 0xef, - 0x80, - 0xb, - 0xe4, - 0x1c, - 0xcf, - 0x33, - 0x16, - 0x3e, - 0x7, - 0x21, - 0xf5, - 0x2f, - 0x0, - 0x2e, - 0xef, - 0x23, - 0x6, - 0x3d, - 0xe7, - 0x23, - 0xe7, - 0x26, - 0xd6, - 0x40, - 0xfd, - 0x30, - 0x80, - 0xa, - 0xf5, - 0x35, - 0x0, - 0x32, - 0xf8, - 0x20, - 0xcf, - 0x2d, - 0xef, - 0x32, - 0x13, - 0x3c, - 0x1c, - 0x0, - 0xfc, - 0x26, - 0xe0, - 0x26, - 0xd6, - 0xec, - 0x80, - 0x16, - 0xf3, - 0xb4, - 0xf1, - 0x31, - 0xcf, - 0x1f, - 0x80, - 0x7, - 0xf6, - 0x19, - 0xfd, - 0xe7, - 0x80, - 0x1, - 0x80, - 0x1c, - 0x2, - 0x2f, - 0x80, - 0x2f, - 0x80, - 0x26, - 0x4, - 0x1c, - 0xb4, - 0x4, - 0xdb, - 0x1e, - 0xcf, - 0x2a, - 0x80, - 0xdb, - 0x80, - 0x1a, - 0xea, - 0x31, - 0xa, - 0x18, - 0x23, - 0x39, - 0xf8, - 0x36, - 0x22, - 0x25, - 0xc5, - 0x1f, - 0x80, - 0x26, - 0xef, - 0x34, - 0x80, - 0x19, - 0xe7, - 0x2d, - 0xe0, - 0x17, - 0xe4, - 0x2f, - 0x17, - 0x34, - 0x7, - 0x31, - 0xef, - 0x25, - 0xe0, - 0x1e, - 0xf8, - 0x1d, - 0xdb, - 0xfd, - 0xb, - 0x11, - 0x80, - 0x11, - 0x80, - 0xe7, - 0xcf, - 0x32, - 0x80, - 0xc, - 0xdb, - 0xa, - 0x80, - 0xf9, - 0x80, - 0x14, - 0x14, - 0x35, - 0x80, - 0x2c, - 0xf9, - 0x1f, - 0xdb, - 0x1b, - 0xea, - 0x11, - 0x80, - 0x26, - 0xc5, - 0xb, - 0xb4, - 0xb, - 0x80, - 0x7, - 0xef, - 0x22, - 0x6, - 0x20, - 0xe0, - 0x0, - 0x80, - 0x1a, - 0x1c, - 0x25, - 0xfb, - 0x2f, - 0x80, - 0x80, - 0xea, - 0x31, - 0x19, - 0x3c, - 0xf, - 0x23, - 0x80, - 0x16, - 0x0, - 0x38, - 0xf1, - 0x21, - 0xea, - 0x2c, - 0x80, - 0x1e, - 0xec, - 0x2a, - 0xe4, - 0x7, - 0x80, - 0xf8, - 0x80, - 0x9, - 0xd6, - 0x20, - 0xc5, - 0x18, - 0x80, - 0x0, - 0x14, - 0x2a, - 0xcf, - 0x1d, - 0x80, - 0xc, - 0xe4, - 0x1c, - 0xa, - 0x3a, - 0x24, - 0x1b, - 0x80, - 0xf8, - 0x80, - 0x8, - 0x80, - 0x9, - 0x80, - 0x20, - 0xdb, - 0x20, - 0xd6, - 0x2d, - 0x19, - 0x1a, - 0xd6, - 0x25, - 0x80, - 0xb4, - 0x80, - 0x38, - 0x12, - 0x17, - 0xec, - 0x14, - 0x80, - 0x20, - 0xb4, - 0x13, - 0xdb, - 0xb, - 0x80, - 0xfc, - 0x15, - 0x2f, - 0x0, - 0xdb, - 0x80, - 0xf5, - 0x0, - 0x8, - 0xcf, - 0xf8, - 0xe4, - 0xc, - 0x13, - 0x34, - 0x80, - 0x17, - 0x80, - 0xe7, - 0x80, - 0x11, - 0xcf, - 0x2f, - 0xf6, - 0x5, - 0xdb, - 0x27, - 0x6, - 0xf1, - 0x80, - 0x11, - 0xc5, - 0x24, - 0x80, - 0x11, - 0xea, - 0xa, - 0x80, - 0x23, - 0x1, - 0x16, - 0xf3, - 0xfb, - 0x80, - 0x15, - 0x13, - 0x33, - 0x6, - 0xfc, - 0x80, - 0xd6, - 0x80, - 0x10, - 0x80, - 0x1a, - 0xf5, - 0x11, - 0x80, - 0x9, - 0xc5, - 0xf, - 0xcf, - 0xef, - 0xc5, - 0x1b, - 0xf9, - 0x8, - 0x80, - 0x20, - 0xc5, - 0x1c, - 0xdb, - 0x1f, - 0x80, - 0x1e, - 0xf3, - 0x12, - 0xea, - 0x26, - 0xcf, - 0x16, - 0xcf, - 0x2, - 0xd6, - 0x7, - 0x80, - 0x24, - 0x80, - 0xf9, - 0xcf, - 0x1a, - 0xb4, - 0x26, - 0xc5, - 0xfb, - 0x80, - 0xfc, - 0xc5, - 0xef, - 0xcf, - 0x28, - 0x80, - 0x19, - 0xcf, - 0x28, - 0xea, - 0x2c, - 0xc5, - 0x2f, - 0xc, - 0x1, - 0xec, - 0x2d, - 0xb4, - 0x14, - 0x80, - 0xc, - 0xec, - 0xf5, - 0xdb, - 0x0, - 0xc5, - 0x20, - 0x80, - 0x21, - 0x1, - 0x0, - 0x80, - 0xa, - 0x80, - 0x29, - 0x80, - 0xdb, - 0x7, - 0xf, - 0xb4, - 0x23, - 0xfb, - 0x27, - 0xdb, - 0x22, - 0xec, - 0x21, - 0x80, - 0xd6, - 0xb4, - 0x15, - 0xd6, - 0x11, - 0x80, - 0x1f, - 0xc5, - 0x1a, - 0xb4, - 0x7, - 0xe0, - 0x21, - 0xcf, - 0x14, - 0x16, - 0x2a, - 0x80, - 0x80, - 0x80, - 0xa, - 0xe7, - 0x6, - 0x80, - 0xb4, - 0x80, - 0xf, - 0x80, - 0xfc, - 0xe4, - 0x13, - 0x80, - 0x19, - 0xb4, - 0xd, - 0xb4, - 0xdb, - 0xc5, - 0x18, - 0x80, - 0x21, - 0xb4, - 0x2d, - 0xc5, - 0xf1, - 0xdb, - 0xf, - 0x80, - 0x23, - 0xd6, - 0x28, - 0x80, - 0xea, - 0xd6, - 0xe7, - 0xcf, - 0x11, - 0xe4, - 0xec, - 0x2, - 0x20, - 0xb4, - 0x29, - 0xdb, - 0x6, - 0x80, - 0xef, - 0x80, - 0xe0, - 0x80, - 0x4, - 0xc5, - 0x32, - 0xb4, - 0x2f, - 0x80, - 0x7, - 0xb4, - 0xe0, - 0x80, - 0xf5, - 0x80, - 0x5, - 0xb4, - 0x8, - 0xcf, - 0x1f, - 0xf6, - 0x28, - 0xdb, - 0x1b, - 0xff, - 0x12, - 0x80, - 0x2a, - 0xff, - 0x2f, - 0xfc, - 0xcf, - 0x80, - 0xc, - 0xf1, - 0x21, - 0x80, - 0x2, - 0x1, - 0x2d, - 0xf8, - 0xf9, - 0xf3, - 0x25, - 0x80, - 0xdb, - 0x80, - 0xd6, - 0x80, - 0xc, - 0xe4, - 0x1b, - 0xc5, - 0xe0, - 0xec, - 0xec, - 0x80, - 0x6, - 0xb4, - 0xf5, - 0xcf, - 0xc, - 0x80, - 0x1, - 0xf6, - 0x1d, - 0x80, - 0xe7, - 0x80, - 0xf3, - 0x80, - 0xc5, - 0x80, - 0xf6, - 0x80, - 0x1b, - 0xcf, - 0x11, - 0x80, - 0xd6, - 0x80, - 0x80, - 0x80, - 0xdb, - 0x80, - 0xec, - 0x80, - 0x19, - 0xe0, - 0x2, - 0x80, - 0x19, - 0xef, - 0x16, - 0x80, - 0xd6, - 0x80, - 0xe7, - 0x80, - 0x11, - 0xd6, - 0xfc, - 0x80, - 0xa, - 0xd6, - 0x17, - 0xe7, - 0xe4, - 0x80, - 0xb4, - 0xb4, - 0x1d, - 0xb4, - 0xf, - 0x80, - 0x32, - 0xfb, - 0x1b, - 0xdb, - 0x25, - 0xec, - 0xf5, - 0x80, - 0xd6, - 0xef, - 0x23, - 0xec, - 0x14, - 0x80, - 0xe0, - 0xdb, - 0xf9, - 0x80, - 0xcf, - 0x80, - 0xff, - 0xb4, - 0xd, - 0x80, - 0xe4, - 0x80, - 0x0, - 0xc5, - 0x1f, - 0xdb, - 0x23, - 0xe0, - 0x1, - 0x80, - 0x80, - 0x80, - 0xcf, - 0x80, - 0xb4, - 0x80, - 0xe0, - 0xf6, - 0x1d, - 0xcf, - 0xdb, - 0x80, - 0xdb, - 0x80, - 0x80, - 0xb4, - 0xb, - 0x80, - 0x80, - 0x80, - 0x1d, - 0x80, - 0x4, - 0xe4, - 0xf5, - 0x80, - 0x80, - 0x80, - 0x4, - 0x80, - 0xe4, - 0x80, - 0xfc, - 0x80, - 0xd6, - 0x80, - 0xf9, - 0x80, - 0x80, - 0xb4, - 0xc, - 0x80, - 0x26, - 0xf9, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xf1, - 0x80, - 0x80, - 0x80, - 0xf3, - 0xb4, - 0x0, - 0x80, - 0x2, - 0xcf, - 0xb4, - 0xea, - 0x14, - 0x80, - 0x18, - 0x80, - 0xcf, - 0x80, - 0xd, - 0x80, - 0xe0, - 0x80, - 0x16, - 0x80, - 0xf8, - 0xc5, - 0x11, - 0xb4, - 0xf8, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xe4, - 0xe, - 0x1c, - 0x80, - 0xfc, - 0xb4, - 0x2a, - 0x6, - 0x31, - 0x10, - 0x1c, - 0x80, - 0xfd, - 0xfc, - 0xc, - 0xe7, - 0xea, - 0x80, - 0xe7, - 0xd6, - 0xd, - 0xb4, - 0x22, - 0xf1, - 0x7, - 0xb4, - 0x1d, - 0xf6, - 0x11, - 0xd6, - 0x28, - 0x80, - 0xc5, - 0xb4, - 0x1f, - 0xe0, - 0x80, - 0x80, - 0x80, - 0x80, - 0xfb, - 0xe7, - 0xc, - 0x80, - 0xdb, - 0x80, - 0xcf, - 0x80, - 0x80, - 0x80, - 0xd6, - 0xc5, - 0xf, - 0x80, - 0x80, - 0xb4, - 0x1b, - 0x80, - 0x0, - 0xdb, - 0xf5, - 0x80, - 0x80, - 0x80, - 0x15, - 0xec, - 0xf, - 0x80, - 0xd6, - 0x80, - 0x80, - 0xb4, - 0xc, - 0xd6, - 0xd6, - 0x80, - 0xd6, - 0xd6, - 0x9, - 0x80, - 0x80, - 0x80, - 0x3, - 0xc5, - 0x9, - 0x80, - 0x80, - 0x80, - 0xe4, - 0x80, - 0xf3, - 0x80, - 0x10, - 0xea, - 0xb4, - 0x80, - 0xdb, - 0xf3, - 0xa, - 0x80, - 0xc5, - 0x80, - 0xef, - 0x80, - 0xc5, - 0x80, - 0xec, - 0x80, - 0xff, - 0x80, - 0xa, - 0xc5, - 0xf1, - 0x80, - 0xb4, - 0x80, - 0xe0, - 0x80, - 0xfb, - 0x80, - 0xf8, - 0x80, - 0x3, - 0x80, - 0xc, - 0xcf, - 0x80, - 0xd6, - 0xe0, - 0x80, - 0x80, - 0xb4, - 0xcf, - 0xc5, - 0x28, - 0xd6, - 0x17, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xec, - 0x14, - 0x80, - 0xf3, - 0x80, - 0xf8, - 0x80, - 0xf3, - 0x80, - 0xcf, - 0x80, - 0xf8, - 0xe0, - 0xea, - 0x80, - 0xc5, - 0x0, - 0x35, - 0xea, - 0x3, - 0x80, - 0x80, - 0x80, - 0x17, - 0xf, - 0x16, - 0x80, - 0x19, - 0xd6, - 0x80, - 0x80, - 0x80, - 0x80, - 0xe0, - 0x80, - 0xfd, - 0x80, - 0x4, - 0xfc, - 0x1e, - 0x80, - 0xef, - 0x80, - 0xef, - 0xf1, - 0x1f, - 0x80, - 0xfc, - 0x80, - 0xe7, - 0x80, - 0xff, - 0x80, - 0xf8, - 0x80, - 0x80, - 0x80, - 0x17, - 0x80, - 0xcf, - 0xfb, - 0x1c, - 0x0, - 0x26, - 0x11, - 0x16, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xcf, - 0xf3, - 0x80, - 0x14, - 0xb4, - 0xdb, - 0x5, - 0x19, - 0x80, - 0xd6, - 0x80, - 0xf5, - 0x80, - 0x17, - 0xc5, - 0x0, - 0xc5, - 0xcf, - 0xc5, - 0x4, - 0x80, - 0x5, - 0x80, - 0xa, - 0x80, - 0x19, - 0xd6, - 0x28, - 0x5, - 0xea, - 0x80, - 0x80, - 0x80, - 0x80, - 0xec, - 0xd, - 0x80, - 0x80, - 0x80, - 0x2, - 0x80, - 0xf1, - 0x80, - 0x80, - 0x80, - 0xd6, - 0x80, - 0xd6, - 0x80, - 0xdb, - 0x80, - 0xf3, - 0x80, - 0xff, - 0x80, - 0x80, - 0xc5, - 0x20, - 0x80, - 0xea, - 0x80, - 0xb4, - 0x80, - 0x22, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xc5, - 0x80, - 0x15, - 0x80, - 0x24, - 0xc5, - 0xfc, - 0x80, - 0xb, - 0xe4, - 0xcf, - 0x80, - 0x80, - 0x80, - 0xe7, - 0xa, - 0x1, - 0xdb, - 0x12, - 0x80, - 0xf5, - 0x80, - 0x80, - 0x80, - 0xa, - 0xd6, - 0xfd, - 0xf5, - 0xfc, - 0xcf, - 0xe, - 0x80, - 0xd6, - 0x80, - 0x80, - 0x80, - 0xef, - 0x80, - 0xfd, - 0xc5, - 0x12, - 0xea, - 0x20, - 0x80, - 0xe0, - 0xdb, - 0xc5, - 0xd6, - 0x1a, - 0x80, - 0x80, - 0xd6, - 0x14, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0xc5, - 0xb4, - 0xe4, - 0xb4, - 0xf6, - 0x3, - 0xfc, - 0x80, - 0x80, - 0x80, - 0xfb, - 0x80, - 0x0, - 0xe4, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x5, - 0xb4, - 0x80, - 0x80, - 0x19, - 0xd6, - 0xe0, - 0x80, - 0x80, - 0x80, - 0xb4, - 0xc5, - 0xb4, - 0x80, - 0xfb, - 0x4, - 0x13, - 0x80, - 0xf, - 0xc5, - 0x2, - 0xec, - 0xb4, - 0xb4, - 0xef, - 0x80, - 0xe0, - 0x80, - 0xcf, - 0xf5, - 0x1, - 0x80, - 0xe4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x1, - 0x1c, - 0x80, - 0x80, - 0x80, - 0xc5, - 0xcf, - 0xc, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xfb, - 0xc5, - 0xcf, - 0xdb, - 0xcf, - 0x80, - 0xd6, - 0x80, - 0xea, - 0x80, - 0x80, - 0x80, - 0xd6, - 0x80, - 0x80, - 0xcf, - 0xf9, - 0xdb, - 0xf8, - 0x80, - 0xdb, - 0xb4, - 0xff, - 0xe0, - 0xb4, - 0x80, - 0x80, - 0x80, - 0x80, - 0x80, - 0xea, - 0x80, - 0xc5, - 0x80, - 0x80, - 0x80, - 0xe0, - 0xec, - 0xf5, - 0x80, - 0x80, - 0x80, - 0x17, - 0x80, - 0xcf, - 0x80, - 0xf8, - 0xf6, - 0xe7, - 0x80, - 0xd6, - 0x80, - 0xcf, - 0x80, - 0x80, - 0x80, - 0xb4, - 0x80, - 0xe4, - 0x80, - 0xf8, - 0x80, - 0x80, - 0x80, - 0xdb, - 0x80, - 0xfb, - 0x80, - 0x80, - 0x80, - 0xf3, - 0x80, - 0x11, - 0xc5, - 0x80, - 0x80, - 0xb4, - 0x80, - 0x80, - 0x80, - 0xd6, - 0x80, - 0xec, - 0xb4, - 0x14, - 0xb4, - 0xf3, - 0x80, - 0xf9, - 0x80, - 0x8, - 0x80, - 0x80, - 0x80, - 0xe7, - 0x80, - 0x80, - 0xc5, - 0xf1, - 0x80, - 0xf3, - 0x80 -}; + 0x7c, 0x66, 0x79, 0x65, 0x7d, 0x67, 0x7c, 0x67, 0x7c, 0x66, 0x7c, 0x67, 0x7c, 0x67, 0x7d, 0x66, + 0x7c, 0x67, 0x7d, 0x66, 0x7c, 0x67, 0x7d, 0x66, 0x7c, 0x67, 0x7d, 0x67, 0x7d, 0x67, 0x7d, 0x67, + 0x7d, 0x67, 0x7d, 0x67, 0x7d, 0x67, 0x7d, 0x67, 0x52, 0x57, 0x78, 0x5a, 0x67, 0x53, 0x6f, 0x4b, + 0x6d, 0x5c, 0x71, 0x52, 0x66, 0x4d, 0x6e, 0x56, 0x73, 0x50, 0x5f, 0x54, 0x6d, 0x55, 0x6a, 0x5b, + 0x6f, 0x57, 0x68, 0x50, 0x71, 0x58, 0x6d, 0x57, 0x69, 0x55, 0x6a, 0x55, 0x6c, 0x59, 0x6c, 0x5a, + 0x5b, 0x3c, 0x54, 0x44, 0x58, 0x4f, 0x66, 0x30, 0x58, 0x50, 0x61, 0x3d, 0x67, 0x36, 0x5b, 0x4d, + 0x64, 0x51, 0x6a, 0x4d, 0x60, 0x4b, 0x61, 0x53, 0x69, 0x54, 0x60, 0x47, 0x5c, 0x4d, 0x63, 0x45, + 0x64, 0x4d, 0x63, 0x4b, 0x67, 0x50, 0x68, 0x4d, 0x64, 0x4b, 0x64, 0x4e, 0x5f, 0x3d, 0x53, 0x42, + 0x59, 0x39, 0x57, 0x43, 0x5e, 0x3a, 0x44, 0x3b, 0x56, 0x3c, 0x5c, 0x46, 0x66, 0x4c, 0x61, 0x3e, + 0x5d, 0x49, 0x55, 0x48, 0x5d, 0x45, 0x5a, 0x48, 0x5f, 0x41, 0x59, 0x49, 0x5a, 0x46, 0x5d, 0x3b, + 0x51, 0x3d, 0x4c, 0x44, 0x57, 0x37, 0x54, 0x43, 0x4f, 0xa, 0x32, 0x28, 0x5b, 0x3a, 0x5e, 0x47, + 0x4d, 0x2b, 0x57, 0x4a, 0x5d, 0x34, 0x52, 0x3e, 0x50, 0x38, 0x54, 0x30, 0x53, 0x41, 0x57, 0x39, + 0x5c, 0x3c, 0x53, 0x41, 0x5a, 0x1e, 0x4e, 0x41, 0x4d, 0x2c, 0x3e, 0x18, 0x4c, 0x1c, 0x36, 0x11, + 0x4b, 0x32, 0x52, 0x2f, 0x50, 0x2d, 0x4e, 0x20, 0x50, 0x3c, 0x4a, 0x16, 0x44, 0x22, 0x48, 0x29, + 0x4d, 0x34, 0x4e, 0x2c, 0x52, 0x2e, 0x46, 0x35, 0x4b, 0x14, 0x50, 0x33, 0x53, 0x3e, 0x50, 0x2d, + 0x4a, 0x0, 0x4b, 0x3a, 0x47, 0x16, 0x45, 0x32, 0x45, 0x10, 0x42, 0x23, 0x49, 0x39, 0x41, 0x10, + 0x48, 0x32, 0x4e, 0x30, 0x40, 0x34, 0x46, 0x39, 0x54, 0xf5, 0x49, 0x38, 0x53, 0x2c, 0x4a, 0x37, + 0x51, 0x2c, 0x46, 0x2f, 0x4c, 0x2a, 0x4d, 0x2b, 0x3d, 0x2f, 0x4e, 0x20, 0x1e, 0x7, 0x41, 0x8, + 0x39, 0xd, 0x46, 0x20, 0x3b, 0x2a, 0x3f, 0x20, 0x40, 0xe, 0x4e, 0x2e, 0x3e, 0x21, 0x4f, 0x16, + 0x2e, 0x35, 0x54, 0x32, 0x41, 0x1c, 0x48, 0x2a, 0x44, 0xc, 0x48, 0x21, 0x41, 0x19, 0x48, 0x2a, + 0x3d, 0x21, 0x44, 0xb4, 0x41, 0x14, 0x3e, 0x2b, 0x45, 0x23, 0x50, 0x28, 0x3e, 0x1f, 0x43, 0x26, + 0x46, 0x1b, 0x48, 0x12, 0x44, 0x2d, 0x47, 0x22, 0x3c, 0x32, 0x48, 0x26, 0x2f, 0x21, 0x45, 0x17, + 0x43, 0x22, 0x43, 0x1d, 0x44, 0x28, 0x4d, 0x14, 0x56, 0x23, 0x40, 0x2c, 0x34, 0x80, 0x44, 0xf, + 0x37, 0x16, 0x49, 0x21, 0x34, 0x1e, 0x3f, 0x22, 0x2b, 0x16, 0x34, 0x28, 0x43, 0x2d, 0x43, 0x11, + 0x49, 0x1a, 0x46, 0x20, 0x46, 0x21, 0x3d, 0x17, 0x3d, 0x28, 0x3e, 0xf5, 0x33, 0x15, 0x39, 0x20, + 0x4d, 0x2d, 0x36, 0x80, 0x1a, 0xdb, 0x3e, 0x17, 0x3b, 0x1f, 0x40, 0x17, 0x2b, 0xcf, 0x39, 0x2d, + 0x4d, 0x2b, 0x35, 0xf6, 0x44, 0x29, 0x3d, 0x24, 0x30, 0x17, 0x3b, 0x28, 0x44, 0xd, 0x38, 0x20, + 0x3b, 0xf3, 0x45, 0x19, 0x4c, 0x24, 0x37, 0x15, 0xf3, 0xb4, 0x3c, 0x28, 0x36, 0xf3, 0x44, 0x1b, + 0x48, 0x25, 0x1d, 0xd6, 0x25, 0xcf, 0x3a, 0x9, 0x3f, 0xfc, 0x31, 0xf1, 0x41, 0x24, 0x44, 0x17, + 0x45, 0x20, 0x42, 0x2, 0x33, 0xb4, 0x31, 0x1b, 0x43, 0x18, 0x2c, 0x14, 0x44, 0xa, 0x43, 0x7, + 0x4, 0x80, 0x2b, 0xf3, 0x49, 0x2a, 0x47, 0xea, 0x3b, 0xec, 0x30, 0xfb, 0x3c, 0x18, 0x35, 0xff, + 0x14, 0x18, 0x39, 0x7, 0x3c, 0x5, 0xa, 0xf, 0x35, 0x12, 0x3a, 0x0, 0x2d, 0xc, 0x46, 0x13, + 0x3e, 0x23, 0x3f, 0x18, 0x3a, 0x16, 0x35, 0xf5, 0x3a, 0x1b, 0x4e, 0x2d, 0x3c, 0xef, 0x3c, 0xfc, + 0x2e, 0xa, 0x32, 0xb4, 0x23, 0xfb, 0x3e, 0x16, 0x40, 0xe, 0x24, 0x3, 0x44, 0x24, 0x3b, 0xa, + 0x19, 0x80, 0x28, 0x1a, 0x3b, 0xfb, 0x2a, 0xf, 0x31, 0x4, 0x3a, 0x4, 0x2d, 0xec, 0x29, 0xa, + 0x25, 0xb4, 0x20, 0xb4, 0x35, 0x1b, 0x31, 0xb4, 0x7, 0xc, 0x4b, 0x1b, 0x1c, 0x80, 0x28, 0xd6, + 0x23, 0x16, 0x2d, 0xf8, 0x35, 0xf6, 0x45, 0x11, 0x1d, 0xc5, 0x2a, 0xf6, 0x37, 0xea, 0x36, 0x11, + 0x3f, 0x7, 0x36, 0x11, 0x2e, 0xf1, 0x3b, 0x11, 0x16, 0x2a, 0x3a, 0x6, 0x37, 0xcf, 0x18, 0x80, + 0x30, 0xd6, 0x14, 0xf1, 0x16, 0xfc, 0x28, 0xe4, 0x3d, 0xe0, 0x2d, 0x80, 0x26, 0xec, 0x3d, 0xf8, + 0x36, 0xcf, 0x11, 0xef, 0x2c, 0x16, 0x2d, 0xff, 0x35, 0x12, 0x3e, 0xa, 0x35, 0xd, 0x2f, 0xf9, + 0x3f, 0x2d, 0x40, 0x80, 0xe7, 0x6, 0x2a, 0x80, 0x34, 0x4, 0x5, 0x1d, 0x3d, 0x12, 0x1e, 0xa, + 0x3f, 0x26, 0x2b, 0xfb, 0x2b, 0x80, 0x26, 0x80, 0x1e, 0x15, 0x24, 0xdb, 0x2a, 0xd6, 0x2b, 0x80, + 0x6, 0xdb, 0x26, 0xfd, 0x37, 0xec, 0x2a, 0xec, 0x2, 0x1c, 0x3c, 0xe7, 0x11, 0x80, 0xf3, 0xfd, + 0x3a, 0x1, 0x28, 0x17, 0x3a, 0xdb, 0xf6, 0x80, 0x2, 0xd6, 0x21, 0xcf, 0x2a, 0xdb, 0xf, 0x80, + 0x2b, 0x17, 0x24, 0xcf, 0x2e, 0xcf, 0x30, 0xf8, 0xa, 0xf1, 0x26, 0xe7, 0x2d, 0xf5, 0x31, 0xef, + 0x25, 0x80, 0x1, 0xfb, 0xd6, 0x80, 0x19, 0x1c, 0x37, 0xfb, 0x39, 0x11, 0x2c, 0x80, 0x23, 0x18, + 0x33, 0xf8, 0x2e, 0xd, 0x34, 0xcf, 0x2b, 0xf1, 0x21, 0x80, 0x29, 0x80, 0x1f, 0xe4, 0xe, 0xb, + 0x25, 0xc5, 0x1f, 0xc5, 0x21, 0x0, 0x19, 0x80, 0xef, 0x80, 0xb, 0xe4, 0x1c, 0xcf, 0x33, 0x16, + 0x3e, 0x7, 0x21, 0xf5, 0x2f, 0x0, 0x2e, 0xef, 0x23, 0x6, 0x3d, 0xe7, 0x23, 0xe7, 0x26, 0xd6, + 0x40, 0xfd, 0x30, 0x80, 0xa, 0xf5, 0x35, 0x0, 0x32, 0xf8, 0x20, 0xcf, 0x2d, 0xef, 0x32, 0x13, + 0x3c, 0x1c, 0x0, 0xfc, 0x26, 0xe0, 0x26, 0xd6, 0xec, 0x80, 0x16, 0xf3, 0xb4, 0xf1, 0x31, 0xcf, + 0x1f, 0x80, 0x7, 0xf6, 0x19, 0xfd, 0xe7, 0x80, 0x1, 0x80, 0x1c, 0x2, 0x2f, 0x80, 0x2f, 0x80, + 0x26, 0x4, 0x1c, 0xb4, 0x4, 0xdb, 0x1e, 0xcf, 0x2a, 0x80, 0xdb, 0x80, 0x1a, 0xea, 0x31, 0xa, + 0x18, 0x23, 0x39, 0xf8, 0x36, 0x22, 0x25, 0xc5, 0x1f, 0x80, 0x26, 0xef, 0x34, 0x80, 0x19, 0xe7, + 0x2d, 0xe0, 0x17, 0xe4, 0x2f, 0x17, 0x34, 0x7, 0x31, 0xef, 0x25, 0xe0, 0x1e, 0xf8, 0x1d, 0xdb, + 0xfd, 0xb, 0x11, 0x80, 0x11, 0x80, 0xe7, 0xcf, 0x32, 0x80, 0xc, 0xdb, 0xa, 0x80, 0xf9, 0x80, + 0x14, 0x14, 0x35, 0x80, 0x2c, 0xf9, 0x1f, 0xdb, 0x1b, 0xea, 0x11, 0x80, 0x26, 0xc5, 0xb, 0xb4, + 0xb, 0x80, 0x7, 0xef, 0x22, 0x6, 0x20, 0xe0, 0x0, 0x80, 0x1a, 0x1c, 0x25, 0xfb, 0x2f, 0x80, + 0x80, 0xea, 0x31, 0x19, 0x3c, 0xf, 0x23, 0x80, 0x16, 0x0, 0x38, 0xf1, 0x21, 0xea, 0x2c, 0x80, + 0x1e, 0xec, 0x2a, 0xe4, 0x7, 0x80, 0xf8, 0x80, 0x9, 0xd6, 0x20, 0xc5, 0x18, 0x80, 0x0, 0x14, + 0x2a, 0xcf, 0x1d, 0x80, 0xc, 0xe4, 0x1c, 0xa, 0x3a, 0x24, 0x1b, 0x80, 0xf8, 0x80, 0x8, 0x80, + 0x9, 0x80, 0x20, 0xdb, 0x20, 0xd6, 0x2d, 0x19, 0x1a, 0xd6, 0x25, 0x80, 0xb4, 0x80, 0x38, 0x12, + 0x17, 0xec, 0x14, 0x80, 0x20, 0xb4, 0x13, 0xdb, 0xb, 0x80, 0xfc, 0x15, 0x2f, 0x0, 0xdb, 0x80, + 0xf5, 0x0, 0x8, 0xcf, 0xf8, 0xe4, 0xc, 0x13, 0x34, 0x80, 0x17, 0x80, 0xe7, 0x80, 0x11, 0xcf, + 0x2f, 0xf6, 0x5, 0xdb, 0x27, 0x6, 0xf1, 0x80, 0x11, 0xc5, 0x24, 0x80, 0x11, 0xea, 0xa, 0x80, + 0x23, 0x1, 0x16, 0xf3, 0xfb, 0x80, 0x15, 0x13, 0x33, 0x6, 0xfc, 0x80, 0xd6, 0x80, 0x10, 0x80, + 0x1a, 0xf5, 0x11, 0x80, 0x9, 0xc5, 0xf, 0xcf, 0xef, 0xc5, 0x1b, 0xf9, 0x8, 0x80, 0x20, 0xc5, + 0x1c, 0xdb, 0x1f, 0x80, 0x1e, 0xf3, 0x12, 0xea, 0x26, 0xcf, 0x16, 0xcf, 0x2, 0xd6, 0x7, 0x80, + 0x24, 0x80, 0xf9, 0xcf, 0x1a, 0xb4, 0x26, 0xc5, 0xfb, 0x80, 0xfc, 0xc5, 0xef, 0xcf, 0x28, 0x80, + 0x19, 0xcf, 0x28, 0xea, 0x2c, 0xc5, 0x2f, 0xc, 0x1, 0xec, 0x2d, 0xb4, 0x14, 0x80, 0xc, 0xec, + 0xf5, 0xdb, 0x0, 0xc5, 0x20, 0x80, 0x21, 0x1, 0x0, 0x80, 0xa, 0x80, 0x29, 0x80, 0xdb, 0x7, + 0xf, 0xb4, 0x23, 0xfb, 0x27, 0xdb, 0x22, 0xec, 0x21, 0x80, 0xd6, 0xb4, 0x15, 0xd6, 0x11, 0x80, + 0x1f, 0xc5, 0x1a, 0xb4, 0x7, 0xe0, 0x21, 0xcf, 0x14, 0x16, 0x2a, 0x80, 0x80, 0x80, 0xa, 0xe7, + 0x6, 0x80, 0xb4, 0x80, 0xf, 0x80, 0xfc, 0xe4, 0x13, 0x80, 0x19, 0xb4, 0xd, 0xb4, 0xdb, 0xc5, + 0x18, 0x80, 0x21, 0xb4, 0x2d, 0xc5, 0xf1, 0xdb, 0xf, 0x80, 0x23, 0xd6, 0x28, 0x80, 0xea, 0xd6, + 0xe7, 0xcf, 0x11, 0xe4, 0xec, 0x2, 0x20, 0xb4, 0x29, 0xdb, 0x6, 0x80, 0xef, 0x80, 0xe0, 0x80, + 0x4, 0xc5, 0x32, 0xb4, 0x2f, 0x80, 0x7, 0xb4, 0xe0, 0x80, 0xf5, 0x80, 0x5, 0xb4, 0x8, 0xcf, + 0x1f, 0xf6, 0x28, 0xdb, 0x1b, 0xff, 0x12, 0x80, 0x2a, 0xff, 0x2f, 0xfc, 0xcf, 0x80, 0xc, 0xf1, + 0x21, 0x80, 0x2, 0x1, 0x2d, 0xf8, 0xf9, 0xf3, 0x25, 0x80, 0xdb, 0x80, 0xd6, 0x80, 0xc, 0xe4, + 0x1b, 0xc5, 0xe0, 0xec, 0xec, 0x80, 0x6, 0xb4, 0xf5, 0xcf, 0xc, 0x80, 0x1, 0xf6, 0x1d, 0x80, + 0xe7, 0x80, 0xf3, 0x80, 0xc5, 0x80, 0xf6, 0x80, 0x1b, 0xcf, 0x11, 0x80, 0xd6, 0x80, 0x80, 0x80, + 0xdb, 0x80, 0xec, 0x80, 0x19, 0xe0, 0x2, 0x80, 0x19, 0xef, 0x16, 0x80, 0xd6, 0x80, 0xe7, 0x80, + 0x11, 0xd6, 0xfc, 0x80, 0xa, 0xd6, 0x17, 0xe7, 0xe4, 0x80, 0xb4, 0xb4, 0x1d, 0xb4, 0xf, 0x80, + 0x32, 0xfb, 0x1b, 0xdb, 0x25, 0xec, 0xf5, 0x80, 0xd6, 0xef, 0x23, 0xec, 0x14, 0x80, 0xe0, 0xdb, + 0xf9, 0x80, 0xcf, 0x80, 0xff, 0xb4, 0xd, 0x80, 0xe4, 0x80, 0x0, 0xc5, 0x1f, 0xdb, 0x23, 0xe0, + 0x1, 0x80, 0x80, 0x80, 0xcf, 0x80, 0xb4, 0x80, 0xe0, 0xf6, 0x1d, 0xcf, 0xdb, 0x80, 0xdb, 0x80, + 0x80, 0xb4, 0xb, 0x80, 0x80, 0x80, 0x1d, 0x80, 0x4, 0xe4, 0xf5, 0x80, 0x80, 0x80, 0x4, 0x80, + 0xe4, 0x80, 0xfc, 0x80, 0xd6, 0x80, 0xf9, 0x80, 0x80, 0xb4, 0xc, 0x80, 0x26, 0xf9, 0x80, 0x80, + 0xb4, 0x80, 0xf1, 0x80, 0x80, 0x80, 0xf3, 0xb4, 0x0, 0x80, 0x2, 0xcf, 0xb4, 0xea, 0x14, 0x80, + 0x18, 0x80, 0xcf, 0x80, 0xd, 0x80, 0xe0, 0x80, 0x16, 0x80, 0xf8, 0xc5, 0x11, 0xb4, 0xf8, 0x80, + 0x80, 0x80, 0x80, 0x80, 0xe4, 0xe, 0x1c, 0x80, 0xfc, 0xb4, 0x2a, 0x6, 0x31, 0x10, 0x1c, 0x80, + 0xfd, 0xfc, 0xc, 0xe7, 0xea, 0x80, 0xe7, 0xd6, 0xd, 0xb4, 0x22, 0xf1, 0x7, 0xb4, 0x1d, 0xf6, + 0x11, 0xd6, 0x28, 0x80, 0xc5, 0xb4, 0x1f, 0xe0, 0x80, 0x80, 0x80, 0x80, 0xfb, 0xe7, 0xc, 0x80, + 0xdb, 0x80, 0xcf, 0x80, 0x80, 0x80, 0xd6, 0xc5, 0xf, 0x80, 0x80, 0xb4, 0x1b, 0x80, 0x0, 0xdb, + 0xf5, 0x80, 0x80, 0x80, 0x15, 0xec, 0xf, 0x80, 0xd6, 0x80, 0x80, 0xb4, 0xc, 0xd6, 0xd6, 0x80, + 0xd6, 0xd6, 0x9, 0x80, 0x80, 0x80, 0x3, 0xc5, 0x9, 0x80, 0x80, 0x80, 0xe4, 0x80, 0xf3, 0x80, + 0x10, 0xea, 0xb4, 0x80, 0xdb, 0xf3, 0xa, 0x80, 0xc5, 0x80, 0xef, 0x80, 0xc5, 0x80, 0xec, 0x80, + 0xff, 0x80, 0xa, 0xc5, 0xf1, 0x80, 0xb4, 0x80, 0xe0, 0x80, 0xfb, 0x80, 0xf8, 0x80, 0x3, 0x80, + 0xc, 0xcf, 0x80, 0xd6, 0xe0, 0x80, 0x80, 0xb4, 0xcf, 0xc5, 0x28, 0xd6, 0x17, 0x80, 0x80, 0x80, + 0xc5, 0xec, 0x14, 0x80, 0xf3, 0x80, 0xf8, 0x80, 0xf3, 0x80, 0xcf, 0x80, 0xf8, 0xe0, 0xea, 0x80, + 0xc5, 0x0, 0x35, 0xea, 0x3, 0x80, 0x80, 0x80, 0x17, 0xf, 0x16, 0x80, 0x19, 0xd6, 0x80, 0x80, + 0x80, 0x80, 0xe0, 0x80, 0xfd, 0x80, 0x4, 0xfc, 0x1e, 0x80, 0xef, 0x80, 0xef, 0xf1, 0x1f, 0x80, + 0xfc, 0x80, 0xe7, 0x80, 0xff, 0x80, 0xf8, 0x80, 0x80, 0x80, 0x17, 0x80, 0xcf, 0xfb, 0x1c, 0x0, + 0x26, 0x11, 0x16, 0x80, 0x80, 0xb4, 0x80, 0x80, 0x80, 0xcf, 0xf3, 0x80, 0x14, 0xb4, 0xdb, 0x5, + 0x19, 0x80, 0xd6, 0x80, 0xf5, 0x80, 0x17, 0xc5, 0x0, 0xc5, 0xcf, 0xc5, 0x4, 0x80, 0x5, 0x80, + 0xa, 0x80, 0x19, 0xd6, 0x28, 0x5, 0xea, 0x80, 0x80, 0x80, 0x80, 0xec, 0xd, 0x80, 0x80, 0x80, + 0x2, 0x80, 0xf1, 0x80, 0x80, 0x80, 0xd6, 0x80, 0xd6, 0x80, 0xdb, 0x80, 0xf3, 0x80, 0xff, 0x80, + 0x80, 0xc5, 0x20, 0x80, 0xea, 0x80, 0xb4, 0x80, 0x22, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc5, 0x80, + 0x15, 0x80, 0x24, 0xc5, 0xfc, 0x80, 0xb, 0xe4, 0xcf, 0x80, 0x80, 0x80, 0xe7, 0xa, 0x1, 0xdb, + 0x12, 0x80, 0xf5, 0x80, 0x80, 0x80, 0xa, 0xd6, 0xfd, 0xf5, 0xfc, 0xcf, 0xe, 0x80, 0xd6, 0x80, + 0x80, 0x80, 0xef, 0x80, 0xfd, 0xc5, 0x12, 0xea, 0x20, 0x80, 0xe0, 0xdb, 0xc5, 0xd6, 0x1a, 0x80, + 0x80, 0xd6, 0x14, 0xc5, 0x80, 0x80, 0x80, 0xb4, 0x80, 0x80, 0xc5, 0xb4, 0xe4, 0xb4, 0xf6, 0x3, + 0xfc, 0x80, 0x80, 0x80, 0xfb, 0x80, 0x0, 0xe4, 0x80, 0x80, 0xb4, 0x80, 0x5, 0xb4, 0x80, 0x80, + 0x19, 0xd6, 0xe0, 0x80, 0x80, 0x80, 0xb4, 0xc5, 0xb4, 0x80, 0xfb, 0x4, 0x13, 0x80, 0xf, 0xc5, + 0x2, 0xec, 0xb4, 0xb4, 0xef, 0x80, 0xe0, 0x80, 0xcf, 0xf5, 0x1, 0x80, 0xe4, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x1, 0x1c, 0x80, 0x80, 0x80, 0xc5, 0xcf, 0xc, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0xfb, 0xc5, 0xcf, 0xdb, 0xcf, 0x80, 0xd6, 0x80, 0xea, 0x80, 0x80, 0x80, 0xd6, 0x80, + 0x80, 0xcf, 0xf9, 0xdb, 0xf8, 0x80, 0xdb, 0xb4, 0xff, 0xe0, 0xb4, 0x80, 0x80, 0x80, 0x80, 0x80, + 0xea, 0x80, 0xc5, 0x80, 0x80, 0x80, 0xe0, 0xec, 0xf5, 0x80, 0x80, 0x80, 0x17, 0x80, 0xcf, 0x80, + 0xf8, 0xf6, 0xe7, 0x80, 0xd6, 0x80, 0xcf, 0x80, 0x80, 0x80, 0xb4, 0x80, 0xe4, 0x80, 0xf8, 0x80, + 0x80, 0x80, 0xdb, 0x80, 0xfb, 0x80, 0x80, 0x80, 0xf3, 0x80, 0x11, 0xc5, 0x80, 0x80, 0xb4, 0x80, + 0x80, 0x80, 0xd6, 0x80, 0xec, 0xb4, 0x14, 0xb4, 0xf3, 0x80, 0xf9, 0x80, 0x8, 0x80, 0x80, 0x80, + 0xe7, 0x80, 0x80, 0xc5, 0xf1, 0x80, 0xf3, 0x80}; From 5d08e538138111bd37babb85cd06d4484e6850dd Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 17:35:23 -0700 Subject: [PATCH 36/47] Slow but usable RNG --- .../arduino/template_project/src/implementation.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c index a28d370b5984..735c1b170247 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -80,10 +80,12 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { } unsigned int random_seed = 0; +// Arduino's built-in RNG is implemented differently on each +// platform, and varies widely in quality + speed. Might be +// worth implementing our own RNG. tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { - for (size_t i = 0; i < num_bytes; ++i) { - buffer[i] = (uint8_t)4; // Chosen by fair die roll - // Guaranteed to be random + for (size_t i = 0; i < num_bytes; i++) { + buffer[i] = (uint8_t) random(256); } return kTvmErrorNoError; } From 711bfbd86a40b25c69cf8e4b39a3e3120035cb00 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 17:36:02 -0700 Subject: [PATCH 37/47] Linting --- apps/microtvm/arduino/template_project/src/implementation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c index 735c1b170247..284572939900 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -85,7 +85,7 @@ unsigned int random_seed = 0; // worth implementing our own RNG. tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { for (size_t i = 0; i < num_bytes; i++) { - buffer[i] = (uint8_t) random(256); + buffer[i] = (uint8_t)random(256); } return kTvmErrorNoError; } From db578c1959180c73dd04148e887d85db2076ade3 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 19 Jul 2021 17:03:39 -0700 Subject: [PATCH 38/47] Add support for other hardware platforms --- .../template_project/microtvm_api_server.py | 11 ++++++++++- .../arduino/template_project/src/implementation.c | 6 +----- .../arduino/template_project/src/parameters.h | 4 ++-- tests/micro/arduino/testdata/project.ino | 14 +++++++------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index dda1bc062d8f..b9253e7c870c 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -26,7 +26,6 @@ BUILD_DIR = API_SERVER_DIR / "build" IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() MODEL_LIBRARY_FORMAT_PATH = "" if IS_TEMPLATE else API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH -_LOG = logging.getLogger(__name__) class InvalidPortException(Exception): @@ -104,6 +103,15 @@ def _remove_unused_components(self, source_dir): for component in self.UNUSED_COMPONENTS: shutil.rmtree(source_dir / "standalone_crt" / component) + # We need to remove these duplicate copies of unit tests from the + # tree to avoid pytest finding two copies + PYTEST_FILE_REGEX = r"(?:test_.*\.py)|(?:.*_test\.py)" + + def _remove_unit_tests(self, source_dir): + for file_path in source_dir.rglob(f"*.py"): + if re.match(self.PYTEST_FILE_REGEX, file_path.name): + file_path.unlink() + GRAPH_JSON_TEMPLATE = 'static const char* graph_json = "{}";\n' def _compile_graph_json(self, model_dir, obj): @@ -238,6 +246,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Copy standalone_crt into src folder self._copy_standalone_crt(source_dir, standalone_crt_dir) self._remove_unused_components(source_dir) + self._remove_unit_tests(project_dir) # Unpack the MLF and copy the relevant files graph = self._disassemble_mlf(model_library_format_path, source_dir) diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c index 284572939900..3e37990ce9b2 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -79,13 +79,9 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { return kTvmErrorNoError; } -unsigned int random_seed = 0; -// Arduino's built-in RNG is implemented differently on each -// platform, and varies widely in quality + speed. Might be -// worth implementing our own RNG. tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { for (size_t i = 0; i < num_bytes; i++) { - buffer[i] = (uint8_t)random(256); + buffer[i] = rand(); } return kTvmErrorNoError; } diff --git a/apps/microtvm/arduino/template_project/src/parameters.h b/apps/microtvm/arduino/template_project/src/parameters.h index f9f6d78f21eb..b0d512ad749f 100644 --- a/apps/microtvm/arduino/template_project/src/parameters.h +++ b/apps/microtvm/arduino/template_project/src/parameters.h @@ -8,12 +8,12 @@ static const DLDevice HARDWARE_DEVICE = {kDLCPU, 0}; static const int INPUT_DATA_DIMENSION = $input_data_dimension; -static const int64_t INPUT_DATA_SHAPE[] = $input_data_shape; +static int64_t INPUT_DATA_SHAPE[] = $input_data_shape; static const DLDataType INPUT_DATA_TYPE = $input_data_type; static const char* INPUT_LAYER = $input_layer_name; static const int OUTPUT_DATA_DIMENSION = $output_data_dimension; -static const int64_t OUTPUT_DATA_SHAPE[] = $output_data_shape; +static int64_t OUTPUT_DATA_SHAPE[] = $output_data_shape; static const DLDataType OUTPUT_DATA_TYPE = $output_data_type; #endif diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino index 7cebce26836c..9d511cc8ca29 100644 --- a/tests/micro/arduino/testdata/project.ino +++ b/tests/micro/arduino/testdata/project.ino @@ -6,12 +6,12 @@ static Model model; -void performInference(int8_t input_data[1960], String data_name) { +void performInference(int8_t input_data[1960], char *data_name) { int8_t output_data[4]; uint64_t start_time = micros(); model.inference(input_data, output_data); uint64_t end_time = micros(); - + Serial.print(data_name); Serial.print(","); Serial.print(end_time - start_time); @@ -24,14 +24,14 @@ void performInference(int8_t input_data[1960], String data_name) { } void setup() { - model = Model(); + model = Model(); Serial.begin(115200); Serial.println(); Serial.println("category,runtime,yes,no,silence,unknown"); - performInference(input_yes, "yes"); - performInference(input_no, "no"); - performInference(input_silence, "silence"); - performInference(input_unknown, "unknown"); + performInference((int8_t*) input_yes, "yes"); + performInference((int8_t*) input_no, "no"); + performInference((int8_t*) input_silence, "silence"); + performInference((int8_t*) input_unknown, "unknown"); Serial.end(); } From b916496193d5243b7f0988a98a52c8a9f7696f6e Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 11:17:32 -0700 Subject: [PATCH 39/47] Only copy project if it's a template --- .../arduino/template_project/microtvm_api_server.py | 3 ++- tests/micro/arduino/test_arduino_workflow.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index b9253e7c870c..cd76cdb528af 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -237,7 +237,8 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Copy template folder to project_dir, creating project/ and src/ # directories in the process. Also copies this file, microtvm_api_server.py, # in case TVM needs to call it from the new location - shutil.copytree(API_SERVER_DIR, project_dir, dirs_exist_ok=True) + if IS_TEMPLATE: + shutil.copytree(API_SERVER_DIR, project_dir, dirs_exist_ok=True) # Reference key directories with pathlib project_dir = pathlib.Path(project_dir) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index afd50065a10b..ff8ce2e57416 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -38,7 +38,7 @@ def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_conf workspace_parent = os.path.dirname(workspace_root) if not os.path.exists(workspace_parent): os.makedirs(workspace_parent) - workspace = tvm.micro.Workspace(debug=True, root=workspace_root) + workspace = tvm.micro.Workspace(debug=False, root=workspace_root) template_project_dir = ( pathlib.Path(__file__).parent @@ -238,7 +238,9 @@ def test_project_inference_runtime(serial_output): assert max(runtimes_us) < MAX_INFERENCE_TIME_US # Clock speeds should be consistent for each input. On - # the Sony spresense, they vary by <100 us. + # the Sony spresense, they vary by <100 us. Note that + # running with other attached hardware (like the + # Spresense extension board) may cause this check to fail range_runtimes_us = max(runtimes_us) - min(runtimes_us) assert range_runtimes_us < MAX_INFERENCE_TIME_RANGE_US From 1290cca23d78facfa4e4bf4904b92d2fe2f50fa5 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 11:17:43 -0700 Subject: [PATCH 40/47] Arduino tests README --- tests/micro/arduino/README.md | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/micro/arduino/README.md diff --git a/tests/micro/arduino/README.md b/tests/micro/arduino/README.md new file mode 100644 index 000000000000..f2c0535ff25a --- /dev/null +++ b/tests/micro/arduino/README.md @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + +This directory contains tests for MicroTVM's integration with Arduino. + +To run the test, you first need to be running in a Python environment with +all of the appropriate TVM dependencies installed. You can run the test with: + +``` +$ cd tvm/tests/micro/arduino +$ pytest test_arduino_workflow.py --platform spresense +$ pytest test_arduino_workflow.py --platform nano33ble +``` + +By default, only project generation and compilation tests are run. If you +have compatible Arduino hardware connected, you can pass the flag +`--run-hardware-tests` to test board auto-detection and code execution: + +``` +pytest test_arduino_workflow.py --platform spresense --run-hardware-tests +``` + +To see the list of supported values for `--platform`, run: +``` +$ pytest test_arduino_workflow.py --help +``` From e831ed620e317111558eaaf1e3aa241d8f5c991c Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 12:38:58 -0700 Subject: [PATCH 41/47] Nano 33 BLE fixes --- src/runtime/crt/graph_executor/graph_executor.c | 5 +++-- tests/micro/arduino/test_arduino_workflow.py | 4 ++++ tests/micro/arduino/testdata/project.ino | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index 9bd14949805d..29ceb382c491 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -100,9 +100,10 @@ void TVMGraphExecutorNode_LoadAttrs(TVMGraphExecutorNode* node, JSONReader* read } else if (!strcmp(key, "flatten_data")) { param->flatten_data = strtoul(value, 0, 10); bitmask |= 8; +#if TVM_CRT_DEBUG } else { - // TODO determine if suppressing these warnings is OK - // fprintf(stderr, "do not support key %s", key); + printf("do not support key %s", key); +#endif // TVM_CRT_DEBUG } } if (bitmask != (1 | 2 | 4 | 8)) { diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index ff8ce2e57416..6e3f2a59fbe7 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -3,6 +3,7 @@ import pathlib import shutil import sys +import time import pytest import tflite @@ -189,6 +190,9 @@ def uploaded_project(compiled_project, run_hardware_tests): @pytest.fixture(scope="module") def serial_output(uploaded_project): + # Give time for the board to open a serial connection + time.sleep(1) + transport = uploaded_project.transport() transport.open() out = transport.read(2048, -1) diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino index 9d511cc8ca29..796022f1867f 100644 --- a/tests/micro/arduino/testdata/project.ino +++ b/tests/micro/arduino/testdata/project.ino @@ -26,13 +26,13 @@ void performInference(int8_t input_data[1960], char *data_name) { void setup() { model = Model(); Serial.begin(115200); +} + +void loop() { Serial.println(); Serial.println("category,runtime,yes,no,silence,unknown"); performInference((int8_t*) input_yes, "yes"); performInference((int8_t*) input_no, "no"); performInference((int8_t*) input_silence, "silence"); performInference((int8_t*) input_unknown, "unknown"); - Serial.end(); } - -void loop() {} From a604b39b321902bcabb4d6b8ae986e5c1152d109 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 12:40:45 -0700 Subject: [PATCH 42/47] Unit tests for project generation --- .../test_microtvm_api_server.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 apps/microtvm/arduino/template_project/test_microtvm_api_server.py diff --git a/apps/microtvm/arduino/template_project/test_microtvm_api_server.py b/apps/microtvm/arduino/template_project/test_microtvm_api_server.py new file mode 100644 index 000000000000..d3b10109a6b7 --- /dev/null +++ b/apps/microtvm/arduino/template_project/test_microtvm_api_server.py @@ -0,0 +1,38 @@ +from unittest.mock import patch +from pathlib import Path + +import pytest + +import microtvm_api_server + + +class TestGenerateProject: + """A class with common parameters, `param1` and `param2`.""" + + def test_print_c_array(self): + handler = microtvm_api_server.Handler() + c_arr = handler._print_c_array([1, 32, 32, 3]) + assert c_arr == "{1, 32, 32, 3}" + + def _set_pathlib_path_exists(self, value): + with patch.object(Path, "exists") as mock_exists: + mock_exists.return_value = value + + @patch("pathlib.Path") + def test_find_modified_include_path(self, MockPath): + handler = microtvm_api_server.Handler() + + project_dir = MockPath("/dummy/project") + file_path = project_dir / "src/standalone_crt/src/runtime/crt/graph_executor/load_json.c" + + # Should return C standard libs unmodified + clib_output = handler._find_modified_include_path(project_dir, file_path, "math.h") + assert clib_output == "math.h" + + # If import already works, should return unmodified + valid_ardino_import = "../../../../include/tvm/runtime/crt/platform.h" + self._set_pathlib_path_exists(True) + valid_output = handler._find_modified_include_path( + project_dir, file_path, valid_ardino_import + ) + assert valid_output == valid_ardino_import From 5c042fe8e4072bfd52dea2adfe1114342de6302a Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 14:20:33 -0700 Subject: [PATCH 43/47] Update graph.json path --- apps/microtvm/arduino/template_project/microtvm_api_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index cd76cdb528af..836098e56430 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -137,7 +137,7 @@ def _disassemble_mlf(self, mlf_tar_path, source_dir): shutil.copy(os.path.join(mlf_unpacking_dir.name, source), model_dir / dest) # Load graph.json, serialize to c format, and extact parameters - with open(os.path.join(mlf_unpacking_dir.name, "runtime-config/graph/graph.json")) as f: + with open(os.path.join(mlf_unpacking_dir.name, "executor-config/graph/graph.json")) as f: graph_data = json.load(f) self._compile_graph_json(model_dir, graph_data) From 4baf6d5433dea43c6c5683c20d5d9d223c5553f0 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 16:31:02 -0700 Subject: [PATCH 44/47] Address PR early feedback --- .../template_project/microtvm_api_server.py | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 836098e56430..59fbf96bb076 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -20,12 +20,12 @@ from tvm.micro.project_api import server -MODEL_LIBRARY_FORMAT_RELPATH = "src/model/model.tar" - +MODEL_LIBRARY_FORMAT_RELPATH = pathlib.Path("src") / "model" / "model.tar" API_SERVER_DIR = pathlib.Path(os.path.dirname(__file__) or os.path.getcwd()) BUILD_DIR = API_SERVER_DIR / "build" +MODEL_LIBRARY_FORMAT_PATH = API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH + IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() -MODEL_LIBRARY_FORMAT_PATH = "" if IS_TEMPLATE else API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH class InvalidPortException(Exception): @@ -40,13 +40,6 @@ class BoardAutodetectFailed(Exception): """Raised when no attached hardware is found matching the requested board""" -PROJECT_OPTIONS = [ - server.ProjectOption("verbose", help="Run build with verbose output"), - server.ProjectOption("arduino_cmd", help="Path to the arduino-cli tool."), - server.ProjectOption("arduino_board", help="Name of the Arduino board to build for"), - server.ProjectOption("port", help="Port to use for connecting to hardware"), -] - BOARD_PROPERTIES = { "spresense": { "package": "SPRESENSE", @@ -60,6 +53,19 @@ class BoardAutodetectFailed(Exception): }, } +PROJECT_OPTIONS = [ + server.ProjectOption( + "arduino_board", + choices=list(BOARD_PROPERTIES), + help="Name of the Arduino board to build for", + ), + server.ProjectOption("arduino_cli_cmd", help="Path to the arduino-cli tool."), + server.ProjectOption("port", help="Port to use for connecting to hardware"), + server.ProjectOption( + "verbose", help="True to pass --verbose flag to arduino-cli compile and upload" + ), +] + class Handler(server.ProjectAPIHandler): def __init__(self): @@ -114,7 +120,7 @@ def _remove_unit_tests(self, source_dir): GRAPH_JSON_TEMPLATE = 'static const char* graph_json = "{}";\n' - def _compile_graph_json(self, model_dir, obj): + def _create_graph_json(self, model_dir, obj): graph_json = json.dumps(obj).replace('"', '\\"') output = self.GRAPH_JSON_TEMPLATE.format(graph_json) graph_json_path = model_dir / "graph_json.c" @@ -122,26 +128,27 @@ def _compile_graph_json(self, model_dir, obj): out_file.write(output) def _disassemble_mlf(self, mlf_tar_path, source_dir): - mlf_unpacking_dir = tempfile.TemporaryDirectory() - with tarfile.open(mlf_tar_path, "r:") as tar: - tar.extractall(mlf_unpacking_dir.name) - - # Copy C files - # TODO are the defaultlib0.c the same? - model_dir = source_dir / "model" - model_dir.mkdir() - for source, dest in [ - ("codegen/host/src/default_lib0.c", "default_lib0.c"), - ("codegen/host/src/default_lib1.c", "default_lib1.c"), - ]: - shutil.copy(os.path.join(mlf_unpacking_dir.name, source), model_dir / dest) - - # Load graph.json, serialize to c format, and extact parameters - with open(os.path.join(mlf_unpacking_dir.name, "executor-config/graph/graph.json")) as f: - graph_data = json.load(f) - self._compile_graph_json(model_dir, graph_data) - - mlf_unpacking_dir.cleanup() + with tempfile.TemporaryDirectory() as mlf_unpacking_dir: + with tarfile.open(mlf_tar_path, "r:") as tar: + tar.extractall(mlf_unpacking_dir.name) + + # Copy C files + # TODO are the defaultlib0.c the same? + model_dir = source_dir / "model" + model_dir.mkdir() + for source, dest in [ + ("codegen/host/src/default_lib0.c", "default_lib0.c"), + ("codegen/host/src/default_lib1.c", "default_lib1.c"), + ]: + shutil.copy(os.path.join(mlf_unpacking_dir.name, source), model_dir / dest) + + # Load graph.json, serialize to c format, and extact parameters + with open( + os.path.join(mlf_unpacking_dir.name, "executor-config/graph/graph.json") + ) as f: + graph_data = json.load(f) + + self._create_graph_json(model_dir, graph_data) return graph_data def _print_c_array(self, l): @@ -269,7 +276,7 @@ def build(self, options): print(BUILD_DIR) compile_cmd = [ - options["arduino_cmd"], + options["arduino_cli_cmd"], "compile", "./project/", "--fqbn", @@ -294,7 +301,7 @@ def build(self, options): """ def _auto_detect_port(self, options): - list_cmd = [options["arduino_cmd"], "board", "list"] + list_cmd = [options["arduino_cli_cmd"], "board", "list"] list_cmd_output = subprocess.check_output(list_cmd).decode("utf-8") # Remove header and new lines at bottom port_options = list_cmd_output.split("\n")[1:-2] @@ -306,7 +313,7 @@ def _auto_detect_port(self, options): return port_option.split(" ")[0] # If no compatible boards, raise an error - raise BoardAutodetectFailed + raise BoardAutodetectFailed() def _get_arduino_port(self, options): if not self._port: @@ -317,14 +324,11 @@ def _get_arduino_port(self, options): return self._port - def _get_baudrate(self, options): - return 115200 - def flash(self, options): port = self._get_arduino_port(options) upload_cmd = [ - options["arduino_cmd"], + options["arduino_cli_cmd"], "upload", "./project", "--fqbn", @@ -334,12 +338,16 @@ def flash(self, options): "--port", port, ] + + if options.get("verbose"): + compile_cmd.append("--verbose") + output = subprocess.check_call(upload_cmd) if output == 2: - raise InvalidPortException + raise InvalidPortException() elif output > 0: - raise SketchUploadException + raise SketchUploadException() def open_transport(self, options): # Zephyr example doesn't throw an error in this case @@ -347,8 +355,7 @@ def open_transport(self, options): return port = self._get_arduino_port(options) - baudrate = self._get_baudrate(options) - self._serial = serial.Serial(port, baudrate=baudrate, timeout=5) + self._serial = serial.Serial(port, baudrate=115200, timeout=5) return server.TransportTimeouts( session_start_retry_timeout_sec=2.0, session_start_timeout_sec=5.0, From 5453d020f4a9a8fe15c996348d67b56f0f47dbad Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 16:31:16 -0700 Subject: [PATCH 45/47] Unit tests for port auto-detect and flashing --- .../test_microtvm_api_server.py | 78 ++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/apps/microtvm/arduino/template_project/test_microtvm_api_server.py b/apps/microtvm/arduino/template_project/test_microtvm_api_server.py index d3b10109a6b7..ab12304024b6 100644 --- a/apps/microtvm/arduino/template_project/test_microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/test_microtvm_api_server.py @@ -1,4 +1,4 @@ -from unittest.mock import patch +from unittest import mock from pathlib import Path import pytest @@ -7,7 +7,7 @@ class TestGenerateProject: - """A class with common parameters, `param1` and `param2`.""" + DEFAULT_OPTIONS = {"arduino_cli_cmd": "arduino-cli", "arduino_board": "nano33ble"} def test_print_c_array(self): handler = microtvm_api_server.Handler() @@ -15,24 +15,82 @@ def test_print_c_array(self): assert c_arr == "{1, 32, 32, 3}" def _set_pathlib_path_exists(self, value): - with patch.object(Path, "exists") as mock_exists: + with mock.patch.object(Path, "exists") as mock_exists: mock_exists.return_value = value - @patch("pathlib.Path") - def test_find_modified_include_path(self, MockPath): + @mock.patch("pathlib.Path") + def test_find_modified_include_path(self, mock_pathlib_path): handler = microtvm_api_server.Handler() - project_dir = MockPath("/dummy/project") - file_path = project_dir / "src/standalone_crt/src/runtime/crt/graph_executor/load_json.c" + project_dir = mock_pathlib_path("/dummy/project") + file_path = ( + project_dir + / "src" + / "standalone_crt" + / "src" + / "runtime" + / "crt" + / "graph_executor" + / "load_json.c" + ) # Should return C standard libs unmodified clib_output = handler._find_modified_include_path(project_dir, file_path, "math.h") assert clib_output == "math.h" # If import already works, should return unmodified - valid_ardino_import = "../../../../include/tvm/runtime/crt/platform.h" + valid_arduino_import = "../../../../include/tvm/runtime/crt/platform.h" self._set_pathlib_path_exists(True) valid_output = handler._find_modified_include_path( - project_dir, file_path, valid_ardino_import + project_dir, file_path, valid_arduino_import ) - assert valid_output == valid_ardino_import + assert valid_output == valid_arduino_import + + BOARD_CONNECTED_OUTPUT = bytes( + "Port Type Board Name FQBN Core \n" + "/dev/ttyACM0 Serial Port (USB) Arduino Nano 33 BLE arduino:mbed_nano:nano33ble arduino:mbed_nano\n" + "/dev/ttyS4 Serial Port Unknown \n" + "\n", + "utf-8", + ) + BOARD_DISCONNECTED_OUTPUT = bytes( + "Port Type Board Name FQBN Core\n" + "/dev/ttyS4 Serial Port Unknown \n" + "\n", + "utf-8", + ) + + @mock.patch("subprocess.check_output") + def test_auto_detect_port(self, mock_subprocess_check_output): + process_mock = mock.Mock() + handler = microtvm_api_server.Handler() + + # Test it returns the correct port when a board is connected + mock_subprocess_check_output.return_value = self.BOARD_CONNECTED_OUTPUT + detected_port = handler._auto_detect_port(self.DEFAULT_OPTIONS) + assert detected_port == "/dev/ttyACM0" + + # Test it raises an exception when no board is connected + mock_subprocess_check_output.return_value = self.BOARD_DISCONNECTED_OUTPUT + with pytest.raises(microtvm_api_server.BoardAutodetectFailed): + handler._auto_detect_port(self.DEFAULT_OPTIONS) + + @mock.patch("subprocess.check_call") + def test_flash(self, mock_subprocess_check_call): + handler = microtvm_api_server.Handler() + handler._port = "/dev/ttyACM0" + + # Test no exception thrown when code 0 returned + mock_subprocess_check_call.return_value = 0 + handler.flash(self.DEFAULT_OPTIONS) + mock_subprocess_check_call.assert_called_once() + + # Test InvalidPortException raised when port incorrect + mock_subprocess_check_call.return_value = 2 + with pytest.raises(microtvm_api_server.InvalidPortException): + handler.flash(self.DEFAULT_OPTIONS) + + # Test SketchUploadException raised for other issues + mock_subprocess_check_call.return_value = 1 + with pytest.raises(microtvm_api_server.SketchUploadException): + handler.flash(self.DEFAULT_OPTIONS) From 6c434b9a53d108c5e226553642ea239f05afe34f Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 17:00:36 -0700 Subject: [PATCH 46/47] Move unit test to dedicated tests folder --- .../arduino/template_project/microtvm_api_server.py | 6 +++--- .../test_arduino_microtvm_api_server.py} | 2 ++ tests/micro/arduino/conftest.py | 6 +++--- tests/micro/arduino/test_arduino_workflow.py | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) rename apps/microtvm/arduino/template_project/{test_microtvm_api_server.py => tests/test_arduino_microtvm_api_server.py} (99%) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 59fbf96bb076..065df21c05ba 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -130,7 +130,7 @@ def _create_graph_json(self, model_dir, obj): def _disassemble_mlf(self, mlf_tar_path, source_dir): with tempfile.TemporaryDirectory() as mlf_unpacking_dir: with tarfile.open(mlf_tar_path, "r:") as tar: - tar.extractall(mlf_unpacking_dir.name) + tar.extractall(mlf_unpacking_dir) # Copy C files # TODO are the defaultlib0.c the same? @@ -140,11 +140,11 @@ def _disassemble_mlf(self, mlf_tar_path, source_dir): ("codegen/host/src/default_lib0.c", "default_lib0.c"), ("codegen/host/src/default_lib1.c", "default_lib1.c"), ]: - shutil.copy(os.path.join(mlf_unpacking_dir.name, source), model_dir / dest) + shutil.copy(os.path.join(mlf_unpacking_dir, source), model_dir / dest) # Load graph.json, serialize to c format, and extact parameters with open( - os.path.join(mlf_unpacking_dir.name, "executor-config/graph/graph.json") + os.path.join(mlf_unpacking_dir, "executor-config/graph/graph.json") ) as f: graph_data = json.load(f) diff --git a/apps/microtvm/arduino/template_project/test_microtvm_api_server.py b/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py similarity index 99% rename from apps/microtvm/arduino/template_project/test_microtvm_api_server.py rename to apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py index ab12304024b6..634f996efdaf 100644 --- a/apps/microtvm/arduino/template_project/test_microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py @@ -1,8 +1,10 @@ from unittest import mock from pathlib import Path +import sys import pytest +sys.path.append("../") import microtvm_api_server diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index ff1c92f739a5..bc9437482032 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -18,7 +18,7 @@ def pytest_addoption(parser): help="Target platform for microTVM tests.", ) parser.addoption( - "--arduino-cmd", + "--arduino-cli-cmd", default="arduino-cli", help="Path to `arduino-cli` command for flashing device.", ) @@ -36,8 +36,8 @@ def platform(request): @pytest.fixture(scope="session") -def arduino_cmd(request): - return request.config.getoption("--arduino-cmd") +def arduino_cli_cmd(request): + return request.config.getoption("--arduino-cli-cmd") @pytest.fixture(scope="session") diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 6e3f2a59fbe7..6104ac5e891e 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -26,7 +26,7 @@ PLATFORMS = conftest.PLATFORMS -def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_config): +def _generate_project(model, target, arduino_board, arduino_cli_cmd, mod, build_config): parent_dir = os.path.dirname(__file__) filename = os.path.splitext(os.path.basename(__file__))[0] prev_build = ( @@ -55,7 +55,7 @@ def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_conf str(template_project_dir), mod, workspace.relpath("project"), - {"arduino_board": arduino_board, "arduino_cmd": arduino_cmd, "verbose": 0}, + {"arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "verbose": 0}, ) return (workspace, project) @@ -65,7 +65,7 @@ def _generate_project(model, target, arduino_board, arduino_cmd, mod, build_conf @pytest.fixture(scope="module") -def yes_no_project(platform, arduino_cmd): +def yes_no_project(platform, arduino_cli_cmd): current_dir = os.path.dirname(__file__) model, arduino_board = PLATFORMS[platform] # target = tvm.target.target.micro(model, options=["-link-params=1"]) @@ -78,7 +78,7 @@ def yes_no_project(platform, arduino_cmd): with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): mod = relay.build(mod, TARGET, params=params) - return _generate_project(model, TARGET, arduino_board, arduino_cmd, mod, build_config) + return _generate_project(model, TARGET, arduino_board, arduino_cli_cmd, mod, build_config) # return tvm.micro.Session(project.transport()) From 1516f18558cbe36ba49b0bec333f81417e909c29 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 17:49:35 -0700 Subject: [PATCH 47/47] Formatting --- apps/microtvm/arduino/template_project/microtvm_api_server.py | 4 +--- .../tests/test_arduino_microtvm_api_server.py | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 065df21c05ba..a5144422f0e9 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -143,9 +143,7 @@ def _disassemble_mlf(self, mlf_tar_path, source_dir): shutil.copy(os.path.join(mlf_unpacking_dir, source), model_dir / dest) # Load graph.json, serialize to c format, and extact parameters - with open( - os.path.join(mlf_unpacking_dir, "executor-config/graph/graph.json") - ) as f: + with open(os.path.join(mlf_unpacking_dir, "executor-config/graph/graph.json")) as f: graph_data = json.load(f) self._create_graph_json(model_dir, graph_data) diff --git a/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py b/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py index 634f996efdaf..a316af35aacb 100644 --- a/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py @@ -4,9 +4,11 @@ import pytest -sys.path.append("../") +sys.path.insert(0, str(Path(__file__).parent.parent)) import microtvm_api_server +sys.path.pop(0) + class TestGenerateProject: DEFAULT_OPTIONS = {"arduino_cli_cmd": "arduino-cli", "arduino_board": "nano33ble"}