From fcf8bf3bfa8a4d9b5fffc623d36bf67aecef20ca Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 14 Jul 2021 13:19:27 -0700 Subject: [PATCH 01/11] ProjectAPI Arduino support --- .../template_project/microtvm_api_server.py | 493 ++++++++++++++++++ .../arduino/template_project/project.ino | 12 + .../template_project/src/implementation.c | 91 ++++ .../arduino/template_project/src/model.cpp | 83 +++ .../arduino/template_project/src/model.h | 20 + .../arduino/template_project/src/parameters.h | 19 + .../standalone_crt/crt_config/crt_config.h | 56 ++ python/tvm/target/target.py | 1 + tests/micro/arduino/conftest.py | 45 ++ tests/micro/arduino/test_arduino.py | 70 +++ tests/micro/arduino/testdata/yes_no.tflite | Bin 0 -> 18712 bytes 11 files changed, 890 insertions(+) create mode 100644 apps/microtvm/arduino/template_project/microtvm_api_server.py create mode 100644 apps/microtvm/arduino/template_project/project.ino 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 create mode 100644 apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h create mode 100644 tests/micro/arduino/conftest.py create mode 100644 tests/micro/arduino/test_arduino.py create mode 100644 tests/micro/arduino/testdata/yes_no.tflite 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..f13877e3c604 --- /dev/null +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -0,0 +1,493 @@ +import collections +import functools +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" + 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) + + 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('"', '\\"') + 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) + + + 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 + if (file_path.parents[0] / import_path).exists(): + return import_path + + relative_path = file_path.relative_to(project_dir) + up_dirs_path = "../" * str(relative_path).count("/") + + for base_path in self.POSSIBLE_BASE_PATHS: + full_potential_path = project_dir / base_path / import_path + if full_potential_path.exists(): + new_include = up_dirs_path + base_path + import_path + return new_include + + # 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 + 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 + new_include = self._find_modified_include_path( + project_dir, filename, result.groups()[0] + ) + + lines[i] = f'#include "{new_include}"\n' + + 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, + # 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) + self._remove_unused_components(source_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()) 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/implementation.c b/apps/microtvm/arduino/template_project/src/implementation.c new file mode 100644 index 000000000000..60c9e7cd14b2 --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -0,0 +1,91 @@ +#ifndef IMPLEMENTATION +#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, + va_list args) { + 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); + 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 diff --git a/apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h b/apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h new file mode 100644 index 000000000000..c3e8fea1ba08 --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/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_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 + +/*! 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_MAX_STRLEN_DLTYPE 10 +/*! Maximum supported string length in function names */ +#define TVM_CRT_MAX_STRLEN_FUNCTION_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_MICRO_CRT_CONFIG_H_ 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"], } 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") 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 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 Date: Thu, 15 Jul 2021 10:05:53 -0700 Subject: [PATCH 02/11] Compile and run integration tests --- .../template_project/microtvm_api_server.py | 399 ++++++------------ .../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 | 7 +- tests/micro/arduino/conftest.py | 40 +- tests/micro/arduino/test_arduino.py | 70 --- tests/micro/arduino/test_arduino_workflow.py | 247 +++++++++++ tests/micro/arduino/testdata/no.c | 124 ++++++ tests/micro/arduino/testdata/project.ino | 38 ++ tests/micro/arduino/testdata/silence.c | 124 ++++++ tests/micro/arduino/testdata/unknown.c | 124 ++++++ tests/micro/arduino/testdata/yes.c | 124 ++++++ 13 files changed, 958 insertions(+), 373 deletions(-) delete mode 100644 tests/micro/arduino/test_arduino.py create mode 100644 tests/micro/arduino/test_arduino_workflow.py 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/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index f13877e3c604..dda1bc062d8f 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -20,213 +20,65 @@ from tvm.micro.project_api import server - -_LOG = logging.getLogger(__name__) - +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" - - -MODEL_LIBRARY_FORMAT_RELPATH = "src/model/model.tar" - - 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") +class InvalidPortException(Exception): + """Raised when the given port could not be opened""" -class BoardError(Exception): - """Raised when an attached board cannot be opened (i.e. missing /dev nodes, etc).""" +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.""" - - -'''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]] + """Raised when no attached hardware is found matching the requested board""" 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"), + server.ProjectOption("port", help="Port to use for connecting to hardware"), ] +BOARD_PROPERTIES = { + "spresense": { + "package": "SPRESENSE", + "architecture": "spresense", + "board": "spresense", + }, + "nano33ble": { + "package": "arduino", + "architecture": "mbed_nano", + "board": "nano33ble", + }, +} + class Handler(server.ProjectAPIHandler): def __init__(self): super(Handler, self).__init__() self._proc = None + self._port = None + self._serial = 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), + model_library_format_path=MODEL_LIBRARY_FORMAT_PATH, 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" @@ -247,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) @@ -260,32 +113,23 @@ 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() - print(mlf_tar_path) - with tarfile.open(mlf_tar_path, 'r:') as tar: + 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 - ) + ("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) @@ -296,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}", @@ -314,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]), @@ -331,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) @@ -339,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 @@ -385,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, @@ -403,90 +242,126 @@ 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) + shutil.copy2(model_library_format_path, source_dir / "model") + # Populate our parameters file self._populate_parameters_file(graph, source_dir) # Recursively change imports self._convert_imports(project_dir, source_dir) + def _get_fqbn(self, options): + 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(), + ] - 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) + compile_cmd.append("--verbose") + + # Specify project to compile + 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) - args = ["make", "-j2"] - if options.get("verbose"): - args.append("VERBOSE=1") - check_call(args, cwd=BUILD_DIR) + return self._port - @classmethod - def _is_qemu(cls, options): - return "qemu" in options["zephyr_board"] + def _get_baudrate(self, options): + return 115200 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) + 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): - 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 + # 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): - if self._transport is not None: - self._transport.close() - self._transport = None + if self._serial is None: + return + self._serial.close() + self._serial = None def read_transport(self, n, timeout_sec): - if self._transport is None: + # 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._transport.read(n, timeout_sec) + return self._serial.read(n) def write_transport(self, data, timeout_sec): - if self._transport is None: + if self._serial is None: raise server.TransportClosedError() - - return self._transport.write(data, timeout_sec) + return self._serial.write(data, timeout_sec) if __name__ == "__main__": 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 0c39fe1a65b8..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(-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 950f3e4ef215..d6ad26c89297 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)) { @@ -130,7 +131,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) { @@ -1091,6 +1092,8 @@ int TVMGraphExecutor_SetupOpExecs(TVMGraphExecutor* executor) { TVMPackedFunc pf; TVMGraphExecutor_CreateTVMOp(executor, &(inode->param), args, args_count, &pf); executor->op_execs[nid] = pf; + } else { + memset(&executor->op_execs[nid], 0, sizeof(TVMPackedFunc)); } } return status; diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 638550946637..ff1c92f739a5 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -5,41 +5,41 @@ # 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"), + "nano33ble": ("nRF52840", "nano33ble"), } def pytest_addoption(parser): parser.addoption( - "--microtvm-platforms", - default="spresense_main", + "--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." + "--arduino-cmd", + default="arduino-cli", + help="Path to `arduino-cli` command for flashing device.", ) parser.addoption( - "--skip-build", + "--run-hardware-tests", 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: ", + 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(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.py deleted file mode 100644 index 975e1b1852b6..000000000000 --- a/tests/micro/arduino/test_arduino.py +++ /dev/null @@ -1,70 +0,0 @@ -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/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py new file mode 100644 index 000000000000..afd50065a10b --- /dev/null +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -0,0 +1,247 @@ +import datetime +import os +import pathlib +import shutil +import sys + +import pytest +import tflite +import tvm +from tvm import micro, relay + +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): + 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) + + 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}, + ) + 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" + + +@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"]) + 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) + + return _generate_project(model, TARGET, arduino_board, arduino_cmd, mod, build_config) + # return tvm.micro.Session(project.transport()) + + +@pytest.fixture(scope="module") +def project(yes_no_project): + workspace, project = yes_no_project + return project + + +@pytest.fixture(scope="module") +def project_dir(yes_no_project): + workspace, project = yes_no_project + return 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)) + + source_dir = project_dir / "src" + 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"] + ) + with (model_dir / "graph_json.c").open() as f: + graph_json_c = f.read() + 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 (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 = project_dir / "src" / "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 + + +# 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 modified_project(project_dir, project): + testdata_dir = pathlib.Path(os.path.dirname(__file__)) / "testdata" + + shutil.copy2(testdata_dir / "project.ino", project_dir / "project.ino") + + 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 uploaded_project(compiled_project, run_hardware_tests): + if not run_hardware_tests: + pytest.skip() + + compiled_project.flash() + return compiled_project + + +""" 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 + + +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 + + +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__": + sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/micro/arduino/testdata/no.c b/tests/micro/arduino/testdata/no.c new file mode 100644 index 000000000000..ee2f07a8d99a --- /dev/null +++ b/tests/micro/arduino/testdata/no.c @@ -0,0 +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}; 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..bf756399157a --- /dev/null +++ b/tests/micro/arduino/testdata/silence.c @@ -0,0 +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}; diff --git a/tests/micro/arduino/testdata/unknown.c b/tests/micro/arduino/testdata/unknown.c new file mode 100644 index 000000000000..2aef2dbcb62e --- /dev/null +++ b/tests/micro/arduino/testdata/unknown.c @@ -0,0 +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}; diff --git a/tests/micro/arduino/testdata/yes.c b/tests/micro/arduino/testdata/yes.c new file mode 100644 index 000000000000..9b500624bff0 --- /dev/null +++ b/tests/micro/arduino/testdata/yes.c @@ -0,0 +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}; From 44d0cf6cf66f6691f2268579eb84c5ed249e8fe7 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Fri, 16 Jul 2021 17:35:23 -0700 Subject: [PATCH 03/11] Add support for other Arduino boards --- .../template_project/microtvm_api_server.py | 14 ++++++- .../template_project/src/implementation.c | 6 +-- .../arduino/template_project/src/parameters.h | 4 +- .../crt/graph_executor/graph_executor.c | 5 ++- tests/micro/arduino/README.md | 40 +++++++++++++++++++ tests/micro/arduino/test_arduino_workflow.py | 10 ++++- tests/micro/arduino/testdata/project.ino | 20 +++++----- 7 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 tests/micro/arduino/README.md diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index dda1bc062d8f..cd76cdb528af 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): @@ -229,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) @@ -238,6 +247,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 a28d370b5984..3e37990ce9b2 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/template_project/src/implementation.c @@ -79,11 +79,9 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { 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 + for (size_t i = 0; i < num_bytes; i++) { + 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/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index d6ad26c89297..c2e465361651 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/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 +``` diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index afd50065a10b..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 @@ -38,7 +39,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 @@ -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) @@ -238,7 +242,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 diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino index 7cebce26836c..796022f1867f 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,15 +24,15 @@ void performInference(int8_t input_data[1960], String data_name) { } void setup() { - model = Model(); + model = Model(); Serial.begin(115200); +} + +void loop() { 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(); + performInference((int8_t*) input_yes, "yes"); + performInference((int8_t*) input_no, "no"); + performInference((int8_t*) input_silence, "silence"); + performInference((int8_t*) input_unknown, "unknown"); } - -void loop() {} From aa819ef66870719b4f347b9ead4942a493c4a316 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 26 Jul 2021 12:40:45 -0700 Subject: [PATCH 04/11] Unit tests for project generation --- .../template_project/microtvm_api_server.py | 89 ++++++++-------- .../tests/test_arduino_microtvm_api_server.py | 100 ++++++++++++++++++ tests/micro/arduino/conftest.py | 6 +- tests/micro/arduino/test_arduino_workflow.py | 8 +- 4 files changed, 154 insertions(+), 49 deletions(-) create mode 100644 apps/microtvm/arduino/template_project/tests/test_arduino_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 index cd76cdb528af..a5144422f0e9 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,25 @@ 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, "runtime-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) + + # 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, 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: + graph_data = json.load(f) + + self._create_graph_json(model_dir, graph_data) return graph_data def _print_c_array(self, l): @@ -269,7 +274,7 @@ def build(self, options): print(BUILD_DIR) compile_cmd = [ - options["arduino_cmd"], + options["arduino_cli_cmd"], "compile", "./project/", "--fqbn", @@ -294,7 +299,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 +311,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 +322,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 +336,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 +353,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, 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 new file mode 100644 index 000000000000..a316af35aacb --- /dev/null +++ b/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py @@ -0,0 +1,100 @@ +from unittest import mock +from pathlib import Path +import sys + +import pytest + +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"} + + 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 mock.patch.object(Path, "exists") as mock_exists: + mock_exists.return_value = value + + @mock.patch("pathlib.Path") + def test_find_modified_include_path(self, mock_pathlib_path): + handler = microtvm_api_server.Handler() + + 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_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_arduino_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) 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 bc73adb0e8c91c9a15f58c3170f4cbfd03c53879 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 28 Jul 2021 11:53:31 -0700 Subject: [PATCH 05/11] AOT support --- apps/microtvm/arduino/host_driven/project.ino | 46 ++++++ .../src/model.c} | 47 ++---- apps/microtvm/arduino/host_driven/src/model.h | 6 + .../standalone_crt/crt_config/crt_config.h | 56 +++++++ .../template_project/microtvm_api_server.py | 156 ++++++++---------- .../arduino/template_project/project.ino | 6 +- .../arduino/template_project/src/model.c | 83 ++++++++++ .../arduino/template_project/src/model.cpp | 83 ---------- .../arduino/template_project/src/model.h | 25 +-- .../arduino/template_project/src/parameters.h | 19 --- tests/micro/arduino/test.txt | 4 + .../micro/arduino/test_arduino_rpc_server.py | 91 ++++++++++ tests/micro/arduino/test_arduino_workflow.py | 43 ++--- tests/micro/arduino/testdata/project.ino | 6 +- 14 files changed, 410 insertions(+), 261 deletions(-) create mode 100644 apps/microtvm/arduino/host_driven/project.ino rename apps/microtvm/arduino/{template_project/src/implementation.c => host_driven/src/model.c} (64%) create mode 100644 apps/microtvm/arduino/host_driven/src/model.h create mode 100644 apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h create mode 100644 apps/microtvm/arduino/template_project/src/model.c delete mode 100644 apps/microtvm/arduino/template_project/src/model.cpp delete mode 100644 apps/microtvm/arduino/template_project/src/parameters.h create mode 100644 tests/micro/arduino/test.txt create mode 100644 tests/micro/arduino/test_arduino_rpc_server.py diff --git a/apps/microtvm/arduino/host_driven/project.ino b/apps/microtvm/arduino/host_driven/project.ino new file mode 100644 index 000000000000..94f21739e2c6 --- /dev/null +++ b/apps/microtvm/arduino/host_driven/project.ino @@ -0,0 +1,46 @@ +#include "src/standalone_crt/include/tvm/runtime/crt/microtvm_rpc_server.h" +#include "src/standalone_crt/include/tvm/runtime/crt/logging.h" +#include "src/model.h" +static size_t g_num_bytes_requested = 0; +static size_t g_num_bytes_written = 0; +microtvm_rpc_server_t server; + +// Called by TVM to write serial data to the UART. +ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { + g_num_bytes_requested += size; + Serial.write(data, size); + return size; +} + +void setup() { + server = MicroTVMRpcServerInit(write_serial, NULL); + TVMLogf("microTVM Arduino runtime - running"); +} + +void loop() { + noInterrupts(); + int to_read = Serial.available(); + uint8_t data[to_read]; + size_t bytes_read = Serial.readBytes(data, to_read); + uint8_t* arr_ptr = data; + uint8_t** data_ptr = &arr_ptr; + + if (bytes_read > 0) { + size_t bytes_remaining = bytes_read; + while (bytes_remaining > 0) { + // Pass the received bytes to the RPC server. + tvm_crt_error_t err = MicroTVMRpcServerLoop(server, data_ptr, &bytes_remaining); + if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { + TVMPlatformAbort(err); + } + if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { + if (g_num_bytes_written != g_num_bytes_requested) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef5); + } + g_num_bytes_written = 0; + g_num_bytes_requested = 0; + } + } + } + interrupts(); +} diff --git a/apps/microtvm/arduino/template_project/src/implementation.c b/apps/microtvm/arduino/host_driven/src/model.c similarity index 64% rename from apps/microtvm/arduino/template_project/src/implementation.c rename to apps/microtvm/arduino/host_driven/src/model.c index 3e37990ce9b2..7ce4ffe0f5f4 100644 --- a/apps/microtvm/arduino/template_project/src/implementation.c +++ b/apps/microtvm/arduino/host_driven/src/model.c @@ -1,30 +1,21 @@ -#ifndef IMPLEMENTATION -#define IMPLEMENTATION +#ifndef TVM_IMPLEMENTATION_ARDUINO +#define TVM_IMPLEMENTATION_ARDUINO + +#include "stdarg.h" + +#include "model.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/internal/aot_executor/aot_executor.h" #include "standalone_crt/include/tvm/runtime/crt/stack_allocator.h" -size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, - va_list args) { - 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);*/ -} +// AOT memory array +static uint8_t g_aot_memory[WORKSPACE_SIZE]; +extern tvm_model_t tvmgen_default_network; +tvm_workspace_t app_workspace; // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { - // pinMode(LED_BUILTIN, OUTPUT); for (;;) { digitalWrite(LED_BUILTIN, HIGH); delay(250); @@ -37,21 +28,17 @@ void TVMPlatformAbort(tvm_crt_error_t error) { } } -// Heap for use by TVMPlatformMemoryAllocate. +size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, + va_list args) { + return vsnprintf(out_buf, out_buf_size_bytes, fmt, args); +} -// 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; + return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr); } -// Called by TVM to deallocate memory. tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { - free(ptr); - return kTvmErrorNoError; + return StackMemoryManager_Free(&app_workspace, ptr); } unsigned long g_utvm_start_time; diff --git a/apps/microtvm/arduino/host_driven/src/model.h b/apps/microtvm/arduino/host_driven/src/model.h new file mode 100644 index 000000000000..b2165ae627da --- /dev/null +++ b/apps/microtvm/arduino/host_driven/src/model.h @@ -0,0 +1,6 @@ +#ifndef IMPLEMENTATION_H_ +#define IMPLEMENTATION_H_ + +#define WORKSPACE_SIZE 0 + +#endif // IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h b/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h new file mode 100644 index 000000000000..c3e8fea1ba08 --- /dev/null +++ b/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/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_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 + +/*! 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_MAX_STRLEN_DLTYPE 10 +/*! Maximum supported string length in function names */ +#define TVM_CRT_MAX_STRLEN_FUNCTION_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_MICRO_CRT_CONFIG_H_ diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index a5144422f0e9..fffd1571bb52 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -53,6 +53,11 @@ class BoardAutodetectFailed(Exception): }, } +PROJECT_TYPES = [ + "template_project", + "host_driven" +] + PROJECT_OPTIONS = [ server.ProjectOption( "arduino_board", @@ -61,6 +66,11 @@ class BoardAutodetectFailed(Exception): ), 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( + "project_type", + help="Type of project to generate.", + choices=tuple(PROJECT_TYPES), + ), server.ProjectOption( "verbose", help="True to pass --verbose flag to arduino-cli compile and upload" ), @@ -82,6 +92,12 @@ def server_info_query(self): project_options=PROJECT_OPTIONS, ) + def _copy_project_files(self, api_server_dir, project_dir, project_type): + project_types_folder = api_server_dir.parents[0] + shutil.copytree(project_types_folder / project_type / "src", project_dir / "src", dirs_exist_ok=True) + # Arduino requires the .ino file have the same filename as its containing folder + shutil.copy2(project_types_folder / project_type / "project.ino", project_dir / f"{project_dir.stem}.ino") + CRT_COPY_ITEMS = ("include", "src") def _copy_standalone_crt(self, source_dir, standalone_crt_dir): @@ -96,44 +112,19 @@ def _copy_standalone_crt(self, source_dir, standalone_crt_dir): 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) - # 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 _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" - with open(graph_json_path, "w") as out_file: - out_file.write(output) - 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) # Copy C files - # TODO are the defaultlib0.c the same? model_dir = source_dir / "model" model_dir.mkdir() for source, dest in [ @@ -142,62 +133,57 @@ 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: - graph_data = json.load(f) - - self._create_graph_json(model_dir, graph_data) - 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" + # Return metadata.json for use in templating + with open(os.path.join(mlf_unpacking_dir, "metadata.json")) as f: + metadata = json.load(f) + return metadata + + def _template_model_header(self, source_dir, metadata): + with open(source_dir / "model.h", "r") as f: + model_h_template = Template(f.read()) 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"]), + "workspace_size_bytes": metadata["memory"]["functions"]["main"][0][ + "workspace_size_bytes" + ], } - # Apply template values - with open(source_dir / "parameters.h", "r") as f: - template_params = Template(f.read()) + with open(source_dir / "model.h", "w") as f: + f.write(model_h_template.substitute(template_values)) + - parameters_h = template_params.substitute(template_values) + # Arduino ONLY recognizes .ino, .ccp, .c, .h + + CPP_FILE_EXTENSION_SYNONYMS = ("cc", "cxx") + def _change_cpp_file_extensions(self, source_dir): + for ext in self.CPP_FILE_EXTENSION_SYNONYMS: + for filename in source_dir.rglob(f"*.{ext}"): + filename.rename(filename.with_suffix('.cpp')) + + for filename in source_dir.rglob(f"*.inc"): + filename.rename(filename.with_suffix('.h')) - with open(source_dir / "parameters.h", "w") as f: - f.write(parameters_h) + + def _process_autogenerated_inc_files(self, source_dir): + for filename in source_dir.rglob(f"*.inc"): + # Individual file fixes + if filename.stem == "gentab_ccitt": + with open(filename, 'r+') as f: + content = f.read() + f.seek(0, 0) + f.write('#include "inttypes.h"\n' + content) + + filename.rename(filename.with_suffix('.c')) 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 for a .inc file we renamed to .c earlier, fix it + if import_path.endswith(self.CPP_FILE_EXTENSION_SYNONYMS): + import_path = re.sub(r'\.[a-z]+$', ".cpp", import_path) + + if import_path.endswith(".inc"): + import_path = re.sub(r'\.[a-z]+$', ".h", import_path) # If the import already works, don't modify it if (file_path.parents[0] / import_path).exists(): @@ -219,7 +205,7 @@ def _find_modified_include_path(self, project_dir, file_path, import_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 ext in ("c", "h", "cpp"): for filename in source_dir.rglob(f"*.{ext}"): with filename.open() as file: lines = file.readlines() @@ -239,28 +225,30 @@ def _convert_imports(self, project_dir, source_dir): 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, - # in case TVM needs to call it from the new location - 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) + project_dir.mkdir() source_dir = project_dir / "src" + source_dir.mkdir() + + # Copies files from the template folder to project_dir. model.h is copied here, + # but will also need to be templated later. + if IS_TEMPLATE: + shutil.copy2(API_SERVER_DIR / "microtvm_api_server.py", project_dir) + self._copy_project_files(API_SERVER_DIR, project_dir, options["project_type"]) # 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) - + metadata = self._disassemble_mlf(model_library_format_path, source_dir) shutil.copy2(model_library_format_path, source_dir / "model") - # Populate our parameters file - self._populate_parameters_file(graph, source_dir) + # Template model.h with metadata to minimize space usage + self._template_model_header(source_dir, metadata) + + self._change_cpp_file_extensions(source_dir) # Recursively change imports self._convert_imports(project_dir, source_dir) diff --git a/apps/microtvm/arduino/template_project/project.ino b/apps/microtvm/arduino/template_project/project.ino index 4920e8b36749..291625309fd0 100644 --- a/apps/microtvm/arduino/template_project/project.ino +++ b/apps/microtvm/arduino/template_project/project.ino @@ -1,10 +1,8 @@ #include "src/model.h" -static Model model; - void setup() { - model = Model(); - //model.inference(input_data, output_data); + TVMInitialize(); + //TVMExecute(input_data, output_data); } void loop() { diff --git a/apps/microtvm/arduino/template_project/src/model.c b/apps/microtvm/arduino/template_project/src/model.c new file mode 100644 index 000000000000..30b50ca149eb --- /dev/null +++ b/apps/microtvm/arduino/template_project/src/model.c @@ -0,0 +1,83 @@ +#ifndef TVM_IMPLEMENTATION_ARDUINO +#define TVM_IMPLEMENTATION_ARDUINO + +#include "model.h" + +#include "Arduino.h" +#include "standalone_crt/include/tvm/runtime/crt/internal/aot_executor/aot_executor.h" +#include "standalone_crt/include/tvm/runtime/crt/stack_allocator.h" + +// AOT memory array +static uint8_t g_aot_memory[WORKSPACE_SIZE]; +extern tvm_model_t tvmgen_default_network; +tvm_workspace_t app_workspace; + +// Blink code for debugging purposes +void TVMPlatformAbort(tvm_crt_error_t error) { + 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); + } +} + +void TVMLogf(const char* msg, ...) {} + +tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, void** out_ptr) { + return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr); +} + +tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { + return StackMemoryManager_Free(&app_workspace, ptr); +} + +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; +} + +tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { + for (size_t i = 0; i < num_bytes; i++) { + buffer[i] = rand(); + } + return kTvmErrorNoError; +} + +void TVMInitialize() { StackMemoryManager_Init(&app_workspace, g_aot_memory, WORKSPACE_SIZE); } + +void TVMExecute(void* input_data, void* output_data) { + void* inputs[1] = { + input_data, + }; + void* outputs[1] = { + output_data, + }; + int ret_val = tvm_runtime_run(&tvmgen_default_network, inputs, outputs); +} + +#endif diff --git a/apps/microtvm/arduino/template_project/src/model.cpp b/apps/microtvm/arduino/template_project/src/model.cpp deleted file mode 100644 index 1142dde24cb7..000000000000 --- a/apps/microtvm/arduino/template_project/src/model.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#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 index 61f2cfbe994c..63339fff25cd 100644 --- a/apps/microtvm/arduino/template_project/src/model.h +++ b/apps/microtvm/arduino/template_project/src/model.h @@ -1,16 +1,19 @@ -#ifndef Model_h -#define Model_h +#ifndef IMPLEMENTATION_H_ +#define IMPLEMENTATION_H_ -#include "standalone_crt/include/tvm/runtime/crt/graph_executor.h" +#define WORKSPACE_SIZE 0 -class Model { - public: - Model(); - void inference(void* input_data, void* output_data); - int infer_category(void* input_data); +#ifdef __cplusplus +extern "C" { +#endif + +#include "standalone_crt/include/tvm/runtime/crt/logging.h" - private: - TVMGraphExecutor* graph_runtime; -}; +void TVMLogf(const char* msg, ...); +void TVMPlatformAbort(tvm_crt_error_t error); +#ifdef __cplusplus +} // extern "C" #endif + +#endif // IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/template_project/src/parameters.h b/apps/microtvm/arduino/template_project/src/parameters.h deleted file mode 100644 index b0d512ad749f..000000000000 --- a/apps/microtvm/arduino/template_project/src/parameters.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 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 int64_t OUTPUT_DATA_SHAPE[] = $output_data_shape; -static const DLDataType OUTPUT_DATA_TYPE = $output_data_type; - -#endif diff --git a/tests/micro/arduino/test.txt b/tests/micro/arduino/test.txt new file mode 100644 index 000000000000..91b416dbc288 --- /dev/null +++ b/tests/micro/arduino/test.txt @@ -0,0 +1,4 @@ +newline +First line +Second line +Third line diff --git a/tests/micro/arduino/test_arduino_rpc_server.py b/tests/micro/arduino/test_arduino_rpc_server.py new file mode 100644 index 000000000000..13cec7269f0a --- /dev/null +++ b/tests/micro/arduino/test_arduino_rpc_server.py @@ -0,0 +1,91 @@ +import datetime +import os +import pathlib +import shutil +import sys +import time + +import numpy as np +import pytest +import tflite +import tvm +from tvm import micro, relay + +import conftest + +""" + +""" + +PLATFORMS = conftest.PLATFORMS + + +def _make_session(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 = ( + 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=False, root=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_cli_cmd": arduino_cli_cmd, "project_type": "host_driven", "verbose": 0}, + ) + project.build() + project.flash() + return tvm.micro.Session(project.transport()) + + +# This is bad, don't do this +TARGET = "c -keys=cpu -executor=aot -link-params=1 -model=host -runtime=c -unpacked-api=1" + +def test_relay(platform, arduino_cli_cmd): + """Testing a simple relay graph""" + model, arduino_board = PLATFORMS[platform] + build_config = {} + shape = (10,) + dtype = "int8" + + # Construct Relay program. + x = relay.var("x", relay.TensorType(shape=shape, dtype=dtype)) + xx = relay.multiply(x, x) + z = relay.add(xx, relay.const(np.ones(shape=shape, dtype=dtype))) + func = relay.Function([x], z) + + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + mod = tvm.relay.build(func, target=TARGET) + + with _make_session(model, TARGET, arduino_board, arduino_cli_cmd, mod, build_config) as session: + graph_mod = tvm.micro.create_local_graph_executor( + mod.get_graph_json(), session.get_system_lib(), session.device + ) + 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() + tvm.testing.assert_allclose(graph_mod.get_input(0).numpy(), x_in) + tvm.testing.assert_allclose(result, x_in * x_in + 1) + + +if __name__ == "__main__": + sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 6104ac5e891e..3270fcda2354 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -55,13 +55,13 @@ def _generate_project(model, target, arduino_board, arduino_cli_cmd, mod, build_ str(template_project_dir), mod, workspace.relpath("project"), - {"arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "verbose": 0}, + {"arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "project_type": "template_project", "verbose": 0}, ) 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" +TARGET = "c -keys=cpu -executor=aot -link-params=1 -model=host -runtime=c -unpacked-api=1" @pytest.fixture(scope="module") @@ -79,7 +79,6 @@ def yes_no_project(platform, arduino_cli_cmd): mod = relay.build(mod, TARGET, params=params) return _generate_project(model, TARGET, arduino_board, arduino_cli_cmd, mod, build_config) - # return tvm.micro.Session(project.transport()) @pytest.fixture(scope="module") @@ -98,39 +97,31 @@ def test_project_folder_structure(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", "model.c", "model.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"] - ) - 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 set(os.listdir(model_dir)) == set(["default_lib0.c", "default_lib1.c", "model.tar"]) -def test_parameter_header_templating(project_dir): - # Ensure parameters.h was templated with correct information - # 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 +def test_model_header_templating(project_dir): + # Ensure model.h was templated with correct WORKSPACE_SIZE + with (project_dir / "src" / "model.h").open() as f: + model_h = f.read() + assert "#define WORKSPACE_SIZE 21312" in model_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() - - 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 + runtime_path = project_dir / "src" / "standalone_crt" / "src" / "runtime" + c_backend_api_path = runtime_path / "crt" / "common" / "crt_backend_api.c" + assert c_backend_api_path.exists() + + with c_backend_api_path.open() as f: + c_backend_api_c = f.read() + assert '#include "inttypes.h"' in c_backend_api_c + assert "include/tvm/runtime/crt/platform.h" in c_backend_api_c # Build on top of the generated project by replacing the diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino index 796022f1867f..1ae6436967fd 100644 --- a/tests/micro/arduino/testdata/project.ino +++ b/tests/micro/arduino/testdata/project.ino @@ -4,12 +4,10 @@ #include "src/data/unknown.c" #include "src/data/silence.c" -static Model model; - 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); + TVMExecute(input_data, output_data); uint64_t end_time = micros(); Serial.print(data_name); @@ -24,7 +22,7 @@ void performInference(int8_t input_data[1960], char *data_name) { } void setup() { - model = Model(); + TVMInitialize(); Serial.begin(115200); } From 18775426134598d81573bc74a3e558778e08cb98 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Mon, 2 Aug 2021 17:49:21 -0700 Subject: [PATCH 06/11] Arduino RPC server --- .../project.ino | 0 .../src/model.c | 13 +- .../arduino/example_project/src/model.h | 19 ++ .../standalone_crt/crt_config/crt_config.h | 0 apps/microtvm/arduino/host_driven/project.ino | 14 +- apps/microtvm/arduino/host_driven/src/model.c | 33 +-- apps/microtvm/arduino/host_driven/src/model.h | 2 +- .../template_project/microtvm_api_server.py | 116 ++++++-- .../arduino/template_project/src/model.h | 19 -- python/tvm/target/target.py | 10 +- tests/micro/arduino/.gitignore | 1 + tests/micro/arduino/conftest.py | 25 +- tests/micro/arduino/test.txt | 4 - .../micro/arduino/test_arduino_rpc_server.py | 256 +++++++++++++++++- tests/micro/arduino/test_arduino_workflow.py | 19 +- tests/micro/arduino/testdata/digit-2.jpg | Bin 0 -> 572 bytes tests/micro/arduino/testdata/digit-9.jpg | Bin 0 -> 535 bytes tests/micro/arduino/testdata/project.ino | 4 +- 18 files changed, 416 insertions(+), 119 deletions(-) rename apps/microtvm/arduino/{template_project => example_project}/project.ino (100%) rename apps/microtvm/arduino/{template_project => example_project}/src/model.c (91%) create mode 100644 apps/microtvm/arduino/example_project/src/model.h rename apps/microtvm/arduino/{template_project => example_project}/src/standalone_crt/crt_config/crt_config.h (100%) delete mode 100644 apps/microtvm/arduino/template_project/src/model.h create mode 100644 tests/micro/arduino/.gitignore delete mode 100644 tests/micro/arduino/test.txt create mode 100644 tests/micro/arduino/testdata/digit-2.jpg create mode 100644 tests/micro/arduino/testdata/digit-9.jpg diff --git a/apps/microtvm/arduino/template_project/project.ino b/apps/microtvm/arduino/example_project/project.ino similarity index 100% rename from apps/microtvm/arduino/template_project/project.ino rename to apps/microtvm/arduino/example_project/project.ino diff --git a/apps/microtvm/arduino/template_project/src/model.c b/apps/microtvm/arduino/example_project/src/model.c similarity index 91% rename from apps/microtvm/arduino/template_project/src/model.c rename to apps/microtvm/arduino/example_project/src/model.c index 30b50ca149eb..788dcccd2728 100644 --- a/apps/microtvm/arduino/template_project/src/model.c +++ b/apps/microtvm/arduino/example_project/src/model.c @@ -15,6 +15,7 @@ tvm_workspace_t app_workspace; // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { for (;;) { + #ifdef LED_BUILTIN digitalWrite(LED_BUILTIN, HIGH); delay(250); digitalWrite(LED_BUILTIN, LOW); @@ -23,6 +24,7 @@ void TVMPlatformAbort(tvm_crt_error_t error) { delay(250); digitalWrite(LED_BUILTIN, LOW); delay(750); + #endif } } @@ -71,13 +73,10 @@ tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { void TVMInitialize() { StackMemoryManager_Init(&app_workspace, g_aot_memory, WORKSPACE_SIZE); } void TVMExecute(void* input_data, void* output_data) { - void* inputs[1] = { - input_data, - }; - void* outputs[1] = { - output_data, - }; - int ret_val = tvm_runtime_run(&tvmgen_default_network, inputs, outputs); + int ret_val = tvmgen_default_run_model(input_data, output_data); + if (ret_val != 0) { + TVMPlatformAbort(kTvmErrorPlatformCheckFailure); + } } #endif diff --git a/apps/microtvm/arduino/example_project/src/model.h b/apps/microtvm/arduino/example_project/src/model.h new file mode 100644 index 000000000000..be1bd5415e77 --- /dev/null +++ b/apps/microtvm/arduino/example_project/src/model.h @@ -0,0 +1,19 @@ +#ifndef IMPLEMENTATION_H_ +#define IMPLEMENTATION_H_ + +#define WORKSPACE_SIZE $workspace_size_bytes + +#ifdef __cplusplus +extern "C" { +#endif + +void TVMInitialize(); + +// TODO template these void* values once MLF format has input and output data +void TVMExecute(void* input_data, void* output_data); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h b/apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h similarity index 100% rename from apps/microtvm/arduino/template_project/src/standalone_crt/crt_config/crt_config.h rename to apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h diff --git a/apps/microtvm/arduino/host_driven/project.ino b/apps/microtvm/arduino/host_driven/project.ino index 94f21739e2c6..5bf13b57243e 100644 --- a/apps/microtvm/arduino/host_driven/project.ino +++ b/apps/microtvm/arduino/host_driven/project.ino @@ -1,13 +1,10 @@ #include "src/standalone_crt/include/tvm/runtime/crt/microtvm_rpc_server.h" #include "src/standalone_crt/include/tvm/runtime/crt/logging.h" #include "src/model.h" -static size_t g_num_bytes_requested = 0; -static size_t g_num_bytes_written = 0; microtvm_rpc_server_t server; // Called by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { - g_num_bytes_requested += size; Serial.write(data, size); return size; } @@ -15,16 +12,15 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { void setup() { server = MicroTVMRpcServerInit(write_serial, NULL); TVMLogf("microTVM Arduino runtime - running"); + Serial.begin(115200); } void loop() { - noInterrupts(); int to_read = Serial.available(); uint8_t data[to_read]; size_t bytes_read = Serial.readBytes(data, to_read); uint8_t* arr_ptr = data; uint8_t** data_ptr = &arr_ptr; - if (bytes_read > 0) { size_t bytes_remaining = bytes_read; while (bytes_remaining > 0) { @@ -33,14 +29,6 @@ void loop() { if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { TVMPlatformAbort(err); } - if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { - if (g_num_bytes_written != g_num_bytes_requested) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef5); - } - g_num_bytes_written = 0; - g_num_bytes_requested = 0; - } } } - interrupts(); } diff --git a/apps/microtvm/arduino/host_driven/src/model.c b/apps/microtvm/arduino/host_driven/src/model.c index 7ce4ffe0f5f4..5d6512e7e5d3 100644 --- a/apps/microtvm/arduino/host_driven/src/model.c +++ b/apps/microtvm/arduino/host_driven/src/model.c @@ -1,30 +1,22 @@ #ifndef TVM_IMPLEMENTATION_ARDUINO #define TVM_IMPLEMENTATION_ARDUINO -#include "stdarg.h" - #include "model.h" #include "Arduino.h" #include "standalone_crt/include/tvm/runtime/crt/internal/aot_executor/aot_executor.h" -#include "standalone_crt/include/tvm/runtime/crt/stack_allocator.h" - -// AOT memory array -static uint8_t g_aot_memory[WORKSPACE_SIZE]; -extern tvm_model_t tvmgen_default_network; -tvm_workspace_t app_workspace; +#include "stdarg.h" // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { 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); + for (int i = 0; i < 4; i++) { + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(250); + } + delay(1000); } } @@ -34,11 +26,16 @@ size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const } tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, void** out_ptr) { - return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr); + if (num_bytes == 0) { + num_bytes = sizeof(int); + } + *out_ptr = malloc(num_bytes); + return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; } tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { - return StackMemoryManager_Free(&app_workspace, ptr); + free(ptr); + return kTvmErrorNoError; } unsigned long g_utvm_start_time; diff --git a/apps/microtvm/arduino/host_driven/src/model.h b/apps/microtvm/arduino/host_driven/src/model.h index b2165ae627da..136b17c061cd 100644 --- a/apps/microtvm/arduino/host_driven/src/model.h +++ b/apps/microtvm/arduino/host_driven/src/model.h @@ -1,6 +1,6 @@ #ifndef IMPLEMENTATION_H_ #define IMPLEMENTATION_H_ -#define WORKSPACE_SIZE 0 +#define WORKSPACE_SIZE 65535 #endif // IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index fffd1571bb52..8ac670575376 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -41,6 +41,19 @@ class BoardAutodetectFailed(Exception): BOARD_PROPERTIES = { + "due": { + "package": "arduino", + "architecture": "sam", + "board": "arduino_due_x" + }, + # Due to the way the Feather S2 bootloader works, compilation + # behaves fine but uploads cannot be done automatically + "feathers2": { + "package": "esp32", + "architecture": "esp32", + "board": "feathers2", + }, + # Spresense only works as of its v2.3.0 sdk "spresense": { "package": "SPRESENSE", "architecture": "spresense", @@ -51,12 +64,27 @@ class BoardAutodetectFailed(Exception): "architecture": "mbed_nano", "board": "nano33ble", }, + "pybadge": { + "package": "adafruit", + "architecture": "samd", + "board": "adafruit_pybadge_m4", + }, + # The Teensy boards are listed here for completeness, but they + # won't work until https://github.com/arduino/arduino-cli/issues/700 + # is finished + "teensy40": { + "package": "teensy", + "architecture": "avr", + "board": "teensy40", + }, + "teensy41": { + "package": "teensy", + "architecture": "avr", + "board": "teensy41", + }, } -PROJECT_TYPES = [ - "template_project", - "host_driven" -] +PROJECT_TYPES = ["example_project", "host_driven"] PROJECT_OPTIONS = [ server.ProjectOption( @@ -67,7 +95,7 @@ class BoardAutodetectFailed(Exception): 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( - "project_type", + "example_project", help="Type of project to generate.", choices=tuple(PROJECT_TYPES), ), @@ -94,9 +122,14 @@ def server_info_query(self): def _copy_project_files(self, api_server_dir, project_dir, project_type): project_types_folder = api_server_dir.parents[0] - shutil.copytree(project_types_folder / project_type / "src", project_dir / "src", dirs_exist_ok=True) + shutil.copytree( + project_types_folder / project_type / "src", project_dir / "src", dirs_exist_ok=True + ) # Arduino requires the .ino file have the same filename as its containing folder - shutil.copy2(project_types_folder / project_type / "project.ino", project_dir / f"{project_dir.stem}.ino") + shutil.copy2( + project_types_folder / project_type / "project.ino", + project_dir / f"{project_dir.stem}.ino", + ) CRT_COPY_ITEMS = ("include", "src") @@ -111,27 +144,40 @@ def _copy_standalone_crt(self, source_dir, standalone_crt_dir): else: shutil.copy2(src_path, dst_path) - UNUSED_COMPONENTS = [ - + # Example project is the "minimum viable project", + # and doesn't need a fancy RPC server + EXAMPLE_PROJECT_UNUSED_COMPONENTS = [ + "include/dmlc", + "src/support", + "src/runtime/minrpc", + "src/runtime/crt/graph_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: + def _remove_unused_components(self, source_dir, project_type): + unused_components = [] + if project_type == "example_project": + unused_components = self.EXAMPLE_PROJECT_UNUSED_COMPONENTS + + for component in unused_components: shutil.rmtree(source_dir / "standalone_crt" / component) def _disassemble_mlf(self, mlf_tar_path, source_dir): - with tempfile.TemporaryDirectory() as mlf_unpacking_dir: + with tempfile.TemporaryDirectory() as mlf_unpacking_dir_str: + mlf_unpacking_dir = pathlib.Path(mlf_unpacking_dir_str) with tarfile.open(mlf_tar_path, "r:") as tar: tar.extractall(mlf_unpacking_dir) - # Copy C files 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, source), model_dir / dest) + + # Copy C files from model. The filesnames and quantity + # depend on the target string, so we just copy all c files + source_dir = mlf_unpacking_dir / "codegen" / "host" / "src" + for file in source_dir.rglob(f"*.c"): + shutil.copy(file, model_dir) # Return metadata.json for use in templating with open(os.path.join(mlf_unpacking_dir, "metadata.json")) as f: @@ -151,39 +197,38 @@ def _template_model_header(self, source_dir, metadata): with open(source_dir / "model.h", "w") as f: f.write(model_h_template.substitute(template_values)) - # Arduino ONLY recognizes .ino, .ccp, .c, .h CPP_FILE_EXTENSION_SYNONYMS = ("cc", "cxx") + def _change_cpp_file_extensions(self, source_dir): for ext in self.CPP_FILE_EXTENSION_SYNONYMS: for filename in source_dir.rglob(f"*.{ext}"): - filename.rename(filename.with_suffix('.cpp')) + filename.rename(filename.with_suffix(".cpp")) for filename in source_dir.rglob(f"*.inc"): - filename.rename(filename.with_suffix('.h')) - + filename.rename(filename.with_suffix(".h")) def _process_autogenerated_inc_files(self, source_dir): for filename in source_dir.rglob(f"*.inc"): # Individual file fixes if filename.stem == "gentab_ccitt": - with open(filename, 'r+') as f: + with open(filename, "r+") as f: content = f.read() f.seek(0, 0) f.write('#include "inttypes.h"\n' + content) - filename.rename(filename.with_suffix('.c')) + filename.rename(filename.with_suffix(".c")) 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 for a .inc file we renamed to .c earlier, fix it if import_path.endswith(self.CPP_FILE_EXTENSION_SYNONYMS): - import_path = re.sub(r'\.[a-z]+$', ".cpp", import_path) + import_path = re.sub(r"\.[a-z]+$", ".cpp", import_path) if import_path.endswith(".inc"): - import_path = re.sub(r'\.[a-z]+$', ".h", import_path) + import_path = re.sub(r"\.[a-z]+$", ".h", import_path) # If the import already works, don't modify it if (file_path.parents[0] / import_path).exists(): @@ -239,14 +284,15 @@ 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_unused_components(source_dir, options["project_type"]) # Unpack the MLF and copy the relevant files metadata = self._disassemble_mlf(model_library_format_path, source_dir) shutil.copy2(model_library_format_path, source_dir / "model") - # Template model.h with metadata to minimize space usage - self._template_model_header(source_dir, metadata) + # For AOT, template model.h with metadata to minimize space usage + if options["project_type"] == "example_project": + self._template_model_header(source_dir, metadata) self._change_cpp_file_extensions(source_dir) @@ -341,7 +387,17 @@ def open_transport(self, options): return port = self._get_arduino_port(options) + + # Wait for port to become available + for attempts in range(10): + if any(serial.tools.list_ports.grep(port)): + break + time.sleep(0.5) + + # TODO figure out why RPC serial communication times out 90% (not 100%) + # of the time on the Nano 33 BLE 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, @@ -364,7 +420,7 @@ def read_transport(self, n, timeout_sec): def write_transport(self, data, timeout_sec): if self._serial is None: raise server.TransportClosedError() - return self._serial.write(data, timeout_sec) + return self._serial.write(data) if __name__ == "__main__": diff --git a/apps/microtvm/arduino/template_project/src/model.h b/apps/microtvm/arduino/template_project/src/model.h deleted file mode 100644 index 63339fff25cd..000000000000 --- a/apps/microtvm/arduino/template_project/src/model.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef IMPLEMENTATION_H_ -#define IMPLEMENTATION_H_ - -#define WORKSPACE_SIZE 0 - -#ifdef __cplusplus -extern "C" { -#endif - -#include "standalone_crt/include/tvm/runtime/crt/logging.h" - -void TVMLogf(const char* msg, ...); -void TVMPlatformAbort(tvm_crt_error_t error); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // IMPLEMENTATION_H_ diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index 75ac81c55ec6..eedd7879cc03 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -281,12 +281,17 @@ def intel_graphics(model="unknown", options=None): MICRO_SUPPORTED_MODELS = { "host": [], + "atsamd51": ["-mcpu=cortex-m4"], + "cxd5602gg": ["-mcpu=cortex-m4"], + "esp32": [], + "imxrt1060": ["-mcpu=cortex-m7"], "mps2_an521": ["-mcpu=cortex-m33"], + "nrf52840": ["-mcpu=cortex-m4"], "nrf5340dk": ["-mcpu=cortex-m33"], + "sam3x8e": ["-mcpu=cortex-m3"], "stm32f746xx": ["-mcpu=cortex-m7", "-march=armv7e-m"], "stm32l4r5zi": ["-mcpu=cortex-m4"], "zynq_mp_r5": ["-mcpu=cortex-r5"], - "cxd5602gg": ["-mcpu=cortex-m4f"], } @@ -308,7 +313,8 @@ def micro(model="unknown", options=None): options, ) - if (not options) or (options and "--executor=aot" not in options): + if (not options) or (options and not any("-executor=aot" in o for o in options)): + print("Adding system libs!") opts = _merge_opts(opts, "--system-lib") # NOTE: in the future, the default micro target will be LLVM except when diff --git a/tests/micro/arduino/.gitignore b/tests/micro/arduino/.gitignore new file mode 100644 index 000000000000..30bf0f9bc376 --- /dev/null +++ b/tests/micro/arduino/.gitignore @@ -0,0 +1 @@ +workspace* diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index bc9437482032..9a0d3623cb89 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -5,17 +5,23 @@ # The models that should pass this configuration. Maps a short, identifying platform string to # (model, zephyr_board). PLATFORMS = { + "due": ("sam3x8e", "due"), + "feathers2": ("esp32", "feathers2"), + "nano33ble": ("nrf52840", "nano33ble"), + "pybadge": ("atsamd51", "pybadge"), "spresense": ("cxd5602gg", "spresense"), - "nano33ble": ("nRF52840", "nano33ble"), + "teensy40": ("imxrt1060", "teensy40"), + "teensy41": ("imxrt1060", "teensy41"), } def pytest_addoption(parser): parser.addoption( - "--platform", - default="spresense", + "--platforms", + default=["due"], + nargs="*", choices=PLATFORMS.keys(), - help="Target platform for microTVM tests.", + help="Target platforms for microTVM tests.", ) parser.addoption( "--arduino-cli-cmd", @@ -28,11 +34,12 @@ def pytest_addoption(parser): help="Run tests that require physical hardware.", ) - -# TODO re-add parameterization -@pytest.fixture(scope="session") -def platform(request): - return request.config.getoption("--platform") +# We might do project generation differently for different boards in the future +# (to take advantage of multiple cores / external memory / etc.), so all tests +# are parameterized by board +def pytest_generate_tests(metafunc): + platforms = metafunc.config.getoption("platforms") + metafunc.parametrize("platform", platforms, scope="session") @pytest.fixture(scope="session") diff --git a/tests/micro/arduino/test.txt b/tests/micro/arduino/test.txt deleted file mode 100644 index 91b416dbc288..000000000000 --- a/tests/micro/arduino/test.txt +++ /dev/null @@ -1,4 +0,0 @@ -newline -First line -Second line -Third line diff --git a/tests/micro/arduino/test_arduino_rpc_server.py b/tests/micro/arduino/test_arduino_rpc_server.py index 13cec7269f0a..7d274eb77240 100644 --- a/tests/micro/arduino/test_arduino_rpc_server.py +++ b/tests/micro/arduino/test_arduino_rpc_server.py @@ -6,21 +6,28 @@ import time import numpy as np +import onnx +from PIL import Image import pytest import tflite + import tvm from tvm import micro, relay +from tvm.relay.testing import byoc import conftest """ +This unit test simulates an autotuning workflow, where we: +1. Instantiate the Arduino RPC server project +2. Build and flash that project onto our target board """ PLATFORMS = conftest.PLATFORMS -def _make_session(model, target, arduino_board, arduino_cli_cmd, mod, build_config): +def _make_session(model, arduino_board, arduino_cli_cmd, mod): parent_dir = os.path.dirname(__file__) filename = os.path.splitext(os.path.basename(__file__))[0] prev_build = ( @@ -49,20 +56,90 @@ def _make_session(model, target, arduino_board, arduino_cli_cmd, mod, build_conf str(template_project_dir), mod, workspace.relpath("project"), - {"arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "project_type": "host_driven", "verbose": 0}, + { + "arduino_board": arduino_board, + "arduino_cli_cmd": arduino_cli_cmd, + "project_type": "host_driven", + "verbose": 0, + }, ) project.build() project.flash() return tvm.micro.Session(project.transport()) -# This is bad, don't do this -TARGET = "c -keys=cpu -executor=aot -link-params=1 -model=host -runtime=c -unpacked-api=1" +def _make_sess_from_op(model, arduino_board, arduino_cli_cmd, op_name, sched, arg_bufs): + target = tvm.target.target.micro(model) + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + mod = tvm.build(sched, arg_bufs, target=target, name=op_name) + + return _make_session(model, arduino_board, arduino_cli_cmd, mod) + + +def _make_add_sess(model, arduino_board, arduino_cli_cmd): + A = tvm.te.placeholder((2,), dtype="int8") + B = tvm.te.placeholder((1,), dtype="int8") + C = tvm.te.compute(A.shape, lambda i: A[i] + B[0], name="C") + sched = tvm.te.create_schedule(C.op) + return _make_sess_from_op(model, arduino_board, arduino_cli_cmd, "add", sched, [A, B, C]) + + +# The same test code can be executed on both the QEMU simulation and on real hardware. +@tvm.testing.requires_micro +def test_compile_runtime(platform, arduino_cli_cmd): + """Test compiling the on-device runtime.""" + + model, arduino_board = PLATFORMS[platform] + + # NOTE: run test in a nested function so cPython will delete arrays before closing the session. + def test_basic_add(sess): + A_data = tvm.nd.array(np.array([2, 3], dtype="int8"), device=sess.device) + assert (A_data.numpy() == np.array([2, 3])).all() + B_data = tvm.nd.array(np.array([4], dtype="int8"), device=sess.device) + assert (B_data.numpy() == np.array([4])).all() + C_data = tvm.nd.array(np.array([0, 0], dtype="int8"), device=sess.device) + assert (C_data.numpy() == np.array([0, 0])).all() + + system_lib = sess.get_system_lib() + system_lib.get_function("add")(A_data, B_data, C_data) + assert (C_data.numpy() == np.array([6, 7])).all() + + with _make_add_sess(model, arduino_board, arduino_cli_cmd) as sess: + test_basic_add(sess) + + +@tvm.testing.requires_micro +def test_platform_timer(platform, arduino_cli_cmd): + """Test compiling the on-device runtime.""" + + model, arduino_board = PLATFORMS[platform] + # NOTE: run test in a nested function so cPython will delete arrays before closing the session. + def test_basic_add(sess): + A_data = tvm.nd.array(np.array([2, 3], dtype="int8"), device=sess.device) + assert (A_data.numpy() == np.array([2, 3])).all() + B_data = tvm.nd.array(np.array([4], dtype="int8"), device=sess.device) + assert (B_data.numpy() == np.array([4])).all() + C_data = tvm.nd.array(np.array([0, 0], dtype="int8"), device=sess.device) + assert (C_data.numpy() == np.array([0, 0])).all() + + system_lib = sess.get_system_lib() + time_eval_f = system_lib.time_evaluator( + "add", sess.device, number=20, repeat=3, min_repeat_ms=40 + ) + result = time_eval_f(A_data, B_data, C_data) + assert (C_data.numpy() == np.array([6, 7])).all() + assert result.mean > 0 + assert len(result.results) == 3 + + with _make_add_sess(model, arduino_board, arduino_cli_cmd) as sess: + test_basic_add(sess) + + +@tvm.testing.requires_micro def test_relay(platform, arduino_cli_cmd): """Testing a simple relay graph""" model, arduino_board = PLATFORMS[platform] - build_config = {} shape = (10,) dtype = "int8" @@ -72,10 +149,11 @@ def test_relay(platform, arduino_cli_cmd): z = relay.add(xx, relay.const(np.ones(shape=shape, dtype=dtype))) func = relay.Function([x], z) + target = tvm.target.target.micro(model) with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): - mod = tvm.relay.build(func, target=TARGET) + mod = tvm.relay.build(func, target=target) - with _make_session(model, TARGET, arduino_board, arduino_cli_cmd, mod, build_config) as session: + with _make_session(model, arduino_board, arduino_cli_cmd, mod) as session: graph_mod = tvm.micro.create_local_graph_executor( mod.get_graph_json(), session.get_system_lib(), session.device ) @@ -87,5 +165,169 @@ def test_relay(platform, arduino_cli_cmd): tvm.testing.assert_allclose(result, x_in * x_in + 1) +@tvm.testing.requires_micro +def test_onnx(platform, arduino_cli_cmd): + """Testing a simple ONNX model.""" + model, arduino_board = PLATFORMS[platform] + + # Load test images. + this_dir = os.path.dirname(__file__) + digit_2 = Image.open(f"{this_dir}/testdata/digit-2.jpg").resize((28, 28)) + digit_2 = np.asarray(digit_2).astype("float32") + digit_2 = np.expand_dims(digit_2, axis=0) + + digit_9 = Image.open(f"{this_dir}/testdata/digit-9.jpg").resize((28, 28)) + digit_9 = np.asarray(digit_9).astype("float32") + digit_9 = np.expand_dims(digit_9, axis=0) + + # Load ONNX model and convert to Relay. + onnx_model = onnx.load(f"{this_dir}/testdata/mnist-8.onnx") + shape = {"Input3": (1, 1, 28, 28)} + relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) + relay_mod = relay.transform.DynamicToStatic()(relay_mod) + + target = tvm.target.target.micro(model, options=["-link-params=1"]) + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + lowered = relay.build(relay_mod, target, params=params) + graph = lowered.get_graph_json() + + with _make_session(model, arduino_board, arduino_cli_cmd, lowered) as session: + graph_mod = tvm.micro.create_local_graph_executor( + graph, session.get_system_lib(), session.device + ) + + # Send the digit-2 image and confirm that the correct result is returned. + graph_mod.set_input("Input3", tvm.nd.array(digit_2)) + graph_mod.run() + result = graph_mod.get_output(0).numpy() + print(result) + assert np.argmax(result) == 2 + + # Send the digit-9 image and confirm that the correct result is returned. + graph_mod.set_input("Input3", tvm.nd.array(digit_9)) + graph_mod.run() + result = graph_mod.get_output(0).numpy() + assert np.argmax(result) == 9 + + +def check_result(relay_mod, model, arduino_board, arduino_cli_cmd, map_inputs, out_shape, result): + """Helper function to verify results""" + TOL = 1e-5 + target = tvm.target.target.micro(model) + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + mod = tvm.relay.build(relay_mod, target=target) + + with _make_session(model, arduino_board, arduino_cli_cmd, mod) as session: + rt_mod = tvm.micro.create_local_graph_executor( + mod.get_graph_json(), session.get_system_lib(), session.device + ) + rt_mod.set_input(**mod.get_params()) + for name, data in map_inputs.items(): + rt_mod.set_input(name, data) + rt_mod.set_input(**mod.get_params()) + rt_mod.run() + + out_shapes = out_shape if isinstance(out_shape, list) else [out_shape] + results = result if isinstance(result, list) else [result] + + for idx, shape in enumerate(out_shapes): + out = tvm.nd.empty(shape, device=session.device) + out = rt_mod.get_output(idx, out) + tvm.testing.assert_allclose(out.numpy(), results[idx], rtol=TOL, atol=TOL) + + +@tvm.testing.requires_micro +def test_byoc_microtvm(platform, arduino_cli_cmd): + """This is a simple test case to check BYOC capabilities of microTVM""" + model, arduino_board = PLATFORMS[platform] + x = relay.var("x", shape=(10, 10)) + w0 = relay.var("w0", shape=(10, 10)) + w1 = relay.var("w1", shape=(10, 10)) + w2 = relay.var("w2", shape=(10, 10)) + w3 = relay.var("w3", shape=(10, 10)) + w4 = relay.var("w4", shape=(10, 10)) + w5 = relay.var("w5", shape=(10, 10)) + w6 = relay.var("w6", shape=(10, 10)) + w7 = relay.var("w7", shape=(10, 10)) + + # C compiler + z0 = relay.add(x, w0) + p0 = relay.subtract(z0, w1) + q0 = relay.multiply(p0, w2) + + z1 = relay.add(x, w3) + p1 = relay.subtract(z1, w4) + q1 = relay.multiply(p1, w5) + + # Other parts on TVM + z2 = relay.add(x, w6) + q2 = relay.subtract(z2, w7) + + 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 = byoc.CcompilerAnnotator() + mod["main"] = ann.visit(f) + mod = tvm.relay.transform.PartitionGraph()(mod) + mod = tvm.relay.transform.InferType()(mod) + + x_data = np.random.rand(10, 10).astype("float32") + w_data = [] + for _ in range(8): + w_data.append(np.random.rand(10, 10).astype("float32")) + + map_inputs = {"w{}".format(i): w_data[i] for i in range(8)} + map_inputs["x"] = x_data + check_result( + relay_mod=mod, + map_inputs=map_inputs, + out_shape=(30, 10), + result=np.concatenate( + ( + ((x_data + w_data[0]) - w_data[1]) * w_data[2], + ((x_data + w_data[3]) - w_data[4]) * w_data[5], + x_data + w_data[6] - w_data[7], + ), + axis=0, + ), + model=model, + arduino_board=arduino_board, + arduino_cli_cmd=arduino_cli_cmd, + ) + + +def _make_add_sess_with_shape(model, arduino_board, arduino_cli_cmd, shape): + A = tvm.te.placeholder(shape, dtype="int8") + C = tvm.te.compute(A.shape, lambda i: A[i] + A[i], name="C") + sched = tvm.te.create_schedule(C.op) + return _make_sess_from_op(model, arduino_board, arduino_cli_cmd, "add", sched, [A, C]) + + +@pytest.mark.parametrize( + "shape,", + [ + pytest.param((1 * 1024,), id="(1*1024)"), + pytest.param((4 * 1024,), id="(4*1024)"), + pytest.param((16 * 1024,), id="(16*1024)"), + ], +) +@tvm.testing.requires_micro +def test_rpc_large_array(platform, arduino_cli_cmd, shape): + """Test large RPC array transfer.""" + model, arduino_board = PLATFORMS[platform] + + # NOTE: run test in a nested function so cPython will delete arrays before closing the session. + def test_tensors(sess): + a_np = np.random.randint(low=-128, high=127, size=shape, dtype="int8") + + A_data = tvm.nd.array(a_np, device=sess.device) + assert (A_data.numpy() == a_np).all() + C_data = tvm.nd.array(np.zeros(shape, dtype="int8"), device=sess.device) + assert (C_data.numpy() == np.zeros(shape)).all() + + with _make_add_sess_with_shape(model, arduino_board, arduino_cli_cmd, shape) as sess: + test_tensors(sess) + + if __name__ == "__main__": sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 3270fcda2354..89f146b923dc 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -55,15 +55,16 @@ def _generate_project(model, target, arduino_board, arduino_cli_cmd, mod, build_ str(template_project_dir), mod, workspace.relpath("project"), - {"arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "project_type": "template_project", "verbose": 0}, + { + "arduino_board": arduino_board, + "arduino_cli_cmd": arduino_cli_cmd, + "project_type": "example_project", + "verbose": 0, + }, ) return (workspace, project) -# This is bad, don't do this -TARGET = "c -keys=cpu -executor=aot -link-params=1 -model=host -runtime=c -unpacked-api=1" - - @pytest.fixture(scope="module") def yes_no_project(platform, arduino_cli_cmd): current_dir = os.path.dirname(__file__) @@ -75,10 +76,14 @@ def yes_no_project(platform, arduino_cli_cmd): tflite_model_buf = f.read() tflite_model = tflite.Model.GetRootAsModel(tflite_model_buf, 0) mod, params = relay.frontend.from_tflite(tflite_model) + target = tvm.target.target.micro( + model, options=["--link-params=1", "--unpacked-api=1", "--executor=aot"] + ) + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): - mod = relay.build(mod, TARGET, params=params) + mod = relay.build(mod, target, params=params) - return _generate_project(model, TARGET, arduino_board, arduino_cli_cmd, mod, build_config) + return _generate_project(model, target, arduino_board, arduino_cli_cmd, mod, build_config) @pytest.fixture(scope="module") diff --git a/tests/micro/arduino/testdata/digit-2.jpg b/tests/micro/arduino/testdata/digit-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b709a206b8d776215dcaa78643b22fe628b3c43a GIT binary patch literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} literal 0 HcmV?d00001 diff --git a/tests/micro/arduino/testdata/digit-9.jpg b/tests/micro/arduino/testdata/digit-9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ce9cde3b322b351847da179555aba35358dbf23 GIT binary patch literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja Date: Tue, 10 Aug 2021 12:55:14 -0700 Subject: [PATCH 07/11] Incorporate ProjectAPI changes Add Arduino tests to CI --- .../arduino/example_project/src/model.c | 4 +- .../template_project/microtvm_api_server.py | 10 +- python/tvm/target/target.py | 1 - tests/micro/arduino/conftest.py | 49 ++++++- .../micro/arduino/test_arduino_rpc_server.py | 127 ++++++++++-------- tests/micro/arduino/test_arduino_workflow.py | 76 ++++------- tests/micro/arduino/testdata/mnist-8.onnx | Bin 0 -> 26454 bytes tests/micro/zephyr/conftest.py | 2 + tests/scripts/task_python_microtvm.sh | 2 + 9 files changed, 153 insertions(+), 118 deletions(-) create mode 100644 tests/micro/arduino/testdata/mnist-8.onnx diff --git a/apps/microtvm/arduino/example_project/src/model.c b/apps/microtvm/arduino/example_project/src/model.c index 788dcccd2728..3268e5961bc9 100644 --- a/apps/microtvm/arduino/example_project/src/model.c +++ b/apps/microtvm/arduino/example_project/src/model.c @@ -15,7 +15,7 @@ tvm_workspace_t app_workspace; // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { for (;;) { - #ifdef LED_BUILTIN +#ifdef LED_BUILTIN digitalWrite(LED_BUILTIN, HIGH); delay(250); digitalWrite(LED_BUILTIN, LOW); @@ -24,7 +24,7 @@ void TVMPlatformAbort(tvm_crt_error_t error) { delay(250); digitalWrite(LED_BUILTIN, LOW); delay(750); - #endif +#endif } } diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 8ac670575376..44088cec2064 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -41,11 +41,7 @@ class BoardAutodetectFailed(Exception): BOARD_PROPERTIES = { - "due": { - "package": "arduino", - "architecture": "sam", - "board": "arduino_due_x" - }, + "due": {"package": "arduino", "architecture": "sam", "board": "arduino_due_x"}, # Due to the way the Feather S2 bootloader works, compilation # behaves fine but uploads cannot be done automatically "feathers2": { @@ -112,7 +108,7 @@ def __init__(self): self._port = None self._serial = None - def server_info_query(self): + def server_info_query(self, tvm_version): return server.ServerInfo( platform_name="arduino", is_template=IS_TEMPLATE, @@ -372,7 +368,7 @@ def flash(self, options): ] if options.get("verbose"): - compile_cmd.append("--verbose") + upload_cmd.append("--verbose") output = subprocess.check_call(upload_cmd) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index eedd7879cc03..559e7b3b28d3 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -314,7 +314,6 @@ def micro(model="unknown", options=None): ) if (not options) or (options and not any("-executor=aot" in o for o in options)): - print("Adding system libs!") opts = _merge_opts(opts, "--system-lib") # NOTE: in the future, the default micro target will be LLVM except when diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 9a0d3623cb89..a4ab46dceafb 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -1,3 +1,6 @@ +import datetime +import pathlib + import pytest import tvm.target.target @@ -14,10 +17,21 @@ "teensy41": ("imxrt1060", "teensy41"), } +TEMPLATE_PROJECT_DIR = ( + pathlib.Path(__file__).parent + / ".." + / ".." + / ".." + / "apps" + / "microtvm" + / "arduino" + / "template_project" +).resolve() + def pytest_addoption(parser): parser.addoption( - "--platforms", + "--microtvm-platforms", default=["due"], nargs="*", choices=PLATFORMS.keys(), @@ -33,12 +47,19 @@ def pytest_addoption(parser): 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: ", + ) + # We might do project generation differently for different boards in the future # (to take advantage of multiple cores / external memory / etc.), so all tests # are parameterized by board def pytest_generate_tests(metafunc): - platforms = metafunc.config.getoption("platforms") + platforms = metafunc.config.getoption("microtvm_platforms") metafunc.parametrize("platform", platforms, scope="session") @@ -47,6 +68,30 @@ def arduino_cli_cmd(request): return request.config.getoption("--arduino-cli-cmd") +@pytest.fixture(scope="session") +def tvm_debug(request): + return request.config.getoption("--tvm-debug") + + @pytest.fixture(scope="session") def run_hardware_tests(request): return request.config.getoption("--run-hardware-tests") + + +def make_workspace_dir(test_name, platform): + _, arduino_board = PLATFORMS[platform] + filepath = pathlib.Path(__file__) + board_workspace = ( + filepath.parent + / f"workspace_{test_name}_{arduino_board}" + / datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S") + ) + + number = 0 + while board_workspace.exists(): + number += 1 + board_workspace = pathlib.Path(str(board_workspace) + f"-{number}") + board_workspace.parent.mkdir(exist_ok=True, parents=True) + t = tvm.contrib.utils.tempdir(board_workspace) + # time.sleep(200) + return t diff --git a/tests/micro/arduino/test_arduino_rpc_server.py b/tests/micro/arduino/test_arduino_rpc_server.py index 7d274eb77240..0b5ba07b8b33 100644 --- a/tests/micro/arduino/test_arduino_rpc_server.py +++ b/tests/micro/arduino/test_arduino_rpc_server.py @@ -24,43 +24,25 @@ """ -PLATFORMS = conftest.PLATFORMS +# We'll make a new workspace for each test +@pytest.fixture(scope="function") +def workspace_dir(platform, run_hardware_tests): + if not run_hardware_tests: + pytest.skip() + return conftest.make_workspace_dir("arduino_rpc_server", platform) -def _make_session(model, arduino_board, arduino_cli_cmd, mod): - 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=False, root=workspace_root) - - template_project_dir = ( - pathlib.Path(__file__).parent - / ".." - / ".." - / ".." - / "apps" - / "microtvm" - / "arduino" - / "template_project" - ).resolve() + +def _make_session(model, arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config): project = tvm.micro.generate_project( - str(template_project_dir), + str(conftest.TEMPLATE_PROJECT_DIR), mod, - workspace.relpath("project"), + workspace_dir / "project", { "arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "project_type": "host_driven", - "verbose": 0, + "verbose": bool(build_config.get("debug")), }, ) project.build() @@ -68,28 +50,33 @@ def _make_session(model, arduino_board, arduino_cli_cmd, mod): return tvm.micro.Session(project.transport()) -def _make_sess_from_op(model, arduino_board, arduino_cli_cmd, op_name, sched, arg_bufs): +def _make_sess_from_op( + model, arduino_board, arduino_cli_cmd, workspace_dir, op_name, sched, arg_bufs, build_config +): target = tvm.target.target.micro(model) with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): mod = tvm.build(sched, arg_bufs, target=target, name=op_name) - return _make_session(model, arduino_board, arduino_cli_cmd, mod) + return _make_session(model, arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config) -def _make_add_sess(model, arduino_board, arduino_cli_cmd): +def _make_add_sess(model, arduino_board, arduino_cli_cmd, workspace_dir, build_config): A = tvm.te.placeholder((2,), dtype="int8") B = tvm.te.placeholder((1,), dtype="int8") C = tvm.te.compute(A.shape, lambda i: A[i] + B[0], name="C") sched = tvm.te.create_schedule(C.op) - return _make_sess_from_op(model, arduino_board, arduino_cli_cmd, "add", sched, [A, B, C]) + return _make_sess_from_op( + model, arduino_board, arduino_cli_cmd, workspace_dir, "add", sched, [A, B, C], build_config + ) # The same test code can be executed on both the QEMU simulation and on real hardware. @tvm.testing.requires_micro -def test_compile_runtime(platform, arduino_cli_cmd): +def test_compile_runtime(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Test compiling the on-device runtime.""" - model, arduino_board = PLATFORMS[platform] + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} # NOTE: run test in a nested function so cPython will delete arrays before closing the session. def test_basic_add(sess): @@ -104,15 +91,17 @@ def test_basic_add(sess): system_lib.get_function("add")(A_data, B_data, C_data) assert (C_data.numpy() == np.array([6, 7])).all() - with _make_add_sess(model, arduino_board, arduino_cli_cmd) as sess: + with _make_add_sess(model, arduino_board, arduino_cli_cmd, workspace_dir, build_config) as sess: test_basic_add(sess) + print(workspace_dir) @tvm.testing.requires_micro -def test_platform_timer(platform, arduino_cli_cmd): +def test_platform_timer(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Test compiling the on-device runtime.""" - model, arduino_board = PLATFORMS[platform] + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} # NOTE: run test in a nested function so cPython will delete arrays before closing the session. def test_basic_add(sess): @@ -132,14 +121,16 @@ def test_basic_add(sess): assert result.mean > 0 assert len(result.results) == 3 - with _make_add_sess(model, arduino_board, arduino_cli_cmd) as sess: + with _make_add_sess(model, arduino_board, arduino_cli_cmd, workspace_dir, build_config) as sess: test_basic_add(sess) @tvm.testing.requires_micro -def test_relay(platform, arduino_cli_cmd): +def test_relay(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Testing a simple relay graph""" - model, arduino_board = PLATFORMS[platform] + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} + shape = (10,) dtype = "int8" @@ -153,7 +144,9 @@ def test_relay(platform, arduino_cli_cmd): with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): mod = tvm.relay.build(func, target=target) - with _make_session(model, arduino_board, arduino_cli_cmd, mod) as session: + with _make_session( + model, arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config + ) as session: graph_mod = tvm.micro.create_local_graph_executor( mod.get_graph_json(), session.get_system_lib(), session.device ) @@ -166,9 +159,10 @@ def test_relay(platform, arduino_cli_cmd): @tvm.testing.requires_micro -def test_onnx(platform, arduino_cli_cmd): +def test_onnx(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Testing a simple ONNX model.""" - model, arduino_board = PLATFORMS[platform] + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} # Load test images. this_dir = os.path.dirname(__file__) @@ -191,7 +185,9 @@ def test_onnx(platform, arduino_cli_cmd): lowered = relay.build(relay_mod, target, params=params) graph = lowered.get_graph_json() - with _make_session(model, arduino_board, arduino_cli_cmd, lowered) as session: + with _make_session( + model, arduino_board, arduino_cli_cmd, workspace_dir, lowered, build_config + ) as session: graph_mod = tvm.micro.create_local_graph_executor( graph, session.get_system_lib(), session.device ) @@ -210,14 +206,26 @@ def test_onnx(platform, arduino_cli_cmd): assert np.argmax(result) == 9 -def check_result(relay_mod, model, arduino_board, arduino_cli_cmd, map_inputs, out_shape, result): +def check_result( + relay_mod, + model, + arduino_board, + arduino_cli_cmd, + workspace_dir, + map_inputs, + out_shape, + result, + build_config, +): """Helper function to verify results""" TOL = 1e-5 target = tvm.target.target.micro(model) with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): mod = tvm.relay.build(relay_mod, target=target) - with _make_session(model, arduino_board, arduino_cli_cmd, mod) as session: + with _make_session( + model, arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config + ) as session: rt_mod = tvm.micro.create_local_graph_executor( mod.get_graph_json(), session.get_system_lib(), session.device ) @@ -237,9 +245,11 @@ def check_result(relay_mod, model, arduino_board, arduino_cli_cmd, map_inputs, o @tvm.testing.requires_micro -def test_byoc_microtvm(platform, arduino_cli_cmd): +def test_byoc_microtvm(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """This is a simple test case to check BYOC capabilities of microTVM""" - model, arduino_board = PLATFORMS[platform] + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} + x = relay.var("x", shape=(10, 10)) w0 = relay.var("w0", shape=(10, 10)) w1 = relay.var("w1", shape=(10, 10)) @@ -291,16 +301,22 @@ def test_byoc_microtvm(platform, arduino_cli_cmd): axis=0, ), model=model, + build_config=build_config, arduino_board=arduino_board, arduino_cli_cmd=arduino_cli_cmd, + workspace_dir=workspace_dir, ) -def _make_add_sess_with_shape(model, arduino_board, arduino_cli_cmd, shape): +def _make_add_sess_with_shape( + model, arduino_board, arduino_cli_cmd, workspace_dir, shape, build_config +): A = tvm.te.placeholder(shape, dtype="int8") C = tvm.te.compute(A.shape, lambda i: A[i] + A[i], name="C") sched = tvm.te.create_schedule(C.op) - return _make_sess_from_op(model, arduino_board, arduino_cli_cmd, "add", sched, [A, C]) + return _make_sess_from_op( + model, arduino_board, arduino_cli_cmd, workspace_dir, "add", sched, [A, C], build_config + ) @pytest.mark.parametrize( @@ -312,9 +328,10 @@ def _make_add_sess_with_shape(model, arduino_board, arduino_cli_cmd, shape): ], ) @tvm.testing.requires_micro -def test_rpc_large_array(platform, arduino_cli_cmd, shape): +def test_rpc_large_array(platform, arduino_cli_cmd, tvm_debug, workspace_dir, shape): """Test large RPC array transfer.""" - model, arduino_board = PLATFORMS[platform] + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} # NOTE: run test in a nested function so cPython will delete arrays before closing the session. def test_tensors(sess): @@ -325,7 +342,9 @@ def test_tensors(sess): C_data = tvm.nd.array(np.zeros(shape, dtype="int8"), device=sess.device) assert (C_data.numpy() == np.zeros(shape)).all() - with _make_add_sess_with_shape(model, arduino_board, arduino_cli_cmd, shape) as sess: + with _make_add_sess_with_shape( + model, arduino_board, arduino_cli_cmd, workspace_dir, shape, build_config + ) as sess: test_tensors(sess) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 89f146b923dc..bece4cb2be52 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -23,54 +23,38 @@ 6. Use serial connection to ensure model behaves correctly """ -PLATFORMS = conftest.PLATFORMS +# Since these tests are sequential, we'll use the same project for all tests +@pytest.fixture(scope="module") +def workspace_dir(request, platform): + return conftest.make_workspace_dir("arduino_workflow", platform) -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 = ( - 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=False, root=workspace_root) - - template_project_dir = ( - pathlib.Path(__file__).parent - / ".." - / ".." - / ".." - / "apps" - / "microtvm" - / "arduino" - / "template_project" - ).resolve() - project = tvm.micro.generate_project( - str(template_project_dir), + +@pytest.fixture(scope="module") +def project_dir(workspace_dir): + return workspace_dir / "project" + + +def _generate_project(arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config): + return tvm.micro.generate_project( + str(conftest.TEMPLATE_PROJECT_DIR), mod, - workspace.relpath("project"), + workspace_dir / "project", { "arduino_board": arduino_board, "arduino_cli_cmd": arduino_cli_cmd, "project_type": "example_project", - "verbose": 0, + "verbose": bool(build_config.get("debug")), }, ) - return (workspace, project) +# We MUST pass workspace_dir, not project_dir, or the workspace will be dereferenced too soon @pytest.fixture(scope="module") -def yes_no_project(platform, arduino_cli_cmd): +def project(platform, arduino_cli_cmd, tvm_debug, workspace_dir): current_dir = os.path.dirname(__file__) - model, arduino_board = PLATFORMS[platform] - # target = tvm.target.target.micro(model, options=["-link-params=1"]) - build_config = {} + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} with open(f"{current_dir}/testdata/yes_no.tflite", "rb") as f: tflite_model_buf = f.read() @@ -83,41 +67,29 @@ def yes_no_project(platform, arduino_cli_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_cli_cmd, mod, build_config) - - -@pytest.fixture(scope="module") -def project(yes_no_project): - workspace, project = yes_no_project - return project - - -@pytest.fixture(scope="module") -def project_dir(yes_no_project): - workspace, project = yes_no_project - return pathlib.Path(workspace.path) / "project" + return _generate_project(arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config) -def test_project_folder_structure(project_dir): +def test_project_folder_structure(project_dir, 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", "model.c", "model.h"]) -def test_project_model_integrity(project_dir): +def test_project_model_integrity(project_dir, project): model_dir = project_dir / "src" / "model" assert set(os.listdir(model_dir)) == set(["default_lib0.c", "default_lib1.c", "model.tar"]) -def test_model_header_templating(project_dir): +def test_model_header_templating(project_dir, project): # Ensure model.h was templated with correct WORKSPACE_SIZE with (project_dir / "src" / "model.h").open() as f: model_h = f.read() assert "#define WORKSPACE_SIZE 21312" in model_h -def test_import_rerouting(project_dir): +def test_import_rerouting(project_dir, project): # Check one file to ensure imports were rerouted runtime_path = project_dir / "src" / "standalone_crt" / "src" / "runtime" c_backend_api_path = runtime_path / "crt" / "common" / "crt_backend_api.c" diff --git a/tests/micro/arduino/testdata/mnist-8.onnx b/tests/micro/arduino/testdata/mnist-8.onnx new file mode 100644 index 0000000000000000000000000000000000000000..fc1a3f733c6e6243dd23dacb125b7a372de55a50 GIT binary patch literal 26454 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

o@PU7;m zYK*CT#LgA51o}gq)=Z8g<&NFt`+kPz2D#A|_c7?XR0}bA+b{2bU+mCz0~mVeIC3x zV1YThdF*E?BoQHpnS$4zXf&PAOm`Ir6%Hf)b!#$&aT%<6%EPJfyGGJHYZ8A-vNRgZ zy98e^s8Dh~mrM$X0;8+LK!~_v!H;m_x?KZ1=gZLQWO*X4l8Q<9^T?8=BBZoEld(-7 z1R1@9FzJ{+Z7(#VYZcU}n}<1;7-f)(P&vACn-;5c+8ak_I)YeN@mWU}vN9Z|9~z&TxS`207S zc-J=s{d{b3ox%=?*bz$>2jya}$wXWdvJ5BgZh$4L&w<0>JZimA%=+_Pawnk{;| zgzoTaqLMo!Ny%4h5;S`wS{16$__!Dn7DK24H?Le1+{5bO12{iRiLU>eM%N5HA=(9x zSysQC9SDwxh^`=NIjBYjU)rF1QZifpu8s8BC*TUxqp;evjaVI;3a27tQRAi+-aq$* zut_?Yveb+m<Ov&M)*r{xQ_`AS1W#WQ@t+;rX1`eEDh@Kgv8I>w~QhrCB z)%i9O^Ky*w@kKYluU*V-nNjS=A10V(e-_rBx(0CY2rH-`4aFCI`6p%V(NX9#?~>4b zlE1-*W)?P3$;l$LR%;CnNll`GveRhE=R@%GXE7Vq*-V~oZs%ulRTi#GrqKi$Wyld) zPA4{6(YI%{Fi|y*ytlgt$}tpYC#FM*w;>$KD{ED|QN%v`b*OHU+*Q_1FcU78=TViWW_I(aDRj5iB)Z$!pX_vA zM-yV^5;al;_mlGZN!MCIgj?_PVuW#=$yG*90m-SHGq5Ak53khpfR1Guc`;lOr2{ij zbbT-gsJl~3X#qBH?r54MC(bO?Na3E*cF;ZK0E~YuD>LOCasO&VMpT~yzS0hSBb<++ zonGj5H=Z4Sp%oVkmf|p}9^5zV8Lm9EAA9sF@hoBS%Ce(4-M1NZMtmmUOkMCmU@gRt zTg7&z9D|M}a_~v#Jh%iehhh3BiH?{oI%&4R9sg03s_4>1=64~_q!K!XUGT2!RaW>* z9K14fC83gcNwfJFdX{I8W18>6V7@Xv^!+ON>ekAPv)O<U3)QZhkUeYssm~J`x=6`}r|;DUtjloP6Jg58zuQQSbV@OJ$ysnasf44A z{IP6L22;T0J)$%C_@THJ9o;`did`A&H|q=eP;Ekw*^MRV)GcuYdy%);f{T48^^nsc zcj=h*Pk^>J6Uk4S$gYy33RwrK<*BpGQ5ki7-IK@9v=^qWPhQiveQBg*r!bO8ar9oj zgjZtkPS<`@VxB78CSvzHsB$QF z|IBJJ4k$Nn2wX4M;-_0(Q0Q|9RK*eYPE@7CtkhA2T_XFDvYDp7Gl&P3;$-CcJH){1=&r(do=AeC{EDq0767TT%}* zXyy&eC6n>!T1ObrsRs4j`_TR*9U_lGb8d zQLs`?i(1*}Gwo_AWSrp6{iJ1%F&Vi!hMyE1 z!9P77#hg$qOxv^-)wbj%Sy;g~aORk;kGV z!98yU8>QRp_B&d9%7#C-vhqQr@`|Ox8^Q)fhUka zTsefwckd$p%a@T%f}1HgE^Q(EI@jRNFPGqq#z8oICWPczT2S4&MeL#Q`DEGf91DKh#7~uS#TS!?<52rKfSU2VE>#WY+HfZ#G&+{rgbyRP@)F>N z{%Q!=YDdi%x-q$C;@I}Z6?dmUgH8`ElvZ?w{Ky^n&}2Mz&=7LTVj47A<+387lh8J- zmUr?)7A(;*z@-t@FeIvv+g?6{pb4X3tepWJ|7!uw8nc!xSSAhjMa?ko{zJ0t2bVV& zodyTa+T&s0Zjkw6j{_#c_|9Gjcdbmqk()Z9=8Q7G>9ROnjy7NgeRXilYjN;B?8JCP z2omwkH{_u2LD-`s3*{G|vp0uJ(s%h${HhaQq3g~pp2kQQ;>$>a(bj129=ry}NCE8I zaUG^+*0JCBh>#Yy9PrADB!U$Hb=K>!LCzHChV;XInI2;5f73$7WD?%YlSe1J({R_b z9<&2$LAN0j+729n#I6f$claB2^WP|)|&^QBxYlori=p2ywu^TE(2B5iUGptyb zNkqKMz(_(HcQF^qT-$rhpxy^|DDgE(G_7D2w!DkDO>`(ZF@+m@%IGcypW_TM>z0gR+>z>g)MknI-Hz6*b6Z&yZq` zZ?K_15pE`DL$~llUW4~9a?4Es=BFoP%jarxV^}@W-an7jHo4J*&-YQkR6Vk-XcBr| zs9`=eO2N~Gxg_228{_z;8@8$oUmTwtQdYT*0kmH6y|F@AJ81sSbH5P9PUqjlW{C&}jG1HU-jX)zzC z9uUBt-y7Kd`^I1cUl|{sy#tlux5-*VEzkQbb<{r>Po^DZo+Jhnd}HtCo=xkXgt1j39;C4g(!{{#YnAtOoVYUvvn|o zY%?At&p+zI{E36)&e0S`%dr7I$QY4VE7i!{84scD%z9S5sEhfLak$QS-w5Qw{@Cat zL#2cE!D*{$bne0h)Hvr0fBl3i;{8yDI4dSGUI$k)mb3N9y5bNJ&GKby1g=BxL+%64 z^MTUqo}_}S!W5M@0KJ-mxTQ}3XFPpOHq_LTwVqNtW=5ZCnnzvtqOG9($ z?Dw|Rz+NBr-n1qcmM!9tfmx6zUI;^L9z#lwFo&Bt#CmU=k1r(z@Vc!#I1q6TPY{h4 z2W{bd{AjW#UkCSoutmk?q39shK<053aMr~d(UNy+^k!{5;Ta|~I_tx6#NIvR=7SQV zd|ru56syo>wT2kwcMbw?x$+J_vLO>TsE{?=PD7Ql2yxjy1vC18f#I94gsfIz3r+#g zy?7oTFHRvpy)*d!+sgs9rjar0y%@`GJ+ewJ7h*4%5%1?Yq$Mtr|6r*(V-c{1^897V zv=Q<+jEKuO?9ugYJln6u~4!!g*o6npEq098jEhk zP>+CbytGB39L|M1Z-Emz;ND5J#Z>XQKpHl12)gd%`>e#=<>+oFjUoE;uzHa+CN3=m z6?;3Zc_NPYMkrzasul3KKpFcMO(VC$gGhNBl3ccSegcKUI{HuxnNJKCbQh>7dyA5931Rc!F;U&M)sL9`YsG(y#}U`{2y0g zSjlcOF;o(39P&VG<5n`fyNB^kKT4eLE~2G#e(}b9HAQieQEc{9A67yp2o8t&V`;V- zB##;a6~Sh>+a{hD{4o;R4bPKtk{duVjzgq(34wd!UUvIve`fMJJMzNE9HcVDF-OJ_ z?3+d5Y_J|I_UvX}rEa3p%VuFva16h@Rf8@$JQYh7Z1{2$H?n(|&!CnW(qyM*98A^m zAa^?>IQHBaDl+yZSh;aH6E418T9yF2zXae%uY*u8_nk5CnT^h$mf)E~@8NUp69}Ga zPE(a+PR1@g4+h_DaKYgZaBlf1Y`nmIlLF2_dfI+Q`_X0gXVp!ZR^fpWa`hz0 zZVY&@a7P<`M{XTziYEHqAo4+nJ}ooHew`)gxjc}3#C`Chy@jVF~hA!3kEwpzu~Eq*$*rpgiWpYTCz{0d+;|AYx^0`Z@0oE2r<0iXmW0|=0Ls=2@cP21A4MS8?5yBCk zn!Hs)La6)q!mqZO&^4$)&jd~*oiD23o|+DJ%g8}y-BaGUN;mW~tbp*v z=h(3k^|12J9uU@(!4TPdpy&LNv}je3MvqWtgQ6IoiO~b`Gvf5J)B~nOXCf23e}IHI z4iKT^2{1Ts26?%pi?rX&Aj%a^#KWo-EO&N+NADcAu5SQvel^&?m_YmV1khou2ECqo z8*HDZ;>&I)v^uOv{q}tVoB89oyiPFkX2|1>)?#8e=^9VIbBNi;v4LLHIbhZjCD^!E z6Af&g@m}c>v=<6R1*z{$|C3yF)-Oe+#iud$X*xM^a}F&WZ-5)P|3U8yzPO}&KW!az zosRzA4+)AUr1xYIRWqrjD(aC~(*BUB=|5%5gPriX`Ye=ijKXR9m&n5tYpB72G%~zy zh%C(iLQL#6>E;oAOwEyMc=&KKy&gV}ro{;$`^X*jmb$`ErxqeTMHcVRR-!N1e5lYx zSaf%Yo%)u;uV#7Cc9~6Nb5{XToH+wpbKkSQa-$(V_9~kr_#PhHX%T!N2*>+E@x&1+ z=vBT1x=-VYqjn;@Ytl`!yXPM8bhtS(Mh*w~d$7MSkIwL#2+7C2F|bY#F6nfywD0J59OgvSeSu&EA{sJLnw zG1@bgT7B~&u|oaKa%lys?sAPddF-O2^2Ldng9NkGBbg3Y^rL<F8s z(&m|qDe1oqKdmC6mp_h;l(fXN>5j;1Gw8YgDcF6;W~9E!(NFgJbVafPIPbp$PA;K9 z!3>%OftRpp7_h3+h%-F!(~Lw^)Ud)EB26&SM4$L>PPTLvuZ7x+r=js!1$ilWnmtG_ z!jM%r&!NeXnC_Fsy9wrazta|7xr$L~q5VWDmSc;)IRuX#_Ttn9O^|qLD}8RnqtkC@ zvR}tVpu>$I*1f))JZsOu2*EU#NwkG9RXv=xEgl@=(#g&^3jL-}V4r#v?^Drn@*>=T zNNCzn!}^)HSWX2k+^(?uFMHxW?jA1;^e37%FJRs~Q`#S&1y0YVlj_Gl*fsGgT)RW4 zfDpkeW+iZ^>>+#Y)+2cN<0c%SUPSkjCTf3(g2CP^#OX^f^I+U1E9Z@|R6%z+xi%^Y z>w*vSbbc9Q?l&iD9GFP5R>fM4aNkM3I1hnQz5;4js^I-V235Gqa;?j((QmIYiZ0&8 zdwWJ2lT7?zm@l7cyyy>mcbhU>dA~rfUI!ADz3C*UD`Zi38Mz@7!L4!b!-F9!E5%p) zsn@D{a_biZax=;qzlK7NC0g|PTqkgj*eOBuVMO5!~n zJXb`XwW$%^)~%TObqrRw$brnKUFfLV%1$&CBhj~%P_A+e3F;`o>9w+S>kU7eyrZ6d zveg;YM1?VMlM!9fIfXtce#nlj*#O1CYILo)8NKYX0u=Nda82TC(0Fs3jP;Br7<`6N z@o<3N+}-fRS{p3{Gbs&iAoq87@RiGI3ZIYpX!zm5R8#C0fd%;jP<;$UV)31n)_ zV@9w1!4!?RK*wP_nWm|(Wb1_vXgXtsWsEaO9qA?f=V55YRRb-$6OEQ4f|w)sl-%cX zBJIO@==kb8=*fSF_LufpmUjq?gZQv%{uKc~t+{)# zC#npNWn|E@;bgubMud7EL*003bQV@Vqd5vj2ZKdy%MN{<|9(@gwRO*wm*#2 z#lPo01TQp}T>x(^cI|B!*ni@zQ=CZKn#i5BzYPu z&5{6nYb>3mOsjY4)5Rb{)oV4GjiowR8&F3(qvud7#q%VbS<085qKpD{-*~!Qo;Gs% z2GYDNiLCydgPU{1vFBkB(>fqZUw?9~hM!w*l zS9Oi==c9FFBW&9@A6NFwC5P2zxxXcUM#Z)h?EY98UB6a@qlO6lgB+44qTDIRb{q&n zEjz*bK8qL_Yc59{5=Yi&FZhCvn$8@BaGG@|SDs__k5^EsCK zYM}&Q+S(;;r1gTg8R$KrUf*A-L9&BaP|F+sczbRydV8m1xsMj)xB1iYUcYG6yEZEM za0c!sO=`1I4c|;FLcJT~aMsN;bmUc}cJt)w^R_qPw2mp5 z8?}lm`jyhwuZ>tW7>Fl|cY$3>5c}&%Doqk?WM7NiBWv3u=@_e8`tG@r_2jTJIvCMP z8L=k1zPOlnwr!!}`rSA`B^!KxD&VrZDY*XRb}SUwT3=;sUO)S?c)imrjr#T3uTZzq z1#>G(aYEfGT#=xJ3D!}dV{rhN8gmrkV+RVqH80~SrTVS3o46fTvHXDqV|#TryevU z=|kJ=A*pWOB-quu>hPnYqH-=@h-O)&X6Ut+%ql^c@$#g3eOh za@ig`#`=)Kep#Ag$D^{dCF@51cd);}-Q(d4k<6c;^(*vd_0uS_=GQXWDpO}pu*V@s*S(aU71m5jCWejhAx@4}aF z7vTG}Iy!yP0QKPC##IgND6oA8CIz?91=*QYAh-)}XJw<;qBCI1(a1WyyU55;Bh-

#@kY=I4PB+Efc4VJ^|ei)vTj|DE2q;iHH7LTG>-U&k0VT!Ho^{V~qp- z?we1?8Od3DmVbt@6OJ^M6{SP<_TmEEt9-M8V9}l;N^5@e4IY4KA0~LE&+C^ z_WB{lUrj~TcXnv^2ZadzsegNQ1UOdJ*fMTXs~PhcHy`Jd5~&0>-5?fRUW;IjT@iCc ztRMc+ZmPZ~$Hx*7``yk)KBO29etQfj*2tpe6*u%)XO7RZ7ozcSaTH$Th`I}Q<5JTY zJl&TK^+&y!#Ya;4oeNq}RLL9*=a2r=%WR=PNlZ{y)JjMoLeTF2Q6^ts_wVKrf)aNB zO;*&HnC4^N(g0fAI9Lo_QPnzk~)PB*er-<%e^Q1-*6u zRKwro-%kG9=JzJyJ>kELnEcV@=s&qMm6w)~ Date: Tue, 10 Aug 2021 17:53:22 -0700 Subject: [PATCH 08/11] Copyright notices --- .../arduino/example_project/project.ino | 19 ++++++++ .../arduino/example_project/src/model.c | 19 ++++++++ .../arduino/example_project/src/model.h | 19 ++++++++ apps/microtvm/arduino/host_driven/project.ino | 19 ++++++++ apps/microtvm/arduino/host_driven/src/model.c | 36 +++++++++++--- apps/microtvm/arduino/host_driven/src/model.h | 19 ++++++++ .../template_project/microtvm_api_server.py | 17 +++++++ .../tests/test_arduino_microtvm_api_server.py | 22 +++++++-- tests/lint/check_file_type.py | 10 ++-- tests/lint/rat-excludes | 3 ++ tests/micro/arduino/conftest.py | 17 +++++++ .../micro/arduino/test_arduino_rpc_server.py | 26 ++++++++-- tests/micro/arduino/test_arduino_workflow.py | 46 ++++++++++++++---- tests/micro/arduino/testdata/project.ino | 19 ++++++++ .../micro/{arduino => }/testdata/digit-2.jpg | Bin .../micro/{arduino => }/testdata/digit-9.jpg | Bin .../micro/{arduino => }/testdata/mnist-8.onnx | Bin tests/micro/{arduino => }/testdata/no.c | 4 ++ tests/micro/{arduino => }/testdata/silence.c | 4 ++ tests/micro/{arduino => }/testdata/unknown.c | 4 ++ tests/micro/{arduino => }/testdata/yes.c | 4 ++ .../{arduino => }/testdata/yes_no.tflite | Bin tests/micro/zephyr/test_zephyr.py | 10 ++-- tests/micro/zephyr/testdata/digit-2.jpg | Bin 572 -> 0 bytes tests/micro/zephyr/testdata/digit-9.jpg | Bin 535 -> 0 bytes tests/micro/zephyr/testdata/mnist-8.onnx | Bin 26454 -> 0 bytes 26 files changed, 283 insertions(+), 34 deletions(-) rename tests/micro/{arduino => }/testdata/digit-2.jpg (100%) rename tests/micro/{arduino => }/testdata/digit-9.jpg (100%) rename tests/micro/{arduino => }/testdata/mnist-8.onnx (100%) rename tests/micro/{arduino => }/testdata/no.c (99%) rename tests/micro/{arduino => }/testdata/silence.c (99%) rename tests/micro/{arduino => }/testdata/unknown.c (99%) rename tests/micro/{arduino => }/testdata/yes.c (99%) rename tests/micro/{arduino => }/testdata/yes_no.tflite (100%) delete mode 100644 tests/micro/zephyr/testdata/digit-2.jpg delete mode 100644 tests/micro/zephyr/testdata/digit-9.jpg delete mode 100644 tests/micro/zephyr/testdata/mnist-8.onnx diff --git a/apps/microtvm/arduino/example_project/project.ino b/apps/microtvm/arduino/example_project/project.ino index 291625309fd0..74b596245624 100644 --- a/apps/microtvm/arduino/example_project/project.ino +++ b/apps/microtvm/arduino/example_project/project.ino @@ -1,3 +1,22 @@ +/* + * 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. + */ + #include "src/model.h" void setup() { diff --git a/apps/microtvm/arduino/example_project/src/model.c b/apps/microtvm/arduino/example_project/src/model.c index 3268e5961bc9..7acb06fa89af 100644 --- a/apps/microtvm/arduino/example_project/src/model.c +++ b/apps/microtvm/arduino/example_project/src/model.c @@ -1,3 +1,22 @@ +/* + * 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. + */ + #ifndef TVM_IMPLEMENTATION_ARDUINO #define TVM_IMPLEMENTATION_ARDUINO diff --git a/apps/microtvm/arduino/example_project/src/model.h b/apps/microtvm/arduino/example_project/src/model.h index be1bd5415e77..489adff0da53 100644 --- a/apps/microtvm/arduino/example_project/src/model.h +++ b/apps/microtvm/arduino/example_project/src/model.h @@ -1,3 +1,22 @@ +/* + * 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. + */ + #ifndef IMPLEMENTATION_H_ #define IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/host_driven/project.ino b/apps/microtvm/arduino/host_driven/project.ino index 5bf13b57243e..34537d4e205f 100644 --- a/apps/microtvm/arduino/host_driven/project.ino +++ b/apps/microtvm/arduino/host_driven/project.ino @@ -1,3 +1,22 @@ +/* + * 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. + */ + #include "src/standalone_crt/include/tvm/runtime/crt/microtvm_rpc_server.h" #include "src/standalone_crt/include/tvm/runtime/crt/logging.h" #include "src/model.h" diff --git a/apps/microtvm/arduino/host_driven/src/model.c b/apps/microtvm/arduino/host_driven/src/model.c index 5d6512e7e5d3..5b87deb526d5 100644 --- a/apps/microtvm/arduino/host_driven/src/model.c +++ b/apps/microtvm/arduino/host_driven/src/model.c @@ -1,3 +1,22 @@ +/* + * 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. + */ + #ifndef TVM_IMPLEMENTATION_ARDUINO #define TVM_IMPLEMENTATION_ARDUINO @@ -10,13 +29,16 @@ // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { for (;;) { - for (int i = 0; i < 4; i++) { - digitalWrite(LED_BUILTIN, HIGH); - delay(250); - digitalWrite(LED_BUILTIN, LOW); - delay(250); - } - delay(1000); +#ifdef LED_BUILTIN + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(250); + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(750); +#endif } } diff --git a/apps/microtvm/arduino/host_driven/src/model.h b/apps/microtvm/arduino/host_driven/src/model.h index 136b17c061cd..edc83e5123f8 100644 --- a/apps/microtvm/arduino/host_driven/src/model.h +++ b/apps/microtvm/arduino/host_driven/src/model.h @@ -1,3 +1,22 @@ +/* + * 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. + */ + #ifndef IMPLEMENTATION_H_ #define IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 44088cec2064..7297bab6405d 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/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 functools import json 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 a316af35aacb..3dd3688fe5b4 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 @@ -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. + from unittest import mock from pathlib import Path import sys @@ -13,11 +30,6 @@ class TestGenerateProject: DEFAULT_OPTIONS = {"arduino_cli_cmd": "arduino-cli", "arduino_board": "nano33ble"} - 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 mock.patch.object(Path, "exists") as mock_exists: mock_exists.return_value = value diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py index f72bd002b56e..88ad7d6a5055 100644 --- a/tests/lint/check_file_type.py +++ b/tests/lint/check_file_type.py @@ -82,6 +82,8 @@ "cl", # zephyr config file "conf", + # arduino sketch file + "ino", # linker scripts "ld", } @@ -131,10 +133,10 @@ # pytest config "pytest.ini", # microTVM tests - "tests/micro/zephyr/testdata/digit-2.jpg", - "tests/micro/zephyr/testdata/digit-9.jpg", - "tests/micro/zephyr/testdata/mnist-8.onnx", - "tests/micro/zephyr/testdata/ic_sample_fp32_8.npy", + "tests/micro/testdata/digit-2.jpg", + "tests/micro/testdata/digit-9.jpg", + "tests/micro/testdata/mnist-8.onnx", + "tests/micro/testdata/yes_no.tflite", # microTVM Zephyr runtime "apps/microtvm/zephyr/template_project/CMakeLists.txt.template", "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-arm", diff --git a/tests/lint/rat-excludes b/tests/lint/rat-excludes index 5f0445134dea..3dff79c565ce 100644 --- a/tests/lint/rat-excludes +++ b/tests/lint/rat-excludes @@ -20,6 +20,9 @@ .*\.interp .*\.tokens +# microTVM test data files +testdata + # Generated modules .*\.egg-info .*gen_modules diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index a4ab46dceafb..f4e27d1458e3 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.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 datetime import pathlib diff --git a/tests/micro/arduino/test_arduino_rpc_server.py b/tests/micro/arduino/test_arduino_rpc_server.py index 0b5ba07b8b33..eee4a5623cff 100644 --- a/tests/micro/arduino/test_arduino_rpc_server.py +++ b/tests/micro/arduino/test_arduino_rpc_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 datetime import os import pathlib @@ -165,17 +182,18 @@ def test_onnx(platform, arduino_cli_cmd, tvm_debug, workspace_dir): build_config = {"debug": tvm_debug} # Load test images. - this_dir = os.path.dirname(__file__) - digit_2 = Image.open(f"{this_dir}/testdata/digit-2.jpg").resize((28, 28)) + this_dir = pathlib.Path(os.path.dirname(__file__)) + testdata_dir = this_dir.parent / "testdata" + digit_2 = Image.open(testdata_dir / "digit-2.jpg").resize((28, 28)) digit_2 = np.asarray(digit_2).astype("float32") digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = Image.open(f"{this_dir}/testdata/digit-9.jpg").resize((28, 28)) + digit_9 = Image.open(testdata_dir / "digit-9.jpg").resize((28, 28)) digit_9 = np.asarray(digit_9).astype("float32") digit_9 = np.expand_dims(digit_9, axis=0) # Load ONNX model and convert to Relay. - onnx_model = onnx.load(f"{this_dir}/testdata/mnist-8.onnx") + onnx_model = onnx.load(testdata_dir / "mnist-8.onnx") shape = {"Input3": (1, 1, 28, 28)} relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) relay_mod = relay.transform.DynamicToStatic()(relay_mod) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index bece4cb2be52..fddb47c8267c 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -1,5 +1,21 @@ +# 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 datetime -import os import pathlib import shutil import sys @@ -52,11 +68,11 @@ def _generate_project(arduino_board, arduino_cli_cmd, workspace_dir, mod, build_ # We MUST pass workspace_dir, not project_dir, or the workspace will be dereferenced too soon @pytest.fixture(scope="module") def project(platform, arduino_cli_cmd, tvm_debug, workspace_dir): - current_dir = os.path.dirname(__file__) + this_dir = pathlib.Path(__file__).parent model, arduino_board = conftest.PLATFORMS[platform] build_config = {"debug": tvm_debug} - with open(f"{current_dir}/testdata/yes_no.tflite", "rb") as f: + with open(this_dir.parent / "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) @@ -70,16 +86,26 @@ def project(platform, arduino_cli_cmd, tvm_debug, workspace_dir): return _generate_project(arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config) +def _get_directory_elements(directory): + return set(f.name for f in directory.iterdir()) + + def test_project_folder_structure(project_dir, project): - assert set(["microtvm_api_server.py", "project.ino", "src"]).issubset(os.listdir(project_dir)) + assert set(["microtvm_api_server.py", "project.ino", "src"]).issubset( + _get_directory_elements(project_dir) + ) source_dir = project_dir / "src" - assert set(os.listdir(source_dir)) == set(["model", "standalone_crt", "model.c", "model.h"]) + assert _get_directory_elements(source_dir) == set( + ["model", "standalone_crt", "model.c", "model.h"] + ) def test_project_model_integrity(project_dir, project): model_dir = project_dir / "src" / "model" - assert set(os.listdir(model_dir)) == set(["default_lib0.c", "default_lib1.c", "model.tar"]) + assert _get_directory_elements(model_dir) == set( + ["default_lib0.c", "default_lib1.c", "model.tar"] + ) def test_model_header_templating(project_dir, project): @@ -106,14 +132,16 @@ def test_import_rerouting(project_dir, project): # like a user would @pytest.fixture(scope="module") def modified_project(project_dir, project): - testdata_dir = pathlib.Path(os.path.dirname(__file__)) / "testdata" + this_dir = pathlib.Path(__file__).parent + micro_testdata_dir = this_dir.parent / "testdata" + arduino_testdata_dir = this_dir / "testdata" - shutil.copy2(testdata_dir / "project.ino", project_dir / "project.ino") + shutil.copy2(arduino_testdata_dir / "project.ino", project_dir / "project.ino") 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) + shutil.copy2(micro_testdata_dir / sample, project_data_dir / sample) return project diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino index 175062e297b9..ebd1c5e0e650 100644 --- a/tests/micro/arduino/testdata/project.ino +++ b/tests/micro/arduino/testdata/project.ino @@ -1,3 +1,22 @@ +/* + * 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. + */ + #include "src/model.h" #include "src/data/yes.c" #include "src/data/no.c" diff --git a/tests/micro/arduino/testdata/digit-2.jpg b/tests/micro/testdata/digit-2.jpg similarity index 100% rename from tests/micro/arduino/testdata/digit-2.jpg rename to tests/micro/testdata/digit-2.jpg diff --git a/tests/micro/arduino/testdata/digit-9.jpg b/tests/micro/testdata/digit-9.jpg similarity index 100% rename from tests/micro/arduino/testdata/digit-9.jpg rename to tests/micro/testdata/digit-9.jpg diff --git a/tests/micro/arduino/testdata/mnist-8.onnx b/tests/micro/testdata/mnist-8.onnx similarity index 100% rename from tests/micro/arduino/testdata/mnist-8.onnx rename to tests/micro/testdata/mnist-8.onnx diff --git a/tests/micro/arduino/testdata/no.c b/tests/micro/testdata/no.c similarity index 99% rename from tests/micro/arduino/testdata/no.c rename to tests/micro/testdata/no.c index ee2f07a8d99a..a3bd78a5328d 100644 --- a/tests/micro/arduino/testdata/no.c +++ b/tests/micro/testdata/no.c @@ -1,3 +1,7 @@ +/* + * This work is a derivative of "Speech Commands V2" by Google, used under CC BY 4.0. + */ + 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, diff --git a/tests/micro/arduino/testdata/silence.c b/tests/micro/testdata/silence.c similarity index 99% rename from tests/micro/arduino/testdata/silence.c rename to tests/micro/testdata/silence.c index bf756399157a..bc26efa70e4f 100644 --- a/tests/micro/arduino/testdata/silence.c +++ b/tests/micro/testdata/silence.c @@ -1,3 +1,7 @@ +/* + * This work is a derivative of "Speech Commands V2" by Google, used under CC BY 4.0. + */ + 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, diff --git a/tests/micro/arduino/testdata/unknown.c b/tests/micro/testdata/unknown.c similarity index 99% rename from tests/micro/arduino/testdata/unknown.c rename to tests/micro/testdata/unknown.c index 2aef2dbcb62e..6e4df3d20b49 100644 --- a/tests/micro/arduino/testdata/unknown.c +++ b/tests/micro/testdata/unknown.c @@ -1,3 +1,7 @@ +/* + * This work is a derivative of "Speech Commands V2" by Google, used under CC BY 4.0. + */ + 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, diff --git a/tests/micro/arduino/testdata/yes.c b/tests/micro/testdata/yes.c similarity index 99% rename from tests/micro/arduino/testdata/yes.c rename to tests/micro/testdata/yes.c index 9b500624bff0..ec18f20e46cf 100644 --- a/tests/micro/arduino/testdata/yes.c +++ b/tests/micro/testdata/yes.c @@ -1,3 +1,7 @@ +/* + * This work is a derivative of "Speech Commands V2" by Google, used under CC BY 4.0. + */ + 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, diff --git a/tests/micro/arduino/testdata/yes_no.tflite b/tests/micro/testdata/yes_no.tflite similarity index 100% rename from tests/micro/arduino/testdata/yes_no.tflite rename to tests/micro/testdata/yes_no.tflite diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index d33033d2a33b..3acccba0a8dd 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -229,18 +229,18 @@ def test_onnx(temp_dir, platform, west_cmd, tvm_debug): model, zephyr_board = PLATFORMS[platform] build_config = {"debug": tvm_debug} - # Load test images. - this_dir = os.path.dirname(__file__) - digit_2 = Image.open(f"{this_dir}/testdata/digit-2.jpg").resize((28, 28)) + this_dir = pathlib.Path(os.path.dirname(__file__)) + testdata_dir = this_dir.parent / "testdata" + digit_2 = Image.open(testdata_dir / "digit-2.jpg").resize((28, 28)) digit_2 = np.asarray(digit_2).astype("float32") digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = Image.open(f"{this_dir}/testdata/digit-9.jpg").resize((28, 28)) + digit_9 = Image.open(testdata_dir / "digit-9.jpg").resize((28, 28)) digit_9 = np.asarray(digit_9).astype("float32") digit_9 = np.expand_dims(digit_9, axis=0) # Load ONNX model and convert to Relay. - onnx_model = onnx.load(f"{this_dir}/testdata/mnist-8.onnx") + onnx_model = onnx.load(testdata_dir / "mnist-8.onnx") shape = {"Input3": (1, 1, 28, 28)} relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) relay_mod = relay.transform.DynamicToStatic()(relay_mod) diff --git a/tests/micro/zephyr/testdata/digit-2.jpg b/tests/micro/zephyr/testdata/digit-2.jpg deleted file mode 100644 index b709a206b8d776215dcaa78643b22fe628b3c43a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} diff --git a/tests/micro/zephyr/testdata/digit-9.jpg b/tests/micro/zephyr/testdata/digit-9.jpg deleted file mode 100644 index 6ce9cde3b322b351847da179555aba35358dbf23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja5+s?$04fGV5X?$Wf+%7_5d(^VqN1We zS7AU5Ac{Go7%*eb%3J%KbNAUN+&9Mi^G5$zqpNGqRb4f^W`%F6$ZIPM@eLYjsMz1m z$jL2UkZmqkp{%jSi2QH8m>SA@tvV zwDtbihlX8(BIbq1^mpk0PkuAyfPWv^U&c*FtxQ%;X?SE*>_SI1wSUgwq}_GM{K&ZZ zb7L1q%#ZBv66zPbP|6b8&#?1fXKhXYxv?>h{r*#GrGF=w$qxz(`?q-psQq^y zZOwr2QNfP=|7Fm>6U-C>!so{R+o-j&YX6-}TibvB{J9a4Go79PbEN7&X=chkQy2f^ z?tkb=&O`3M>F7TwUDOn$>WT4?`SZJqzHpo8GIx*M?cVy!JLGj){T?LALDrfOyhfPkv={3RXO>g;zgp#IPQ7arwSBKK#^N z5<(-z+tza6*SM7(%hn6uU)Brlrt7gsH+yuLxS5vKtQGj|Ejp1^OuvT2!V&!L>g>>+ z7k2sv7nk3MXECSXROcM}9DY@_if@7Tv8Ci>S^{U@II_clKAanP3%pkK=Z#7GsNmCE z?DXuLP;G6_5uu}SaeEU*5BS5+YxkkqSto4TaTVuA{f0mG*=)$~_=7%k%kMJrfVMoF zwRwXWaRx7~p2x;Fui)E)D#0wS5c;pPt!-Yvl>V-92c1_+G`pk=zS}L&R_Ct^`gPi3 zcEv!f?5s*7mpv3`&Q(O`5I0h2nE+OQD}>q0&(yq`KaxgPo3Y!=hj7Evj{E3~>P&RJ?A8@-s zU$K6t-=>xPxL09>N^g@)8-h^J??_V?e`(##4t`6BQNaUod82pqJ?;2Jvp@Ah1GAD zvdq&Zf?PovZyu#YrBBRQW38V1mDL+*iL!$GUO7wL=i(cp4r*G^~WeKio z*Ts-=ThQy_WcF~~3tN_^qknE5Z&;O%C*Sz-xtzbWZpj(Rq}p(t-K>tqTb_v^sOF^ApN;>uga_NmWm=f#4Zf+-Vde9vHRrgo& z=+rpA+TKhj*H**McbPaQRhOp}SHQ-G91^;zNu8%cj8@#7gO@SN>Ho)L*~oAa{Re(G;c#b9c+?y z@3GMmH8oVBU;iLc+v_phPjlt=t|2_4(GJ(d24iH)btrmng~2C-==ZYC_;K17vRv)N z(MsCf?uj+#M|$wj%}c4itAL#?v(PT>1btiZk(9k!ggt%Au)a=*_q48OwRMU7%HN=dbJPnc_qGjGeH)|&HV;Nh9%P2@#}bSngo??|KP0lO?b;-HN%IWHGU)h z@)PkOzN$G51Mg?EPqi10{<4@?xQC(j7Z;55E$71c3ql{wuXOobDNZ>S!)0@S3H_T( zh1K2W(w8wKMSPXOBe(45r#t@Q&i73?B>jXy`r+JtyqqL7a5o!Od=`f1IpW846VU2( zI{q2?2nN4j%Qk0=ak_Rke$YO}`#1Ng?c{Wa$31rB^jXvK<8~!?YwW=JUnI3NXXeq~ zo^llI+}-`)=PWFK>&hlq`w4*_YtUoZQ23PPiH)UQc-gg+AitvvU*A528sa-+s@zMk zD(X@*`;)gYSYMvcwvEB@trwu>!~2>m<~7u|2D$D|Ex21w#fO)s3U^vI2}SOgU~%Rg z$nK~Bt7FNmQ8*gE2k3 z-D8LQhN-%T`;21skgA%{mqWRy%wNi!kqHNP{UEc*P)O*R&pIzOt9NX-$4_=%?%R)K zvYzh(Q2nIM+k8)nrrwp};IjGrwqG{)4QzrdwkoXkL5eu#0@J8SO=rzrO^djn>>WtAW0bbYR^Z=fOWR1pHoW!_0wcuK7x>q#$z7OKl)80Y7mq7mFcaR+V0HdE(;x^SmtoTfsEe1?N`3vjl z((^c~kDM)TdVU_(20x&fmm%;2idI?`@V7&e6D4z^3m)z^-LOPlA z;i!8m-<3p@if1KXc^8OXCuHK5eG^eh<~YCC(H3)j8rh+8is1S5wxG4S3(lMB$D2#m zpj^*;RT?H9M#-Cxfr8S|G_j$u zzr=Q>o@iM68d64O!TxL4DEsSM7+SDcI5_38FfIIpSb6UV#Eh}SJN9?!Mao;UE$GZ{ zMt)*IM+#i?I}m$zVci=X>3W2yoEFl>}1#a zO0l?6hYQm3DAdM`SC8+>im%d0bJQ{XuKidt`idb$r{!>(;uhiPs1jk>`7F?i_r-p; zXJJCw2O7HgE4-MOEf%fwgRG(!2pwlf`fs0$=ciA{5rK-Rsy&wKJUSrt^dT7jcLbQ! zcw?Uh#T*O)?D0E>)y`$J{=OwJ^Yl>c+haE*Me1|sLSMG;*g=Y#2gIv^rtY3<@~q`m zO=x(nS@SH@5GWOzn&lfbu+grS`<{oJ0Zp$-nRzlcRZMgHMlI;D3n(E=IylLrNR{CuUMni1*>kDUm=6H%K zo41JNft@hFqlo9;n2d%$OYlJQCq8HK49D**;Ik8qc~G_+Ui*EFo+w#kZf_eHq%6Vm zCW)l>_f{UBV~EdlGby*;hL8R1#BL51P_WmM>{sZMx2G$2wzPuHxqqRmxGzO6Pozmt z)L2|{0=Cc6!QDHD$F7MAfXP9uitt&pXIgY6_9ch<6quh};{!{OctAsIP)m&{3^Vg~E9i+hHVja-v z%^6&-+#N5EGUs`p^g&l|jTkq*GhI}6WP50V9-Fs-i8M~SCTC5?O_{KoP59vA-dGS2 zfvcao!}al>MdLr$0foVQ^{X0soIgP(jj6amT*o;tE!|(tR%bt-WVn;{Rmi`aja|k} zXNwmWIDbPd_iPB~@bU<;r_M_>)t@astRK#%L$bK*GAC4hxgJg){7e;trMZoN3yhrN z$c^JF39j0s+UFSBn(`7h+gsxn#iQIq=uDbX@9FOZRi5?5ob}z(FuPw1e6%=D+vYg1 z)9T4&zeWO=hZe&rzjDa)E`o&YMJ%_OQAuo~-~~OnG}9lFa!28Zx~1Hrp@zZ*O^H)b zhZvUm9bUYX<;p4m5 z2&8F~esH|CC8W(g00UFx-AlcsG4?A1T$F7=o&zTF*mFzq(_l3|8hlflce%K~ag^hA zwrTu){4a>Lnj!h@Wm?;KF%f*5-wRzd_T$IQ4`Q!jEpXzZG%s`X<7w9xV|+vl29NB1#@aoU<(?zAui zf9wyzYcGfJ1T4YOu&tc=+q$O6LWa)-$Kd+PWf;@l2NiXn(~+SG?CkV{nmzXM;6dYg zkz+o+7=0F(L_~AIt83_6@sVb}w1lY>q9A?DJuJskZauJBd=s&SSDKw5`MT>g#cs63 zw@QY0yfCJ7%bI!EOcu^K4rHzTBJ4Pv$fHI+frmpb!JFmsh;bt%9&z#*BzKKIde1?H zw}q_tQV(Sug5b^^SB&btjn?QU2~}20_{H+A=-&4(YbCG7b!Nt}*E>O|n6ne7C6tR= z*M@=8#SqbTQZ`&WRwv1M<;E8;9|uYD8$n0GfE+@9LGs0w(ECLT9GzKBDph@P(X>50 z;*J)#HYeee;tojqHG#gUKa`A+yc4=as)(%(5}csmCn&VWQ0Uz@iHf%+Iu{3mdxBC((0C*i%m5?QpA!>cn~Gw5nIR*hhq{-76x-$^FP z`wWkAG)IZgqsJbVCGn#ngwNDYA@YAiqWmc{5Wqt%-dCtvnI!jdp>Oy zR!w_M{`H!y?zIn07w*I#?{?wwPP#Q!YRceTzmZlfoyV)G1ZZFhDx7Gib-z0E#l`QT z$D@yAaBm4-jJ6fFf9}PpSq(H!zL{bVcOkRuBYE{iK)K7dZev~y5I+SXJ<^x1VWVLF zt6?whUHBN(9~ZGo@>FX2n1}sF`d}$-;=Ir6>Df$Ae8k`QviEwt@i7*z<#izce;7lT*#l;mIjSIAvx7S=cq>-p9SsB{2%4w(X+OchgU>&{P_ipU~v}JA?4+FM(gqFvD{;9nf^;j#%yQ1Jik* zP-;C7_TNk*uSKiGv3cdV z=XUABvfgJvQknpJ-`?TKcTByjGNJ39>tH$Y80wF=7JQ5M(9{urr1r{(#p-h5>RbWW zhV|vYD=UTamPd4T{$?8MtW#Um;}{;%`UVST1>k@~^0Z~MGA^)+=Cj63d)F#(X}5>) zCgCIZSnomV{g-j%r#H0an}jW{ZE_d=$MA;bow#S$1N7eR3jR3R2d6B13C3@JgUQQd z{C$QeX2k&?^-Bd!Egv*aDyCf_p1fP}03AABPM!TLYx?yI;i|z0!Q)VXBV2)tzo zF<-vZ?7uhQV$YL8^MuDVWonePW-y(W^zg0mm%BjgZD&I1=u%4lu#R#RO4#n_SoCP? zOr}4F;A(Y6OwGLmx09o}i|s>5om0q9&-6vJbK7a-AvyQWmLFhdlmv(DwCC@m#-P`W z&1_zH8b>S*5)aPKgAlt6u0QOHS0@ajojv}-h(BqBoC(7xq|*E!;h=R~g^KzD#awwx zzQMIM>n{uzUcW9Ae-DmF)wD2BRJZ>4;g9$HvB$9HD5-Paaj$xoZCQy=G><@H^*VG^^;L@vm9#U7IFO3 zmBNG=J?w}|!0u2@je88SY>+cSi7LuA$#JieVLTwd7ze*6ZrFE%zu)MMN;M<+D-q*pN~P^6AB2-K%Jj;b6G&;xEvm zBjUb?RrFZ8#|&+}NI$g&w3fz#lI)ed?~Jq-`$3+}7OLQx!;2`8w?g*|4s6)fRa)cO z#-r-)!^ZFqiK|})Oia$#Gc&A@)Q3AtB~Y@?8CsXz z5+~4F65}6$zNwP1M7JMTmssHEi@V6oJp>jVjKKQ2+c>c4J~*{afd_6^QSZWNp=TJd zdqNXfefuQrj0@wUZFlL|fn08D>@R6Puth8heJac~XcL7YPUvd8p*k(RFF*U}2GN6L zd3IAhEqi;Hwk=A8h3ke>3aPMiksQYlsS|Dvsu28A3gPTz8%_!d$LO&hxMIk6TB08d z&5E+T{&yrV{ zl;hDtIXoN@&ILou!Ew$&+I-fEHumf!j#z)6a@t>#{CP88+dm9F%HB}M=YE_RvPE=D zN)&x%e1)#I=Y^#|^ij3S26I=8Wt|gILd^nio;=Bz3r^VK1H~;6SNn+)9UEw7vL5IU zDMVpa9TliFQ=0a3JUr2xPsT6AS#}CstA1a2J}n6cri|pZ2jqEPK_&pwL$wJ=hk0g*eZLWVwtpGwDJw7cM-71jKjj2U%Tm->S|%l zFekDawH0>Pc1Eq*`8aCQZgG8U1}wO-hH{ts!k>Fa;N@pXxc1GX+3A}hy zcid)BOz$o@HT&7hqsX zcWfFoho4^ttlu^bp6a%WfqPHV=W(s{EV+~pth2_&(O!7wy(N!6v5tddrq{&%ZXvA~ zX_#?JnQFV&(nDJfPPSSH&7D&4-hzHuoSVo;+-A|hrVU^o8cP$BMY?fo2Jf&k;p>`P zs%`eAW17A!3crg`X_*3#?`s9NS;l;QMmuJYjOV@c_K?`Q8^`-@g6s8rS#L}xjZgh8 zNoa}}m5ieCmeE`6Q@@6zZg=Bl&IMFAdI2u92_(1mGVZHKD%K7&>Lxf=^%D*A>QJ-1 zCy(~IClp4+bLd+YG#xur7_u=!+H*UNUuyQjJM^W#mN^`mXMkgk6!_?cE0A=klC)cQ z(-gDEk}s2V`H=9Dp2!@AYem0F_0>6aN*gXYT{V(sXiVXn3&yy`Ko#dLdjreX$kS=V zTlDpK2u`(nBbM%*$D83g)+&KvIHAnEy zob&MD(*touR5xy1cUhcrcOi~jHc9ldX`~|+)s%Q7Nr?QEjYCJ>BAa=2v?8@HB-#5> z>6vltZ2AEfgpLEnrTT2{b5GQEt%YCrE>o|JdQst;516@{i2Y^nK$oH-FzsQ$sU_>7 z)3kliH=n8aC&P2K9L~4xAgQ{uhIcGEaFY1Cey&ed?H3zX@$PIeg z;~9)`_QJm{-yk!5r9>8O$-cilFFAQx@*?3c8MscA#%nQjUsQwsJ+sguk z9!e7yho+EfR425SB=CzHi|~0$5MI!9q{-8hvFU&l8a-9T=NY@X%aBH~x8Dlx{UU-p z`)ovYX&p4Te_oAm!g}a`qzD@|a-nv0*P07`?$KLm9GKc3Tm5Tsrt}-VM=n1zc>J(h zQgl4VHv0LT5_}X*Zl+Od-Y$ON7{SMEr&0E!PdIwSPW(jelIyu=d5BvRjp}TGy)OI} zOrDm*>~F`xV8vze&VlpPRGNaFt_{a1{8i)r>o~TMmm6fLO z;*t^kyvGmttgsq}_teFf@l)A!8d7skiFj#ev0F-J7n~Fu&(;bngv;U~%ndorzx$O5 z^&bi_*f9!Zt{}J^ohHq}{?c*tLh1gng*La{Bh%!~RJhBKR;eaIuKNqveYF$YEwsVs zV-=w)@;-!m_om5R%xYSV3Q&J~AFiLfj-P+2#J7D+QSPrBo;ak3?Ymvr&a>UX!+IEWR+6 z)_PuwuzYe6efyG0-!~WWpX{;ttU!gI9*g3MX#@Gj*d>r~(4N+hpNu5~19@nx500I- z2NYL@W2BjG?R*(8vJQ)8@3Q@vq@|6eYMb%U-wc-d;|I(8#){>S)wpVAys&uE457E5 zn){-&ci4@Rxnt-&=>0bpGxLi;ZqH(lmH!5ldc{(+`bPV0Bju_nJ+^ZX;WThjugQ z%zQH(_CSY+hFRgRy~eDlWy_~`*uu@JBF+7K22?KG5<5K_KqlG!`Ch_0+@0{92NZ7P z;d4?^Bl9R7SU8?b&$eQKS`+EDUqIikoofx17DCEzW1d{Bg2nb9u~<(6^3H=<(yH!W z-dMq5EaR?hn$G)Tc2S4TS@ylP4Y}P0lU81YmXfYw&Xq7YD~&7f&DDaf$)V(Fx2r}q zTbbWIxk8Vh{1rYpe;1wg4p7`QBTBh?ooYSnNwWDex!3MNmy8TfzC02_w%-Bw?xm8C z4=wp%|0XC1pF~GyjN!khK7+M#Dt7Cv&g0(83Awwfarl`$cD`eX)^>jcy`j}G?Z+F= z|2mnEI~Uh@ch^IuwG;6Ej{vOOuEk29dZO7S4YBt1d6GT73l7R_vcuiwuy1Y#tnkYe z8)sgG(NDr?;ksqQ!!|Y4Fpy!#vkEjSXEn%n4}|9nj+3*VHyh8ifKI(-ab`f7SpCQt zy3Eetq$*oB4O77x?}wt{jR#`T+$mzevTj&>;W1grXYi3CJFI)Uoo7TT^5%ELA@Qvl z_xZF+RKGMDR+?*~!I@ihbgVw#Z|uVoodODw=4cw%J!=*WapnyZhY5KpbI8rOmASP@6;zCI}3aII!O!GS3fEjOZQf%icF1mh&eP^q< zkL%vb(_BB|!RomRiVBw>|l0&vUf9ekKh&wVw?9b7@SwDUNvJij!<@`EThd zYMcIz8vXNyZ<)RDXnQ7^zMT)DKK*Fp>n`Xi+lxcGAuptbVBI4FU5-2v4Gt@DcCaHZ z4>V+dTxUpjrh8!J^p}F)LPN3t=PF^E zxAgmJbK#05?Rb84FYf5q1anMxh>5U{rY82G{rx^l#GJ7t>++TqhRCwPl2{nIPn!?4 z-=OWCt)biNc3Ab|3&nU0z`aS1Z1yz?&s+Dw4Xy>^uOFUl;H%HQRrK-9%xJ#0@d4y} zkA*>B5_tK;3%D%iQ;m%0HwskH;IALgk&MDnaP#TMkM2%@&Q=-Vn}19g_@%IFpr<@3 zC@rK3iRrN2(g?JZ*VC_xdE(bY7s+H%B(*PHL(`wFA`8z)bfiHY4PV}Y#p-tKf5DTZ zM;7wbt?A@DA_F(%A@9j=z`QQC(!Tp#Shr#_PT7=#gQlbd58NyH^i!_3WUekX4O7G= zlh)wffeP?Qqn54he1&6Hd8{9thSgt&@NG#F?7ZHBGm}qB`$iw=z?V$?nX>~9ExJR= z8)n0g+)JW!h$00_eb=`=_SCGE0b4#42qV@ipwV_E)>pm)?G_jDZP%&nH>8Aj-%e)T zM_0uhdk>Dtw}6sl1^0v@3bbg0GW@ulMoXLt6Wb4gY{6unaJ-lo4)({)92=;+-i?fh zCSl^f6qXO!j8UvC?Q2)y;v0<|KIe>RnQe^k6OXXsx=Yy0t*U1GuSC}U@=J70{!Xv2 zYw=OtXo%WUK^{3(;B-S*uW>MxT^PeNr8>dO1E#`z#o436bWTeBG$zVi?rA`(b> znIZog_MF@eav&pZxLd~YQt%3MWt$@($!pCdNkVBn8VvEqZ-JrYG*F%&7~f&{WXYZznRJnXC`sCw8K>NHW?0_PUhA3u9NDrQZ9U~%C?2F zw78;8csu7fs1!dWnfLB2YZuP9M_+@Pe+QF+?j2FkJtX>ee=ST^_JmcV8>;cxb?_c_ zl=$})$}%c|{xfe2^1qV!dD%KP+ZoIU*X9Z)4>CbG(TRS3-pX(H%kr!y8!>t5d(isU z7p=x*kip8S@K?nV75b#oywMtbNp3rTQ@lxPO9;&Jw&2YfOCe`SIN$j;nT1VoFtlT`AWeFy|~b!&6>?tLAtLnWxTwCZ+RuyYbv!`$xQ&KN&7>w`2PWd0bd*O-?z> zxpQa(1iTw6-8nB%?uty-pJc$*gAS7Y23bjZ`XpGo)E!QJu;9B#Ss1r21y)~QCiNtX zA<8X482RHASs%Sk7jDnS_5q{un$ZceUbcca)aan!)gUnNs1rjkHA4FL2;#9@DPYtb z64s7}&jC{OqwXp&b~53uPc(Vq_C11OM5|z7eud&EzZS0R%5z@yES_JG!M;%{*wI=D zzpno#gD1MIt>M6p5nEvJqX}4dce7|X7RA8qo_zgd57PQNTin-ElT@yBLb+%0IHb2D zq^e)wN7;oGzTN?BQ1I$za(Sx@$7grK(`g1Y*sPXV zuT`???j9jMat>av)e)}^vu7*Og|!<=5XA26*)2qD`lC#jlgnwp`*dEcpTMWAfS-(< zL2X;kK-1XkU{dS}rY;&WSm@Dc}5@aC7S| ziK&bR5A=Tl`W7+L4oe0d^_OGqCG}#>)5&;bL>yL@N3iO`PNMmfVSMgsCAl7V<}EKq za%}W=PCe2}^|L>dVx0}x8VbU*v?@xOXvwK7EqTeQTDswR8D4aoi{@HoxIe>;BfOu9 zH{Kax&2?qa_~ws`$K8i*E^?Thd0X)P0qi<)BByB^v9-!j9wb~Lo4e_B?~^M(Xqmxf z4_8uD_!xFwP(e#?EAhqmpD5JUfd3w7ge%uS!^AfcSY!7}?67bGk5N5wZeI~(+Gb(d zo(l2Xa8Go5y%9Fd7zX#Z^q_Us9wZYnn}bz!sjS=@M*Y}MZHH5M`MGMU(s5vk)w)2qYZANB2_m?5m}i$M|cg0aB>S5z}4RLC~BJt|Sfo!Z9MRVnMQTmUm zoFdhKlBZnl!u{Jh=4KbZtsEvy8>T|ZYnRfm=m>WF(uaqJzo&uYtLeQ`3heh@z>Urs z;{GY^!pvhK*eAxG&5rKCVkxTdhCvLgJhbC1w@`jGXe6rt>Vv+0mxJB}dpa4B!MTlT zurqc8UTrjht>bKQ`sF=5CNr7GAJ3!hI>R{5XdSGZSs)CH-9VZ1rqbGxw?*%G54Q1O z*nV@6_;SC%@Z!6erLdQ-kNgN#I_mI6iuee3?I5i|CvZdVR&3j5jvJS}ht=}^(R$%z z^geb$T)QHOu9Ovt{vCJVXWSdoc;5_mT@wkmzN5~fw4(bukH#ABfHK9Mg5B~va5AU_ z)OAd7YnM%2Q0>Y7=4jD;EO}APGjHTTn@N!fmb*vujunJc{dz7otI3`DI(j_B-r3{H+E;XD=4k9X3jwF*flYd?)2lT3c; zGW^wSKY5&(B`nwI0#7$t@!j~PEHNiOcf*B0)o5VKsjC3BjkH|flxx>q;={T{Fw_4X zJp4TrI<*Gk+LbpUMsssbg3Tg{)%Za?cu@@`?mkAU(yX+oz>=@}cBfLup`?=@L<@gQ zyI74!gyp;Cc))OPJ}@kkCyzP>rDta0?NMhy{ZcRLxjTm4=V)?-n+1ITFaclKuK~N? zTd`$MCT%odM4q#!i+ROiIKAX6T#}Eciw=dfx;aNe|A-4z^4U;JgN>c1acF-_jvlv~ zo5R*|RntTUwN2EtcNoh&Tg?u$QaPj!neWX&i{t~sh&MrQCSC)Dtfo{5{Gm#^ae=J6 zI)D`x1o7awUE=vizMzqrFDccPqHJ0h;Eqp8BriV!rf~*7`+5j}gpI~|be(&4Ue4+r zH%K>aAf4WEU#Rr2fJKerth91A7Yw-}QPIy6zqstjo{~T~vb&kGZqB8@1{S>jg^9S< zJ%q(K6|jHEMGCyE&d-MXlcAqEPo1$BtUk^M!-F%)LW(ff5M`wMWOponqa_9N7mGAK=By5A82=acZ}*|#?(8<}=iClqt9yH-=r$w1UXv+gDBlvg`>$uk$)@~1;4R@BYi`yXDn*FZ z(96*WIsV-~I`dMGx712A)!Za1JA4bQlNF&n&VugFwxyGA^_UhRi2Xddm-ac@g7?JQ zN)0jjwuJ9$DMH%aDc}@46QTpFAkAh2Rd)1(@5<|hQ)i{1{Ws+>OKT(e_-%q?X~VI- ziwbTw-b)LTL^94kASSA$fc2DgA@am1-cfm;PwgHL6Cdm2!T3b+>N$6E?(4z*Z<|o> zx;ly&u!tZ2c}yC+JILskH{W&~%O_-yQ}?Q8)SfhgpB#Hh(+w9=O5ckRaA;`FP50AK zI3t^rqq4c+!#yZ_X@lx2rc$)nC5q6SNrz+mvP?=VTsb4p#wHQGTsTXM=?XP}ZYJCL z)wI|DBAwkQ%ioG~=yIAWt-I^b2li<2e480u*Q1Q07oHK8Ra@ZqeLeYvPBhQbap!Xn zOQ7!YZ;A4v!CY~_E4k!$;nLR<>8TKXxX`keC$6yOm0y8_*3IFvHO_R-I*1KSR#8LU z9dY;9DmwCJt*G#FJSQmIvGXfmN?mAy)0f_)0#V>`k;SyJ@T_p>Y7MBcAOM<(MzGF*8`Hz+(A`s zt&*U)FXE_pe^isb25q6sKzMzdnsf`G@5^+zfmc1zHDEq$?;FDVFSODyUn3lA+>K7R zO`?%WGssQS7uTvd;N!mQNiEBSqrJ|-%L9kVu*eX+D!eHrg?9BQpyl!(L`#)3 zn2GNpza~M@$s5Qc&DM%bG;YC5|2D~?5wWyqYi>|5xz>4lmp?vaABUb`T(zb|cf)Z?vU8q3A_@#*k+Y^LFf zN>wUoe#D4Vw@u~NfQ_7dXFat=hR_iIwGe4&i>~dxFs{;-4u)8>;M9{PTMyB#jhf<; zQz_(gtDWNH^H_4G3l~dr=&0{TYTDuf;dN_a-ANSotUm8{(mn&%Pq>aXZ%VLx+&E}| zSxxCHs%gRNA_{5VPTOx7v5m_b`k5_7wi&GzCeO(bP9)u+hS`QJb3l!6>rNFlY+J;; z-@e27za#klgd1Y2;#J|#r3JL3V}|6a?b5gv)#a?jxj=?ryVk?SSr+J+c$)qe zr?LGLf3At0Dwud1!-v*dvQh5I7yIQ>llwio-=;#!rrqHCgA+7t?S5MDP=+to8?s^K zQ^~>bV7Pm;RM5ib@FYNr4C`Y6*L>3;^W;Op*kPKW*yjnI{xg^Ro|eFq^nLK_?hL*s zbCtH3w~*Y7^HiGnOWd<(5Sl%7f>W#WMZa`4cJ#34w2%d8H z$kuA0^w_)A0JOUHJY}XAU~J2sUlA5OQbF zfFJVVc%ZO1R1Z(CF?urGO=)QkYR7yQHjPrJ!}9~U#$ggKZ@S8ha0HLqIEm%emDuw0 zboPx*;PAi>B@uT+tUWS%BbUMlVFh6h52lgBdhlujV@A@ z^wFm#d~9?AudQ^3oBr0Aw6PF%lHG(ogW{lQp%MGaJraJ~0R_f25I9zW-m#zL^k4>! z^u9=AmOK|XHP%z$y4N)-&Ms2)P7?X1JrskEWwQHH7tWQQ7WDnHOvsU+%GI^&#l_*> zd2)gcPpFmWkJ*{5plL;+VP^Po`ZJjGxd;X%4(CH1$GH5II*y(kiid)~35VCai9sJ` zahxPU95%QwwoUDh+0Wv^a6k{~{Qg3CfhHVZa!njkeO45=x{CWhTf*eB4vF3MJMgU4 zhIJks50c$%!q!C{a6CT*n}(>06|O15 z^v}v@W|{$8rKtXkT3(>{zzkc)*y6YzA4oY?i_;cnQ)A+9$XR-cRQ#1`k#R1C3?EH> zLnrdc6+L($#e2=2q%@XDdAX-XESs$5FcVBu+Q%&Sg#`VPMl#VZfEnd}*!>4x3T~wD7B3uNA-fqX`K;klK|#XW6V@WI`ge~k_ow*T%Xp1j+{;l|bM zD0e{!*xOF~*AC~%?TdMn>?R(Ra)zS+Z028fA$+x8GOf#~C8s(?Uf*pynHsMYO{S@E z(3eY)x?A8RH>qA+G|=8!iNltZ3hy$92>Z{Z3s;x_rm<-bY5WePH;?D4y@Z5hA3#QWC(a)RY^-^HMq6Trr zrVu{$U72HDgYiwtW?Wu&k>}6d>E^iH0)IDj!^PVDIBrcipFQ-G)<~gwX1~fQ=S3_p z=o`dcp^duw6p~f)FJ8VhgW`bCA8_HCygF^MoJ z+Z$hcZxGhZeolYppP8?b;O%%U0tZzXLVz{#H`vJzG{faUIpP_JhNVBQ-fM`tkm+PW);>A`g%D zpfNC`6@F$xa$xPyO<{UHYVb%=9@T0&WY6@^x|j@ z18Jkv7u-+x1?XDL#lkM}_@zTI>$;0@v(AAUcH9s?KNG>`ikj$>S0f%$dPI$DhQRKI zL$If31(kfhMsKf1bByH@A^zr8eEe1!%<62p$EW+SAW$2-^fbeDI#=kD$xJK^*#-gr zU7+hU9WlLrDVnGD2ZJZd7!>)AzK-_*Dw5$&8x{HSmSo&=S`YU>3=`h=b>%5;zi9T; zN$9m!5qCUa!S&a+lkvgZu&()#^mL#R9;tNU8*bZRQ-dYF&fAHRBNp%ksgIWQEfp!fc>1#bD4*E#|7q;X zynoDR{+R1rb7s!WnK{?>{a#;|wVr}K-y)d$ zd(6Ec8*==dDYJ6TTUcf#OlJ!8Ft2@!Nb3$$%x;gu9r8EGEZsNYYqkm%yrNNRxHA

o@PU7;m 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 Date: Wed, 11 Aug 2021 17:19:15 -0700 Subject: [PATCH 09/11] Fix Andrew's PR comments --- .../arduino/example_project/project.ino | 5 +- .../arduino/example_project/src/model.c | 15 +- .../arduino/example_project/src/model.h | 14 +- apps/microtvm/arduino/host_driven/project.ino | 24 +-- apps/microtvm/arduino/host_driven/src/model.h | 25 --- .../src/{model.c => model_support.c} | 33 +--- .../template_project/microtvm_api_server.py | 183 ++++++++++-------- .../tests/test_arduino_microtvm_api_server.py | 5 +- tests/micro/arduino/README.md | 17 +- tests/micro/arduino/conftest.py | 30 +-- .../micro/arduino/test_arduino_rpc_server.py | 34 ++-- tests/micro/arduino/test_arduino_workflow.py | 11 +- tests/micro/zephyr/conftest.py | 2 - tests/scripts/task_python_microtvm.sh | 4 +- 14 files changed, 189 insertions(+), 213 deletions(-) delete mode 100644 apps/microtvm/arduino/host_driven/src/model.h rename apps/microtvm/arduino/host_driven/src/{model.c => model_support.c} (80%) diff --git a/apps/microtvm/arduino/example_project/project.ino b/apps/microtvm/arduino/example_project/project.ino index 74b596245624..5f5683161e0a 100644 --- a/apps/microtvm/arduino/example_project/project.ino +++ b/apps/microtvm/arduino/example_project/project.ino @@ -21,9 +21,10 @@ void setup() { TVMInitialize(); - //TVMExecute(input_data, output_data); + // If desired, initialize the RNG with random noise + // randomSeed(analogRead(0)); } void loop() { - // put your main code here, to run repeatedly: + //TVMExecute(input_data, output_data); } diff --git a/apps/microtvm/arduino/example_project/src/model.c b/apps/microtvm/arduino/example_project/src/model.c index 7acb06fa89af..fc8b5836314b 100644 --- a/apps/microtvm/arduino/example_project/src/model.c +++ b/apps/microtvm/arduino/example_project/src/model.c @@ -17,9 +17,6 @@ * under the License. */ -#ifndef TVM_IMPLEMENTATION_ARDUINO -#define TVM_IMPLEMENTATION_ARDUINO - #include "model.h" #include "Arduino.h" @@ -33,6 +30,7 @@ tvm_workspace_t app_workspace; // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { + TVMLogf("TVMPlatformAbort: %08x\n", error); for (;;) { #ifdef LED_BUILTIN digitalWrite(LED_BUILTIN, HIGH); @@ -57,10 +55,7 @@ tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { return StackMemoryManager_Free(&app_workspace, ptr); } -unsigned long g_utvm_start_time; - -#define MILLIS_TIL_EXPIRY 200 - +unsigned long g_utvm_start_time_micros; int g_utvm_timer_running = 0; tvm_crt_error_t TVMPlatformTimerStart() { @@ -68,7 +63,7 @@ tvm_crt_error_t TVMPlatformTimerStart() { return kTvmErrorPlatformTimerBadState; } g_utvm_timer_running = 1; - g_utvm_start_time = micros(); + g_utvm_start_time_micros = micros(); return kTvmErrorNoError; } @@ -77,7 +72,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { return kTvmErrorPlatformTimerBadState; } g_utvm_timer_running = 0; - unsigned long g_utvm_stop_time = micros() - g_utvm_start_time; + unsigned long g_utvm_stop_time = micros() - g_utvm_start_time_micros; *elapsed_time_seconds = ((double)g_utvm_stop_time) / 1e6; return kTvmErrorNoError; } @@ -97,5 +92,3 @@ void TVMExecute(void* input_data, void* output_data) { TVMPlatformAbort(kTvmErrorPlatformCheckFailure); } } - -#endif diff --git a/apps/microtvm/arduino/example_project/src/model.h b/apps/microtvm/arduino/example_project/src/model.h index 489adff0da53..7381c97e9b3f 100644 --- a/apps/microtvm/arduino/example_project/src/model.h +++ b/apps/microtvm/arduino/example_project/src/model.h @@ -17,9 +17,6 @@ * under the License. */ -#ifndef IMPLEMENTATION_H_ -#define IMPLEMENTATION_H_ - #define WORKSPACE_SIZE $workspace_size_bytes #ifdef __cplusplus @@ -28,11 +25,16 @@ extern "C" { void TVMInitialize(); -// TODO template these void* values once MLF format has input and output data +/* TODO template this function signature with the input and output + * data types and sizes. For example: + * + * void TVMExecute(uint8_t input_data[9216], uint8_t output_data[3]); + * + * Note this can only be done once MLF has JSON metadata describing + * inputs and outputs. + */ void TVMExecute(void* input_data, void* output_data); #ifdef __cplusplus } // extern "C" #endif - -#endif // IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/host_driven/project.ino b/apps/microtvm/arduino/host_driven/project.ino index 34537d4e205f..c1b7f3870400 100644 --- a/apps/microtvm/arduino/host_driven/project.ino +++ b/apps/microtvm/arduino/host_driven/project.ino @@ -19,7 +19,6 @@ #include "src/standalone_crt/include/tvm/runtime/crt/microtvm_rpc_server.h" #include "src/standalone_crt/include/tvm/runtime/crt/logging.h" -#include "src/model.h" microtvm_rpc_server_t server; // Called by TVM to write serial data to the UART. @@ -32,22 +31,23 @@ void setup() { server = MicroTVMRpcServerInit(write_serial, NULL); TVMLogf("microTVM Arduino runtime - running"); Serial.begin(115200); + + // If desired, initialize the RNG with random noise + // randomSeed(analogRead(0)); } void loop() { - int to_read = Serial.available(); + // Read at most 128 bytes at a time to prevent stack blowup + int to_read = min(Serial.available(), 128); + uint8_t data[to_read]; - size_t bytes_read = Serial.readBytes(data, to_read); + size_t bytes_remaining = Serial.readBytes(data, to_read); uint8_t* arr_ptr = data; - uint8_t** data_ptr = &arr_ptr; - if (bytes_read > 0) { - size_t bytes_remaining = bytes_read; - while (bytes_remaining > 0) { - // Pass the received bytes to the RPC server. - tvm_crt_error_t err = MicroTVMRpcServerLoop(server, data_ptr, &bytes_remaining); - if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { - TVMPlatformAbort(err); - } + while (bytes_remaining > 0) { + // Pass the received bytes to the RPC server. + tvm_crt_error_t err = MicroTVMRpcServerLoop(server, &arr_ptr, &bytes_remaining); + if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { + TVMPlatformAbort(err); } } } diff --git a/apps/microtvm/arduino/host_driven/src/model.h b/apps/microtvm/arduino/host_driven/src/model.h deleted file mode 100644 index edc83e5123f8..000000000000 --- a/apps/microtvm/arduino/host_driven/src/model.h +++ /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. - */ - -#ifndef IMPLEMENTATION_H_ -#define IMPLEMENTATION_H_ - -#define WORKSPACE_SIZE 65535 - -#endif // IMPLEMENTATION_H_ diff --git a/apps/microtvm/arduino/host_driven/src/model.c b/apps/microtvm/arduino/host_driven/src/model_support.c similarity index 80% rename from apps/microtvm/arduino/host_driven/src/model.c rename to apps/microtvm/arduino/host_driven/src/model_support.c index 5b87deb526d5..6fe36099227f 100644 --- a/apps/microtvm/arduino/host_driven/src/model.c +++ b/apps/microtvm/arduino/host_driven/src/model_support.c @@ -17,29 +17,13 @@ * under the License. */ -#ifndef TVM_IMPLEMENTATION_ARDUINO -#define TVM_IMPLEMENTATION_ARDUINO - -#include "model.h" - -#include "Arduino.h" -#include "standalone_crt/include/tvm/runtime/crt/internal/aot_executor/aot_executor.h" #include "stdarg.h" +#include "standalone_crt/include/tvm/runtime/crt/internal/aot_executor/aot_executor.h" // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { - for (;;) { -#ifdef LED_BUILTIN - digitalWrite(LED_BUILTIN, HIGH); - delay(250); - digitalWrite(LED_BUILTIN, LOW); - delay(250); - digitalWrite(LED_BUILTIN, HIGH); - delay(250); - digitalWrite(LED_BUILTIN, LOW); - delay(750); -#endif - } + TVMLogf("TVMPlatformAbort: %08x\n", error); + for (;;); } size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, @@ -60,10 +44,7 @@ tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { return kTvmErrorNoError; } -unsigned long g_utvm_start_time; - -#define MILLIS_TIL_EXPIRY 200 - +unsigned long g_utvm_start_time_micros; int g_utvm_timer_running = 0; tvm_crt_error_t TVMPlatformTimerStart() { @@ -71,7 +52,7 @@ tvm_crt_error_t TVMPlatformTimerStart() { return kTvmErrorPlatformTimerBadState; } g_utvm_timer_running = 1; - g_utvm_start_time = micros(); + g_utvm_start_time_micros = micros(); return kTvmErrorNoError; } @@ -80,7 +61,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { return kTvmErrorPlatformTimerBadState; } g_utvm_timer_running = 0; - unsigned long g_utvm_stop_time = micros() - g_utvm_start_time; + unsigned long g_utvm_stop_time = micros() - g_utvm_start_time_micros; *elapsed_time_seconds = ((double)g_utvm_stop_time) / 1e6; return kTvmErrorNoError; } @@ -91,5 +72,3 @@ tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { } return kTvmErrorNoError; } - -#endif diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 7297bab6405d..a348d37288cc 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -28,13 +28,12 @@ import subprocess import sys import tarfile -from string import Template import tempfile import time +from string import Template import serial import serial.tools.list_ports - from tvm.micro.project_api import server MODEL_LIBRARY_FORMAT_RELPATH = pathlib.Path("src") / "model" / "model.tar" @@ -45,20 +44,20 @@ IS_TEMPLATE = not (API_SERVER_DIR / MODEL_LIBRARY_FORMAT_RELPATH).exists() -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""" +# Data structure to hold the information microtvm_api_server.py needs +# to communicate with each of these boards. Currently just holds the +# components of each board's FQBN, but might be extended in the future +# to include the SRAM, PSRAM, flash, etc. on each board. BOARD_PROPERTIES = { - "due": {"package": "arduino", "architecture": "sam", "board": "arduino_due_x"}, + "due": { + "package": "arduino", + "architecture": "sam", + "board": "arduino_due_x", + }, # Due to the way the Feather S2 bootloader works, compilation # behaves fine but uploads cannot be done automatically "feathers2": { @@ -108,7 +107,7 @@ class BoardAutodetectFailed(Exception): 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( - "example_project", + "project_type", help="Type of project to generate.", choices=tuple(PROJECT_TYPES), ), @@ -201,6 +200,10 @@ def _template_model_header(self, source_dir, metadata): with open(source_dir / "model.h", "r") as f: model_h_template = Template(f.read()) + # The structure of the "memory" key depends on the style - + # only style="full-model" works with AOT, so we'll check that + assert metadata["style"] == "full-model" + template_values = { "workspace_size_bytes": metadata["memory"]["functions"]["main"][0][ "workspace_size_bytes" @@ -222,47 +225,20 @@ def _change_cpp_file_extensions(self, source_dir): for filename in source_dir.rglob(f"*.inc"): filename.rename(filename.with_suffix(".h")) - def _process_autogenerated_inc_files(self, source_dir): - for filename in source_dir.rglob(f"*.inc"): - # Individual file fixes - if filename.stem == "gentab_ccitt": - with open(filename, "r+") as f: - content = f.read() - f.seek(0, 0) - f.write('#include "inttypes.h"\n' + content) - - filename.rename(filename.with_suffix(".c")) - - 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 for a .inc file we renamed to .c earlier, fix it - if import_path.endswith(self.CPP_FILE_EXTENSION_SYNONYMS): - import_path = re.sub(r"\.[a-z]+$", ".cpp", import_path) + """Arduino only supports includes relative to the top-level project, so this + finds each time we #include a file and changes the path to be relative to the + top-level project.ino file. For example, the line: - if import_path.endswith(".inc"): - import_path = re.sub(r"\.[a-z]+$", ".h", import_path) + #include - # If the import already works, don't modify it - if (file_path.parents[0] / import_path).exists(): - return import_path + Might be changed to (depending on the source file's location): - relative_path = file_path.relative_to(project_dir) - up_dirs_path = "../" * str(relative_path).count("/") - - for base_path in self.POSSIBLE_BASE_PATHS: - full_potential_path = project_dir / base_path / import_path - if full_potential_path.exists(): - new_include = up_dirs_path + base_path + import_path - return new_include + #include "../../../../include/tvm/runtime/crt/platform.h" - # If we can't find the file, just leave it untouched - # It's probably a standard C/C++ header - return import_path + We also need to leave standard library includes as-is. + """ - # 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): + def _convert_includes(self, project_dir, source_dir): for ext in ("c", "h", "cpp"): for filename in source_dir.rglob(f"*.{ext}"): with filename.open() as file: @@ -282,6 +258,55 @@ def _convert_imports(self, project_dir, source_dir): with filename.open("w") as file: file.writelines(lines) + # Most of the files we used to be able to point to directly are under "src/standalone_crt/include/". + # Howver, crt_config.h lives under "src/standalone_crt/crt_config/", and more exceptions might + # be added in the future. + POSSIBLE_BASE_PATHS = ["src/standalone_crt/include/", "src/standalone_crt/crt_config/"] + + """Takes a single #include path, and returns the new location + it should point to (as described above). For example, one of the + includes for "src/standalone_crt/src/runtime/crt/common/ndarray.c" is: + + #include + + For that line, _convert_includes might call _find_modified_include_path + with the arguments: + + project_dir = "/path/to/project/dir" + file_path = "/path/to/project/dir/src/standalone_crt/src/runtime/crt/common/ndarray.c" + include_path = "tvm/runtime/crt/platform.h" + + Given these arguments, _find_modified_include_path should return: + + "../../../../../../src/standalone_crt/include/tvm/runtime/crt/platform.h" + + See unit test in ./tests/test_arduino_microtvm_api_server.py + """ + + def _find_modified_include_path(self, project_dir, file_path, include_path): + if include_path.endswith(".inc"): + include_path = re.sub(r"\.[a-z]+$", ".h", include_path) + + # Change includes referencing .cc and .cxx files to point to the renamed .cpp file + if include_path.endswith(self.CPP_FILE_EXTENSION_SYNONYMS): + include_path = re.sub(r"\.[a-z]+$", ".cpp", include_path) + + # If the include already works, don't modify it + if (file_path.parents[0] / include_path).exists(): + return include_path + + relative_path = file_path.relative_to(project_dir) + up_dirs_path = "../" * str(relative_path).count("/") + + for base_path in self.POSSIBLE_BASE_PATHS: + full_potential_path = project_dir / base_path / include_path + if full_potential_path.exists(): + return up_dirs_path + base_path + include_path + + # If we can't find the file, just leave it untouched + # It's probably a standard C/C++ header + return include_path + def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): # Reference key directories with pathlib project_dir = pathlib.Path(project_dir) @@ -291,9 +316,8 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec # Copies files from the template folder to project_dir. model.h is copied here, # but will also need to be templated later. - if IS_TEMPLATE: - shutil.copy2(API_SERVER_DIR / "microtvm_api_server.py", project_dir) - self._copy_project_files(API_SERVER_DIR, project_dir, options["project_type"]) + shutil.copy2(API_SERVER_DIR / "microtvm_api_server.py", project_dir) + self._copy_project_files(API_SERVER_DIR, project_dir, options["project_type"]) # Copy standalone_crt into src folder self._copy_standalone_crt(source_dir, standalone_crt_dir) @@ -309,8 +333,8 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec self._change_cpp_file_extensions(source_dir) - # Recursively change imports - self._convert_imports(project_dir, source_dir) + # Recursively change includes + self._convert_includes(project_dir, source_dir) def _get_fqbn(self, options): o = BOARD_PROPERTIES[options["arduino_board"]] @@ -318,7 +342,6 @@ def _get_fqbn(self, options): def build(self, options): BUILD_DIR.mkdir() - print(BUILD_DIR) compile_cmd = [ options["arduino_cli_cmd"], @@ -334,28 +357,42 @@ def build(self, options): compile_cmd.append("--verbose") # Specify project to compile - output = subprocess.check_call(compile_cmd) - assert output == 0 + subprocess.run(compile_cmd) + + """We run the command `arduino-cli board list`, which produces + outputs of the form: - # 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 """ + BOARD_LIST_HEADERS = ("Port", "Type", "Board Name", "FQBN", "Core") + + def _parse_boards_tabular_str(self, tabular_str): + str_rows = tabular_str.split("\n")[:-2] + header = str_rows[0] + indices = [header.index(h) for h in self.BOARD_LIST_HEADERS] + [len(header)] + + for str_row in str_rows[1:]: + parsed_row = [] + for cell_index in range(len(self.BOARD_LIST_HEADERS)): + start = indices[cell_index] + end = indices[cell_index + 1] + str_cell = str_row[start:end] + + # Remove trailing whitespace used for padding + parsed_row.append(str_cell.rstrip()) + yield parsed_row + def _auto_detect_port(self, options): 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] + list_cmd_output = subprocess.run(list_cmd, capture_output=True).stdout.decode("utf-8") - # 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] + desired_fqbn = self._get_fqbn(options) + for line in self._parse_boards_tabular_str(list_cmd_output): + if line[3] == desired_fqbn: + return line[0] # If no compatible boards, raise an error raise BoardAutodetectFailed() @@ -387,12 +424,7 @@ def flash(self, options): if options.get("verbose"): upload_cmd.append("--verbose") - output = subprocess.check_call(upload_cmd) - - if output == 2: - raise InvalidPortException() - elif output > 0: - raise SketchUploadException() + subprocess.run(upload_cmd) def open_transport(self, options): # Zephyr example doesn't throw an error in this case @@ -401,14 +433,13 @@ def open_transport(self, options): port = self._get_arduino_port(options) - # Wait for port to become available + # It takes a moment for the Arduino code to finish initializing + # and start communicating over serial for attempts in range(10): if any(serial.tools.list_ports.grep(port)): break time.sleep(0.5) - # TODO figure out why RPC serial communication times out 90% (not 100%) - # of the time on the Nano 33 BLE self._serial = serial.Serial(port, baudrate=115200, timeout=5) return server.TransportTimeouts( 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 3dd3688fe5b4..e576cc2c2d88 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 @@ -15,9 +15,9 @@ # specific language governing permissions and limitations # under the License. -from unittest import mock -from pathlib import Path import sys +from pathlib import Path +from unittest import mock import pytest @@ -64,6 +64,7 @@ def test_find_modified_include_path(self, mock_pathlib_path): BOARD_CONNECTED_OUTPUT = bytes( "Port Type Board Name FQBN Core \n" + "/dev/ttyACM1 Serial Port (USB) Wrong Arduino arduino:mbed_nano:nano33 arduino:mbed_nano\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", diff --git a/tests/micro/arduino/README.md b/tests/micro/arduino/README.md index f2c0535ff25a..78e63cabb7e2 100644 --- a/tests/micro/arduino/README.md +++ b/tests/micro/arduino/README.md @@ -22,19 +22,14 @@ 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 +$ pytest --microtvm-platforms spresense ``` -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: +Most of these tests require a supported Arduino board to be connected. +If you don't want to run these tests, you can pass the flag +`--test-build-only` to only test project generation and compilation. +To see the list of supported values for `----microtvm-platforms`, run: ``` -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 +$ pytest --help ``` diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index f4e27d1458e3..19bba9b39536 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -19,7 +19,6 @@ import pathlib import pytest - import tvm.target.target # The models that should pass this configuration. Maps a short, identifying platform string to @@ -49,8 +48,8 @@ def pytest_addoption(parser): parser.addoption( "--microtvm-platforms", - default=["due"], - nargs="*", + nargs="+", + required=True, choices=PLATFORMS.keys(), help="Target platforms for microTVM tests.", ) @@ -60,18 +59,32 @@ def pytest_addoption(parser): help="Path to `arduino-cli` command for flashing device.", ) parser.addoption( - "--run-hardware-tests", + "--test-build-only", action="store_true", - help="Run tests that require physical hardware.", + help="Only run tests that don't 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: ", + help="If given, enable a debug session while the test is running. Before running the test, in a separate shell, you should run: ", ) +def pytest_configure(config): + config.addinivalue_line( + "markers", "requires_hardware: mark test to run only when an Arduino board is connected" + ) + + +def pytest_collection_modifyitems(config, items): + if config.getoption("--test-build-only"): + skip_hardware_tests = pytest.mark.skip(reason="--test-build-only was passed") + for item in items: + if "requires_hardware" in item.keywords: + item.add_marker(skip_hardware_tests) + + # We might do project generation differently for different boards in the future # (to take advantage of multiple cores / external memory / etc.), so all tests # are parameterized by board @@ -90,11 +103,6 @@ def tvm_debug(request): return request.config.getoption("--tvm-debug") -@pytest.fixture(scope="session") -def run_hardware_tests(request): - return request.config.getoption("--run-hardware-tests") - - def make_workspace_dir(test_name, platform): _, arduino_board = PLATFORMS[platform] filepath = pathlib.Path(__file__) diff --git a/tests/micro/arduino/test_arduino_rpc_server.py b/tests/micro/arduino/test_arduino_rpc_server.py index eee4a5623cff..fd656dbe38c3 100644 --- a/tests/micro/arduino/test_arduino_rpc_server.py +++ b/tests/micro/arduino/test_arduino_rpc_server.py @@ -15,38 +15,31 @@ # specific language governing permissions and limitations # under the License. +""" +This unit test simulates an autotuning workflow, where we: +1. Instantiate the Arduino RPC server project +2. Build and flash that project onto our target board + +""" + import datetime -import os import pathlib -import shutil import sys -import time import numpy as np import onnx -from PIL import Image import pytest -import tflite - import tvm +from PIL import Image from tvm import micro, relay from tvm.relay.testing import byoc import conftest -""" -This unit test simulates an autotuning workflow, where we: -1. Instantiate the Arduino RPC server project -2. Build and flash that project onto our target board - -""" # We'll make a new workspace for each test @pytest.fixture(scope="function") -def workspace_dir(platform, run_hardware_tests): - if not run_hardware_tests: - pytest.skip() - +def workspace_dir(platform): return conftest.make_workspace_dir("arduino_rpc_server", platform) @@ -89,6 +82,7 @@ def _make_add_sess(model, arduino_board, arduino_cli_cmd, workspace_dir, build_c # The same test code can be executed on both the QEMU simulation and on real hardware. @tvm.testing.requires_micro +@pytest.mark.requires_hardware def test_compile_runtime(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Test compiling the on-device runtime.""" @@ -110,10 +104,10 @@ def test_basic_add(sess): with _make_add_sess(model, arduino_board, arduino_cli_cmd, workspace_dir, build_config) as sess: test_basic_add(sess) - print(workspace_dir) @tvm.testing.requires_micro +@pytest.mark.requires_hardware def test_platform_timer(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Test compiling the on-device runtime.""" @@ -143,6 +137,7 @@ def test_basic_add(sess): @tvm.testing.requires_micro +@pytest.mark.requires_hardware def test_relay(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Testing a simple relay graph""" model, arduino_board = conftest.PLATFORMS[platform] @@ -176,13 +171,14 @@ def test_relay(platform, arduino_cli_cmd, tvm_debug, workspace_dir): @tvm.testing.requires_micro +@pytest.mark.requires_hardware def test_onnx(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """Testing a simple ONNX model.""" model, arduino_board = conftest.PLATFORMS[platform] build_config = {"debug": tvm_debug} # Load test images. - this_dir = pathlib.Path(os.path.dirname(__file__)) + this_dir = pathlib.Path(__file__).parent testdata_dir = this_dir.parent / "testdata" digit_2 = Image.open(testdata_dir / "digit-2.jpg").resize((28, 28)) digit_2 = np.asarray(digit_2).astype("float32") @@ -263,6 +259,7 @@ def check_result( @tvm.testing.requires_micro +@pytest.mark.requires_hardware def test_byoc_microtvm(platform, arduino_cli_cmd, tvm_debug, workspace_dir): """This is a simple test case to check BYOC capabilities of microTVM""" model, arduino_board = conftest.PLATFORMS[platform] @@ -346,6 +343,7 @@ def _make_add_sess_with_shape( ], ) @tvm.testing.requires_micro +@pytest.mark.requires_hardware def test_rpc_large_array(platform, arduino_cli_cmd, tvm_debug, workspace_dir, shape): """Test large RPC array transfer.""" model, arduino_board = conftest.PLATFORMS[platform] diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index fddb47c8267c..18457763b9d0 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -19,7 +19,6 @@ import pathlib import shutil import sys -import time import pytest import tflite @@ -165,10 +164,7 @@ def test_compile_yes_no_project(project_dir, project, compiled_project): @pytest.fixture(scope="module") -def uploaded_project(compiled_project, run_hardware_tests): - if not run_hardware_tests: - pytest.skip() - +def uploaded_project(compiled_project): compiled_project.flash() return compiled_project @@ -186,9 +182,6 @@ 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) @@ -212,6 +205,7 @@ def serial_output(uploaded_project): 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} @@ -228,6 +222,7 @@ def test_project_inference_correctness(serial_output): 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] diff --git a/tests/micro/zephyr/conftest.py b/tests/micro/zephyr/conftest.py index 1aa477596000..cfdb208c92b8 100644 --- a/tests/micro/zephyr/conftest.py +++ b/tests/micro/zephyr/conftest.py @@ -79,8 +79,6 @@ def temp_dir(platform): _, zephyr_board = PLATFORMS[platform] parent_dir = pathlib.Path(os.path.dirname(__file__)) filename = os.path.splitext(os.path.basename(__file__))[0] - print(filename) - print("-----------------") board_workspace = ( parent_dir / f"workspace_{filename}_{zephyr_board}" diff --git a/tests/scripts/task_python_microtvm.sh b/tests/scripts/task_python_microtvm.sh index eabea73a4dcd..68efc38a1260 100755 --- a/tests/scripts/task_python_microtvm.sh +++ b/tests/scripts/task_python_microtvm.sh @@ -25,5 +25,5 @@ source tests/scripts/setup-pytest-env.sh make cython3 run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr --microtvm-platforms=qemu_x86 run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr --microtvm-platforms=mps2_an521 -run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --microtvm-platforms=due -run_pytest ctypes python-microtvm-arduino-nano33ble tests/micro/arduino --microtvm-platforms=nano33ble +run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=due +run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=nano33ble From d2f5e0910f7d438d5cdcb88d897078f08ef37368 Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Wed, 11 Aug 2021 17:45:39 -0700 Subject: [PATCH 10/11] Additional PR comments PR comments and Python 3.6 support Linting fix Re-add test onnx file Test Arduino cli bug workaround Support new hardware targets Temporary fix for tests Formatting issue Spelling fix Add test case for exact FQBN matching --- .../arduino/example_project/src/model.c | 2 +- .../standalone_crt/crt_config/crt_config.h | 1 - apps/microtvm/arduino/host_driven/project.ino | 2 +- .../arduino/host_driven/src/model_support.c | 7 +- .../standalone_crt/crt_config/crt_config.h | 1 - .../template_project/microtvm_api_server.py | 115 ++++++++++-------- .../tests/test_arduino_microtvm_api_server.py | 42 ++++--- tests/lint/check_file_type.py | 8 +- tests/micro/arduino/conftest.py | 1 + .../micro/arduino/test_arduino_rpc_server.py | 8 +- tests/micro/arduino/test_arduino_workflow.py | 19 ++- tests/micro/testdata/{ => kws}/no.c | 0 tests/micro/testdata/{ => kws}/silence.c | 0 tests/micro/testdata/{ => kws}/unknown.c | 0 tests/micro/testdata/{ => kws}/yes.c | 0 tests/micro/testdata/{ => kws}/yes_no.tflite | Bin tests/micro/testdata/{ => mnist}/digit-2.jpg | Bin tests/micro/testdata/{ => mnist}/digit-9.jpg | Bin tests/micro/testdata/{ => mnist}/mnist-8.onnx | Bin tests/micro/zephyr/test_zephyr.py | 8 +- tests/scripts/task_python_microtvm.sh | 4 +- 21 files changed, 123 insertions(+), 95 deletions(-) rename tests/micro/testdata/{ => kws}/no.c (100%) rename tests/micro/testdata/{ => kws}/silence.c (100%) rename tests/micro/testdata/{ => kws}/unknown.c (100%) rename tests/micro/testdata/{ => kws}/yes.c (100%) rename tests/micro/testdata/{ => kws}/yes_no.tflite (100%) rename tests/micro/testdata/{ => mnist}/digit-2.jpg (100%) rename tests/micro/testdata/{ => mnist}/digit-9.jpg (100%) rename tests/micro/testdata/{ => mnist}/mnist-8.onnx (100%) diff --git a/apps/microtvm/arduino/example_project/src/model.c b/apps/microtvm/arduino/example_project/src/model.c index fc8b5836314b..77566ffc6a8f 100644 --- a/apps/microtvm/arduino/example_project/src/model.c +++ b/apps/microtvm/arduino/example_project/src/model.c @@ -30,7 +30,7 @@ tvm_workspace_t app_workspace; // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { - TVMLogf("TVMPlatformAbort: %08x\n", error); + TVMLogf("TVMPlatformAbort: 0x%08x\n", error); for (;;) { #ifdef LED_BUILTIN digitalWrite(LED_BUILTIN, HIGH); diff --git a/apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h b/apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h index c3e8fea1ba08..cf73103aff8b 100644 --- a/apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h +++ b/apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h @@ -18,7 +18,6 @@ */ /*! - * \file tvm/runtime/crt/host/crt_config.h * \brief CRT configuration for the host-linked CRT. */ #ifndef TVM_RUNTIME_MICRO_CRT_CONFIG_H_ diff --git a/apps/microtvm/arduino/host_driven/project.ino b/apps/microtvm/arduino/host_driven/project.ino index c1b7f3870400..d394059e1bf5 100644 --- a/apps/microtvm/arduino/host_driven/project.ino +++ b/apps/microtvm/arduino/host_driven/project.ino @@ -41,7 +41,7 @@ void loop() { int to_read = min(Serial.available(), 128); uint8_t data[to_read]; - size_t bytes_remaining = Serial.readBytes(data, to_read); + size_t bytes_remaining = Serial.readBytes((char*) data, to_read); uint8_t* arr_ptr = data; while (bytes_remaining > 0) { // Pass the received bytes to the RPC server. diff --git a/apps/microtvm/arduino/host_driven/src/model_support.c b/apps/microtvm/arduino/host_driven/src/model_support.c index 6fe36099227f..ae467441fede 100644 --- a/apps/microtvm/arduino/host_driven/src/model_support.c +++ b/apps/microtvm/arduino/host_driven/src/model_support.c @@ -17,13 +17,14 @@ * under the License. */ -#include "stdarg.h" #include "standalone_crt/include/tvm/runtime/crt/internal/aot_executor/aot_executor.h" +#include "stdarg.h" // Blink code for debugging purposes void TVMPlatformAbort(tvm_crt_error_t error) { - TVMLogf("TVMPlatformAbort: %08x\n", error); - for (;;); + TVMLogf("TVMPlatformAbort: 0x%08x\n", error); + for (;;) + ; } size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, diff --git a/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h b/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h index c3e8fea1ba08..cf73103aff8b 100644 --- a/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h +++ b/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h @@ -18,7 +18,6 @@ */ /*! - * \file tvm/runtime/crt/host/crt_config.h * \brief CRT configuration for the host-linked CRT. */ #ifndef TVM_RUNTIME_MICRO_CRT_CONFIG_H_ diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index a348d37288cc..91beaf558249 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -56,7 +56,7 @@ class BoardAutodetectFailed(Exception): "due": { "package": "arduino", "architecture": "sam", - "board": "arduino_due_x", + "board": "arduino_due_x_dbg", }, # Due to the way the Feather S2 bootloader works, compilation # behaves fine but uploads cannot be done automatically @@ -94,6 +94,11 @@ class BoardAutodetectFailed(Exception): "architecture": "avr", "board": "teensy41", }, + "wioterminal": { + "package": "Seeeduino", + "architecture": "samd", + "board": "seeed_wio_terminal", + }, } PROJECT_TYPES = ["example_project", "host_driven"] @@ -133,10 +138,23 @@ def server_info_query(self, tvm_version): ) def _copy_project_files(self, api_server_dir, project_dir, project_type): + """Copies the files for project_type into project_dir. + + Notes + ----- + template_dir is NOT a project type, and that directory is never copied + in this function. template_dir only holds this file and its unit tests, + so this file is copied separately in generate_project. + + """ project_types_folder = api_server_dir.parents[0] - shutil.copytree( - project_types_folder / project_type / "src", project_dir / "src", dirs_exist_ok=True - ) + for item in (project_types_folder / project_type / "src").iterdir(): + dest = project_dir / "src" / item.name + if item.is_dir(): + shutil.copytree(item, dest) + else: + shutil.copy2(item, dest) + # Arduino requires the .ino file have the same filename as its containing folder shutil.copy2( project_types_folder / project_type / "project.ino", @@ -146,7 +164,6 @@ def _copy_project_files(self, api_server_dir, project_dir, project_type): 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" for item in self.CRT_COPY_ITEMS: src_path = os.path.join(standalone_crt_dir, item) @@ -200,9 +217,9 @@ def _template_model_header(self, source_dir, metadata): with open(source_dir / "model.h", "r") as f: model_h_template = Template(f.read()) - # The structure of the "memory" key depends on the style - - # only style="full-model" works with AOT, so we'll check that - assert metadata["style"] == "full-model" + assert ( + metadata["style"] == "full-model" + ), "when generating AOT, expect only full-model Model Library Format" template_values = { "workspace_size_bytes": metadata["memory"]["functions"]["main"][0][ @@ -225,20 +242,20 @@ def _change_cpp_file_extensions(self, source_dir): for filename in source_dir.rglob(f"*.inc"): filename.rename(filename.with_suffix(".h")) - """Arduino only supports includes relative to the top-level project, so this - finds each time we #include a file and changes the path to be relative to the - top-level project.ino file. For example, the line: - - #include - - Might be changed to (depending on the source file's location): + def _convert_includes(self, project_dir, source_dir): + """Changes all #include statements in project_dir to be relevant to their + containing file's location. - #include "../../../../include/tvm/runtime/crt/platform.h" + Arduino only supports includes relative to a file's location, so this + function finds each time we #include a file and changes the path to + be relative to the file location. Does not do this for standard C + libraries. Also changes angle brackets syntax to double quotes syntax. - We also need to leave standard library includes as-is. - """ + See Also + ----- + https://www.arduino.cc/reference/en/language/structure/further-syntax/include/ - def _convert_includes(self, project_dir, source_dir): + """ for ext in ("c", "h", "cpp"): for filename in source_dir.rglob(f"*.{ext}"): with filename.open() as file: @@ -263,27 +280,19 @@ def _convert_includes(self, project_dir, source_dir): # be added in the future. POSSIBLE_BASE_PATHS = ["src/standalone_crt/include/", "src/standalone_crt/crt_config/"] - """Takes a single #include path, and returns the new location - it should point to (as described above). For example, one of the - includes for "src/standalone_crt/src/runtime/crt/common/ndarray.c" is: - - #include - - For that line, _convert_includes might call _find_modified_include_path - with the arguments: - - project_dir = "/path/to/project/dir" - file_path = "/path/to/project/dir/src/standalone_crt/src/runtime/crt/common/ndarray.c" - include_path = "tvm/runtime/crt/platform.h" - - Given these arguments, _find_modified_include_path should return: - - "../../../../../../src/standalone_crt/include/tvm/runtime/crt/platform.h" - - See unit test in ./tests/test_arduino_microtvm_api_server.py - """ - def _find_modified_include_path(self, project_dir, file_path, include_path): + """Takes a single #include path, and returns the location it should point to. + + Examples + -------- + >>> _find_modified_include_path( + ... "/path/to/project/dir" + ... "/path/to/project/dir/src/standalone_crt/src/runtime/crt/common/ndarray.c" + ... "tvm/runtime/crt/platform.h" + ... ) + "../../../../../../src/standalone_crt/include/tvm/runtime/crt/platform.h" + + """ if include_path.endswith(".inc"): include_path = re.sub(r"\.[a-z]+$", ".h", include_path) @@ -314,8 +323,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec source_dir = project_dir / "src" source_dir.mkdir() - # Copies files from the template folder to project_dir. model.h is copied here, - # but will also need to be templated later. + # Copies files from the template folder to project_dir shutil.copy2(API_SERVER_DIR / "microtvm_api_server.py", project_dir) self._copy_project_files(API_SERVER_DIR, project_dir, options["project_type"]) @@ -359,17 +367,24 @@ def build(self, options): # Specify project to compile subprocess.run(compile_cmd) - """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 - """ - BOARD_LIST_HEADERS = ("Port", "Type", "Board Name", "FQBN", "Core") def _parse_boards_tabular_str(self, tabular_str): + """Parses the tabular output from `arduino-cli board list` into a 2D array + + Examples + -------- + >>> list(_parse_boards_tabular_str(bytes( + ... "Port Type Board Name FQBN Core \n" + ... "/dev/ttyS4 Serial Port Unknown \n" + ... "/dev/ttyUSB0 Serial Port (USB) Spresense SPRESENSE:spresense:spresense SPRESENSE:spresense\n" + ... "\n", + ... "utf-8"))) + [['/dev/ttys4', 'Serial Port', 'Unknown', '', ''], ['/dev/ttyUSB0', 'Serial Port (USB)', + 'Spresense', 'SPRESENSE:spresense:spresense', 'SPRESENSE:spresense']] + + """ + str_rows = tabular_str.split("\n")[:-2] header = str_rows[0] indices = [header.index(h) for h in self.BOARD_LIST_HEADERS] + [len(header)] @@ -387,7 +402,7 @@ def _parse_boards_tabular_str(self, tabular_str): def _auto_detect_port(self, options): list_cmd = [options["arduino_cli_cmd"], "board", "list"] - list_cmd_output = subprocess.run(list_cmd, capture_output=True).stdout.decode("utf-8") + list_cmd_output = subprocess.run(list_cmd, stdout=subprocess.PIPE).stdout.decode("utf-8") desired_fqbn = self._get_fqbn(options) for line in self._parse_boards_tabular_str(list_cmd_output): 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 e576cc2c2d88..00969a5a892b 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 @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +import subprocess import sys from pathlib import Path from unittest import mock @@ -64,8 +65,8 @@ def test_find_modified_include_path(self, mock_pathlib_path): BOARD_CONNECTED_OUTPUT = bytes( "Port Type Board Name FQBN Core \n" - "/dev/ttyACM1 Serial Port (USB) Wrong Arduino arduino:mbed_nano:nano33 arduino:mbed_nano\n" "/dev/ttyACM0 Serial Port (USB) Arduino Nano 33 BLE arduino:mbed_nano:nano33ble arduino:mbed_nano\n" + "/dev/ttyACM1 Serial Port (USB) Arduino Nano 33 arduino:mbed_nano:nano33 arduino:mbed_nano\n" "/dev/ttyS4 Serial Port Unknown \n" "\n", "utf-8", @@ -77,37 +78,38 @@ def test_find_modified_include_path(self, mock_pathlib_path): "utf-8", ) - @mock.patch("subprocess.check_output") - def test_auto_detect_port(self, mock_subprocess_check_output): + @mock.patch("subprocess.run") + def test_auto_detect_port(self, mock_subprocess_run): 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" + mock_subprocess_run.return_value.stdout = self.BOARD_CONNECTED_OUTPUT + assert handler._auto_detect_port(self.DEFAULT_OPTIONS) == "/dev/ttyACM0" # Test it raises an exception when no board is connected - mock_subprocess_check_output.return_value = self.BOARD_DISCONNECTED_OUTPUT + mock_subprocess_run.return_value.stdout = 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): + # Test that the FQBN needs to match EXACTLY + handler._get_fqbn = mock.MagicMock(return_value="arduino:mbed_nano:nano33") + mock_subprocess_run.return_value.stdout = self.BOARD_CONNECTED_OUTPUT + assert ( + handler._auto_detect_port({**self.DEFAULT_OPTIONS, "arduino_board": "nano33"}) + == "/dev/ttyACM1" + ) + + @mock.patch("subprocess.run") + def test_flash(self, mock_subprocess_run): handler = microtvm_api_server.Handler() handler._port = "/dev/ttyACM0" - # Test no exception thrown when code 0 returned - mock_subprocess_check_call.return_value = 0 + # Test no exception thrown when command works 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) + mock_subprocess_run.assert_called_once() - # Test SketchUploadException raised for other issues - mock_subprocess_check_call.return_value = 1 - with pytest.raises(microtvm_api_server.SketchUploadException): + # Test exception raised when `arduino-cli upload` returns error code + mock_subprocess_run.side_effect = subprocess.CalledProcessError(2, []) + with pytest.raises(subprocess.CalledProcessError): handler.flash(self.DEFAULT_OPTIONS) diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py index 88ad7d6a5055..ed7288ef00d4 100644 --- a/tests/lint/check_file_type.py +++ b/tests/lint/check_file_type.py @@ -133,10 +133,10 @@ # pytest config "pytest.ini", # microTVM tests - "tests/micro/testdata/digit-2.jpg", - "tests/micro/testdata/digit-9.jpg", - "tests/micro/testdata/mnist-8.onnx", - "tests/micro/testdata/yes_no.tflite", + "tests/micro/testdata/mnist/digit-2.jpg", + "tests/micro/testdata/mnist/digit-9.jpg", + "tests/micro/testdata/mnist/mnist-8.onnx", + "tests/micro/testdata/kws/yes_no.tflite", # microTVM Zephyr runtime "apps/microtvm/zephyr/template_project/CMakeLists.txt.template", "apps/microtvm/zephyr/template_project/qemu-hack/qemu-system-arm", diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 19bba9b39536..bcb2bddf2cab 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -31,6 +31,7 @@ "spresense": ("cxd5602gg", "spresense"), "teensy40": ("imxrt1060", "teensy40"), "teensy41": ("imxrt1060", "teensy41"), + "wioterminal": ("atsamd51", "wioterminal"), } TEMPLATE_PROJECT_DIR = ( diff --git a/tests/micro/arduino/test_arduino_rpc_server.py b/tests/micro/arduino/test_arduino_rpc_server.py index fd656dbe38c3..1b165a02e9d1 100644 --- a/tests/micro/arduino/test_arduino_rpc_server.py +++ b/tests/micro/arduino/test_arduino_rpc_server.py @@ -179,17 +179,17 @@ def test_onnx(platform, arduino_cli_cmd, tvm_debug, workspace_dir): # Load test images. this_dir = pathlib.Path(__file__).parent - testdata_dir = this_dir.parent / "testdata" - digit_2 = Image.open(testdata_dir / "digit-2.jpg").resize((28, 28)) + mnist_testdata = this_dir.parent / "testdata" / "mnist" + digit_2 = Image.open(mnist_testdata / "digit-2.jpg").resize((28, 28)) digit_2 = np.asarray(digit_2).astype("float32") digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = Image.open(testdata_dir / "digit-9.jpg").resize((28, 28)) + digit_9 = Image.open(mnist_testdata / "digit-9.jpg").resize((28, 28)) digit_9 = np.asarray(digit_9).astype("float32") digit_9 = np.expand_dims(digit_9, axis=0) # Load ONNX model and convert to Relay. - onnx_model = onnx.load(testdata_dir / "mnist-8.onnx") + onnx_model = onnx.load(mnist_testdata / "mnist-8.onnx") shape = {"Input3": (1, 1, 28, 28)} relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) relay_mod = relay.transform.DynamicToStatic()(relay_mod) diff --git a/tests/micro/arduino/test_arduino_workflow.py b/tests/micro/arduino/test_arduino_workflow.py index 18457763b9d0..101d36f9bd2d 100644 --- a/tests/micro/arduino/test_arduino_workflow.py +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -21,7 +21,6 @@ import sys import pytest -import tflite import tvm from tvm import micro, relay @@ -71,9 +70,19 @@ def project(platform, arduino_cli_cmd, tvm_debug, workspace_dir): model, arduino_board = conftest.PLATFORMS[platform] build_config = {"debug": tvm_debug} - with open(this_dir.parent / "testdata" / "yes_no.tflite", "rb") as f: + with open(this_dir.parent / "testdata" / "kws" / "yes_no.tflite", "rb") as f: tflite_model_buf = f.read() - tflite_model = tflite.Model.GetRootAsModel(tflite_model_buf, 0) + + # TFLite.Model.Model has changed to TFLite.Model from 1.14 to 2.1 + try: + import tflite.Model + + tflite_model = tflite.Model.Model.GetRootAsModel(tflite_model_buf, 0) + except AttributeError: + import tflite + + tflite_model = tflite.Model.GetRootAsModel(tflite_model_buf, 0) + mod, params = relay.frontend.from_tflite(tflite_model) target = tvm.target.target.micro( model, options=["--link-params=1", "--unpacked-api=1", "--executor=aot"] @@ -132,7 +141,7 @@ def test_import_rerouting(project_dir, project): @pytest.fixture(scope="module") def modified_project(project_dir, project): this_dir = pathlib.Path(__file__).parent - micro_testdata_dir = this_dir.parent / "testdata" + kws_testdata_dir = this_dir.parent / "testdata" / "kws" arduino_testdata_dir = this_dir / "testdata" shutil.copy2(arduino_testdata_dir / "project.ino", project_dir / "project.ino") @@ -140,7 +149,7 @@ def modified_project(project_dir, project): project_data_dir = project_dir / "src" / "data" project_data_dir.mkdir() for sample in ["yes.c", "no.c", "silence.c", "unknown.c"]: - shutil.copy2(micro_testdata_dir / sample, project_data_dir / sample) + shutil.copy2(kws_testdata_dir / sample, project_data_dir / sample) return project diff --git a/tests/micro/testdata/no.c b/tests/micro/testdata/kws/no.c similarity index 100% rename from tests/micro/testdata/no.c rename to tests/micro/testdata/kws/no.c diff --git a/tests/micro/testdata/silence.c b/tests/micro/testdata/kws/silence.c similarity index 100% rename from tests/micro/testdata/silence.c rename to tests/micro/testdata/kws/silence.c diff --git a/tests/micro/testdata/unknown.c b/tests/micro/testdata/kws/unknown.c similarity index 100% rename from tests/micro/testdata/unknown.c rename to tests/micro/testdata/kws/unknown.c diff --git a/tests/micro/testdata/yes.c b/tests/micro/testdata/kws/yes.c similarity index 100% rename from tests/micro/testdata/yes.c rename to tests/micro/testdata/kws/yes.c diff --git a/tests/micro/testdata/yes_no.tflite b/tests/micro/testdata/kws/yes_no.tflite similarity index 100% rename from tests/micro/testdata/yes_no.tflite rename to tests/micro/testdata/kws/yes_no.tflite diff --git a/tests/micro/testdata/digit-2.jpg b/tests/micro/testdata/mnist/digit-2.jpg similarity index 100% rename from tests/micro/testdata/digit-2.jpg rename to tests/micro/testdata/mnist/digit-2.jpg diff --git a/tests/micro/testdata/digit-9.jpg b/tests/micro/testdata/mnist/digit-9.jpg similarity index 100% rename from tests/micro/testdata/digit-9.jpg rename to tests/micro/testdata/mnist/digit-9.jpg diff --git a/tests/micro/testdata/mnist-8.onnx b/tests/micro/testdata/mnist/mnist-8.onnx similarity index 100% rename from tests/micro/testdata/mnist-8.onnx rename to tests/micro/testdata/mnist/mnist-8.onnx diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 3acccba0a8dd..5a7e69e3c7f9 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -230,17 +230,17 @@ def test_onnx(temp_dir, platform, west_cmd, tvm_debug): build_config = {"debug": tvm_debug} this_dir = pathlib.Path(os.path.dirname(__file__)) - testdata_dir = this_dir.parent / "testdata" - digit_2 = Image.open(testdata_dir / "digit-2.jpg").resize((28, 28)) + mnist_testdata = this_dir.parent / "testdata" / "mnist" + digit_2 = Image.open(mnist_testdata / "digit-2.jpg").resize((28, 28)) digit_2 = np.asarray(digit_2).astype("float32") digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = Image.open(testdata_dir / "digit-9.jpg").resize((28, 28)) + digit_9 = Image.open(mnist_testdata / "digit-9.jpg").resize((28, 28)) digit_9 = np.asarray(digit_9).astype("float32") digit_9 = np.expand_dims(digit_9, axis=0) # Load ONNX model and convert to Relay. - onnx_model = onnx.load(testdata_dir / "mnist-8.onnx") + onnx_model = onnx.load(mnist_testdata / "mnist-8.onnx") shape = {"Input3": (1, 1, 28, 28)} relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) relay_mod = relay.transform.DynamicToStatic()(relay_mod) diff --git a/tests/scripts/task_python_microtvm.sh b/tests/scripts/task_python_microtvm.sh index 68efc38a1260..25f9150ae9cc 100755 --- a/tests/scripts/task_python_microtvm.sh +++ b/tests/scripts/task_python_microtvm.sh @@ -25,5 +25,7 @@ source tests/scripts/setup-pytest-env.sh make cython3 run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr --microtvm-platforms=qemu_x86 run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr --microtvm-platforms=mps2_an521 -run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=due + +run_pytest ctypes python-microtvm-arduino-due apps/microtvm/arduino/template_project/tests run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=nano33ble +run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=due From 895bd38356fec6549ab4197f32fd8d6db0c12b8c Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Tue, 17 Aug 2021 11:34:03 -0700 Subject: [PATCH 11/11] Add unit tests from apps directory to task_python_microtvm.sh --- tests/scripts/task_python_microtvm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/scripts/task_python_microtvm.sh b/tests/scripts/task_python_microtvm.sh index 25f9150ae9cc..7b7758c3da24 100755 --- a/tests/scripts/task_python_microtvm.sh +++ b/tests/scripts/task_python_microtvm.sh @@ -26,6 +26,6 @@ make cython3 run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr --microtvm-platforms=qemu_x86 run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr --microtvm-platforms=mps2_an521 -run_pytest ctypes python-microtvm-arduino-due apps/microtvm/arduino/template_project/tests -run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=nano33ble +run_pytest ctypes python-microtvm-arduino apps/microtvm/arduino/template_project/tests +run_pytest ctypes python-microtvm-arduino-nano33ble tests/micro/arduino --test-build-only --microtvm-platforms=nano33ble run_pytest ctypes python-microtvm-arduino-due tests/micro/arduino --test-build-only --microtvm-platforms=due