From 281d6861b03939492e1e37422ab55aaa47be773f Mon Sep 17 00:00:00 2001 From: Gavin Uberti Date: Thu, 19 Aug 2021 00:03:26 +0100 Subject: [PATCH] [microTVM] Project API Arduino support (#8708) * ProjectAPI Arduino support * Compile and run integration tests * Add support for other Arduino boards * Unit tests for project generation * AOT support * Arduino RPC server * Incorporate ProjectAPI changes Add Arduino tests to CI * Copyright notices * Fix Andrew's PR comments * 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 * Add unit tests from apps directory to task_python_microtvm.sh --- .../arduino/example_project/project.ino | 30 ++ .../arduino/example_project/src/model.c | 94 ++++ .../arduino/example_project/src/model.h | 40 ++ .../standalone_crt/crt_config/crt_config.h | 55 ++ apps/microtvm/arduino/host_driven/project.ino | 53 ++ .../arduino/host_driven/src/model_support.c | 75 +++ .../standalone_crt/crt_config/crt_config.h | 55 ++ .../template_project/microtvm_api_server.py | 486 ++++++++++++++++++ .../tests/test_arduino_microtvm_api_server.py | 115 +++++ include/tvm/runtime/crt/packed_func.h | 4 +- python/tvm/target/target.py | 8 +- .../crt/graph_executor/graph_executor.c | 8 +- tests/lint/check_file_type.py | 10 +- tests/lint/rat-excludes | 3 + tests/micro/arduino/.gitignore | 1 + tests/micro/arduino/README.md | 35 ++ tests/micro/arduino/conftest.py | 123 +++++ .../micro/arduino/test_arduino_rpc_server.py | 368 +++++++++++++ tests/micro/arduino/test_arduino_workflow.py | 253 +++++++++ tests/micro/arduino/testdata/project.ino | 55 ++ tests/micro/testdata/kws/no.c | 128 +++++ tests/micro/testdata/kws/silence.c | 128 +++++ tests/micro/testdata/kws/unknown.c | 128 +++++ tests/micro/testdata/kws/yes.c | 128 +++++ tests/micro/testdata/kws/yes_no.tflite | Bin 0 -> 18712 bytes .../testdata => testdata/mnist}/digit-2.jpg | Bin .../testdata => testdata/mnist}/digit-9.jpg | Bin .../testdata => testdata/mnist}/mnist-8.onnx | Bin tests/micro/zephyr/test_zephyr.py | 10 +- tests/scripts/task_python_microtvm.sh | 4 + 30 files changed, 2383 insertions(+), 14 deletions(-) create mode 100644 apps/microtvm/arduino/example_project/project.ino create mode 100644 apps/microtvm/arduino/example_project/src/model.c create mode 100644 apps/microtvm/arduino/example_project/src/model.h create mode 100644 apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h create mode 100644 apps/microtvm/arduino/host_driven/project.ino create mode 100644 apps/microtvm/arduino/host_driven/src/model_support.c create mode 100644 apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h create mode 100644 apps/microtvm/arduino/template_project/microtvm_api_server.py create mode 100644 apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py create mode 100644 tests/micro/arduino/.gitignore create mode 100644 tests/micro/arduino/README.md create mode 100644 tests/micro/arduino/conftest.py create mode 100644 tests/micro/arduino/test_arduino_rpc_server.py create mode 100644 tests/micro/arduino/test_arduino_workflow.py create mode 100644 tests/micro/arduino/testdata/project.ino create mode 100644 tests/micro/testdata/kws/no.c create mode 100644 tests/micro/testdata/kws/silence.c create mode 100644 tests/micro/testdata/kws/unknown.c create mode 100644 tests/micro/testdata/kws/yes.c create mode 100644 tests/micro/testdata/kws/yes_no.tflite rename tests/micro/{zephyr/testdata => testdata/mnist}/digit-2.jpg (100%) rename tests/micro/{zephyr/testdata => testdata/mnist}/digit-9.jpg (100%) rename tests/micro/{zephyr/testdata => testdata/mnist}/mnist-8.onnx (100%) diff --git a/apps/microtvm/arduino/example_project/project.ino b/apps/microtvm/arduino/example_project/project.ino new file mode 100644 index 000000000000..5f5683161e0a --- /dev/null +++ b/apps/microtvm/arduino/example_project/project.ino @@ -0,0 +1,30 @@ +/* + * 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() { + TVMInitialize(); + // If desired, initialize the RNG with random noise + // randomSeed(analogRead(0)); +} + +void loop() { + //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 new file mode 100644 index 000000000000..77566ffc6a8f --- /dev/null +++ b/apps/microtvm/arduino/example_project/src/model.c @@ -0,0 +1,94 @@ +/* + * 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 "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) { + TVMLogf("TVMPlatformAbort: 0x%08x\n", 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 + } +} + +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_micros; +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 = 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_micros; + *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) { + int ret_val = tvmgen_default_run_model(input_data, output_data); + if (ret_val != 0) { + TVMPlatformAbort(kTvmErrorPlatformCheckFailure); + } +} 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..7381c97e9b3f --- /dev/null +++ b/apps/microtvm/arduino/example_project/src/model.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#define WORKSPACE_SIZE $workspace_size_bytes + +#ifdef __cplusplus +extern "C" { +#endif + +void TVMInitialize(); + +/* 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 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 new file mode 100644 index 000000000000..cf73103aff8b --- /dev/null +++ b/apps/microtvm/arduino/example_project/src/standalone_crt/crt_config/crt_config.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/*! + * \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/host_driven/project.ino b/apps/microtvm/arduino/host_driven/project.ino new file mode 100644 index 000000000000..d394059e1bf5 --- /dev/null +++ b/apps/microtvm/arduino/host_driven/project.ino @@ -0,0 +1,53 @@ +/* + * 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" +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) { + Serial.write(data, size); + return size; +} + +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() { + // 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_remaining = Serial.readBytes((char*) data, to_read); + uint8_t* arr_ptr = data; + 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_support.c b/apps/microtvm/arduino/host_driven/src/model_support.c new file mode 100644 index 000000000000..ae467441fede --- /dev/null +++ b/apps/microtvm/arduino/host_driven/src/model_support.c @@ -0,0 +1,75 @@ +/* + * 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 "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: 0x%08x\n", error); + for (;;) + ; +} + +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); +} + +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; +} + +tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { + free(ptr); + return kTvmErrorNoError; +} + +unsigned long g_utvm_start_time_micros; +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 = 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_micros; + *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; +} 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..cf73103aff8b --- /dev/null +++ b/apps/microtvm/arduino/host_driven/src/standalone_crt/crt_config/crt_config.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/*! + * \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 new file mode 100644 index 000000000000..91beaf558249 --- /dev/null +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -0,0 +1,486 @@ +# 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 +import logging +import os +import os.path +import pathlib +import re +import shlex +import shutil +import subprocess +import sys +import tarfile +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" +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() + + +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_dbg", + }, + # 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", + "board": "spresense", + }, + "nano33ble": { + "package": "arduino", + "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", + }, + "wioterminal": { + "package": "Seeeduino", + "architecture": "samd", + "board": "seeed_wio_terminal", + }, +} + +PROJECT_TYPES = ["example_project", "host_driven"] + +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( + "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" + ), +] + + +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, tvm_version): + return server.ServerInfo( + platform_name="arduino", + is_template=IS_TEMPLATE, + model_library_format_path=MODEL_LIBRARY_FORMAT_PATH, + project_options=PROJECT_OPTIONS, + ) + + 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] + 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", + project_dir / f"{project_dir.stem}.ino", + ) + + CRT_COPY_ITEMS = ("include", "src") + + def _copy_standalone_crt(self, source_dir, standalone_crt_dir): + 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) + + # 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, 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_str: + mlf_unpacking_dir = pathlib.Path(mlf_unpacking_dir_str) + with tarfile.open(mlf_tar_path, "r:") as tar: + tar.extractall(mlf_unpacking_dir) + + model_dir = source_dir / "model" + model_dir.mkdir() + + # 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: + 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()) + + 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][ + "workspace_size_bytes" + ], + } + + 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")) + + for filename in source_dir.rglob(f"*.inc"): + filename.rename(filename.with_suffix(".h")) + + def _convert_includes(self, project_dir, source_dir): + """Changes all #include statements in project_dir to be relevant to their + containing file's location. + + 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. + + See Also + ----- + https://www.arduino.cc/reference/en/language/structure/further-syntax/include/ + + """ + for ext in ("c", "h", "cpp"): + 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) + + # 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/"] + + 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) + + # 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) + project_dir.mkdir() + source_dir = project_dir / "src" + source_dir.mkdir() + + # 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"]) + + # Copy standalone_crt into src folder + self._copy_standalone_crt(source_dir, standalone_crt_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") + + # 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) + + # Recursively change includes + self._convert_includes(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() + + compile_cmd = [ + options["arduino_cli_cmd"], + "compile", + "./project/", + "--fqbn", + self._get_fqbn(options), + "--build-path", + BUILD_DIR.resolve(), + ] + + if options.get("verbose"): + compile_cmd.append("--verbose") + + # Specify project to compile + subprocess.run(compile_cmd) + + 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)] + + 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.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): + if line[3] == desired_fqbn: + return line[0] + + # If no compatible boards, raise an error + raise BoardAutodetectFailed() + + def _get_arduino_port(self, options): + if not self._port: + if "port" in options and options["port"]: + self._port = options["port"] + else: + self._port = self._auto_detect_port(options) + + return self._port + + def flash(self, options): + port = self._get_arduino_port(options) + + upload_cmd = [ + options["arduino_cli_cmd"], + "upload", + "./project", + "--fqbn", + self._get_fqbn(options), + "--input-dir", + BUILD_DIR.resolve(), + "--port", + port, + ] + + if options.get("verbose"): + upload_cmd.append("--verbose") + + subprocess.run(upload_cmd) + + def open_transport(self, options): + # Zephyr example doesn't throw an error in this case + if self._serial is not None: + return + + port = self._get_arduino_port(options) + + # 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) + + 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, + session_established_timeout_sec=5.0, + ) + + def close_transport(self): + if self._serial is None: + return + self._serial.close() + self._serial = None + + def read_transport(self, n, timeout_sec): + # It's hard to set timeout_sec, so we just throw it away + # TODO fix this + if self._serial is None: + raise server.TransportClosedError() + return self._serial.read(n) + + def write_transport(self, data, timeout_sec): + if self._serial is None: + raise server.TransportClosedError() + return self._serial.write(data) + + +if __name__ == "__main__": + server.main(Handler()) 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..00969a5a892b --- /dev/null +++ b/apps/microtvm/arduino/template_project/tests/test_arduino_microtvm_api_server.py @@ -0,0 +1,115 @@ +# 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 subprocess +import sys +from pathlib import Path +from unittest import mock + +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 _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/ttyACM1 Serial Port (USB) Arduino Nano 33 arduino:mbed_nano:nano33 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.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_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_run.return_value.stdout = self.BOARD_DISCONNECTED_OUTPUT + with pytest.raises(microtvm_api_server.BoardAutodetectFailed): + handler._auto_detect_port(self.DEFAULT_OPTIONS) + + # 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 command works + handler.flash(self.DEFAULT_OPTIONS) + mock_subprocess_run.assert_called_once() + + # 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/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/python/tvm/target/target.py b/python/tvm/target/target.py index 106432cd44f7..559e7b3b28d3 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -281,8 +281,14 @@ 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"], @@ -307,7 +313,7 @@ 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)): opts = _merge_opts(opts, "--system-lib") # NOTE: in the future, the default micro target will be LLVM except when diff --git a/src/runtime/crt/graph_executor/graph_executor.c b/src/runtime/crt/graph_executor/graph_executor.c index 950f3e4ef215..c2e465361651 100644 --- a/src/runtime/crt/graph_executor/graph_executor.c +++ b/src/runtime/crt/graph_executor/graph_executor.c @@ -100,8 +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 { - 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)) { @@ -130,7 +132,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 +1093,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/lint/check_file_type.py b/tests/lint/check_file_type.py index f72bd002b56e..ed7288ef00d4 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/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/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/.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/README.md b/tests/micro/arduino/README.md new file mode 100644 index 000000000000..78e63cabb7e2 --- /dev/null +++ b/tests/micro/arduino/README.md @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + +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 --microtvm-platforms spresense +``` + +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 --help +``` diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py new file mode 100644 index 000000000000..bcb2bddf2cab --- /dev/null +++ b/tests/micro/arduino/conftest.py @@ -0,0 +1,123 @@ +# 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 + +import pytest +import tvm.target.target + +# 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"), + "teensy40": ("imxrt1060", "teensy40"), + "teensy41": ("imxrt1060", "teensy41"), + "wioterminal": ("atsamd51", "wioterminal"), +} + +TEMPLATE_PROJECT_DIR = ( + pathlib.Path(__file__).parent + / ".." + / ".." + / ".." + / "apps" + / "microtvm" + / "arduino" + / "template_project" +).resolve() + + +def pytest_addoption(parser): + parser.addoption( + "--microtvm-platforms", + nargs="+", + required=True, + choices=PLATFORMS.keys(), + help="Target platforms for microTVM tests.", + ) + parser.addoption( + "--arduino-cli-cmd", + default="arduino-cli", + help="Path to `arduino-cli` command for flashing device.", + ) + parser.addoption( + "--test-build-only", + action="store_true", + help="Only run tests that don't require physical hardware.", + ) + parser.addoption( + "--tvm-debug", + action="store_true", + default=False, + 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 +def pytest_generate_tests(metafunc): + platforms = metafunc.config.getoption("microtvm_platforms") + metafunc.parametrize("platform", platforms, scope="session") + + +@pytest.fixture(scope="session") +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") + + +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 new file mode 100644 index 000000000000..1b165a02e9d1 --- /dev/null +++ b/tests/micro/arduino/test_arduino_rpc_server.py @@ -0,0 +1,368 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +""" +This 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 pathlib +import sys + +import numpy as np +import onnx +import pytest +import tvm +from PIL import Image +from tvm import micro, relay +from tvm.relay.testing import byoc + +import conftest + + +# We'll make a new workspace for each test +@pytest.fixture(scope="function") +def workspace_dir(platform): + return conftest.make_workspace_dir("arduino_rpc_server", platform) + + +def _make_session(model, arduino_board, arduino_cli_cmd, workspace_dir, mod, build_config): + project = tvm.micro.generate_project( + str(conftest.TEMPLATE_PROJECT_DIR), + mod, + workspace_dir / "project", + { + "arduino_board": arduino_board, + "arduino_cli_cmd": arduino_cli_cmd, + "project_type": "host_driven", + "verbose": bool(build_config.get("debug")), + }, + ) + project.build() + project.flash() + return tvm.micro.Session(project.transport()) + + +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, workspace_dir, mod, build_config) + + +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, 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 +@pytest.mark.requires_hardware +def test_compile_runtime(platform, arduino_cli_cmd, tvm_debug, workspace_dir): + """Test compiling the on-device runtime.""" + + 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): + 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, workspace_dir, build_config) as sess: + test_basic_add(sess) + + +@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.""" + + 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): + 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, workspace_dir, build_config) as sess: + 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] + build_config = {"debug": tvm_debug} + + 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) + + 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) + + 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 + ) + 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) + + +@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(__file__).parent + 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(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(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) + + 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, workspace_dir, lowered, build_config + ) 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, + 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, 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 + ) + 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 +@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] + 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)) + 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, + 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, 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, workspace_dir, "add", sched, [A, C], build_config + ) + + +@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 +@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] + 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): + 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, workspace_dir, shape, build_config + ) 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 new file mode 100644 index 000000000000..101d36f9bd2d --- /dev/null +++ b/tests/micro/arduino/test_arduino_workflow.py @@ -0,0 +1,253 @@ +# 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 +import shutil +import sys + +import pytest +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 +""" + + +# 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) + + +@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_dir / "project", + { + "arduino_board": arduino_board, + "arduino_cli_cmd": arduino_cli_cmd, + "project_type": "example_project", + "verbose": bool(build_config.get("debug")), + }, + ) + + +# 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): + this_dir = pathlib.Path(__file__).parent + model, arduino_board = conftest.PLATFORMS[platform] + build_config = {"debug": tvm_debug} + + with open(this_dir.parent / "testdata" / "kws" / "yes_no.tflite", "rb") as f: + tflite_model_buf = f.read() + + # 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"] + ) + + with tvm.transform.PassContext(opt_level=3, config={"tir.disable_vectorize": True}): + mod = relay.build(mod, target, params=params) + + 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( + _get_directory_elements(project_dir) + ) + + source_dir = project_dir / "src" + 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 _get_directory_elements(model_dir) == set( + ["default_lib0.c", "default_lib1.c", "model.tar"] + ) + + +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, 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" + 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 +# top-level .ino fileand adding data input files, much +# like a user would +@pytest.fixture(scope="module") +def modified_project(project_dir, project): + this_dir = pathlib.Path(__file__).parent + 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") + + project_data_dir = project_dir / "src" / "data" + project_data_dir.mkdir() + for sample in ["yes.c", "no.c", "silence.c", "unknown.c"]: + shutil.copy2(kws_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): + 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 + + +@pytest.mark.requires_hardware +def test_project_inference_correctness(serial_output): + predictions = {line[0]: line[2:] for line in serial_output} + + for sample, prediction in predictions.items(): + # Due to rounding issues, we don't get the *exact* same + # values as Tensorflow gives, but they're pretty close + + reference_prediction = TENSORFLOW_EVALUATIONS[sample] + deltas = [prediction[i] - reference_prediction[i] for i in range(4)] + assert max(deltas) < MAX_PREDICTION_DIFFERENCE + + +MAX_INFERENCE_TIME_US = 200 * 1000 +MAX_INFERENCE_TIME_RANGE_US = 1000 + + +@pytest.mark.requires_hardware +def test_project_inference_runtime(serial_output): + runtimes_us = [line[1] for line in serial_output] + + # Inference time will vary based on architecture + # and clock speed. However, anything more than 200 ms + # is way too long. Each inference takes ~60 ms on the + # Sony spresense, running at 156 MHz + assert max(runtimes_us) < MAX_INFERENCE_TIME_US + + # Clock speeds should be consistent for each input. On + # the Sony spresense, they vary by <100 us. 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 + + +if __name__ == "__main__": + sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/tests/micro/arduino/testdata/project.ino b/tests/micro/arduino/testdata/project.ino new file mode 100644 index 000000000000..ebd1c5e0e650 --- /dev/null +++ b/tests/micro/arduino/testdata/project.ino @@ -0,0 +1,55 @@ +/* + * 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" +#include "src/data/unknown.c" +#include "src/data/silence.c" + +void performInference(int8_t input_data[1960], char *data_name) { + int8_t output_data[4]; + unsigned long start_time = micros(); + TVMExecute(input_data, output_data); + unsigned long 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() { + TVMInitialize(); + Serial.begin(115200); +} + +void loop() { + Serial.println(); + Serial.println("category,runtime,yes,no,silence,unknown"); + performInference((int8_t*) input_yes, "yes"); + performInference((int8_t*) input_no, "no"); + performInference((int8_t*) input_silence, "silence"); + performInference((int8_t*) input_unknown, "unknown"); +} diff --git a/tests/micro/testdata/kws/no.c b/tests/micro/testdata/kws/no.c new file mode 100644 index 000000000000..a3bd78a5328d --- /dev/null +++ b/tests/micro/testdata/kws/no.c @@ -0,0 +1,128 @@ +/* + * 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, + 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/testdata/kws/silence.c b/tests/micro/testdata/kws/silence.c new file mode 100644 index 000000000000..bc26efa70e4f --- /dev/null +++ b/tests/micro/testdata/kws/silence.c @@ -0,0 +1,128 @@ +/* + * 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, + 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/testdata/kws/unknown.c b/tests/micro/testdata/kws/unknown.c new file mode 100644 index 000000000000..6e4df3d20b49 --- /dev/null +++ b/tests/micro/testdata/kws/unknown.c @@ -0,0 +1,128 @@ +/* + * 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, + 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/testdata/kws/yes.c b/tests/micro/testdata/kws/yes.c new file mode 100644 index 000000000000..ec18f20e46cf --- /dev/null +++ b/tests/micro/testdata/kws/yes.c @@ -0,0 +1,128 @@ +/* + * 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, + 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}; diff --git a/tests/micro/testdata/kws/yes_no.tflite b/tests/micro/testdata/kws/yes_no.tflite new file mode 100644 index 0000000000000000000000000000000000000000..4f533dac840504c65ac16ee7f6db8001d3b0d76a GIT binary patch literal 18712 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