From 6a38dc94a66e2ea6f25a45a544694d144287fceb Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 28 Jan 2021 14:41:32 -0800 Subject: [PATCH 01/53] Some docstring fixes. --- python/tvm/contrib/graph_runtime.py | 4 ++-- python/tvm/relay/build_module.py | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/python/tvm/contrib/graph_runtime.py b/python/tvm/contrib/graph_runtime.py index a960e552f68f..59db716e917c 100644 --- a/python/tvm/contrib/graph_runtime.py +++ b/python/tvm/contrib/graph_runtime.py @@ -141,11 +141,11 @@ class GraphModule(object): lib = relay.build(...) lib.export_library("compiled_lib.so") # load it back as a runtime - lib:tvm.runtime.Module = tvm.runtime.load_module("compiled_lib.so") + lib: tvm.runtime.Module = tvm.runtime.load_module("compiled_lib.so") # Call the library factory function for default and create # a new runtime.Module, wrap with graph module. gmod = graph_runtime.GraphModule(lib["default"](ctx)) - # use the gmod + # use the graph module. gmod.set_input("x", data) gmod.run() """ diff --git a/python/tvm/relay/build_module.py b/python/tvm/relay/build_module.py index 20cdc24ebc69..f05e105ed2a2 100644 --- a/python/tvm/relay/build_module.py +++ b/python/tvm/relay/build_module.py @@ -110,14 +110,8 @@ def build(self, mod, target=None, target_host=None, params=None): Returns ------- - graph_json : str - The json string that can be accepted by graph runtime. - - mod : tvm.Module - The module containing necessary libraries. - - params : dict - The parameters of the final graph. + factory_module : tvm.relay.backend.graph_runtime_factory.GraphRuntimeFactoryModule + The runtime factory for the TVM graph runtime. """ target = _update_target(target) From 82cee46c415e51f3a5f880b84c5088c9851eaae9 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 08:48:01 -0800 Subject: [PATCH 02/53] Couple of small fixes: - Use `west attach` instead of `west debug` in commandline to prevent debugger from resetting device. - Fix warning on use of led_pin in zephyr-runtime/src/main.c. --- python/tvm/micro/contrib/zephyr.py | 5 +++-- tests/micro/qemu/zephyr-runtime/src/main.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/tvm/micro/contrib/zephyr.py b/python/tvm/micro/contrib/zephyr.py index ed1c9866c741..f70bfc9414b7 100644 --- a/python/tvm/micro/contrib/zephyr.py +++ b/python/tvm/micro/contrib/zephyr.py @@ -629,10 +629,10 @@ def popen_kwargs(self): env = dict(os.environ) env["ZEPHYR_BASE"] = self._zephyr_base - return dict( + args = dict( args=self._west_cmd + [ - "debug", + "attach", "--skip-rebuild", "--build-dir", self._build_dir, @@ -641,3 +641,4 @@ def popen_kwargs(self): ], env=env, ) + return args diff --git a/tests/micro/qemu/zephyr-runtime/src/main.c b/tests/micro/qemu/zephyr-runtime/src/main.c index e04fc20508b4..8a3879f5113a 100644 --- a/tests/micro/qemu/zephyr-runtime/src/main.c +++ b/tests/micro/qemu/zephyr-runtime/src/main.c @@ -97,7 +97,7 @@ int g_utvm_timer_running = 0; #define PIN DT_GPIO_PIN(LED0_NODE, gpios) #define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) -static struct device* led_pin; +static const struct device* led_pin; #endif // CONFIG_LED tvm_crt_error_t TVMPlatformTimerStart() { From 545a241cb9bfbbfc8cfbc39e9b94c7f89a80371a Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 10:08:48 -0800 Subject: [PATCH 03/53] Adding Zephyr demo runtime. --- src/runtime/micro/zephyr/README.md | 19 ++ .../micro/zephyr/demo_runtime/CMakeLists.txt | 21 ++ .../micro/zephyr/demo_runtime/README.md | 21 ++ .../zephyr/demo_runtime/crt/crt_config.h | 64 ++++ .../micro/zephyr/demo_runtime/prj.conf | 46 +++ .../micro/zephyr/demo_runtime/src/main.c | 321 ++++++++++++++++++ 6 files changed, 492 insertions(+) create mode 100644 src/runtime/micro/zephyr/README.md create mode 100644 src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt create mode 100644 src/runtime/micro/zephyr/demo_runtime/README.md create mode 100644 src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h create mode 100644 src/runtime/micro/zephyr/demo_runtime/prj.conf create mode 100644 src/runtime/micro/zephyr/demo_runtime/src/main.c diff --git a/src/runtime/micro/zephyr/README.md b/src/runtime/micro/zephyr/README.md new file mode 100644 index 000000000000..52dd8829669e --- /dev/null +++ b/src/runtime/micro/zephyr/README.md @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + +This directory code to interface uTVM with the [Zephyr RTOS](https://zephyrproject.org/). + diff --git a/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt b/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt new file mode 100644 index 000000000000..61011a44678e --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(microtvm_zephyr_demo_runtime) + +set(CMAKE_VERBOSE_MAKEFILE ON) +file(GLOB TVM_SOURCES ${CMAKE_SOURCE_DIR}/__tvm*.c) +target_sources(app PRIVATE src/main.c ${TVM_SOURCES}) + +foreach(tvm_lib ${TVM_LIBS}) + string(LENGTH ${tvm_lib} tvm_lib_length) + math(EXPR tvm_lib_cut "${tvm_lib_length} - 2") + string(SUBSTRING ${tvm_lib} 3 ${tvm_lib_cut} tvm_lib_name) + add_library(${tvm_lib_name} STATIC IMPORTED) + set_target_properties(${tvm_lib_name} PROPERTIES + IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/${tvm_lib}) + target_link_libraries(app PRIVATE ${tvm_lib_name}) +endforeach(tvm_lib ${TVM_LIBS}) + +target_include_directories(app PRIVATE ${TVM_INCLUDE_DIRS}) diff --git a/src/runtime/micro/zephyr/demo_runtime/README.md b/src/runtime/micro/zephyr/demo_runtime/README.md new file mode 100644 index 000000000000..e4dc92409ce5 --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/README.md @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + +This directory contains a Zephyr-based "demo" runtime environment that +pulls together the uTVM runtime dependencies into a single application +that can communicate with a Python-based host program via the UART, using +TVM's RPC protocol. diff --git a/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h b/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h new file mode 100644 index 000000000000..a7f4f90b0538 --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \file tvm/runtime/crt_config.h.template + * \brief Template for CRT configuration, to be modified on each target. + */ +#ifndef TVM_RUNTIME_CRT_CONFIG_H_ +#define TVM_RUNTIME_CRT_CONFIG_H_ + +#include + +/*! Log level of the CRT runtime */ +#define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG + +/*! Maximum supported dimension in NDArray */ +#define TVM_CRT_MAX_NDIM 6 + +/*! Maximum supported arguments in generated functions */ +#define TVM_CRT_MAX_ARGS 10 + +/*! Size of the global function registry, in bytes. */ +#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 200 + +/*! Maximum number of registered modules. */ +#define TVM_CRT_MAX_REGISTERED_MODULES 2 + +/*! Maximum packet size, in bytes, including the length header. */ +#define TVM_CRT_MAX_PACKET_SIZE_BYTES 8192 + +/*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ +#define TVM_CRT_MAX_STRLEN_DLTYPE 10 + +/*! Maximum supported string length in function names */ +#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 + +/*! \brief Maximum length of a PackedFunc function name. */ +#define TVM_CRT_MAX_FUNCTION_NAME_LENGTH_BYTES 30 + +/*! \brief Log2 of the page size (bytes) for a virtual memory page. */ +#define TVM_CRT_PAGE_BITS 10 // 1 kB + +/*! \brief Number of pages on device. */ +#define TVM_CRT_MAX_PAGES 300 + +//#define TVM_CRT_FRAMER_ENABLE_LOGS + +#endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/src/runtime/micro/zephyr/demo_runtime/prj.conf b/src/runtime/micro/zephyr/demo_runtime/prj.conf new file mode 100644 index 000000000000..33bff7f12a58 --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/prj.conf @@ -0,0 +1,46 @@ +# 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. + +# For UART implementation in main(). +CONFIG_RING_BUFFER=y +CONFIG_UART_CONSOLE=n +CONFIG_UART_INTERRUPT_DRIVEN=y + +# For RPC server C++ bindings. +CONFIG_CPLUSPLUS=y +CONFIG_NEWLIB_LIBC=y + +# For models with floating point. +CONFIG_FPU=y + +# For TVMPlatformAbort(). +CONFIG_REBOOT=y + +# For intrinsics used by generated optimized operators. +CONFIG_CMSIS_DSP=y + +# Required for Cortex-M33 devices. +CONFIG_MAIN_STACK_SIZE=1536 + +# For random number generation. +CONFIG_ENTROPY_GENERATOR=y + +# Allow for k_sys_fatal_error_handler to be overridden. +CONFIG_RESET_ON_FATAL_ERROR=n + +# For debugging. +CONFIG_LED=y diff --git a/src/runtime/micro/zephyr/demo_runtime/src/main.c b/src/runtime/micro/zephyr/demo_runtime/src/main.c new file mode 100644 index 000000000000..053e2386238e --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/src/main.c @@ -0,0 +1,321 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/* + * This is a sample Zephyr-based application that contains the logic + * needed to upload and control a uTVM-based model via the UART. + * This is only intended to be a demonstration, since typically you + * will want to incorporate this logic into your own application. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_POSIX +#include "posix_board_if.h" +#endif + +#include "crt_config.h" + +//K_SEM_DEFINE(tx_sem, 0, 1); // XXX MDW NEEDED? + +static const struct device* tvm_uart; + +// XXX MDW needed? +//int write_hook(int c) { +// uart_poll_out(tvm_uart, c); +// return 0; +//} + +#ifdef CONFIG_LED +/* The devicetree node identifier for the "led0" alias. */ +#define LED0_NODE DT_ALIAS(led0) +#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) +#define PIN DT_GPIO_PIN(LED0_NODE, gpios) +#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) +static const struct device* led_pin; +#endif // CONFIG_LED + +static size_t g_num_bytes_requested = 0; +static size_t g_num_bytes_written = 0; + +static const uint8_t* g_transmit_data = NULL; +static size_t g_transmit_data_size = 0; +static volatile size_t g_transmitted_bytes = 0; +static volatile bool g_transmit_complete = true; + +ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 1); +#endif + g_num_bytes_requested += size; + + for (size_t i = 0; i < size; i++) { + uart_poll_out(tvm_uart, data[i]); + g_num_bytes_written++; + } + +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 0); +#endif + + return size; +} + +// This is an error handler which will be invoked if the device crashes. +// Here, we turn on the LED and spin. +void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 1); +#endif + for (;;) ; +} + +// Used by TVM when a message needs to be formatted. +size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, + const char* fmt, va_list args) { + return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); +} + +// Used by TVM when an abort operation occurs. +void TVMPlatformAbort(tvm_crt_error_t error) { + sys_reboot(SYS_REBOOT_COLD); + for (;;) ; +} + +uint32_t g_utvm_start_time; + +#define MILLIS_TIL_EXPIRY 200 +#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY)) +K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); + +int g_utvm_timer_running = 0; + +// Used to start system timer. +tvm_crt_error_t TVMPlatformTimerStart() { + if (g_utvm_timer_running) { + TVMLogf("timer already running"); + return kTvmErrorSystemErrorMask | 1; + } + +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 1); +#endif + k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); + g_utvm_start_time = k_cycle_get_32(); + g_utvm_timer_running = 1; + return kTvmErrorNoError; +} + +// Used to stop system timer. +tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { + if (!g_utvm_timer_running) { + TVMLogf("timer not running"); + return kTvmErrorSystemErrorMask | 2; + } + + uint32_t stop_time = k_cycle_get_32(); +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 0); +#endif + + // compute how long the work took + uint32_t cycles_spent = stop_time - g_utvm_start_time; + if (stop_time < g_utvm_start_time) { + // we rolled over *at least* once, so correct the rollover it was *only* + // once, because we might still use this result + cycles_spent = ~((uint32_t)0) - (g_utvm_start_time - stop_time); + } + + uint32_t ns_spent = (uint32_t)k_cyc_to_ns_floor64(cycles_spent); + double hw_clock_res_us = ns_spent / 1000.0; + + // need to grab time remaining *before* stopping. when stopped, this function + // always returns 0. + int32_t time_remaining_ms = k_timer_remaining_get(&g_utvm_timer); + k_timer_stop(&g_utvm_timer); + // check *after* stopping to prevent extra expiries on the happy path + if (time_remaining_ms < 0) { + TVMLogf("negative time remaining"); + return kTvmErrorSystemErrorMask | 3; + } + uint32_t num_expiries = k_timer_status_get(&g_utvm_timer); + uint32_t timer_res_ms = ((num_expiries * MILLIS_TIL_EXPIRY) + time_remaining_ms); + double approx_num_cycles = + (double)k_ticks_to_cyc_floor32(1) * (double)k_ms_to_ticks_ceil32(timer_res_ms); + // if we approach the limits of the HW clock datatype (uint32_t), use the + // coarse-grained timer result instead + if (approx_num_cycles > (0.5 * (~((uint32_t)0)))) { + *res_us = timer_res_ms * 1000.0; + } else { + *res_us = hw_clock_res_us; + } + + g_utvm_timer_running = 0; + return kTvmErrorNoError; +} + +// Memory pool for use by TVMPlatformMemoryAllocate. +K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); + +tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { + *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); + return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; +} + +tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { + k_free(ptr); + return kTvmErrorNoError; +} + +#define RING_BUF_SIZE 512 +struct uart_rx_buf_t { + struct ring_buf buf; + uint32_t buffer[RING_BUF_SIZE]; +}; + +struct uart_rx_buf_t uart_rx_buf; + +void uart_irq_cb(const struct device* dev, void* user_data) { + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; + if (uart_irq_rx_ready(dev) != 0) { + uint8_t data[32]; + for (;;) { + int bytes_read = uart_fifo_read(dev, data, sizeof(data)); + if (bytes_read < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef1); + } else if (bytes_read == 0) { + break; + } + int bytes_written = ring_buf_put(&buf->buf, data, bytes_read); + CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, + bytes_written); + } + } + } +} + +void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { + ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); + uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); + uart_irq_rx_enable(dev); +} + +int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { + unsigned int key = irq_lock(); + int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); + irq_unlock(key); + return bytes_read; +} + +extern void __stdout_hook_install(int (*hook)(int)); +void main(void) { +#ifdef CONFIG_LED + led_pin = device_get_binding(LED0); + if (led_pin == NULL) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef2); + } + int ret = gpio_pin_configure(led_pin, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef3); + } + gpio_pin_set(led_pin, PIN, 1); +#endif + + /* Claim console device. */ + tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); + const struct device* shadow_tvm_uart = tvm_uart; + uart_rx_init(&uart_rx_buf, tvm_uart); + + utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); + TVMLogf("uTVM Zephyr runtime - running"); +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 0); +#endif + + + // The main application loop. We continuously read commands from the UART + // and dispatch them to UTvmRpcServerLoop(). + while (true) { + uint8_t buf[256]; + int bytes_read = uart_rx_buf_read(&uart_rx_buf, buf, sizeof(buf)); + if (bytes_read > 0) { + size_t bytes_remaining = bytes_read; + uint8_t* cursor = buf; + while (bytes_remaining > 0) { + tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); + if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { + TVMPlatformAbort(err); + } + if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { + if (g_num_bytes_written != g_num_bytes_requested) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); + } + g_num_bytes_written = 0; + g_num_bytes_requested = 0; + } + } + } + } + +#ifdef CONFIG_ARCH_POSIX + posix_exit(0); +#endif +} + +// Used by TVM to generate random data. +tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { + uint32_t random; // one unit of random data. + + // Fill parts of `buffer` which are as large as `random`. + size_t num_full_blocks = num_bytes / sizeof(random); + for (int i = 0; i < num_full_blocks; ++i) { + random = sys_rand32_get(); + memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); + } + + // Fill any leftover tail which is smaller than `random`. + size_t num_tail_bytes = num_bytes % sizeof(random); + if (num_tail_bytes > 0) { + random = sys_rand32_get(); + memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); + } + + return kTvmErrorNoError; +} + From c611d3099ef943114f042d1474aabb7095e52c09 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 11:16:51 -0800 Subject: [PATCH 04/53] Cleanup of uTVM tests and demo runtime. --- apps/microtvm/README.md | 17 +- .../{reference-vm/zephyr => }/pyproject.toml | 9 +- .../micro => apps/microtvm}/zephyr/README.md | 0 .../zephyr/demo_runtime/CMakeLists.txt | 0 .../microtvm}/zephyr/demo_runtime/README.md | 0 .../zephyr/demo_runtime/crt/crt_config.h | 0 .../microtvm}/zephyr/demo_runtime/prj.conf | 0 .../microtvm}/zephyr/demo_runtime/src/main.c | 0 tests/micro/qemu/.gitignore | 2 - tests/micro/qemu/zephyr-runtime/.gitignore | 3 - .../micro/qemu/zephyr-runtime/CMakeLists.txt | 27 -- .../qemu/zephyr-runtime/crt/crt_config.h | 64 ----- tests/micro/qemu/zephyr-runtime/prj.conf | 35 --- .../zephyr-runtime/qemu-hack/qemu-system-i386 | 33 --- tests/micro/qemu/zephyr-runtime/sample.yaml | 22 -- tests/micro/qemu/zephyr-runtime/src/main.c | 270 ------------------ tests/micro/zephyr/README.md | 35 +++ tests/micro/{qemu => zephyr}/conftest.py | 0 tests/micro/{qemu => zephyr}/test_zephyr.py | 8 +- 19 files changed, 56 insertions(+), 469 deletions(-) rename apps/microtvm/{reference-vm/zephyr => }/pyproject.toml (94%) rename {src/runtime/micro => apps/microtvm}/zephyr/README.md (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/CMakeLists.txt (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/README.md (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/crt/crt_config.h (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/prj.conf (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/src/main.c (100%) delete mode 100644 tests/micro/qemu/.gitignore delete mode 100644 tests/micro/qemu/zephyr-runtime/.gitignore delete mode 100644 tests/micro/qemu/zephyr-runtime/CMakeLists.txt delete mode 100644 tests/micro/qemu/zephyr-runtime/crt/crt_config.h delete mode 100644 tests/micro/qemu/zephyr-runtime/prj.conf delete mode 100755 tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 delete mode 100644 tests/micro/qemu/zephyr-runtime/sample.yaml delete mode 100644 tests/micro/qemu/zephyr-runtime/src/main.c create mode 100644 tests/micro/zephyr/README.md rename tests/micro/{qemu => zephyr}/conftest.py (100%) rename tests/micro/{qemu => zephyr}/test_zephyr.py (97%) diff --git a/apps/microtvm/README.md b/apps/microtvm/README.md index 97b844a4c01b..9cbb16ac00ed 100644 --- a/apps/microtvm/README.md +++ b/apps/microtvm/README.md @@ -15,14 +15,17 @@ -# microTVM Reference Virtual Machines +# microTVM +microTVM is the effort that allows TVM to build and execute models on bare-metal microcontrollers. -microTVM is the effort to allow TVM to build and execute models on bare-metal microcontrollers. -These Virtual Machines are used to reproduce results and bugs when using microTVM with real -physical hardware. Note that they are not used to run Continuous Integration regression tests-- -those are instead run by the QEMU container (they run against an emulator, rather than real -hardware). +The `pyproject.toml` file in this directory can be used to create a +[Poetry](https://python-poetry.org/) Python environment with all of the required +dependencies installed. To use it, run: + +``` +$ poetry lock && poetry install +$ poetry shell +``` -See the "microTVM Reference Virtual Machines" tutorial for information on how to use these. diff --git a/apps/microtvm/reference-vm/zephyr/pyproject.toml b/apps/microtvm/pyproject.toml similarity index 94% rename from apps/microtvm/reference-vm/zephyr/pyproject.toml rename to apps/microtvm/pyproject.toml index b4cfc544df58..096736ae52aa 100644 --- a/apps/microtvm/reference-vm/zephyr/pyproject.toml +++ b/apps/microtvm/pyproject.toml @@ -15,6 +15,9 @@ # specific language governing permissions and limitations # under the License. +# This `pyproject.toml` file is used to allow MicroTVM +# to run within a Poetry-managed environment. + [tool.black] line-length = 100 target-version = ['py36'] @@ -47,12 +50,12 @@ exclude = ''' ) ''' [tool.poetry] -name = "tvm" +name = "microtvm" version = "0.1.0" description = "" -authors = ["Your Name "] +authors = ["Andrew Reusch "] packages = [ - { include = "tvm", from = "../../../../python" }, + { include = "tvm", from = "../../python" }, ] [tool.poetry.dependencies] diff --git a/src/runtime/micro/zephyr/README.md b/apps/microtvm/zephyr/README.md similarity index 100% rename from src/runtime/micro/zephyr/README.md rename to apps/microtvm/zephyr/README.md diff --git a/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt b/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt rename to apps/microtvm/zephyr/demo_runtime/CMakeLists.txt diff --git a/src/runtime/micro/zephyr/demo_runtime/README.md b/apps/microtvm/zephyr/demo_runtime/README.md similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/README.md rename to apps/microtvm/zephyr/demo_runtime/README.md diff --git a/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h b/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h rename to apps/microtvm/zephyr/demo_runtime/crt/crt_config.h diff --git a/src/runtime/micro/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/prj.conf rename to apps/microtvm/zephyr/demo_runtime/prj.conf diff --git a/src/runtime/micro/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/src/main.c rename to apps/microtvm/zephyr/demo_runtime/src/main.c diff --git a/tests/micro/qemu/.gitignore b/tests/micro/qemu/.gitignore deleted file mode 100644 index c920d8f93ff8..000000000000 --- a/tests/micro/qemu/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/test_zephyr*-workspace -/*.micro-binary diff --git a/tests/micro/qemu/zephyr-runtime/.gitignore b/tests/micro/qemu/zephyr-runtime/.gitignore deleted file mode 100644 index 64be5d3a487c..000000000000 --- a/tests/micro/qemu/zephyr-runtime/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -__tvm* -libtvm__* -/build diff --git a/tests/micro/qemu/zephyr-runtime/CMakeLists.txt b/tests/micro/qemu/zephyr-runtime/CMakeLists.txt deleted file mode 100644 index ce5605469fcb..000000000000 --- a/tests/micro/qemu/zephyr-runtime/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.13.1) - -set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") - -set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. - -find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) -project(microtvm_zephyr_runtime) - - -set(CMAKE_VERBOSE_MAKEFILE ON) -file(GLOB TVM_SOURCES ${CMAKE_SOURCE_DIR}/__tvm*.c) -target_sources(app PRIVATE src/main.c ${TVM_SOURCES}) - -foreach(tvm_lib ${TVM_LIBS}) - string(LENGTH ${tvm_lib} tvm_lib_length) - math(EXPR tvm_lib_cut "${tvm_lib_length} - 2") - string(SUBSTRING ${tvm_lib} 3 ${tvm_lib_cut} tvm_lib_name) - add_library(${tvm_lib_name} STATIC IMPORTED) - set_target_properties(${tvm_lib_name} PROPERTIES - IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/${tvm_lib}) - target_link_libraries(app PRIVATE ${tvm_lib_name}) -endforeach(tvm_lib ${TVM_LIBS}) - -target_include_directories(app PRIVATE ${TVM_INCLUDE_DIRS}) diff --git a/tests/micro/qemu/zephyr-runtime/crt/crt_config.h b/tests/micro/qemu/zephyr-runtime/crt/crt_config.h deleted file mode 100644 index a7f4f90b0538..000000000000 --- a/tests/micro/qemu/zephyr-runtime/crt/crt_config.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/*! - * \file tvm/runtime/crt_config.h.template - * \brief Template for CRT configuration, to be modified on each target. - */ -#ifndef TVM_RUNTIME_CRT_CONFIG_H_ -#define TVM_RUNTIME_CRT_CONFIG_H_ - -#include - -/*! Log level of the CRT runtime */ -#define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG - -/*! Maximum supported dimension in NDArray */ -#define TVM_CRT_MAX_NDIM 6 - -/*! Maximum supported arguments in generated functions */ -#define TVM_CRT_MAX_ARGS 10 - -/*! Size of the global function registry, in bytes. */ -#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 200 - -/*! Maximum number of registered modules. */ -#define TVM_CRT_MAX_REGISTERED_MODULES 2 - -/*! Maximum packet size, in bytes, including the length header. */ -#define TVM_CRT_MAX_PACKET_SIZE_BYTES 8192 - -/*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ -#define TVM_CRT_MAX_STRLEN_DLTYPE 10 - -/*! Maximum supported string length in function names */ -#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 - -/*! \brief Maximum length of a PackedFunc function name. */ -#define TVM_CRT_MAX_FUNCTION_NAME_LENGTH_BYTES 30 - -/*! \brief Log2 of the page size (bytes) for a virtual memory page. */ -#define TVM_CRT_PAGE_BITS 10 // 1 kB - -/*! \brief Number of pages on device. */ -#define TVM_CRT_MAX_PAGES 300 - -//#define TVM_CRT_FRAMER_ENABLE_LOGS - -#endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/tests/micro/qemu/zephyr-runtime/prj.conf b/tests/micro/qemu/zephyr-runtime/prj.conf deleted file mode 100644 index 7be42b260bbb..000000000000 --- a/tests/micro/qemu/zephyr-runtime/prj.conf +++ /dev/null @@ -1,35 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# For UART implementation in main(). -CONFIG_RING_BUFFER=y -CONFIG_UART_CONSOLE=n -CONFIG_UART_INTERRUPT_DRIVEN=y - -# For RPC server C++ bindings. -CONFIG_CPLUSPLUS=y -CONFIG_NEWLIB_LIBC=y - -# For models with floating point. -CONFIG_FPU=y - -# For TVMPlatformAbort(). -CONFIG_REBOOT=y - -# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_TIMER_RANDOM_GENERATOR=y diff --git a/tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 b/tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 deleted file mode 100755 index a0bf0f2c4dee..000000000000 --- a/tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -e -# 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. - -# Zephyr insists on running qemu with a -pidfile option, but that option doesn't appear to -# work given the way we've configured docker (the underlying filesystem doesn't suppor the -# file locking it needs to). This script strips any -pidfile option, then invokes qemu. - -ARGS=( "$(basename $0)" ) -while [ "$#" -gt 0 ]; do - if [ "$1" == "-pidfile" ]; then - shift - else - ARGS=( "${ARGS[@]}" "$1" ) - fi - shift -done - -"${ARGS[@]}" diff --git a/tests/micro/qemu/zephyr-runtime/sample.yaml b/tests/micro/qemu/zephyr-runtime/sample.yaml deleted file mode 100644 index 88616b4acc40..000000000000 --- a/tests/micro/qemu/zephyr-runtime/sample.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -sample: - description: uTVM RPC Server unit test - name: utvm rpc server -common: - tags: introduction diff --git a/tests/micro/qemu/zephyr-runtime/src/main.c b/tests/micro/qemu/zephyr-runtime/src/main.c deleted file mode 100644 index 8a3879f5113a..000000000000 --- a/tests/micro/qemu/zephyr-runtime/src/main.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_ARCH_POSIX -#include "posix_board_if.h" -#endif - -#include "crt_config.h" - -K_SEM_DEFINE(tx_sem, 0, 1); - -static const struct device* tvm_uart; - -int write_hook(int c) { - uart_poll_out(tvm_uart, c); - return 0; -} - -ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { - for (size_t i = 0; i < size; i++) { - uart_poll_out(tvm_uart, data[i]); - } - - return size; -} - -size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, - va_list args) { - return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); -} - -void TVMPlatformAbort(tvm_crt_error_t error) { - sys_reboot(SYS_REBOOT_COLD); - for (;;) - ; -} - -K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 120, 4); - -tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { - *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); - return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; -} - -tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { - k_free(ptr); - return kTvmErrorNoError; -} - -uint32_t g_utvm_start_time; - -#define MILLIS_TIL_EXPIRY 200 -#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY)) -K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); - -int g_utvm_timer_running = 0; - -#ifdef CONFIG_LED -/* The devicetree node identifier for the "led0" alias. */ -#define LED0_NODE DT_ALIAS(led0) - -#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) -#define PIN DT_GPIO_PIN(LED0_NODE, gpios) -#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) - -static const struct device* led_pin; -#endif // CONFIG_LED - -tvm_crt_error_t TVMPlatformTimerStart() { - if (g_utvm_timer_running) { - TVMLogf("timer already running"); - return kTvmErrorPlatformTimerBadState; - } - -#ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); -#endif - k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); - g_utvm_start_time = k_cycle_get_32(); - g_utvm_timer_running = 1; - return kTvmErrorNoError; -} - -tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { - if (!g_utvm_timer_running) { - TVMLogf("timer not running"); - return kTvmErrorPlatformTimerBadState; - } - - uint32_t stop_time = k_cycle_get_32(); -#ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); -#endif - - // compute how long the work took - uint32_t cycles_spent = stop_time - g_utvm_start_time; - if (stop_time < g_utvm_start_time) { - // we rolled over *at least* once, so correct the rollover it was *only* - // once, because we might still use this result - cycles_spent = ~((uint32_t)0) - (g_utvm_start_time - stop_time); - } - - uint32_t ns_spent = (uint32_t)k_cyc_to_ns_floor64(cycles_spent); - double hw_clock_elapsed_seconds = ns_spent / 1e9; - - // need to grab time remaining *before* stopping. when stopped, this function - // always returns 0. - int32_t time_remaining_ms = k_timer_remaining_get(&g_utvm_timer); - k_timer_stop(&g_utvm_timer); - // check *after* stopping to prevent extra expiries on the happy path - if (time_remaining_ms < 0) { - TVMLogf("negative time remaining"); - return -1; - } - uint32_t num_expiries = k_timer_status_get(&g_utvm_timer); - uint32_t timer_res_ms = ((num_expiries * MILLIS_TIL_EXPIRY) + time_remaining_ms); - double approx_num_cycles = - (double)k_ticks_to_cyc_floor32(1) * (double)k_ms_to_ticks_ceil32(timer_res_ms); - // if we approach the limits of the HW clock datatype (uint32_t), use the - // coarse-grained timer result instead - if (approx_num_cycles > (0.5 * (~((uint32_t)0)))) { - *elapsed_time_seconds = timer_res_ms / 1e3; - } else { - *elapsed_time_seconds = hw_clock_elapsed_seconds; - } - - g_utvm_timer_running = 0; - return kTvmErrorNoError; -} - -tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { - uint32_t random; // one unit of random data. - - // Fill parts of `buffer` which are as large as `random`. - size_t num_full_blocks = num_bytes / sizeof(random); - for (int i = 0; i < num_full_blocks; ++i) { - random = sys_rand32_get(); - memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); - } - - // Fill any leftover tail which is smaller than `random`. - size_t num_tail_bytes = num_bytes % sizeof(random); - if (num_tail_bytes > 0) { - random = sys_rand32_get(); - memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); - } - - return kTvmErrorNoError; -} - -#define RING_BUF_SIZE 512 -struct uart_rx_buf_t { - struct ring_buf buf; - uint32_t buffer[RING_BUF_SIZE]; -}; - -struct uart_rx_buf_t uart_rx_buf; - -void uart_irq_cb(const struct device* dev, void* user_data) { - while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { - struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; - if (uart_irq_rx_ready(dev) == 0) { - continue; - } - - uint8_t data[32]; - for (;;) { - int bytes_read = uart_fifo_read(dev, data, sizeof(data)); - if (bytes_read < 0) { - TVMPlatformAbort(0xbeef); - } else if (bytes_read == 0) { - break; - } - int bytes_written = ring_buf_put(&buf->buf, data, bytes_read); - CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, - bytes_written); - } - } -} - -void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { - ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); - uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); - uart_irq_rx_enable(dev); -} - -int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { - unsigned int key = irq_lock(); - int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); - irq_unlock(key); - return bytes_read; -} - -extern void __stdout_hook_install(int (*hook)(int)); -void main(void) { -#ifdef CONFIG_LED - led_pin = device_get_binding(LED0); - if (led_pin == NULL) { - for (;;) - ; - } - int ret = gpio_pin_configure(led_pin, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); - if (ret < 0) { - for (;;) - ; - } - gpio_pin_set(led_pin, PIN, 0); -#endif - - /* Claim console device */ - tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); - uart_rx_init(&uart_rx_buf, tvm_uart); - __stdout_hook_install(&write_hook); - - utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); - TVMLogf("uTVM On-Device Runtime"); - - while (true) { - uint8_t buf[256]; - int bytes_read = uart_rx_buf_read(&uart_rx_buf, buf, sizeof(buf)); - if (bytes_read > 0) { - size_t bytes_remaining = bytes_read; - uint8_t* cursor = buf; - while (bytes_remaining > 0) { - tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); - if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { - TVMPlatformAbort(err); - } - } - } - } - -#ifdef CONFIG_ARCH_POSIX - posix_exit(0); -#endif -} diff --git a/tests/micro/zephyr/README.md b/tests/micro/zephyr/README.md new file mode 100644 index 000000000000..4a8b4049ea4b --- /dev/null +++ b/tests/micro/zephyr/README.md @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + +This directory contains units tests for MicroTVM integration with Zephyr. + +To run the test, you first need to be running in a Python environment with +all of the appropriate TVM dependencies installed. If you have [Poetry](https://python-poetry.org/) +installed, you can do the following to get an appropriately-configured Python +environment: + +``` +$ cd tvm/apps/microtvm/ +$ poetry lock && poetry install && poetry shell +``` + +You can then run this test using: + +``` +$ cd tvm/tests/micro/zephyr +$ pytest test_zephyr.py --microtvm-platforms=nrf5340dk +``` diff --git a/tests/micro/qemu/conftest.py b/tests/micro/zephyr/conftest.py similarity index 100% rename from tests/micro/qemu/conftest.py rename to tests/micro/zephyr/conftest.py diff --git a/tests/micro/qemu/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py similarity index 97% rename from tests/micro/qemu/test_zephyr.py rename to tests/micro/zephyr/test_zephyr.py index 865c7f88806f..a1a847aa5ce2 100644 --- a/tests/micro/qemu/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -62,15 +62,17 @@ def _make_session(model, target, zephyr_board, west_cmd, mod): os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) - project_dir = os.path.join(os.path.dirname(__file__) or ".", "zephyr-runtime") + test_dir = os.path.dirname(os.path.realpath(os.path.expanduser(__file__))) + tvm_source_dir = os.path.join(test_dir, "..", "..", "..") + runtime_path = os.path.join(tvm_source_dir, "apps", "microtvm", "zephyr", "demo_runtime") compiler = zephyr.ZephyrCompiler( - project_dir=project_dir, + project_dir=runtime_path, board=zephyr_board, zephyr_toolchain_variant="zephyr", west_cmd=west_cmd, ) - opts = tvm.micro.default_options(f"{project_dir}/crt") + opts = tvm.micro.default_options(os.path.join(runtime_path, "crt")) # TODO(weberlo) verify this is necessary opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] opts["lib_opts"]["ccflags"] = ["-std=gnu++14"] From 28b92de4a5e29a2ffb66f2f90ca468583193a6d5 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 13:44:01 -0800 Subject: [PATCH 05/53] Working on QEMU support. Need to add board-specific prj.conf files. --- apps/microtvm/pyproject.toml | 2 ++ .../zephyr/demo_runtime/CMakeLists.txt | 7 +++++- apps/microtvm/zephyr/demo_runtime/prj.conf | 23 +++++++++++++++++++ docs/microtvm/index.rst | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/microtvm/pyproject.toml b/apps/microtvm/pyproject.toml index 096736ae52aa..52de9c265661 100644 --- a/apps/microtvm/pyproject.toml +++ b/apps/microtvm/pyproject.toml @@ -69,6 +69,8 @@ tornado = "^6" typed_ast = "^1.4" pyyaml = "^5.4.1" pyserial = "^3.5" +pyelftools = "^0.27" + # AutoTVM diff --git a/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt b/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt index 61011a44678e..a99d5edb07e6 100644 --- a/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt +++ b/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt @@ -1,8 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.13.1) + +set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") + +set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. + find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) -project(microtvm_zephyr_demo_runtime) +project(microtvm_zephyr_runtime) set(CMAKE_VERBOSE_MAKEFILE ON) file(GLOB TVM_SOURCES ${CMAKE_SOURCE_DIR}/__tvm*.c) diff --git a/apps/microtvm/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf index 33bff7f12a58..2cb4f3828cb3 100644 --- a/apps/microtvm/zephyr/demo_runtime/prj.conf +++ b/apps/microtvm/zephyr/demo_runtime/prj.conf @@ -14,6 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +# +# For nRF5340 +# XXX MDW - Put these in a board-specific conf file. # For UART implementation in main(). CONFIG_RING_BUFFER=y @@ -44,3 +47,23 @@ CONFIG_RESET_ON_FATAL_ERROR=n # For debugging. CONFIG_LED=y + +# For QEMU +## For UART implementation in main(). +#CONFIG_RING_BUFFER=y +#CONFIG_UART_CONSOLE=n +#CONFIG_UART_INTERRUPT_DRIVEN=y +# +## For RPC server C++ bindings. +#CONFIG_CPLUSPLUS=y +#CONFIG_NEWLIB_LIBC=y +# +## For models with floating point. +#CONFIG_FPU=y +# +## For TVMPlatformAbort(). +#CONFIG_REBOOT=y +# +## For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. +#CONFIG_TEST_RANDOM_GENERATOR=y +#CONFIG_TIMER_RANDOM_GENERATOR=y diff --git a/docs/microtvm/index.rst b/docs/microtvm/index.rst index 2371219af27f..a67b1547d229 100644 --- a/docs/microtvm/index.rst +++ b/docs/microtvm/index.rst @@ -43,7 +43,7 @@ demos run against QEMU and the following hardware: * `STM Nucleo-F746ZG `_ * `STM STM32F746 Discovery `_ -* `nRF 5340 Preview Development Kit `_ +* `nRF 5340 Development Kit `_ Getting Started with microTVM From 123c64c1ac88a05dc5310e7e9f2390958fbf382a Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 15:49:20 -0800 Subject: [PATCH 06/53] Adding board-specific prj.conf files. --- .../boards/nrf5340dk_nrf5340_cpuapp.conf | 33 +++++++++++++++ .../zephyr/demo_runtime/boards/qemu_x86.conf | 23 +++++++++++ apps/microtvm/zephyr/demo_runtime/prj.conf | 40 ++----------------- .../demo_runtime/qemu-hack/qemu-system-i386 | 33 +++++++++++++++ 4 files changed, 92 insertions(+), 37 deletions(-) create mode 100644 apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf create mode 100644 apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf create mode 100755 apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 diff --git a/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf b/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 000000000000..a7df4d25c4c3 --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# This file is specific to the nRF5340 DK board. + +# For intrinsics used by generated optimized operators. +CONFIG_CMSIS_DSP=y + +# Required for Cortex-M33 devices. +CONFIG_MAIN_STACK_SIZE=1536 + +# For random number generation. +CONFIG_ENTROPY_GENERATOR=y + +# Allow for k_sys_fatal_error_handler to be overridden. +CONFIG_RESET_ON_FATAL_ERROR=n + +# For debugging. +CONFIG_LED=y diff --git a/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf b/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf new file mode 100644 index 000000000000..f29dbf9c574d --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# This file is specific to the QEMU-emulated uTVM board. + +# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_TIMER_RANDOM_GENERATOR=y + diff --git a/apps/microtvm/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf index 2cb4f3828cb3..06d10fc5d417 100644 --- a/apps/microtvm/zephyr/demo_runtime/prj.conf +++ b/apps/microtvm/zephyr/demo_runtime/prj.conf @@ -14,9 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -# -# For nRF5340 -# XXX MDW - Put these in a board-specific conf file. + +# The settings in this file are generic for all boards, and are merged +# with the settings in the file boards/.conf. # For UART implementation in main(). CONFIG_RING_BUFFER=y @@ -33,37 +33,3 @@ CONFIG_FPU=y # For TVMPlatformAbort(). CONFIG_REBOOT=y -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# Required for Cortex-M33 devices. -CONFIG_MAIN_STACK_SIZE=1536 - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y - -# Allow for k_sys_fatal_error_handler to be overridden. -CONFIG_RESET_ON_FATAL_ERROR=n - -# For debugging. -CONFIG_LED=y - -# For QEMU -## For UART implementation in main(). -#CONFIG_RING_BUFFER=y -#CONFIG_UART_CONSOLE=n -#CONFIG_UART_INTERRUPT_DRIVEN=y -# -## For RPC server C++ bindings. -#CONFIG_CPLUSPLUS=y -#CONFIG_NEWLIB_LIBC=y -# -## For models with floating point. -#CONFIG_FPU=y -# -## For TVMPlatformAbort(). -#CONFIG_REBOOT=y -# -## For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -#CONFIG_TEST_RANDOM_GENERATOR=y -#CONFIG_TIMER_RANDOM_GENERATOR=y diff --git a/apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 b/apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 new file mode 100755 index 000000000000..a0bf0f2c4dee --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 @@ -0,0 +1,33 @@ +#!/bin/bash -e +# 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. + +# Zephyr insists on running qemu with a -pidfile option, but that option doesn't appear to +# work given the way we've configured docker (the underlying filesystem doesn't suppor the +# file locking it needs to). This script strips any -pidfile option, then invokes qemu. + +ARGS=( "$(basename $0)" ) +while [ "$#" -gt 0 ]; do + if [ "$1" == "-pidfile" ]; then + shift + else + ARGS=( "${ARGS[@]}" "$1" ) + fi + shift +done + +"${ARGS[@]}" From bc874503ce10043b24721747d8021a8ae143d153 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 15:55:24 -0800 Subject: [PATCH 07/53] Some cleanup. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 62 ++++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 053e2386238e..8209382353e2 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -51,16 +51,8 @@ #include "crt_config.h" -//K_SEM_DEFINE(tx_sem, 0, 1); // XXX MDW NEEDED? - static const struct device* tvm_uart; -// XXX MDW needed? -//int write_hook(int c) { -// uart_poll_out(tvm_uart, c); -// return 0; -//} - #ifdef CONFIG_LED /* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) @@ -78,6 +70,7 @@ static size_t g_transmit_data_size = 0; static volatile size_t g_transmitted_bytes = 0; static volatile bool g_transmit_complete = true; +// Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED gpio_pin_set(led_pin, PIN, 1); @@ -117,12 +110,32 @@ void TVMPlatformAbort(tvm_crt_error_t error) { for (;;) ; } -uint32_t g_utvm_start_time; +// Used by TVM to generate random data. +tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { + uint32_t random; // one unit of random data. + + // Fill parts of `buffer` which are as large as `random`. + size_t num_full_blocks = num_bytes / sizeof(random); + for (int i = 0; i < num_full_blocks; ++i) { + random = sys_rand32_get(); + memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); + } + + // Fill any leftover tail which is smaller than `random`. + size_t num_tail_bytes = num_bytes % sizeof(random); + if (num_tail_bytes > 0) { + random = sys_rand32_get(); + memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); + } + + return kTvmErrorNoError; +} #define MILLIS_TIL_EXPIRY 200 #define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY)) K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); +uint32_t g_utvm_start_time; int g_utvm_timer_running = 0; // Used to start system timer. @@ -192,11 +205,13 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { // Memory pool for use by TVMPlatformMemoryAllocate. K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); +// Used by TVM to allocate memory. tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; } +// Used by TVM to deallocate memory. tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { k_free(ptr); return kTvmErrorNoError; @@ -207,9 +222,9 @@ struct uart_rx_buf_t { struct ring_buf buf; uint32_t buffer[RING_BUF_SIZE]; }; - struct uart_rx_buf_t uart_rx_buf; +// UART interrupt callback. void uart_irq_cb(const struct device* dev, void* user_data) { while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; @@ -230,12 +245,14 @@ void uart_irq_cb(const struct device* dev, void* user_data) { } } +// Used to initialize the UART receiver. void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); uart_irq_rx_enable(dev); } +// Used to read data from the UART. int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { unsigned int key = irq_lock(); int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); @@ -243,6 +260,7 @@ int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_ return bytes_read; } +// The main function of this application. extern void __stdout_hook_install(int (*hook)(int)); void main(void) { #ifdef CONFIG_LED @@ -257,11 +275,12 @@ void main(void) { gpio_pin_set(led_pin, PIN, 1); #endif - /* Claim console device. */ + // Claim console device. tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); const struct device* shadow_tvm_uart = tvm_uart; uart_rx_init(&uart_rx_buf, tvm_uart); + // Initialize uTVM RPC server, which will receive commands from the UART and execute them. utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); TVMLogf("uTVM Zephyr runtime - running"); #ifdef CONFIG_LED @@ -278,6 +297,7 @@ void main(void) { size_t bytes_remaining = bytes_read; uint8_t* cursor = buf; while (bytes_remaining > 0) { + // Pass the received bytes to the RPC server. tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { TVMPlatformAbort(err); @@ -298,24 +318,4 @@ void main(void) { #endif } -// Used by TVM to generate random data. -tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { - uint32_t random; // one unit of random data. - - // Fill parts of `buffer` which are as large as `random`. - size_t num_full_blocks = num_bytes / sizeof(random); - for (int i = 0; i < num_full_blocks; ++i) { - random = sys_rand32_get(); - memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); - } - - // Fill any leftover tail which is smaller than `random`. - size_t num_tail_bytes = num_bytes % sizeof(random); - if (num_tail_bytes > 0) { - random = sys_rand32_get(); - memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); - } - - return kTvmErrorNoError; -} From 98f347550cacd0e98955143ae8d519c1a24301ae Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sat, 27 Feb 2021 16:56:08 -0800 Subject: [PATCH 08/53] Lots of hacking to get ONNX model to run on QEMU and nRF5340. Added test_onnx unit test. Still need to clean up tutorial. --- .../zephyr/demo_runtime/crt/crt_config.h | 2 +- apps/microtvm/zephyr/demo_runtime/src/main.c | 140 ++++++++++++--- tests/micro/zephyr/test_zephyr.py | 57 +++++- tutorials/micro/data/digit-2.jpg | Bin 0 -> 572 bytes tutorials/micro/data/digit-9.jpg | Bin 0 -> 535 bytes tutorials/micro/micro_onnx.py | 170 ++++++++++++++++++ tutorials/micro/micro_tflite.py | 34 ++-- 7 files changed, 354 insertions(+), 49 deletions(-) create mode 100644 tutorials/micro/data/digit-2.jpg create mode 100644 tutorials/micro/data/digit-9.jpg create mode 100755 tutorials/micro/micro_onnx.py diff --git a/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h b/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h index a7f4f90b0538..f8fc7514a28d 100644 --- a/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h +++ b/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h @@ -59,6 +59,6 @@ /*! \brief Number of pages on device. */ #define TVM_CRT_MAX_PAGES 300 -//#define TVM_CRT_FRAMER_ENABLE_LOGS +// #define TVM_CRT_FRAMER_ENABLE_LOGS #endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 8209382353e2..7a7539c82147 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -53,13 +53,33 @@ static const struct device* tvm_uart; +// XXX MDW - TODO: Clean up the LED stuff in here to remove the 4 LEDs. + #ifdef CONFIG_LED -/* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) #define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) -#define PIN DT_GPIO_PIN(LED0_NODE, gpios) -#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) -static const struct device* led_pin; +#define LED0_PIN DT_GPIO_PIN(LED0_NODE, gpios) +#define LED0_FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) +static const struct device* led0_pin; + +#define LED1_NODE DT_ALIAS(led1) +#define LED1 DT_GPIO_LABEL(LED1_NODE, gpios) +#define LED1_PIN DT_GPIO_PIN(LED1_NODE, gpios) +#define LED1_FLAGS DT_GPIO_FLAGS(LED1_NODE, gpios) +static const struct device* led1_pin; + +#define LED2_NODE DT_ALIAS(led2) +#define LED2 DT_GPIO_LABEL(LED2_NODE, gpios) +#define LED2_PIN DT_GPIO_PIN(LED2_NODE, gpios) +#define LED2_FLAGS DT_GPIO_FLAGS(LED2_NODE, gpios) +static const struct device* led2_pin; + +#define LED3_NODE DT_ALIAS(led3) +#define LED3 DT_GPIO_LABEL(LED3_NODE, gpios) +#define LED3_PIN DT_GPIO_PIN(LED3_NODE, gpios) +#define LED3_FLAGS DT_GPIO_FLAGS(LED3_NODE, gpios) +static const struct device* led3_pin; + #endif // CONFIG_LED static size_t g_num_bytes_requested = 0; @@ -73,7 +93,7 @@ static volatile bool g_transmit_complete = true; // Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); +// gpio_pin_set(led0_pin, LED0_PIN, 1); #endif g_num_bytes_requested += size; @@ -83,7 +103,7 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { } #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); +// gpio_pin_set(led0_pin, LED0_PIN, 0); #endif return size; @@ -93,7 +113,8 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { // Here, we turn on the LED and spin. void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led2_pin, LED2_PIN, 1); #endif for (;;) ; } @@ -104,9 +125,14 @@ size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); } +static volatile tvm_crt_error_t fatal_error; // Used by TVM when an abort operation occurs. void TVMPlatformAbort(tvm_crt_error_t error) { - sys_reboot(SYS_REBOOT_COLD); + fatal_error = error; // Save it in a global for debugging. + //sys_reboot(SYS_REBOOT_COLD); +#ifdef CONFIG_LED + gpio_pin_set(led3_pin, LED3_PIN, 1); +#endif for (;;) ; } @@ -146,7 +172,7 @@ tvm_crt_error_t TVMPlatformTimerStart() { } #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); +// gpio_pin_set(led0_pin, LED0_PIN, 1); #endif k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); g_utvm_start_time = k_cycle_get_32(); @@ -163,7 +189,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { uint32_t stop_time = k_cycle_get_32(); #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); +// gpio_pin_set(led0_pin, LED0_PIN, 0); #endif // compute how long the work took @@ -217,29 +243,44 @@ tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { return kTvmErrorNoError; } -#define RING_BUF_SIZE 512 +// Ring buffer used to store data read from the UART on rx interrupt. +#define RING_BUF_SIZE 20 * 1024 struct uart_rx_buf_t { struct ring_buf buf; uint32_t buffer[RING_BUF_SIZE]; }; struct uart_rx_buf_t uart_rx_buf; +// Small buffer used to read data from the UART into the ring buffer. +static uint8_t uart_data[32]; + // UART interrupt callback. void uart_irq_cb(const struct device* dev, void* user_data) { while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; if (uart_irq_rx_ready(dev) != 0) { - uint8_t data[32]; for (;;) { - int bytes_read = uart_fifo_read(dev, data, sizeof(data)); + // Read a small chunk of data from the UART. + int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data)); if (bytes_read < 0) { +#ifdef CONFIG_LED + gpio_pin_set(led2_pin, LED2_PIN, 1); +#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef1); } else if (bytes_read == 0) { break; } - int bytes_written = ring_buf_put(&buf->buf, data, bytes_read); - CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, - bytes_written); + // Write it into the ring buffer. + int bytes_written = ring_buf_put(&buf->buf, uart_data, bytes_read); + if (bytes_read != bytes_written) { +#ifdef CONFIG_LED + gpio_pin_set(led1_pin, LED1_PIN, 1); + gpio_pin_set(led2_pin, LED2_PIN, 1); +#endif + TVMPlatformAbort((tvm_crt_error_t)0xbeef2); + } + //CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, + // bytes_written); } } } @@ -260,19 +301,53 @@ int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_ return bytes_read; } +// Buffer used to read from the UART rx ring buffer and feed it to the UTvmRpcServerLoop. +static uint8_t main_rx_buf[RING_BUF_SIZE]; + // The main function of this application. extern void __stdout_hook_install(int (*hook)(int)); void main(void) { #ifdef CONFIG_LED - led_pin = device_get_binding(LED0); - if (led_pin == NULL) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef2); + int ret; + led0_pin = device_get_binding(LED0); + if (led0_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led0_pin, LED0_PIN, GPIO_OUTPUT_ACTIVE | LED0_FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); + } + gpio_pin_set(led0_pin, LED0_PIN, 1); + + led1_pin = device_get_binding(LED1); + if (led1_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led1_pin, LED1_PIN, GPIO_OUTPUT_ACTIVE | LED1_FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); + } + gpio_pin_set(led1_pin, LED1_PIN, 1); + + led2_pin = device_get_binding(LED2); + if (led2_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led2_pin, LED2_PIN, GPIO_OUTPUT_ACTIVE | LED2_FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); } - int ret = gpio_pin_configure(led_pin, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); + gpio_pin_set(led2_pin, LED2_PIN, 1); + + led3_pin = device_get_binding(LED3); + if (led3_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led3_pin, LED3_PIN, GPIO_OUTPUT_ACTIVE | LED3_FLAGS); if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef3); + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); } - gpio_pin_set(led_pin, PIN, 1); + gpio_pin_set(led3_pin, LED3_PIN, 1); #endif // Claim console device. @@ -284,27 +359,36 @@ void main(void) { utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); TVMLogf("uTVM Zephyr runtime - running"); #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); + gpio_pin_set(led0_pin, LED0_PIN, 0); + gpio_pin_set(led1_pin, LED1_PIN, 0); + gpio_pin_set(led2_pin, LED2_PIN, 0); + gpio_pin_set(led3_pin, LED3_PIN, 0); #endif - // The main application loop. We continuously read commands from the UART // and dispatch them to UTvmRpcServerLoop(). while (true) { - uint8_t buf[256]; - int bytes_read = uart_rx_buf_read(&uart_rx_buf, buf, sizeof(buf)); + //uint8_t buf[256]; + int bytes_read = uart_rx_buf_read(&uart_rx_buf, main_rx_buf, sizeof(main_rx_buf)); if (bytes_read > 0) { size_t bytes_remaining = bytes_read; - uint8_t* cursor = buf; + uint8_t* cursor = main_rx_buf; while (bytes_remaining > 0) { // Pass the received bytes to the RPC server. tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { +#ifdef CONFIG_LED + gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led2_pin, LED2_PIN, 1); +#endif TVMPlatformAbort(err); } if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { if (g_num_bytes_written != g_num_bytes_requested) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); +#ifdef CONFIG_LED + gpio_pin_set(led1_pin, LED1_PIN, 1); +#endif + TVMPlatformAbort((tvm_crt_error_t)0xbeef5); } g_num_bytes_written = 0; g_num_bytes_requested = 0; diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index a1a847aa5ce2..527afd07ff66 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -19,12 +19,15 @@ import copy import datetime import glob +import logging import os import subprocess import sys import pytest import numpy as np +import onnx +from PIL import Image import tvm import tvm.rpc @@ -36,11 +39,14 @@ from tvm.relay.expr_functor import ExprMutator from tvm.relay.op.annotation import compiler_begin, compiler_end +# If set, build the uTVM binary from scratch on each test. +# Otherwise, reuses the build from the previous test run. BUILD = True -DEBUG = False - -TARGET = None +# If set, enable a debug session while the test is running. +# Before running the test, in a separate shell, you should run: +# python -m tvm.exec.microtvm_debug_shell +DEBUG = False def _make_sess_from_op(model, zephyr_board, west_cmd, op_name, sched, arg_bufs): @@ -204,6 +210,51 @@ def test_relay(platform, west_cmd): tvm.testing.assert_allclose(result, x_in * x_in + 1) +def test_onnx(platform, west_cmd): + """Testing a simple ONNX model.""" + model, zephyr_board = PLATFORMS[platform] + + # Load test images. + digit_2 = Image.open("testdata/digit-2.jpg").resize((28, 28)) + digit_2 = np.asarray(digit_2).astype("float32") + digit_2 = np.expand_dims(digit_2, axis=0) + + digit_9 = Image.open("testdata/digit-9.jpg").resize((28, 28)) + digit_9 = np.asarray(digit_9).astype("float32") + digit_9 = np.expand_dims(digit_9, axis=0) + + # Load ONNX model and convert to Relay. + onnx_model = onnx.load("testdata/mnist-8.onnx") + shape = (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) + + # We add the -link-params=1 option to ensure the model parameters are compiled in. + # There is currently a bug preventing the demo_runtime environment from receiving + # the model weights when set using graph_mod.set_input(). + 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_json() + + with _make_session(model, target, zephyr_board, west_cmd, lowered.lib) as session: + graph_mod = tvm.micro.create_local_graph_runtime( + graph, session.get_system_lib(), session.context + ) + + # 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).asnumpy() + 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).asnumpy() + assert np.argmax(result) == 9 + + class CcompilerAnnotator(ExprMutator): """ This is used to create external functions for ccompiler. diff --git a/tutorials/micro/data/digit-2.jpg b/tutorials/micro/data/digit-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b709a206b8d776215dcaa78643b22fe628b3c43a GIT binary patch literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} literal 0 HcmV?d00001 diff --git a/tutorials/micro/data/digit-9.jpg b/tutorials/micro/data/digit-9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ce9cde3b322b351847da179555aba35358dbf23 GIT binary patch literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja Date: Sat, 27 Feb 2021 16:56:49 -0800 Subject: [PATCH 09/53] Adding data for unit tests. --- tests/micro/zephyr/testdata/digit-2.jpg | Bin 0 -> 572 bytes tests/micro/zephyr/testdata/digit-9.jpg | Bin 0 -> 535 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/micro/zephyr/testdata/digit-2.jpg create mode 100644 tests/micro/zephyr/testdata/digit-9.jpg diff --git a/tests/micro/zephyr/testdata/digit-2.jpg b/tests/micro/zephyr/testdata/digit-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b709a206b8d776215dcaa78643b22fe628b3c43a GIT binary patch literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} literal 0 HcmV?d00001 diff --git a/tests/micro/zephyr/testdata/digit-9.jpg b/tests/micro/zephyr/testdata/digit-9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ce9cde3b322b351847da179555aba35358dbf23 GIT binary patch literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja Date: Sat, 27 Feb 2021 17:00:57 -0800 Subject: [PATCH 10/53] Cleanup demo_runtime code. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 83 ++------------------ 1 file changed, 6 insertions(+), 77 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 7a7539c82147..b10b0700b369 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -53,33 +53,12 @@ static const struct device* tvm_uart; -// XXX MDW - TODO: Clean up the LED stuff in here to remove the 4 LEDs. - #ifdef CONFIG_LED #define LED0_NODE DT_ALIAS(led0) #define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) #define LED0_PIN DT_GPIO_PIN(LED0_NODE, gpios) #define LED0_FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) static const struct device* led0_pin; - -#define LED1_NODE DT_ALIAS(led1) -#define LED1 DT_GPIO_LABEL(LED1_NODE, gpios) -#define LED1_PIN DT_GPIO_PIN(LED1_NODE, gpios) -#define LED1_FLAGS DT_GPIO_FLAGS(LED1_NODE, gpios) -static const struct device* led1_pin; - -#define LED2_NODE DT_ALIAS(led2) -#define LED2 DT_GPIO_LABEL(LED2_NODE, gpios) -#define LED2_PIN DT_GPIO_PIN(LED2_NODE, gpios) -#define LED2_FLAGS DT_GPIO_FLAGS(LED2_NODE, gpios) -static const struct device* led2_pin; - -#define LED3_NODE DT_ALIAS(led3) -#define LED3 DT_GPIO_LABEL(LED3_NODE, gpios) -#define LED3_PIN DT_GPIO_PIN(LED3_NODE, gpios) -#define LED3_FLAGS DT_GPIO_FLAGS(LED3_NODE, gpios) -static const struct device* led3_pin; - #endif // CONFIG_LED static size_t g_num_bytes_requested = 0; @@ -93,7 +72,7 @@ static volatile bool g_transmit_complete = true; // Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); #endif g_num_bytes_requested += size; @@ -103,7 +82,7 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { } #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 0); + gpio_pin_set(led0_pin, LED0_PIN, 0); #endif return size; @@ -114,7 +93,6 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 1); - gpio_pin_set(led2_pin, LED2_PIN, 1); #endif for (;;) ; } @@ -125,13 +103,11 @@ size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); } -static volatile tvm_crt_error_t fatal_error; // Used by TVM when an abort operation occurs. void TVMPlatformAbort(tvm_crt_error_t error) { - fatal_error = error; // Save it in a global for debugging. - //sys_reboot(SYS_REBOOT_COLD); + sys_reboot(SYS_REBOOT_COLD); #ifdef CONFIG_LED - gpio_pin_set(led3_pin, LED3_PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); #endif for (;;) ; } @@ -172,7 +148,7 @@ tvm_crt_error_t TVMPlatformTimerStart() { } #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); #endif k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); g_utvm_start_time = k_cycle_get_32(); @@ -189,7 +165,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { uint32_t stop_time = k_cycle_get_32(); #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 0); + gpio_pin_set(led0_pin, LED0_PIN, 0); #endif // compute how long the work took @@ -263,9 +239,6 @@ void uart_irq_cb(const struct device* dev, void* user_data) { // Read a small chunk of data from the UART. int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data)); if (bytes_read < 0) { -#ifdef CONFIG_LED - gpio_pin_set(led2_pin, LED2_PIN, 1); -#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef1); } else if (bytes_read == 0) { break; @@ -273,10 +246,6 @@ void uart_irq_cb(const struct device* dev, void* user_data) { // Write it into the ring buffer. int bytes_written = ring_buf_put(&buf->buf, uart_data, bytes_read); if (bytes_read != bytes_written) { -#ifdef CONFIG_LED - gpio_pin_set(led1_pin, LED1_PIN, 1); - gpio_pin_set(led2_pin, LED2_PIN, 1); -#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef2); } //CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, @@ -318,36 +287,6 @@ void main(void) { TVMPlatformAbort((tvm_crt_error_t)0xbeef4); } gpio_pin_set(led0_pin, LED0_PIN, 1); - - led1_pin = device_get_binding(LED1); - if (led1_pin == NULL) { - for (;;) ; - } - ret = gpio_pin_configure(led1_pin, LED1_PIN, GPIO_OUTPUT_ACTIVE | LED1_FLAGS); - if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); - } - gpio_pin_set(led1_pin, LED1_PIN, 1); - - led2_pin = device_get_binding(LED2); - if (led2_pin == NULL) { - for (;;) ; - } - ret = gpio_pin_configure(led2_pin, LED2_PIN, GPIO_OUTPUT_ACTIVE | LED2_FLAGS); - if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); - } - gpio_pin_set(led2_pin, LED2_PIN, 1); - - led3_pin = device_get_binding(LED3); - if (led3_pin == NULL) { - for (;;) ; - } - ret = gpio_pin_configure(led3_pin, LED3_PIN, GPIO_OUTPUT_ACTIVE | LED3_FLAGS); - if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); - } - gpio_pin_set(led3_pin, LED3_PIN, 1); #endif // Claim console device. @@ -360,9 +299,6 @@ void main(void) { TVMLogf("uTVM Zephyr runtime - running"); #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 0); - gpio_pin_set(led1_pin, LED1_PIN, 0); - gpio_pin_set(led2_pin, LED2_PIN, 0); - gpio_pin_set(led3_pin, LED3_PIN, 0); #endif // The main application loop. We continuously read commands from the UART @@ -377,17 +313,10 @@ void main(void) { // Pass the received bytes to the RPC server. tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { -#ifdef CONFIG_LED - gpio_pin_set(led0_pin, LED0_PIN, 1); - gpio_pin_set(led2_pin, LED2_PIN, 1); -#endif TVMPlatformAbort(err); } if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { if (g_num_bytes_written != g_num_bytes_requested) { -#ifdef CONFIG_LED - gpio_pin_set(led1_pin, LED1_PIN, 1); -#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef5); } g_num_bytes_written = 0; From d04e04ddddb435e3a0f4fc5707e027850dc80345 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sun, 28 Feb 2021 17:27:16 -0800 Subject: [PATCH 11/53] Fix up tutorial. --- tutorials/micro/micro_onnx.py | 245 ++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 99 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 46f5b49aa389..2138e0d4e310 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -1,6 +1,75 @@ #!/usr/bin/env python3 -# This is a MicroTVM version of `mnist.py`. +# 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. + +""" +microTVM with ONNX models +========================= +**Author**: `Matt Welsh ` + +This tutorial is an introduction to compiling and +running an ONNX model on a device using microTVM. +""" + +# Setup +# ----- +# +# Build TVM wth ``USE_MICRO`` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# Download the TVM sources, and build TVM from source following the +# instructions `on this page +# `. +# When bulding TVM, ensure that the following lines are present in +# your ``config.cmake`` file: +# .. code-block:: bash +# +# # Whether enable MicroTVM runtime +# set(USE_MICRO ON) +# +# Install Python dependencies +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# microTVM and this tutorial require a number of Python dependencies -- +# in addition to TVM itself -- to be installed in your Python environment. +# For convenience, we have provided a `Poetry ` +# ``pyproject.toml`` configuration file in the directory ``apps/microtvm``. +# You can use this as follows: +# +# .. code-block:: bash +# +# $ cd $TVM_HOME/apps/microtvm +# $ poetry lock && poetry install +# $ poetry shell +# +# You should now be in a Python virtual environment with all of the appropriate +# dependencies installed. +# +# Install Zephyr +# ^^^^^^^^^^^^^^ +# +# microTVM currently uses the `Zephyr ` RTOS as +# the basis for the device-side runtime. To get started, install Zephyr +# and an appropriate device-specific toolchain using the +# `Zephyr installation instructions `_. +# +# Be sure you are able to build and flash a sample Zephyr program +# (e.g., the "Blinky" demo) to your device before you proceed with this tutorial. import datetime import io @@ -17,107 +86,81 @@ from PIL import Image import numpy as np +###################################################################### +# For this tutorial, we use a pretrained ONNX model implementing +# the MNIST handwritten digit recognition on 28x28 px input images. + MODEL_FILE = "models/mnist-8.onnx" MODEL_SHAPE = (1, 1, 28, 28) -DIGIT_2_IMAGE = "data/digit-2.jpg" -DIGIT_9_IMAGE = "data/digit-9.jpg" INPUT_TENSOR_NAME = "Input3" +onnx_model = onnx.load(MODEL_FILE) +print(f"Loaded ONNX model: {MODEL_FILE}") -def relay_build(mod, params): - TVM_OPT_LEVEL = 3 - target = tvm.target.Target("llvm") - with tvm.transform.PassContext(opt_level=TVM_OPT_LEVEL): - lib = relay.build(mod, target, params=params) - - # Instantiate the Graph runtime. - gmodule = runtime.GraphModule(lib["default"](tvm.cpu())) - print("Built module is:") - print(gmodule) - return gmodule - - -def run_inference(gmodule, sess, perf_eval=True): - - # Load test images. - digit_2 = Image.open(DIGIT_2_IMAGE).resize((28, 28)) - digit_9 = Image.open(DIGIT_9_IMAGE).resize((28, 28)) - digit_2 = np.asarray(digit_2).astype("float32") - digit_9 = np.asarray(digit_9).astype("float32") - digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = np.expand_dims(digit_9, axis=0) - - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) - gmodule.run() - sess.context.sync() - output = gmodule.get_output(0).asnumpy() - print(f"Top result for digit-2 is: {np.argmax(output)}") - - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) - gmodule.run() - output = gmodule.get_output(0).asnumpy() - print(f"Top result for digit-9 is: {np.argmax(output)}") +###################################################################### +# Next, we convert the model to Relay format. +relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=MODEL_SHAPE, freeze_params=True) +relay_mod = relay.transform.DynamicToStatic()(relay_mod) - if perf_eval: - print("Performance eval...") - ctx = tvm.cpu() - ftimer = gmodule.module.time_evaluator("run", ctx, number=25, repeat=3) - prof_res = np.array(ftimer().results) * 1000 # convert to millisecond - print( - "Mean inference time (std dev): %.2f ms (%.2f ms)" - % (np.mean(prof_res), np.std(prof_res)) - ) +###################################################################### +# Next, we lower the Relay model to the specific device we are +# targeting. In this case, we are using the Nordic Semiconductor +# `nRF5340DK development board `. +# This is the device target name used by microTVM. It is used to select default +# options for the device when we use ``tvm.target.target.micro()`` below. +UTVM_TARGET = "nrf5340dk" -UTVM_MODEL = "nrf5340dk" +# This is the board designation used by Zephyr, and required for the compilation process. UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" -#UTVM_MODEL = "host" -#UTVM_ZEPHYR_BOARD = "qemu_x86" -UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime/" -UTVM_WEST_CMD = "west" - - -onnx_model = onnx.load(MODEL_FILE) - -print(f"Loaded ONNX model: {MODEL_FILE}") -print(type(onnx_model)) -print(f"IR version: {onnx_model.ir_version}") -print(f"Producer: {onnx_model.producer_name} {onnx_model.producer_version}") -print(f"Domain: {onnx_model.domain}") -print(f"Version: {onnx_model.model_version}") -print(f"Doc string: {onnx_model.doc_string}") -print(f"Graph name: {onnx_model.graph.name}") -print(f"Graph doc string: {onnx_model.graph.doc_string}") +###################################################################### +# If you wish to run against an emulated Zephyr device using QEMU, +# you can uncomment these lines instead: +# UTVM_TARGET = "host" +# UTVM_ZEPHYR_BOARD = "qemu_x86" -print("Graph:") -print(onnx.helper.printable_graph(onnx_model.graph)) -# Convert to Relay. -relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=MODEL_SHAPE, freeze_params=True) -relay_mod = relay.transform.DynamicToStatic()(relay_mod) +###################################################################### +# We define the TVM target here. +# We add -link-params=1 option here, so that the model parameters are +# included in the resulting binary image. +target = tvm.target.target.micro(UTVM_TARGET, options=["-link-params=1"]) -# Do the build. -# We add -link-params here so the model parameters are included in the compiled build. -target = tvm.target.target.micro(UTVM_MODEL, options=["-link-params=1"]) +###################################################################### +# Now, we do the Relay build. TVM_OPT_LEVEL = 3 with tvm.transform.PassContext(opt_level=TVM_OPT_LEVEL, config={"tir.disable_vectorize": True}): lowered = relay.build(relay_mod, target, params=params) graph_json_str = lowered.get_json() -# gmodule = runtime.GraphModule(lib["default"](target)) - -# Create a uTVM Workspace. +###################################################################### +# Next, create a uTVM Workspace. This is a location where the +# generated Zephyr project will be compiled. workspace_root = os.path.abspath( f'workspace/{datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")}' ) -print(f"Using workspace: {workspace_root}") workspace_parent = os.path.dirname(workspace_root) if not os.path.exists(workspace_parent): os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) +print(f"Using workspace: {workspace_root}") + +###################################################################### +# Now we create the ``ZephyrCompiler`` object, which generates the +# Zephyr project for hosting the device runtime code, as well as +# generating the device-specific binary from our model. + +# You are welcome to implement your own Zephyr-based runtime environment +# for your project. This runtime is a demo which provides basic capabilities +# for interfacing to the microTVM device code via the device's serial +# port. +UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime/" + +# The ``west`` command is used by Zephyr for compilation and +# flashing devices. +UTVM_WEST_CMD = "west" -# Create Zephyr compiler. compiler = zephyr.ZephyrCompiler( project_dir=UTVM_ZEPHYR_RUNTIME_DIR, board=UTVM_ZEPHYR_BOARD, @@ -125,6 +168,7 @@ def run_inference(gmodule, sess, perf_eval=True): west_cmd=UTVM_WEST_CMD, ) +###################################################################### # Do the actual build. opts = tvm.micro.default_options(f"{UTVM_ZEPHYR_RUNTIME_DIR}/crt") opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] @@ -138,33 +182,36 @@ def run_inference(gmodule, sess, perf_eval=True): bin_opts=opts["bin_opts"], ) -build_tarfile = "./build.tar" -if os.path.exists(build_tarfile): - os.unlink(build_tarfile) -micro_bin.archive(build_tarfile, metadata_only=True) - -DEBUG = False -if DEBUG: - debug_rpc_session = tvm.rpc.connect("127.0.0.1", 9090) - flasher = compiler.flasher(debug_rpc_session=debug_rpc_session, flash_args=["--softreset"]) -else: - flasher = compiler.flasher(flash_args=["--softreset"]) +###################################################################### +# Next, we create a ``tvm.micro.Session`` which handles the details +# of flashing the binary to the device and opening a serial-port +# RPC session with the device for controlling it. +flasher = compiler.flasher() with tvm.micro.Session(binary=micro_bin, flasher=flasher) as sess: mod = tvm.micro.create_local_graph_runtime(graph_json_str, sess.get_system_lib(), sess.context) - print(f"Created runtime: {mod}") - print(f"sess.context.sync()...") - # Populate model parameters. - #for k, v in lowered.params.items(): - # num_bytes = np.prod(v.shape) * 4 - # print(f"MDW: Setting parameter: {k} with size {v.shape}, {num_bytes} bytes") - # mod.set_input(k, v) + # Load test images. + DIGIT_2_IMAGE = "data/digit-2.jpg" + DIGIT_9_IMAGE = "data/digit-9.jpg" + + digit_2 = Image.open(DIGIT_2_IMAGE).resize((28, 28)) + digit_9 = Image.open(DIGIT_9_IMAGE).resize((28, 28)) + digit_2 = np.asarray(digit_2).astype("float32") + digit_9 = np.asarray(digit_9).astype("float32") + digit_2 = np.expand_dims(digit_2, axis=0) + digit_9 = np.expand_dims(digit_9, axis=0) - sess.context.sync() # Ensure all args have been pushed to the device. + # Set the input tensor of the model to the digit-2 test image. + gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) - print(f"mod is: {mod}") - print(f"mod.run is: {mod._run}") - print(f"Running inference...") + # Run inference and get the result. + gmodule.run() + output = gmodule.get_output(0).asnumpy() + print(f"Top result for digit-2 is: {np.argmax(output)}") - run_inference(mod, sess, perf_eval=False) + # Do likewise for the digit-9 image. + gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) + gmodule.run() + output = gmodule.get_output(0).asnumpy() + print(f"Top result for digit-9 is: {np.argmax(output)}") From 7a38c7fd3a0b10ee4d696c3cebb0d89b6858f4c0 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 08:48:01 -0800 Subject: [PATCH 12/53] Couple of small fixes: - Use `west attach` instead of `west debug` in commandline to prevent debugger from resetting device. - Fix warning on use of led_pin in zephyr-runtime/src/main.c. --- python/tvm/micro/contrib/zephyr.py | 5 +++-- tests/micro/qemu/zephyr-runtime/src/main.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/tvm/micro/contrib/zephyr.py b/python/tvm/micro/contrib/zephyr.py index cd9c23cd2f9d..104d955835a1 100644 --- a/python/tvm/micro/contrib/zephyr.py +++ b/python/tvm/micro/contrib/zephyr.py @@ -650,10 +650,10 @@ def popen_kwargs(self): env = dict(os.environ) env["ZEPHYR_BASE"] = self._zephyr_base - return dict( + args = dict( args=self._west_cmd + [ - "debug", + "attach", "--skip-rebuild", "--build-dir", self._build_dir, @@ -662,3 +662,4 @@ def popen_kwargs(self): ], env=env, ) + return args diff --git a/tests/micro/qemu/zephyr-runtime/src/main.c b/tests/micro/qemu/zephyr-runtime/src/main.c index e04fc20508b4..8a3879f5113a 100644 --- a/tests/micro/qemu/zephyr-runtime/src/main.c +++ b/tests/micro/qemu/zephyr-runtime/src/main.c @@ -97,7 +97,7 @@ int g_utvm_timer_running = 0; #define PIN DT_GPIO_PIN(LED0_NODE, gpios) #define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) -static struct device* led_pin; +static const struct device* led_pin; #endif // CONFIG_LED tvm_crt_error_t TVMPlatformTimerStart() { From 443a42971ecf7a4cd48f20ee967a16a6f299a912 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 10:08:48 -0800 Subject: [PATCH 13/53] Adding Zephyr demo runtime. --- src/runtime/micro/zephyr/README.md | 19 ++ .../micro/zephyr/demo_runtime/CMakeLists.txt | 21 ++ .../micro/zephyr/demo_runtime/README.md | 21 ++ .../zephyr/demo_runtime/crt/crt_config.h | 64 ++++ .../micro/zephyr/demo_runtime/prj.conf | 46 +++ .../micro/zephyr/demo_runtime/src/main.c | 321 ++++++++++++++++++ 6 files changed, 492 insertions(+) create mode 100644 src/runtime/micro/zephyr/README.md create mode 100644 src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt create mode 100644 src/runtime/micro/zephyr/demo_runtime/README.md create mode 100644 src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h create mode 100644 src/runtime/micro/zephyr/demo_runtime/prj.conf create mode 100644 src/runtime/micro/zephyr/demo_runtime/src/main.c diff --git a/src/runtime/micro/zephyr/README.md b/src/runtime/micro/zephyr/README.md new file mode 100644 index 000000000000..52dd8829669e --- /dev/null +++ b/src/runtime/micro/zephyr/README.md @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + +This directory code to interface uTVM with the [Zephyr RTOS](https://zephyrproject.org/). + diff --git a/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt b/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt new file mode 100644 index 000000000000..61011a44678e --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(microtvm_zephyr_demo_runtime) + +set(CMAKE_VERBOSE_MAKEFILE ON) +file(GLOB TVM_SOURCES ${CMAKE_SOURCE_DIR}/__tvm*.c) +target_sources(app PRIVATE src/main.c ${TVM_SOURCES}) + +foreach(tvm_lib ${TVM_LIBS}) + string(LENGTH ${tvm_lib} tvm_lib_length) + math(EXPR tvm_lib_cut "${tvm_lib_length} - 2") + string(SUBSTRING ${tvm_lib} 3 ${tvm_lib_cut} tvm_lib_name) + add_library(${tvm_lib_name} STATIC IMPORTED) + set_target_properties(${tvm_lib_name} PROPERTIES + IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/${tvm_lib}) + target_link_libraries(app PRIVATE ${tvm_lib_name}) +endforeach(tvm_lib ${TVM_LIBS}) + +target_include_directories(app PRIVATE ${TVM_INCLUDE_DIRS}) diff --git a/src/runtime/micro/zephyr/demo_runtime/README.md b/src/runtime/micro/zephyr/demo_runtime/README.md new file mode 100644 index 000000000000..e4dc92409ce5 --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/README.md @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + +This directory contains a Zephyr-based "demo" runtime environment that +pulls together the uTVM runtime dependencies into a single application +that can communicate with a Python-based host program via the UART, using +TVM's RPC protocol. diff --git a/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h b/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h new file mode 100644 index 000000000000..a7f4f90b0538 --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*! + * \file tvm/runtime/crt_config.h.template + * \brief Template for CRT configuration, to be modified on each target. + */ +#ifndef TVM_RUNTIME_CRT_CONFIG_H_ +#define TVM_RUNTIME_CRT_CONFIG_H_ + +#include + +/*! Log level of the CRT runtime */ +#define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG + +/*! Maximum supported dimension in NDArray */ +#define TVM_CRT_MAX_NDIM 6 + +/*! Maximum supported arguments in generated functions */ +#define TVM_CRT_MAX_ARGS 10 + +/*! Size of the global function registry, in bytes. */ +#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 200 + +/*! Maximum number of registered modules. */ +#define TVM_CRT_MAX_REGISTERED_MODULES 2 + +/*! Maximum packet size, in bytes, including the length header. */ +#define TVM_CRT_MAX_PACKET_SIZE_BYTES 8192 + +/*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ +#define TVM_CRT_MAX_STRLEN_DLTYPE 10 + +/*! Maximum supported string length in function names */ +#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 + +/*! \brief Maximum length of a PackedFunc function name. */ +#define TVM_CRT_MAX_FUNCTION_NAME_LENGTH_BYTES 30 + +/*! \brief Log2 of the page size (bytes) for a virtual memory page. */ +#define TVM_CRT_PAGE_BITS 10 // 1 kB + +/*! \brief Number of pages on device. */ +#define TVM_CRT_MAX_PAGES 300 + +//#define TVM_CRT_FRAMER_ENABLE_LOGS + +#endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/src/runtime/micro/zephyr/demo_runtime/prj.conf b/src/runtime/micro/zephyr/demo_runtime/prj.conf new file mode 100644 index 000000000000..33bff7f12a58 --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/prj.conf @@ -0,0 +1,46 @@ +# 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. + +# For UART implementation in main(). +CONFIG_RING_BUFFER=y +CONFIG_UART_CONSOLE=n +CONFIG_UART_INTERRUPT_DRIVEN=y + +# For RPC server C++ bindings. +CONFIG_CPLUSPLUS=y +CONFIG_NEWLIB_LIBC=y + +# For models with floating point. +CONFIG_FPU=y + +# For TVMPlatformAbort(). +CONFIG_REBOOT=y + +# For intrinsics used by generated optimized operators. +CONFIG_CMSIS_DSP=y + +# Required for Cortex-M33 devices. +CONFIG_MAIN_STACK_SIZE=1536 + +# For random number generation. +CONFIG_ENTROPY_GENERATOR=y + +# Allow for k_sys_fatal_error_handler to be overridden. +CONFIG_RESET_ON_FATAL_ERROR=n + +# For debugging. +CONFIG_LED=y diff --git a/src/runtime/micro/zephyr/demo_runtime/src/main.c b/src/runtime/micro/zephyr/demo_runtime/src/main.c new file mode 100644 index 000000000000..053e2386238e --- /dev/null +++ b/src/runtime/micro/zephyr/demo_runtime/src/main.c @@ -0,0 +1,321 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/* + * This is a sample Zephyr-based application that contains the logic + * needed to upload and control a uTVM-based model via the UART. + * This is only intended to be a demonstration, since typically you + * will want to incorporate this logic into your own application. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_POSIX +#include "posix_board_if.h" +#endif + +#include "crt_config.h" + +//K_SEM_DEFINE(tx_sem, 0, 1); // XXX MDW NEEDED? + +static const struct device* tvm_uart; + +// XXX MDW needed? +//int write_hook(int c) { +// uart_poll_out(tvm_uart, c); +// return 0; +//} + +#ifdef CONFIG_LED +/* The devicetree node identifier for the "led0" alias. */ +#define LED0_NODE DT_ALIAS(led0) +#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) +#define PIN DT_GPIO_PIN(LED0_NODE, gpios) +#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) +static const struct device* led_pin; +#endif // CONFIG_LED + +static size_t g_num_bytes_requested = 0; +static size_t g_num_bytes_written = 0; + +static const uint8_t* g_transmit_data = NULL; +static size_t g_transmit_data_size = 0; +static volatile size_t g_transmitted_bytes = 0; +static volatile bool g_transmit_complete = true; + +ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 1); +#endif + g_num_bytes_requested += size; + + for (size_t i = 0; i < size; i++) { + uart_poll_out(tvm_uart, data[i]); + g_num_bytes_written++; + } + +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 0); +#endif + + return size; +} + +// This is an error handler which will be invoked if the device crashes. +// Here, we turn on the LED and spin. +void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 1); +#endif + for (;;) ; +} + +// Used by TVM when a message needs to be formatted. +size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, + const char* fmt, va_list args) { + return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); +} + +// Used by TVM when an abort operation occurs. +void TVMPlatformAbort(tvm_crt_error_t error) { + sys_reboot(SYS_REBOOT_COLD); + for (;;) ; +} + +uint32_t g_utvm_start_time; + +#define MILLIS_TIL_EXPIRY 200 +#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY)) +K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); + +int g_utvm_timer_running = 0; + +// Used to start system timer. +tvm_crt_error_t TVMPlatformTimerStart() { + if (g_utvm_timer_running) { + TVMLogf("timer already running"); + return kTvmErrorSystemErrorMask | 1; + } + +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 1); +#endif + k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); + g_utvm_start_time = k_cycle_get_32(); + g_utvm_timer_running = 1; + return kTvmErrorNoError; +} + +// Used to stop system timer. +tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { + if (!g_utvm_timer_running) { + TVMLogf("timer not running"); + return kTvmErrorSystemErrorMask | 2; + } + + uint32_t stop_time = k_cycle_get_32(); +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 0); +#endif + + // compute how long the work took + uint32_t cycles_spent = stop_time - g_utvm_start_time; + if (stop_time < g_utvm_start_time) { + // we rolled over *at least* once, so correct the rollover it was *only* + // once, because we might still use this result + cycles_spent = ~((uint32_t)0) - (g_utvm_start_time - stop_time); + } + + uint32_t ns_spent = (uint32_t)k_cyc_to_ns_floor64(cycles_spent); + double hw_clock_res_us = ns_spent / 1000.0; + + // need to grab time remaining *before* stopping. when stopped, this function + // always returns 0. + int32_t time_remaining_ms = k_timer_remaining_get(&g_utvm_timer); + k_timer_stop(&g_utvm_timer); + // check *after* stopping to prevent extra expiries on the happy path + if (time_remaining_ms < 0) { + TVMLogf("negative time remaining"); + return kTvmErrorSystemErrorMask | 3; + } + uint32_t num_expiries = k_timer_status_get(&g_utvm_timer); + uint32_t timer_res_ms = ((num_expiries * MILLIS_TIL_EXPIRY) + time_remaining_ms); + double approx_num_cycles = + (double)k_ticks_to_cyc_floor32(1) * (double)k_ms_to_ticks_ceil32(timer_res_ms); + // if we approach the limits of the HW clock datatype (uint32_t), use the + // coarse-grained timer result instead + if (approx_num_cycles > (0.5 * (~((uint32_t)0)))) { + *res_us = timer_res_ms * 1000.0; + } else { + *res_us = hw_clock_res_us; + } + + g_utvm_timer_running = 0; + return kTvmErrorNoError; +} + +// Memory pool for use by TVMPlatformMemoryAllocate. +K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); + +tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { + *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); + return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; +} + +tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { + k_free(ptr); + return kTvmErrorNoError; +} + +#define RING_BUF_SIZE 512 +struct uart_rx_buf_t { + struct ring_buf buf; + uint32_t buffer[RING_BUF_SIZE]; +}; + +struct uart_rx_buf_t uart_rx_buf; + +void uart_irq_cb(const struct device* dev, void* user_data) { + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; + if (uart_irq_rx_ready(dev) != 0) { + uint8_t data[32]; + for (;;) { + int bytes_read = uart_fifo_read(dev, data, sizeof(data)); + if (bytes_read < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef1); + } else if (bytes_read == 0) { + break; + } + int bytes_written = ring_buf_put(&buf->buf, data, bytes_read); + CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, + bytes_written); + } + } + } +} + +void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { + ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); + uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); + uart_irq_rx_enable(dev); +} + +int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { + unsigned int key = irq_lock(); + int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); + irq_unlock(key); + return bytes_read; +} + +extern void __stdout_hook_install(int (*hook)(int)); +void main(void) { +#ifdef CONFIG_LED + led_pin = device_get_binding(LED0); + if (led_pin == NULL) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef2); + } + int ret = gpio_pin_configure(led_pin, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef3); + } + gpio_pin_set(led_pin, PIN, 1); +#endif + + /* Claim console device. */ + tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); + const struct device* shadow_tvm_uart = tvm_uart; + uart_rx_init(&uart_rx_buf, tvm_uart); + + utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); + TVMLogf("uTVM Zephyr runtime - running"); +#ifdef CONFIG_LED + gpio_pin_set(led_pin, PIN, 0); +#endif + + + // The main application loop. We continuously read commands from the UART + // and dispatch them to UTvmRpcServerLoop(). + while (true) { + uint8_t buf[256]; + int bytes_read = uart_rx_buf_read(&uart_rx_buf, buf, sizeof(buf)); + if (bytes_read > 0) { + size_t bytes_remaining = bytes_read; + uint8_t* cursor = buf; + while (bytes_remaining > 0) { + tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); + if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { + TVMPlatformAbort(err); + } + if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { + if (g_num_bytes_written != g_num_bytes_requested) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); + } + g_num_bytes_written = 0; + g_num_bytes_requested = 0; + } + } + } + } + +#ifdef CONFIG_ARCH_POSIX + posix_exit(0); +#endif +} + +// Used by TVM to generate random data. +tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { + uint32_t random; // one unit of random data. + + // Fill parts of `buffer` which are as large as `random`. + size_t num_full_blocks = num_bytes / sizeof(random); + for (int i = 0; i < num_full_blocks; ++i) { + random = sys_rand32_get(); + memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); + } + + // Fill any leftover tail which is smaller than `random`. + size_t num_tail_bytes = num_bytes % sizeof(random); + if (num_tail_bytes > 0) { + random = sys_rand32_get(); + memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); + } + + return kTvmErrorNoError; +} + From 280479acf9e3a1bb57b9b19b71a25f7390288ff3 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 11:16:51 -0800 Subject: [PATCH 14/53] Cleanup of uTVM tests and demo runtime. --- apps/microtvm/README.md | 17 +- .../{reference-vm/zephyr => }/pyproject.toml | 9 +- .../micro => apps/microtvm}/zephyr/README.md | 0 .../zephyr/demo_runtime/CMakeLists.txt | 0 .../microtvm}/zephyr/demo_runtime/README.md | 0 .../zephyr/demo_runtime/crt/crt_config.h | 0 .../microtvm}/zephyr/demo_runtime/prj.conf | 0 .../microtvm}/zephyr/demo_runtime/src/main.c | 0 tests/micro/qemu/.gitignore | 2 - tests/micro/qemu/zephyr-runtime/.gitignore | 3 - .../micro/qemu/zephyr-runtime/CMakeLists.txt | 27 -- .../qemu/zephyr-runtime/crt/crt_config.h | 64 ----- tests/micro/qemu/zephyr-runtime/prj.conf | 35 --- .../zephyr-runtime/qemu-hack/qemu-system-i386 | 33 --- tests/micro/qemu/zephyr-runtime/sample.yaml | 22 -- tests/micro/qemu/zephyr-runtime/src/main.c | 270 ------------------ tests/micro/zephyr/README.md | 35 +++ tests/micro/{qemu => zephyr}/conftest.py | 0 tests/micro/{qemu => zephyr}/test_zephyr.py | 8 +- 19 files changed, 56 insertions(+), 469 deletions(-) rename apps/microtvm/{reference-vm/zephyr => }/pyproject.toml (94%) rename {src/runtime/micro => apps/microtvm}/zephyr/README.md (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/CMakeLists.txt (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/README.md (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/crt/crt_config.h (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/prj.conf (100%) rename {src/runtime/micro => apps/microtvm}/zephyr/demo_runtime/src/main.c (100%) delete mode 100644 tests/micro/qemu/.gitignore delete mode 100644 tests/micro/qemu/zephyr-runtime/.gitignore delete mode 100644 tests/micro/qemu/zephyr-runtime/CMakeLists.txt delete mode 100644 tests/micro/qemu/zephyr-runtime/crt/crt_config.h delete mode 100644 tests/micro/qemu/zephyr-runtime/prj.conf delete mode 100755 tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 delete mode 100644 tests/micro/qemu/zephyr-runtime/sample.yaml delete mode 100644 tests/micro/qemu/zephyr-runtime/src/main.c create mode 100644 tests/micro/zephyr/README.md rename tests/micro/{qemu => zephyr}/conftest.py (100%) rename tests/micro/{qemu => zephyr}/test_zephyr.py (97%) diff --git a/apps/microtvm/README.md b/apps/microtvm/README.md index 97b844a4c01b..9cbb16ac00ed 100644 --- a/apps/microtvm/README.md +++ b/apps/microtvm/README.md @@ -15,14 +15,17 @@ -# microTVM Reference Virtual Machines +# microTVM +microTVM is the effort that allows TVM to build and execute models on bare-metal microcontrollers. -microTVM is the effort to allow TVM to build and execute models on bare-metal microcontrollers. -These Virtual Machines are used to reproduce results and bugs when using microTVM with real -physical hardware. Note that they are not used to run Continuous Integration regression tests-- -those are instead run by the QEMU container (they run against an emulator, rather than real -hardware). +The `pyproject.toml` file in this directory can be used to create a +[Poetry](https://python-poetry.org/) Python environment with all of the required +dependencies installed. To use it, run: + +``` +$ poetry lock && poetry install +$ poetry shell +``` -See the "microTVM Reference Virtual Machines" tutorial for information on how to use these. diff --git a/apps/microtvm/reference-vm/zephyr/pyproject.toml b/apps/microtvm/pyproject.toml similarity index 94% rename from apps/microtvm/reference-vm/zephyr/pyproject.toml rename to apps/microtvm/pyproject.toml index b4cfc544df58..096736ae52aa 100644 --- a/apps/microtvm/reference-vm/zephyr/pyproject.toml +++ b/apps/microtvm/pyproject.toml @@ -15,6 +15,9 @@ # specific language governing permissions and limitations # under the License. +# This `pyproject.toml` file is used to allow MicroTVM +# to run within a Poetry-managed environment. + [tool.black] line-length = 100 target-version = ['py36'] @@ -47,12 +50,12 @@ exclude = ''' ) ''' [tool.poetry] -name = "tvm" +name = "microtvm" version = "0.1.0" description = "" -authors = ["Your Name "] +authors = ["Andrew Reusch "] packages = [ - { include = "tvm", from = "../../../../python" }, + { include = "tvm", from = "../../python" }, ] [tool.poetry.dependencies] diff --git a/src/runtime/micro/zephyr/README.md b/apps/microtvm/zephyr/README.md similarity index 100% rename from src/runtime/micro/zephyr/README.md rename to apps/microtvm/zephyr/README.md diff --git a/src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt b/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/CMakeLists.txt rename to apps/microtvm/zephyr/demo_runtime/CMakeLists.txt diff --git a/src/runtime/micro/zephyr/demo_runtime/README.md b/apps/microtvm/zephyr/demo_runtime/README.md similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/README.md rename to apps/microtvm/zephyr/demo_runtime/README.md diff --git a/src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h b/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/crt/crt_config.h rename to apps/microtvm/zephyr/demo_runtime/crt/crt_config.h diff --git a/src/runtime/micro/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/prj.conf rename to apps/microtvm/zephyr/demo_runtime/prj.conf diff --git a/src/runtime/micro/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c similarity index 100% rename from src/runtime/micro/zephyr/demo_runtime/src/main.c rename to apps/microtvm/zephyr/demo_runtime/src/main.c diff --git a/tests/micro/qemu/.gitignore b/tests/micro/qemu/.gitignore deleted file mode 100644 index c920d8f93ff8..000000000000 --- a/tests/micro/qemu/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/test_zephyr*-workspace -/*.micro-binary diff --git a/tests/micro/qemu/zephyr-runtime/.gitignore b/tests/micro/qemu/zephyr-runtime/.gitignore deleted file mode 100644 index 64be5d3a487c..000000000000 --- a/tests/micro/qemu/zephyr-runtime/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -__tvm* -libtvm__* -/build diff --git a/tests/micro/qemu/zephyr-runtime/CMakeLists.txt b/tests/micro/qemu/zephyr-runtime/CMakeLists.txt deleted file mode 100644 index ce5605469fcb..000000000000 --- a/tests/micro/qemu/zephyr-runtime/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.13.1) - -set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") - -set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. - -find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) -project(microtvm_zephyr_runtime) - - -set(CMAKE_VERBOSE_MAKEFILE ON) -file(GLOB TVM_SOURCES ${CMAKE_SOURCE_DIR}/__tvm*.c) -target_sources(app PRIVATE src/main.c ${TVM_SOURCES}) - -foreach(tvm_lib ${TVM_LIBS}) - string(LENGTH ${tvm_lib} tvm_lib_length) - math(EXPR tvm_lib_cut "${tvm_lib_length} - 2") - string(SUBSTRING ${tvm_lib} 3 ${tvm_lib_cut} tvm_lib_name) - add_library(${tvm_lib_name} STATIC IMPORTED) - set_target_properties(${tvm_lib_name} PROPERTIES - IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/${tvm_lib}) - target_link_libraries(app PRIVATE ${tvm_lib_name}) -endforeach(tvm_lib ${TVM_LIBS}) - -target_include_directories(app PRIVATE ${TVM_INCLUDE_DIRS}) diff --git a/tests/micro/qemu/zephyr-runtime/crt/crt_config.h b/tests/micro/qemu/zephyr-runtime/crt/crt_config.h deleted file mode 100644 index a7f4f90b0538..000000000000 --- a/tests/micro/qemu/zephyr-runtime/crt/crt_config.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/*! - * \file tvm/runtime/crt_config.h.template - * \brief Template for CRT configuration, to be modified on each target. - */ -#ifndef TVM_RUNTIME_CRT_CONFIG_H_ -#define TVM_RUNTIME_CRT_CONFIG_H_ - -#include - -/*! Log level of the CRT runtime */ -#define TVM_CRT_LOG_LEVEL TVM_CRT_LOG_LEVEL_DEBUG - -/*! Maximum supported dimension in NDArray */ -#define TVM_CRT_MAX_NDIM 6 - -/*! Maximum supported arguments in generated functions */ -#define TVM_CRT_MAX_ARGS 10 - -/*! Size of the global function registry, in bytes. */ -#define TVM_CRT_GLOBAL_FUNC_REGISTRY_SIZE_BYTES 200 - -/*! Maximum number of registered modules. */ -#define TVM_CRT_MAX_REGISTERED_MODULES 2 - -/*! Maximum packet size, in bytes, including the length header. */ -#define TVM_CRT_MAX_PACKET_SIZE_BYTES 8192 - -/*! Maximum supported string length in dltype, e.g. "int8", "int16", "float32" */ -#define TVM_CRT_MAX_STRLEN_DLTYPE 10 - -/*! Maximum supported string length in function names */ -#define TVM_CRT_MAX_STRLEN_FUNCTION_NAME 80 - -/*! \brief Maximum length of a PackedFunc function name. */ -#define TVM_CRT_MAX_FUNCTION_NAME_LENGTH_BYTES 30 - -/*! \brief Log2 of the page size (bytes) for a virtual memory page. */ -#define TVM_CRT_PAGE_BITS 10 // 1 kB - -/*! \brief Number of pages on device. */ -#define TVM_CRT_MAX_PAGES 300 - -//#define TVM_CRT_FRAMER_ENABLE_LOGS - -#endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/tests/micro/qemu/zephyr-runtime/prj.conf b/tests/micro/qemu/zephyr-runtime/prj.conf deleted file mode 100644 index 7be42b260bbb..000000000000 --- a/tests/micro/qemu/zephyr-runtime/prj.conf +++ /dev/null @@ -1,35 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# For UART implementation in main(). -CONFIG_RING_BUFFER=y -CONFIG_UART_CONSOLE=n -CONFIG_UART_INTERRUPT_DRIVEN=y - -# For RPC server C++ bindings. -CONFIG_CPLUSPLUS=y -CONFIG_NEWLIB_LIBC=y - -# For models with floating point. -CONFIG_FPU=y - -# For TVMPlatformAbort(). -CONFIG_REBOOT=y - -# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_TIMER_RANDOM_GENERATOR=y diff --git a/tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 b/tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 deleted file mode 100755 index a0bf0f2c4dee..000000000000 --- a/tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386 +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -e -# 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. - -# Zephyr insists on running qemu with a -pidfile option, but that option doesn't appear to -# work given the way we've configured docker (the underlying filesystem doesn't suppor the -# file locking it needs to). This script strips any -pidfile option, then invokes qemu. - -ARGS=( "$(basename $0)" ) -while [ "$#" -gt 0 ]; do - if [ "$1" == "-pidfile" ]; then - shift - else - ARGS=( "${ARGS[@]}" "$1" ) - fi - shift -done - -"${ARGS[@]}" diff --git a/tests/micro/qemu/zephyr-runtime/sample.yaml b/tests/micro/qemu/zephyr-runtime/sample.yaml deleted file mode 100644 index 88616b4acc40..000000000000 --- a/tests/micro/qemu/zephyr-runtime/sample.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -sample: - description: uTVM RPC Server unit test - name: utvm rpc server -common: - tags: introduction diff --git a/tests/micro/qemu/zephyr-runtime/src/main.c b/tests/micro/qemu/zephyr-runtime/src/main.c deleted file mode 100644 index 8a3879f5113a..000000000000 --- a/tests/micro/qemu/zephyr-runtime/src/main.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_ARCH_POSIX -#include "posix_board_if.h" -#endif - -#include "crt_config.h" - -K_SEM_DEFINE(tx_sem, 0, 1); - -static const struct device* tvm_uart; - -int write_hook(int c) { - uart_poll_out(tvm_uart, c); - return 0; -} - -ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { - for (size_t i = 0; i < size; i++) { - uart_poll_out(tvm_uart, data[i]); - } - - return size; -} - -size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, - va_list args) { - return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); -} - -void TVMPlatformAbort(tvm_crt_error_t error) { - sys_reboot(SYS_REBOOT_COLD); - for (;;) - ; -} - -K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 120, 4); - -tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { - *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); - return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; -} - -tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { - k_free(ptr); - return kTvmErrorNoError; -} - -uint32_t g_utvm_start_time; - -#define MILLIS_TIL_EXPIRY 200 -#define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY)) -K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); - -int g_utvm_timer_running = 0; - -#ifdef CONFIG_LED -/* The devicetree node identifier for the "led0" alias. */ -#define LED0_NODE DT_ALIAS(led0) - -#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) -#define PIN DT_GPIO_PIN(LED0_NODE, gpios) -#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) - -static const struct device* led_pin; -#endif // CONFIG_LED - -tvm_crt_error_t TVMPlatformTimerStart() { - if (g_utvm_timer_running) { - TVMLogf("timer already running"); - return kTvmErrorPlatformTimerBadState; - } - -#ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); -#endif - k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); - g_utvm_start_time = k_cycle_get_32(); - g_utvm_timer_running = 1; - return kTvmErrorNoError; -} - -tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { - if (!g_utvm_timer_running) { - TVMLogf("timer not running"); - return kTvmErrorPlatformTimerBadState; - } - - uint32_t stop_time = k_cycle_get_32(); -#ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); -#endif - - // compute how long the work took - uint32_t cycles_spent = stop_time - g_utvm_start_time; - if (stop_time < g_utvm_start_time) { - // we rolled over *at least* once, so correct the rollover it was *only* - // once, because we might still use this result - cycles_spent = ~((uint32_t)0) - (g_utvm_start_time - stop_time); - } - - uint32_t ns_spent = (uint32_t)k_cyc_to_ns_floor64(cycles_spent); - double hw_clock_elapsed_seconds = ns_spent / 1e9; - - // need to grab time remaining *before* stopping. when stopped, this function - // always returns 0. - int32_t time_remaining_ms = k_timer_remaining_get(&g_utvm_timer); - k_timer_stop(&g_utvm_timer); - // check *after* stopping to prevent extra expiries on the happy path - if (time_remaining_ms < 0) { - TVMLogf("negative time remaining"); - return -1; - } - uint32_t num_expiries = k_timer_status_get(&g_utvm_timer); - uint32_t timer_res_ms = ((num_expiries * MILLIS_TIL_EXPIRY) + time_remaining_ms); - double approx_num_cycles = - (double)k_ticks_to_cyc_floor32(1) * (double)k_ms_to_ticks_ceil32(timer_res_ms); - // if we approach the limits of the HW clock datatype (uint32_t), use the - // coarse-grained timer result instead - if (approx_num_cycles > (0.5 * (~((uint32_t)0)))) { - *elapsed_time_seconds = timer_res_ms / 1e3; - } else { - *elapsed_time_seconds = hw_clock_elapsed_seconds; - } - - g_utvm_timer_running = 0; - return kTvmErrorNoError; -} - -tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { - uint32_t random; // one unit of random data. - - // Fill parts of `buffer` which are as large as `random`. - size_t num_full_blocks = num_bytes / sizeof(random); - for (int i = 0; i < num_full_blocks; ++i) { - random = sys_rand32_get(); - memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); - } - - // Fill any leftover tail which is smaller than `random`. - size_t num_tail_bytes = num_bytes % sizeof(random); - if (num_tail_bytes > 0) { - random = sys_rand32_get(); - memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); - } - - return kTvmErrorNoError; -} - -#define RING_BUF_SIZE 512 -struct uart_rx_buf_t { - struct ring_buf buf; - uint32_t buffer[RING_BUF_SIZE]; -}; - -struct uart_rx_buf_t uart_rx_buf; - -void uart_irq_cb(const struct device* dev, void* user_data) { - while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { - struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; - if (uart_irq_rx_ready(dev) == 0) { - continue; - } - - uint8_t data[32]; - for (;;) { - int bytes_read = uart_fifo_read(dev, data, sizeof(data)); - if (bytes_read < 0) { - TVMPlatformAbort(0xbeef); - } else if (bytes_read == 0) { - break; - } - int bytes_written = ring_buf_put(&buf->buf, data, bytes_read); - CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, - bytes_written); - } - } -} - -void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { - ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); - uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); - uart_irq_rx_enable(dev); -} - -int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { - unsigned int key = irq_lock(); - int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); - irq_unlock(key); - return bytes_read; -} - -extern void __stdout_hook_install(int (*hook)(int)); -void main(void) { -#ifdef CONFIG_LED - led_pin = device_get_binding(LED0); - if (led_pin == NULL) { - for (;;) - ; - } - int ret = gpio_pin_configure(led_pin, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); - if (ret < 0) { - for (;;) - ; - } - gpio_pin_set(led_pin, PIN, 0); -#endif - - /* Claim console device */ - tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); - uart_rx_init(&uart_rx_buf, tvm_uart); - __stdout_hook_install(&write_hook); - - utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); - TVMLogf("uTVM On-Device Runtime"); - - while (true) { - uint8_t buf[256]; - int bytes_read = uart_rx_buf_read(&uart_rx_buf, buf, sizeof(buf)); - if (bytes_read > 0) { - size_t bytes_remaining = bytes_read; - uint8_t* cursor = buf; - while (bytes_remaining > 0) { - tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); - if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { - TVMPlatformAbort(err); - } - } - } - } - -#ifdef CONFIG_ARCH_POSIX - posix_exit(0); -#endif -} diff --git a/tests/micro/zephyr/README.md b/tests/micro/zephyr/README.md new file mode 100644 index 000000000000..4a8b4049ea4b --- /dev/null +++ b/tests/micro/zephyr/README.md @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + +This directory contains units tests for MicroTVM integration with Zephyr. + +To run the test, you first need to be running in a Python environment with +all of the appropriate TVM dependencies installed. If you have [Poetry](https://python-poetry.org/) +installed, you can do the following to get an appropriately-configured Python +environment: + +``` +$ cd tvm/apps/microtvm/ +$ poetry lock && poetry install && poetry shell +``` + +You can then run this test using: + +``` +$ cd tvm/tests/micro/zephyr +$ pytest test_zephyr.py --microtvm-platforms=nrf5340dk +``` diff --git a/tests/micro/qemu/conftest.py b/tests/micro/zephyr/conftest.py similarity index 100% rename from tests/micro/qemu/conftest.py rename to tests/micro/zephyr/conftest.py diff --git a/tests/micro/qemu/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py similarity index 97% rename from tests/micro/qemu/test_zephyr.py rename to tests/micro/zephyr/test_zephyr.py index 4c8bd5f5dae8..c21d1e6e0b03 100644 --- a/tests/micro/qemu/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -62,15 +62,17 @@ def _make_session(model, target, zephyr_board, west_cmd, mod): os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) - project_dir = os.path.join(os.path.dirname(__file__) or ".", "zephyr-runtime") + test_dir = os.path.dirname(os.path.realpath(os.path.expanduser(__file__))) + tvm_source_dir = os.path.join(test_dir, "..", "..", "..") + runtime_path = os.path.join(tvm_source_dir, "apps", "microtvm", "zephyr", "demo_runtime") compiler = zephyr.ZephyrCompiler( - project_dir=project_dir, + project_dir=runtime_path, board=zephyr_board, zephyr_toolchain_variant="zephyr", west_cmd=west_cmd, ) - opts = tvm.micro.default_options(f"{project_dir}/crt") + opts = tvm.micro.default_options(os.path.join(runtime_path, "crt")) # TODO(weberlo) verify this is necessary opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] opts["lib_opts"]["ccflags"] = ["-std=gnu++14"] From b62ab3895910ea4a1b2fd076957fbf90647849eb Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 13:44:01 -0800 Subject: [PATCH 15/53] Working on QEMU support. Need to add board-specific prj.conf files. --- apps/microtvm/pyproject.toml | 2 ++ .../zephyr/demo_runtime/CMakeLists.txt | 7 +++++- apps/microtvm/zephyr/demo_runtime/prj.conf | 23 +++++++++++++++++++ docs/microtvm/index.rst | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/microtvm/pyproject.toml b/apps/microtvm/pyproject.toml index 096736ae52aa..52de9c265661 100644 --- a/apps/microtvm/pyproject.toml +++ b/apps/microtvm/pyproject.toml @@ -69,6 +69,8 @@ tornado = "^6" typed_ast = "^1.4" pyyaml = "^5.4.1" pyserial = "^3.5" +pyelftools = "^0.27" + # AutoTVM diff --git a/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt b/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt index 61011a44678e..a99d5edb07e6 100644 --- a/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt +++ b/apps/microtvm/zephyr/demo_runtime/CMakeLists.txt @@ -1,8 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.13.1) + +set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") + +set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. + find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) -project(microtvm_zephyr_demo_runtime) +project(microtvm_zephyr_runtime) set(CMAKE_VERBOSE_MAKEFILE ON) file(GLOB TVM_SOURCES ${CMAKE_SOURCE_DIR}/__tvm*.c) diff --git a/apps/microtvm/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf index 33bff7f12a58..2cb4f3828cb3 100644 --- a/apps/microtvm/zephyr/demo_runtime/prj.conf +++ b/apps/microtvm/zephyr/demo_runtime/prj.conf @@ -14,6 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +# +# For nRF5340 +# XXX MDW - Put these in a board-specific conf file. # For UART implementation in main(). CONFIG_RING_BUFFER=y @@ -44,3 +47,23 @@ CONFIG_RESET_ON_FATAL_ERROR=n # For debugging. CONFIG_LED=y + +# For QEMU +## For UART implementation in main(). +#CONFIG_RING_BUFFER=y +#CONFIG_UART_CONSOLE=n +#CONFIG_UART_INTERRUPT_DRIVEN=y +# +## For RPC server C++ bindings. +#CONFIG_CPLUSPLUS=y +#CONFIG_NEWLIB_LIBC=y +# +## For models with floating point. +#CONFIG_FPU=y +# +## For TVMPlatformAbort(). +#CONFIG_REBOOT=y +# +## For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. +#CONFIG_TEST_RANDOM_GENERATOR=y +#CONFIG_TIMER_RANDOM_GENERATOR=y diff --git a/docs/microtvm/index.rst b/docs/microtvm/index.rst index 2371219af27f..a67b1547d229 100644 --- a/docs/microtvm/index.rst +++ b/docs/microtvm/index.rst @@ -43,7 +43,7 @@ demos run against QEMU and the following hardware: * `STM Nucleo-F746ZG `_ * `STM STM32F746 Discovery `_ -* `nRF 5340 Preview Development Kit `_ +* `nRF 5340 Development Kit `_ Getting Started with microTVM From 853f01e4354614cc5790d2d5b083c1abb72b5fbe Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 15:49:20 -0800 Subject: [PATCH 16/53] Adding board-specific prj.conf files. --- .../boards/nrf5340dk_nrf5340_cpuapp.conf | 33 +++++++++++++++ .../zephyr/demo_runtime/boards/qemu_x86.conf | 23 +++++++++++ apps/microtvm/zephyr/demo_runtime/prj.conf | 40 ++----------------- .../demo_runtime/qemu-hack/qemu-system-i386 | 33 +++++++++++++++ 4 files changed, 92 insertions(+), 37 deletions(-) create mode 100644 apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf create mode 100644 apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf create mode 100755 apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 diff --git a/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf b/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 000000000000..a7df4d25c4c3 --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# This file is specific to the nRF5340 DK board. + +# For intrinsics used by generated optimized operators. +CONFIG_CMSIS_DSP=y + +# Required for Cortex-M33 devices. +CONFIG_MAIN_STACK_SIZE=1536 + +# For random number generation. +CONFIG_ENTROPY_GENERATOR=y + +# Allow for k_sys_fatal_error_handler to be overridden. +CONFIG_RESET_ON_FATAL_ERROR=n + +# For debugging. +CONFIG_LED=y diff --git a/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf b/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf new file mode 100644 index 000000000000..f29dbf9c574d --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# This file is specific to the QEMU-emulated uTVM board. + +# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_TIMER_RANDOM_GENERATOR=y + diff --git a/apps/microtvm/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf index 2cb4f3828cb3..06d10fc5d417 100644 --- a/apps/microtvm/zephyr/demo_runtime/prj.conf +++ b/apps/microtvm/zephyr/demo_runtime/prj.conf @@ -14,9 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -# -# For nRF5340 -# XXX MDW - Put these in a board-specific conf file. + +# The settings in this file are generic for all boards, and are merged +# with the settings in the file boards/.conf. # For UART implementation in main(). CONFIG_RING_BUFFER=y @@ -33,37 +33,3 @@ CONFIG_FPU=y # For TVMPlatformAbort(). CONFIG_REBOOT=y -# For intrinsics used by generated optimized operators. -CONFIG_CMSIS_DSP=y - -# Required for Cortex-M33 devices. -CONFIG_MAIN_STACK_SIZE=1536 - -# For random number generation. -CONFIG_ENTROPY_GENERATOR=y - -# Allow for k_sys_fatal_error_handler to be overridden. -CONFIG_RESET_ON_FATAL_ERROR=n - -# For debugging. -CONFIG_LED=y - -# For QEMU -## For UART implementation in main(). -#CONFIG_RING_BUFFER=y -#CONFIG_UART_CONSOLE=n -#CONFIG_UART_INTERRUPT_DRIVEN=y -# -## For RPC server C++ bindings. -#CONFIG_CPLUSPLUS=y -#CONFIG_NEWLIB_LIBC=y -# -## For models with floating point. -#CONFIG_FPU=y -# -## For TVMPlatformAbort(). -#CONFIG_REBOOT=y -# -## For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. -#CONFIG_TEST_RANDOM_GENERATOR=y -#CONFIG_TIMER_RANDOM_GENERATOR=y diff --git a/apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 b/apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 new file mode 100755 index 000000000000..a0bf0f2c4dee --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386 @@ -0,0 +1,33 @@ +#!/bin/bash -e +# 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. + +# Zephyr insists on running qemu with a -pidfile option, but that option doesn't appear to +# work given the way we've configured docker (the underlying filesystem doesn't suppor the +# file locking it needs to). This script strips any -pidfile option, then invokes qemu. + +ARGS=( "$(basename $0)" ) +while [ "$#" -gt 0 ]; do + if [ "$1" == "-pidfile" ]; then + shift + else + ARGS=( "${ARGS[@]}" "$1" ) + fi + shift +done + +"${ARGS[@]}" From 4b1da9a73064403914465997ff6dffed08127556 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Feb 2021 15:55:24 -0800 Subject: [PATCH 17/53] Some cleanup. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 62 ++++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 053e2386238e..8209382353e2 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -51,16 +51,8 @@ #include "crt_config.h" -//K_SEM_DEFINE(tx_sem, 0, 1); // XXX MDW NEEDED? - static const struct device* tvm_uart; -// XXX MDW needed? -//int write_hook(int c) { -// uart_poll_out(tvm_uart, c); -// return 0; -//} - #ifdef CONFIG_LED /* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) @@ -78,6 +70,7 @@ static size_t g_transmit_data_size = 0; static volatile size_t g_transmitted_bytes = 0; static volatile bool g_transmit_complete = true; +// Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED gpio_pin_set(led_pin, PIN, 1); @@ -117,12 +110,32 @@ void TVMPlatformAbort(tvm_crt_error_t error) { for (;;) ; } -uint32_t g_utvm_start_time; +// Used by TVM to generate random data. +tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { + uint32_t random; // one unit of random data. + + // Fill parts of `buffer` which are as large as `random`. + size_t num_full_blocks = num_bytes / sizeof(random); + for (int i = 0; i < num_full_blocks; ++i) { + random = sys_rand32_get(); + memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); + } + + // Fill any leftover tail which is smaller than `random`. + size_t num_tail_bytes = num_bytes % sizeof(random); + if (num_tail_bytes > 0) { + random = sys_rand32_get(); + memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); + } + + return kTvmErrorNoError; +} #define MILLIS_TIL_EXPIRY 200 #define TIME_TIL_EXPIRY (K_MSEC(MILLIS_TIL_EXPIRY)) K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); +uint32_t g_utvm_start_time; int g_utvm_timer_running = 0; // Used to start system timer. @@ -192,11 +205,13 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { // Memory pool for use by TVMPlatformMemoryAllocate. K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); +// Used by TVM to allocate memory. tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; } +// Used by TVM to deallocate memory. tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { k_free(ptr); return kTvmErrorNoError; @@ -207,9 +222,9 @@ struct uart_rx_buf_t { struct ring_buf buf; uint32_t buffer[RING_BUF_SIZE]; }; - struct uart_rx_buf_t uart_rx_buf; +// UART interrupt callback. void uart_irq_cb(const struct device* dev, void* user_data) { while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; @@ -230,12 +245,14 @@ void uart_irq_cb(const struct device* dev, void* user_data) { } } +// Used to initialize the UART receiver. void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); uart_irq_rx_enable(dev); } +// Used to read data from the UART. int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { unsigned int key = irq_lock(); int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); @@ -243,6 +260,7 @@ int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_ return bytes_read; } +// The main function of this application. extern void __stdout_hook_install(int (*hook)(int)); void main(void) { #ifdef CONFIG_LED @@ -257,11 +275,12 @@ void main(void) { gpio_pin_set(led_pin, PIN, 1); #endif - /* Claim console device. */ + // Claim console device. tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); const struct device* shadow_tvm_uart = tvm_uart; uart_rx_init(&uart_rx_buf, tvm_uart); + // Initialize uTVM RPC server, which will receive commands from the UART and execute them. utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); TVMLogf("uTVM Zephyr runtime - running"); #ifdef CONFIG_LED @@ -278,6 +297,7 @@ void main(void) { size_t bytes_remaining = bytes_read; uint8_t* cursor = buf; while (bytes_remaining > 0) { + // Pass the received bytes to the RPC server. tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { TVMPlatformAbort(err); @@ -298,24 +318,4 @@ void main(void) { #endif } -// Used by TVM to generate random data. -tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { - uint32_t random; // one unit of random data. - - // Fill parts of `buffer` which are as large as `random`. - size_t num_full_blocks = num_bytes / sizeof(random); - for (int i = 0; i < num_full_blocks; ++i) { - random = sys_rand32_get(); - memcpy(&buffer[i * sizeof(random)], &random, sizeof(random)); - } - - // Fill any leftover tail which is smaller than `random`. - size_t num_tail_bytes = num_bytes % sizeof(random); - if (num_tail_bytes > 0) { - random = sys_rand32_get(); - memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); - } - - return kTvmErrorNoError; -} From 058985b9c42a0238c3245ebd07db71c04368735b Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sun, 28 Feb 2021 20:04:07 -0800 Subject: [PATCH 18/53] Lots of hacking to get ONNX model to run on QEMU and nRF5340. Added test_onnx unit test. Still need to clean up tutorial. --- tutorials/micro/micro_tflite.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tutorials/micro/micro_tflite.py b/tutorials/micro/micro_tflite.py index 6ad0da5aecba..ffe4eef3fab8 100644 --- a/tutorials/micro/micro_tflite.py +++ b/tutorials/micro/micro_tflite.py @@ -177,7 +177,7 @@ # Now we create a build config for relay. turning off two options # and then calling relay.build which will result in a C source # file. When running on a simulated target, choose "host" below: -TARGET = tvm.target.target.micro("host") +#TARGET = tvm.target.target.micro("host") # %% # Compiling for physical hardware @@ -190,8 +190,9 @@ # # .. code-block:: python # -# TARGET = tvm.target.target.micro("stm32f746xx") -# BOARD = "nucleo_f746zg" # or "stm32f746g_disco" +TARGET = tvm.target.target.micro("host") +#BOARD = "nucleo_f746zg" # or "stm32f746g_disco" +BOARD = "qemu_x86" ###################################################################### # Now, compile the model for the target: From fe2c9a1662bfa01f590866cc5f23fc05ebeffb51 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sat, 27 Feb 2021 16:56:08 -0800 Subject: [PATCH 19/53] Lots of hacking to get ONNX model to run on QEMU and nRF5340. Added test_onnx unit test. Still need to clean up tutorial. --- .../zephyr/demo_runtime/crt/crt_config.h | 2 +- apps/microtvm/zephyr/demo_runtime/src/main.c | 140 ++++++++++++--- tests/micro/zephyr/test_zephyr.py | 57 +++++- tutorials/micro/data/digit-2.jpg | Bin 0 -> 572 bytes tutorials/micro/data/digit-9.jpg | Bin 0 -> 535 bytes tutorials/micro/micro_onnx.py | 170 ++++++++++++++++++ 6 files changed, 337 insertions(+), 32 deletions(-) create mode 100644 tutorials/micro/data/digit-2.jpg create mode 100644 tutorials/micro/data/digit-9.jpg create mode 100755 tutorials/micro/micro_onnx.py diff --git a/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h b/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h index a7f4f90b0538..f8fc7514a28d 100644 --- a/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h +++ b/apps/microtvm/zephyr/demo_runtime/crt/crt_config.h @@ -59,6 +59,6 @@ /*! \brief Number of pages on device. */ #define TVM_CRT_MAX_PAGES 300 -//#define TVM_CRT_FRAMER_ENABLE_LOGS +// #define TVM_CRT_FRAMER_ENABLE_LOGS #endif // TVM_RUNTIME_CRT_CONFIG_H_ diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 8209382353e2..7a7539c82147 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -53,13 +53,33 @@ static const struct device* tvm_uart; +// XXX MDW - TODO: Clean up the LED stuff in here to remove the 4 LEDs. + #ifdef CONFIG_LED -/* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) #define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) -#define PIN DT_GPIO_PIN(LED0_NODE, gpios) -#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) -static const struct device* led_pin; +#define LED0_PIN DT_GPIO_PIN(LED0_NODE, gpios) +#define LED0_FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) +static const struct device* led0_pin; + +#define LED1_NODE DT_ALIAS(led1) +#define LED1 DT_GPIO_LABEL(LED1_NODE, gpios) +#define LED1_PIN DT_GPIO_PIN(LED1_NODE, gpios) +#define LED1_FLAGS DT_GPIO_FLAGS(LED1_NODE, gpios) +static const struct device* led1_pin; + +#define LED2_NODE DT_ALIAS(led2) +#define LED2 DT_GPIO_LABEL(LED2_NODE, gpios) +#define LED2_PIN DT_GPIO_PIN(LED2_NODE, gpios) +#define LED2_FLAGS DT_GPIO_FLAGS(LED2_NODE, gpios) +static const struct device* led2_pin; + +#define LED3_NODE DT_ALIAS(led3) +#define LED3 DT_GPIO_LABEL(LED3_NODE, gpios) +#define LED3_PIN DT_GPIO_PIN(LED3_NODE, gpios) +#define LED3_FLAGS DT_GPIO_FLAGS(LED3_NODE, gpios) +static const struct device* led3_pin; + #endif // CONFIG_LED static size_t g_num_bytes_requested = 0; @@ -73,7 +93,7 @@ static volatile bool g_transmit_complete = true; // Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); +// gpio_pin_set(led0_pin, LED0_PIN, 1); #endif g_num_bytes_requested += size; @@ -83,7 +103,7 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { } #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); +// gpio_pin_set(led0_pin, LED0_PIN, 0); #endif return size; @@ -93,7 +113,8 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { // Here, we turn on the LED and spin. void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led2_pin, LED2_PIN, 1); #endif for (;;) ; } @@ -104,9 +125,14 @@ size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); } +static volatile tvm_crt_error_t fatal_error; // Used by TVM when an abort operation occurs. void TVMPlatformAbort(tvm_crt_error_t error) { - sys_reboot(SYS_REBOOT_COLD); + fatal_error = error; // Save it in a global for debugging. + //sys_reboot(SYS_REBOOT_COLD); +#ifdef CONFIG_LED + gpio_pin_set(led3_pin, LED3_PIN, 1); +#endif for (;;) ; } @@ -146,7 +172,7 @@ tvm_crt_error_t TVMPlatformTimerStart() { } #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 1); +// gpio_pin_set(led0_pin, LED0_PIN, 1); #endif k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); g_utvm_start_time = k_cycle_get_32(); @@ -163,7 +189,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { uint32_t stop_time = k_cycle_get_32(); #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); +// gpio_pin_set(led0_pin, LED0_PIN, 0); #endif // compute how long the work took @@ -217,29 +243,44 @@ tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { return kTvmErrorNoError; } -#define RING_BUF_SIZE 512 +// Ring buffer used to store data read from the UART on rx interrupt. +#define RING_BUF_SIZE 20 * 1024 struct uart_rx_buf_t { struct ring_buf buf; uint32_t buffer[RING_BUF_SIZE]; }; struct uart_rx_buf_t uart_rx_buf; +// Small buffer used to read data from the UART into the ring buffer. +static uint8_t uart_data[32]; + // UART interrupt callback. void uart_irq_cb(const struct device* dev, void* user_data) { while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; if (uart_irq_rx_ready(dev) != 0) { - uint8_t data[32]; for (;;) { - int bytes_read = uart_fifo_read(dev, data, sizeof(data)); + // Read a small chunk of data from the UART. + int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data)); if (bytes_read < 0) { +#ifdef CONFIG_LED + gpio_pin_set(led2_pin, LED2_PIN, 1); +#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef1); } else if (bytes_read == 0) { break; } - int bytes_written = ring_buf_put(&buf->buf, data, bytes_read); - CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, - bytes_written); + // Write it into the ring buffer. + int bytes_written = ring_buf_put(&buf->buf, uart_data, bytes_read); + if (bytes_read != bytes_written) { +#ifdef CONFIG_LED + gpio_pin_set(led1_pin, LED1_PIN, 1); + gpio_pin_set(led2_pin, LED2_PIN, 1); +#endif + TVMPlatformAbort((tvm_crt_error_t)0xbeef2); + } + //CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, + // bytes_written); } } } @@ -260,19 +301,53 @@ int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_ return bytes_read; } +// Buffer used to read from the UART rx ring buffer and feed it to the UTvmRpcServerLoop. +static uint8_t main_rx_buf[RING_BUF_SIZE]; + // The main function of this application. extern void __stdout_hook_install(int (*hook)(int)); void main(void) { #ifdef CONFIG_LED - led_pin = device_get_binding(LED0); - if (led_pin == NULL) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef2); + int ret; + led0_pin = device_get_binding(LED0); + if (led0_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led0_pin, LED0_PIN, GPIO_OUTPUT_ACTIVE | LED0_FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); + } + gpio_pin_set(led0_pin, LED0_PIN, 1); + + led1_pin = device_get_binding(LED1); + if (led1_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led1_pin, LED1_PIN, GPIO_OUTPUT_ACTIVE | LED1_FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); + } + gpio_pin_set(led1_pin, LED1_PIN, 1); + + led2_pin = device_get_binding(LED2); + if (led2_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led2_pin, LED2_PIN, GPIO_OUTPUT_ACTIVE | LED2_FLAGS); + if (ret < 0) { + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); } - int ret = gpio_pin_configure(led_pin, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); + gpio_pin_set(led2_pin, LED2_PIN, 1); + + led3_pin = device_get_binding(LED3); + if (led3_pin == NULL) { + for (;;) ; + } + ret = gpio_pin_configure(led3_pin, LED3_PIN, GPIO_OUTPUT_ACTIVE | LED3_FLAGS); if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef3); + TVMPlatformAbort((tvm_crt_error_t)0xbeef4); } - gpio_pin_set(led_pin, PIN, 1); + gpio_pin_set(led3_pin, LED3_PIN, 1); #endif // Claim console device. @@ -284,27 +359,36 @@ void main(void) { utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); TVMLogf("uTVM Zephyr runtime - running"); #ifdef CONFIG_LED - gpio_pin_set(led_pin, PIN, 0); + gpio_pin_set(led0_pin, LED0_PIN, 0); + gpio_pin_set(led1_pin, LED1_PIN, 0); + gpio_pin_set(led2_pin, LED2_PIN, 0); + gpio_pin_set(led3_pin, LED3_PIN, 0); #endif - // The main application loop. We continuously read commands from the UART // and dispatch them to UTvmRpcServerLoop(). while (true) { - uint8_t buf[256]; - int bytes_read = uart_rx_buf_read(&uart_rx_buf, buf, sizeof(buf)); + //uint8_t buf[256]; + int bytes_read = uart_rx_buf_read(&uart_rx_buf, main_rx_buf, sizeof(main_rx_buf)); if (bytes_read > 0) { size_t bytes_remaining = bytes_read; - uint8_t* cursor = buf; + uint8_t* cursor = main_rx_buf; while (bytes_remaining > 0) { // Pass the received bytes to the RPC server. tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { +#ifdef CONFIG_LED + gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led2_pin, LED2_PIN, 1); +#endif TVMPlatformAbort(err); } if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { if (g_num_bytes_written != g_num_bytes_requested) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); +#ifdef CONFIG_LED + gpio_pin_set(led1_pin, LED1_PIN, 1); +#endif + TVMPlatformAbort((tvm_crt_error_t)0xbeef5); } g_num_bytes_written = 0; g_num_bytes_requested = 0; diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index c21d1e6e0b03..22bce39b91c3 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -19,12 +19,15 @@ import copy import datetime import glob +import logging import os import subprocess import sys import pytest import numpy as np +import onnx +from PIL import Image import tvm import tvm.rpc @@ -36,11 +39,14 @@ from tvm.relay.expr_functor import ExprMutator from tvm.relay.op.annotation import compiler_begin, compiler_end +# If set, build the uTVM binary from scratch on each test. +# Otherwise, reuses the build from the previous test run. BUILD = True -DEBUG = False - -TARGET = None +# If set, enable a debug session while the test is running. +# Before running the test, in a separate shell, you should run: +# python -m tvm.exec.microtvm_debug_shell +DEBUG = False def _make_sess_from_op(model, zephyr_board, west_cmd, op_name, sched, arg_bufs): @@ -203,6 +209,51 @@ def test_relay(platform, west_cmd): tvm.testing.assert_allclose(result, x_in * x_in + 1) +def test_onnx(platform, west_cmd): + """Testing a simple ONNX model.""" + model, zephyr_board = PLATFORMS[platform] + + # Load test images. + digit_2 = Image.open("testdata/digit-2.jpg").resize((28, 28)) + digit_2 = np.asarray(digit_2).astype("float32") + digit_2 = np.expand_dims(digit_2, axis=0) + + digit_9 = Image.open("testdata/digit-9.jpg").resize((28, 28)) + digit_9 = np.asarray(digit_9).astype("float32") + digit_9 = np.expand_dims(digit_9, axis=0) + + # Load ONNX model and convert to Relay. + onnx_model = onnx.load("testdata/mnist-8.onnx") + shape = (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) + + # We add the -link-params=1 option to ensure the model parameters are compiled in. + # There is currently a bug preventing the demo_runtime environment from receiving + # the model weights when set using graph_mod.set_input(). + 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_json() + + with _make_session(model, target, zephyr_board, west_cmd, lowered.lib) as session: + graph_mod = tvm.micro.create_local_graph_runtime( + graph, session.get_system_lib(), session.context + ) + + # 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).asnumpy() + 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).asnumpy() + assert np.argmax(result) == 9 + + class CcompilerAnnotator(ExprMutator): """ This is used to create external functions for ccompiler. diff --git a/tutorials/micro/data/digit-2.jpg b/tutorials/micro/data/digit-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b709a206b8d776215dcaa78643b22fe628b3c43a GIT binary patch literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} literal 0 HcmV?d00001 diff --git a/tutorials/micro/data/digit-9.jpg b/tutorials/micro/data/digit-9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ce9cde3b322b351847da179555aba35358dbf23 GIT binary patch literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja Date: Sat, 27 Feb 2021 16:56:49 -0800 Subject: [PATCH 20/53] Adding data for unit tests. --- tests/micro/zephyr/testdata/digit-2.jpg | Bin 0 -> 572 bytes tests/micro/zephyr/testdata/digit-9.jpg | Bin 0 -> 535 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/micro/zephyr/testdata/digit-2.jpg create mode 100644 tests/micro/zephyr/testdata/digit-9.jpg diff --git a/tests/micro/zephyr/testdata/digit-2.jpg b/tests/micro/zephyr/testdata/digit-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b709a206b8d776215dcaa78643b22fe628b3c43a GIT binary patch literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} literal 0 HcmV?d00001 diff --git a/tests/micro/zephyr/testdata/digit-9.jpg b/tests/micro/zephyr/testdata/digit-9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ce9cde3b322b351847da179555aba35358dbf23 GIT binary patch literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja Date: Sat, 27 Feb 2021 17:00:57 -0800 Subject: [PATCH 21/53] Cleanup demo_runtime code. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 83 ++------------------ 1 file changed, 6 insertions(+), 77 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 7a7539c82147..b10b0700b369 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -53,33 +53,12 @@ static const struct device* tvm_uart; -// XXX MDW - TODO: Clean up the LED stuff in here to remove the 4 LEDs. - #ifdef CONFIG_LED #define LED0_NODE DT_ALIAS(led0) #define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) #define LED0_PIN DT_GPIO_PIN(LED0_NODE, gpios) #define LED0_FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) static const struct device* led0_pin; - -#define LED1_NODE DT_ALIAS(led1) -#define LED1 DT_GPIO_LABEL(LED1_NODE, gpios) -#define LED1_PIN DT_GPIO_PIN(LED1_NODE, gpios) -#define LED1_FLAGS DT_GPIO_FLAGS(LED1_NODE, gpios) -static const struct device* led1_pin; - -#define LED2_NODE DT_ALIAS(led2) -#define LED2 DT_GPIO_LABEL(LED2_NODE, gpios) -#define LED2_PIN DT_GPIO_PIN(LED2_NODE, gpios) -#define LED2_FLAGS DT_GPIO_FLAGS(LED2_NODE, gpios) -static const struct device* led2_pin; - -#define LED3_NODE DT_ALIAS(led3) -#define LED3 DT_GPIO_LABEL(LED3_NODE, gpios) -#define LED3_PIN DT_GPIO_PIN(LED3_NODE, gpios) -#define LED3_FLAGS DT_GPIO_FLAGS(LED3_NODE, gpios) -static const struct device* led3_pin; - #endif // CONFIG_LED static size_t g_num_bytes_requested = 0; @@ -93,7 +72,7 @@ static volatile bool g_transmit_complete = true; // Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); #endif g_num_bytes_requested += size; @@ -103,7 +82,7 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { } #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 0); + gpio_pin_set(led0_pin, LED0_PIN, 0); #endif return size; @@ -114,7 +93,6 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 1); - gpio_pin_set(led2_pin, LED2_PIN, 1); #endif for (;;) ; } @@ -125,13 +103,11 @@ size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); } -static volatile tvm_crt_error_t fatal_error; // Used by TVM when an abort operation occurs. void TVMPlatformAbort(tvm_crt_error_t error) { - fatal_error = error; // Save it in a global for debugging. - //sys_reboot(SYS_REBOOT_COLD); + sys_reboot(SYS_REBOOT_COLD); #ifdef CONFIG_LED - gpio_pin_set(led3_pin, LED3_PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); #endif for (;;) ; } @@ -172,7 +148,7 @@ tvm_crt_error_t TVMPlatformTimerStart() { } #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 1); + gpio_pin_set(led0_pin, LED0_PIN, 1); #endif k_timer_start(&g_utvm_timer, TIME_TIL_EXPIRY, TIME_TIL_EXPIRY); g_utvm_start_time = k_cycle_get_32(); @@ -189,7 +165,7 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { uint32_t stop_time = k_cycle_get_32(); #ifdef CONFIG_LED -// gpio_pin_set(led0_pin, LED0_PIN, 0); + gpio_pin_set(led0_pin, LED0_PIN, 0); #endif // compute how long the work took @@ -263,9 +239,6 @@ void uart_irq_cb(const struct device* dev, void* user_data) { // Read a small chunk of data from the UART. int bytes_read = uart_fifo_read(dev, uart_data, sizeof(uart_data)); if (bytes_read < 0) { -#ifdef CONFIG_LED - gpio_pin_set(led2_pin, LED2_PIN, 1); -#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef1); } else if (bytes_read == 0) { break; @@ -273,10 +246,6 @@ void uart_irq_cb(const struct device* dev, void* user_data) { // Write it into the ring buffer. int bytes_written = ring_buf_put(&buf->buf, uart_data, bytes_read); if (bytes_read != bytes_written) { -#ifdef CONFIG_LED - gpio_pin_set(led1_pin, LED1_PIN, 1); - gpio_pin_set(led2_pin, LED2_PIN, 1); -#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef2); } //CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, @@ -318,36 +287,6 @@ void main(void) { TVMPlatformAbort((tvm_crt_error_t)0xbeef4); } gpio_pin_set(led0_pin, LED0_PIN, 1); - - led1_pin = device_get_binding(LED1); - if (led1_pin == NULL) { - for (;;) ; - } - ret = gpio_pin_configure(led1_pin, LED1_PIN, GPIO_OUTPUT_ACTIVE | LED1_FLAGS); - if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); - } - gpio_pin_set(led1_pin, LED1_PIN, 1); - - led2_pin = device_get_binding(LED2); - if (led2_pin == NULL) { - for (;;) ; - } - ret = gpio_pin_configure(led2_pin, LED2_PIN, GPIO_OUTPUT_ACTIVE | LED2_FLAGS); - if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); - } - gpio_pin_set(led2_pin, LED2_PIN, 1); - - led3_pin = device_get_binding(LED3); - if (led3_pin == NULL) { - for (;;) ; - } - ret = gpio_pin_configure(led3_pin, LED3_PIN, GPIO_OUTPUT_ACTIVE | LED3_FLAGS); - if (ret < 0) { - TVMPlatformAbort((tvm_crt_error_t)0xbeef4); - } - gpio_pin_set(led3_pin, LED3_PIN, 1); #endif // Claim console device. @@ -360,9 +299,6 @@ void main(void) { TVMLogf("uTVM Zephyr runtime - running"); #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 0); - gpio_pin_set(led1_pin, LED1_PIN, 0); - gpio_pin_set(led2_pin, LED2_PIN, 0); - gpio_pin_set(led3_pin, LED3_PIN, 0); #endif // The main application loop. We continuously read commands from the UART @@ -377,17 +313,10 @@ void main(void) { // Pass the received bytes to the RPC server. tvm_crt_error_t err = UTvmRpcServerLoop(server, &cursor, &bytes_remaining); if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { -#ifdef CONFIG_LED - gpio_pin_set(led0_pin, LED0_PIN, 1); - gpio_pin_set(led2_pin, LED2_PIN, 1); -#endif TVMPlatformAbort(err); } if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { if (g_num_bytes_written != g_num_bytes_requested) { -#ifdef CONFIG_LED - gpio_pin_set(led1_pin, LED1_PIN, 1); -#endif TVMPlatformAbort((tvm_crt_error_t)0xbeef5); } g_num_bytes_written = 0; From 6645f50204b5d724384aecf6ccb6268dbaca8774 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sun, 28 Feb 2021 17:27:16 -0800 Subject: [PATCH 22/53] Fix up tutorial. --- tutorials/micro/micro_onnx.py | 245 ++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 99 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 46f5b49aa389..2138e0d4e310 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -1,6 +1,75 @@ #!/usr/bin/env python3 -# This is a MicroTVM version of `mnist.py`. +# 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. + +""" +microTVM with ONNX models +========================= +**Author**: `Matt Welsh ` + +This tutorial is an introduction to compiling and +running an ONNX model on a device using microTVM. +""" + +# Setup +# ----- +# +# Build TVM wth ``USE_MICRO`` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# Download the TVM sources, and build TVM from source following the +# instructions `on this page +# `. +# When bulding TVM, ensure that the following lines are present in +# your ``config.cmake`` file: +# .. code-block:: bash +# +# # Whether enable MicroTVM runtime +# set(USE_MICRO ON) +# +# Install Python dependencies +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# microTVM and this tutorial require a number of Python dependencies -- +# in addition to TVM itself -- to be installed in your Python environment. +# For convenience, we have provided a `Poetry ` +# ``pyproject.toml`` configuration file in the directory ``apps/microtvm``. +# You can use this as follows: +# +# .. code-block:: bash +# +# $ cd $TVM_HOME/apps/microtvm +# $ poetry lock && poetry install +# $ poetry shell +# +# You should now be in a Python virtual environment with all of the appropriate +# dependencies installed. +# +# Install Zephyr +# ^^^^^^^^^^^^^^ +# +# microTVM currently uses the `Zephyr ` RTOS as +# the basis for the device-side runtime. To get started, install Zephyr +# and an appropriate device-specific toolchain using the +# `Zephyr installation instructions `_. +# +# Be sure you are able to build and flash a sample Zephyr program +# (e.g., the "Blinky" demo) to your device before you proceed with this tutorial. import datetime import io @@ -17,107 +86,81 @@ from PIL import Image import numpy as np +###################################################################### +# For this tutorial, we use a pretrained ONNX model implementing +# the MNIST handwritten digit recognition on 28x28 px input images. + MODEL_FILE = "models/mnist-8.onnx" MODEL_SHAPE = (1, 1, 28, 28) -DIGIT_2_IMAGE = "data/digit-2.jpg" -DIGIT_9_IMAGE = "data/digit-9.jpg" INPUT_TENSOR_NAME = "Input3" +onnx_model = onnx.load(MODEL_FILE) +print(f"Loaded ONNX model: {MODEL_FILE}") -def relay_build(mod, params): - TVM_OPT_LEVEL = 3 - target = tvm.target.Target("llvm") - with tvm.transform.PassContext(opt_level=TVM_OPT_LEVEL): - lib = relay.build(mod, target, params=params) - - # Instantiate the Graph runtime. - gmodule = runtime.GraphModule(lib["default"](tvm.cpu())) - print("Built module is:") - print(gmodule) - return gmodule - - -def run_inference(gmodule, sess, perf_eval=True): - - # Load test images. - digit_2 = Image.open(DIGIT_2_IMAGE).resize((28, 28)) - digit_9 = Image.open(DIGIT_9_IMAGE).resize((28, 28)) - digit_2 = np.asarray(digit_2).astype("float32") - digit_9 = np.asarray(digit_9).astype("float32") - digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = np.expand_dims(digit_9, axis=0) - - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) - gmodule.run() - sess.context.sync() - output = gmodule.get_output(0).asnumpy() - print(f"Top result for digit-2 is: {np.argmax(output)}") - - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) - gmodule.run() - output = gmodule.get_output(0).asnumpy() - print(f"Top result for digit-9 is: {np.argmax(output)}") +###################################################################### +# Next, we convert the model to Relay format. +relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=MODEL_SHAPE, freeze_params=True) +relay_mod = relay.transform.DynamicToStatic()(relay_mod) - if perf_eval: - print("Performance eval...") - ctx = tvm.cpu() - ftimer = gmodule.module.time_evaluator("run", ctx, number=25, repeat=3) - prof_res = np.array(ftimer().results) * 1000 # convert to millisecond - print( - "Mean inference time (std dev): %.2f ms (%.2f ms)" - % (np.mean(prof_res), np.std(prof_res)) - ) +###################################################################### +# Next, we lower the Relay model to the specific device we are +# targeting. In this case, we are using the Nordic Semiconductor +# `nRF5340DK development board `. +# This is the device target name used by microTVM. It is used to select default +# options for the device when we use ``tvm.target.target.micro()`` below. +UTVM_TARGET = "nrf5340dk" -UTVM_MODEL = "nrf5340dk" +# This is the board designation used by Zephyr, and required for the compilation process. UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" -#UTVM_MODEL = "host" -#UTVM_ZEPHYR_BOARD = "qemu_x86" -UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime/" -UTVM_WEST_CMD = "west" - - -onnx_model = onnx.load(MODEL_FILE) - -print(f"Loaded ONNX model: {MODEL_FILE}") -print(type(onnx_model)) -print(f"IR version: {onnx_model.ir_version}") -print(f"Producer: {onnx_model.producer_name} {onnx_model.producer_version}") -print(f"Domain: {onnx_model.domain}") -print(f"Version: {onnx_model.model_version}") -print(f"Doc string: {onnx_model.doc_string}") -print(f"Graph name: {onnx_model.graph.name}") -print(f"Graph doc string: {onnx_model.graph.doc_string}") +###################################################################### +# If you wish to run against an emulated Zephyr device using QEMU, +# you can uncomment these lines instead: +# UTVM_TARGET = "host" +# UTVM_ZEPHYR_BOARD = "qemu_x86" -print("Graph:") -print(onnx.helper.printable_graph(onnx_model.graph)) -# Convert to Relay. -relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=MODEL_SHAPE, freeze_params=True) -relay_mod = relay.transform.DynamicToStatic()(relay_mod) +###################################################################### +# We define the TVM target here. +# We add -link-params=1 option here, so that the model parameters are +# included in the resulting binary image. +target = tvm.target.target.micro(UTVM_TARGET, options=["-link-params=1"]) -# Do the build. -# We add -link-params here so the model parameters are included in the compiled build. -target = tvm.target.target.micro(UTVM_MODEL, options=["-link-params=1"]) +###################################################################### +# Now, we do the Relay build. TVM_OPT_LEVEL = 3 with tvm.transform.PassContext(opt_level=TVM_OPT_LEVEL, config={"tir.disable_vectorize": True}): lowered = relay.build(relay_mod, target, params=params) graph_json_str = lowered.get_json() -# gmodule = runtime.GraphModule(lib["default"](target)) - -# Create a uTVM Workspace. +###################################################################### +# Next, create a uTVM Workspace. This is a location where the +# generated Zephyr project will be compiled. workspace_root = os.path.abspath( f'workspace/{datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")}' ) -print(f"Using workspace: {workspace_root}") workspace_parent = os.path.dirname(workspace_root) if not os.path.exists(workspace_parent): os.makedirs(workspace_parent) workspace = tvm.micro.Workspace(debug=True, root=workspace_root) +print(f"Using workspace: {workspace_root}") + +###################################################################### +# Now we create the ``ZephyrCompiler`` object, which generates the +# Zephyr project for hosting the device runtime code, as well as +# generating the device-specific binary from our model. + +# You are welcome to implement your own Zephyr-based runtime environment +# for your project. This runtime is a demo which provides basic capabilities +# for interfacing to the microTVM device code via the device's serial +# port. +UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime/" + +# The ``west`` command is used by Zephyr for compilation and +# flashing devices. +UTVM_WEST_CMD = "west" -# Create Zephyr compiler. compiler = zephyr.ZephyrCompiler( project_dir=UTVM_ZEPHYR_RUNTIME_DIR, board=UTVM_ZEPHYR_BOARD, @@ -125,6 +168,7 @@ def run_inference(gmodule, sess, perf_eval=True): west_cmd=UTVM_WEST_CMD, ) +###################################################################### # Do the actual build. opts = tvm.micro.default_options(f"{UTVM_ZEPHYR_RUNTIME_DIR}/crt") opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] @@ -138,33 +182,36 @@ def run_inference(gmodule, sess, perf_eval=True): bin_opts=opts["bin_opts"], ) -build_tarfile = "./build.tar" -if os.path.exists(build_tarfile): - os.unlink(build_tarfile) -micro_bin.archive(build_tarfile, metadata_only=True) - -DEBUG = False -if DEBUG: - debug_rpc_session = tvm.rpc.connect("127.0.0.1", 9090) - flasher = compiler.flasher(debug_rpc_session=debug_rpc_session, flash_args=["--softreset"]) -else: - flasher = compiler.flasher(flash_args=["--softreset"]) +###################################################################### +# Next, we create a ``tvm.micro.Session`` which handles the details +# of flashing the binary to the device and opening a serial-port +# RPC session with the device for controlling it. +flasher = compiler.flasher() with tvm.micro.Session(binary=micro_bin, flasher=flasher) as sess: mod = tvm.micro.create_local_graph_runtime(graph_json_str, sess.get_system_lib(), sess.context) - print(f"Created runtime: {mod}") - print(f"sess.context.sync()...") - # Populate model parameters. - #for k, v in lowered.params.items(): - # num_bytes = np.prod(v.shape) * 4 - # print(f"MDW: Setting parameter: {k} with size {v.shape}, {num_bytes} bytes") - # mod.set_input(k, v) + # Load test images. + DIGIT_2_IMAGE = "data/digit-2.jpg" + DIGIT_9_IMAGE = "data/digit-9.jpg" + + digit_2 = Image.open(DIGIT_2_IMAGE).resize((28, 28)) + digit_9 = Image.open(DIGIT_9_IMAGE).resize((28, 28)) + digit_2 = np.asarray(digit_2).astype("float32") + digit_9 = np.asarray(digit_9).astype("float32") + digit_2 = np.expand_dims(digit_2, axis=0) + digit_9 = np.expand_dims(digit_9, axis=0) - sess.context.sync() # Ensure all args have been pushed to the device. + # Set the input tensor of the model to the digit-2 test image. + gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) - print(f"mod is: {mod}") - print(f"mod.run is: {mod._run}") - print(f"Running inference...") + # Run inference and get the result. + gmodule.run() + output = gmodule.get_output(0).asnumpy() + print(f"Top result for digit-2 is: {np.argmax(output)}") - run_inference(mod, sess, perf_eval=False) + # Do likewise for the digit-9 image. + gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) + gmodule.run() + output = gmodule.get_output(0).asnumpy() + print(f"Top result for digit-9 is: {np.argmax(output)}") From b6ae7cf0d0c0419f5bfb1d183e65847a53c2c4aa Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sun, 28 Feb 2021 17:32:38 -0800 Subject: [PATCH 23/53] Fix tutorial. --- tutorials/micro/micro_onnx.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 2138e0d4e310..a346ae87469f 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -203,15 +203,15 @@ digit_9 = np.expand_dims(digit_9, axis=0) # Set the input tensor of the model to the digit-2 test image. - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) + mod.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) # Run inference and get the result. - gmodule.run() - output = gmodule.get_output(0).asnumpy() + mod.run() + output = mod.get_output(0).asnumpy() print(f"Top result for digit-2 is: {np.argmax(output)}") # Do likewise for the digit-9 image. - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) - gmodule.run() - output = gmodule.get_output(0).asnumpy() + mod.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) + mod.run() + output = mod.get_output(0).asnumpy() print(f"Top result for digit-9 is: {np.argmax(output)}") From 8934c19e098e8c22f6772b0d785fb694b2880d14 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Mon, 1 Mar 2021 08:49:50 -0800 Subject: [PATCH 24/53] Fix tutorial and runtime. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 6 ------ tutorials/micro/micro_onnx.py | 12 ++---------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index b10b0700b369..a0a01cf75a80 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -64,11 +64,6 @@ static const struct device* led0_pin; static size_t g_num_bytes_requested = 0; static size_t g_num_bytes_written = 0; -static const uint8_t* g_transmit_data = NULL; -static size_t g_transmit_data_size = 0; -static volatile size_t g_transmitted_bytes = 0; -static volatile bool g_transmit_complete = true; - // Used by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED @@ -291,7 +286,6 @@ void main(void) { // Claim console device. tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); - const struct device* shadow_tvm_uart = tvm_uart; uart_rx_init(&uart_rx_buf, tvm_uart); // Initialize uTVM RPC server, which will receive commands from the UART and execute them. diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index a346ae87469f..d1d57cba809b 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -171,16 +171,8 @@ ###################################################################### # Do the actual build. opts = tvm.micro.default_options(f"{UTVM_ZEPHYR_RUNTIME_DIR}/crt") -opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] -opts["lib_opts"]["ccflags"] = ["-std=gnu++14"] - -micro_bin = tvm.micro.build_static_runtime( - workspace, - compiler, - lowered.lib, - lib_opts=opts["lib_opts"], - bin_opts=opts["bin_opts"], -) + +micro_bin = tvm.micro.build_static_runtime(workspace, compiler, lowered.lib, opts) ###################################################################### # Next, we create a ``tvm.micro.Session`` which handles the details From fbdc1cb53cceec0f6329d78f82a09c2c3186fde0 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Mon, 1 Mar 2021 09:13:34 -0800 Subject: [PATCH 25/53] Fix merge conflicts. --- tutorials/micro/micro_onnx.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index b620cb8110ff..d1d57cba809b 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -171,21 +171,8 @@ ###################################################################### # Do the actual build. opts = tvm.micro.default_options(f"{UTVM_ZEPHYR_RUNTIME_DIR}/crt") -<<<<<<< HEAD micro_bin = tvm.micro.build_static_runtime(workspace, compiler, lowered.lib, opts) -======= -opts["bin_opts"]["ccflags"] = ["-std=gnu++14"] -opts["lib_opts"]["ccflags"] = ["-std=gnu++14"] - -micro_bin = tvm.micro.build_static_runtime( - workspace, - compiler, - lowered.lib, - lib_opts=opts["lib_opts"], - bin_opts=opts["bin_opts"], -) ->>>>>>> d04e04ddddb435e3a0f4fc5707e027850dc80345 ###################################################################### # Next, we create a ``tvm.micro.Session`` which handles the details @@ -208,7 +195,6 @@ digit_9 = np.expand_dims(digit_9, axis=0) # Set the input tensor of the model to the digit-2 test image. -<<<<<<< HEAD mod.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) # Run inference and get the result. @@ -220,17 +206,4 @@ mod.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) mod.run() output = mod.get_output(0).asnumpy() -======= - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) - - # Run inference and get the result. - gmodule.run() - output = gmodule.get_output(0).asnumpy() - print(f"Top result for digit-2 is: {np.argmax(output)}") - - # Do likewise for the digit-9 image. - gmodule.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) - gmodule.run() - output = gmodule.get_output(0).asnumpy() ->>>>>>> d04e04ddddb435e3a0f4fc5707e027850dc80345 print(f"Top result for digit-9 is: {np.argmax(output)}") From ec2c374ba821f0cbaaaeda6329e21654d240490f Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Mon, 1 Mar 2021 09:22:51 -0800 Subject: [PATCH 26/53] Fix merge conflict. --- tutorials/micro/micro_tflite.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tutorials/micro/micro_tflite.py b/tutorials/micro/micro_tflite.py index 5550c624bced..ffe4eef3fab8 100644 --- a/tutorials/micro/micro_tflite.py +++ b/tutorials/micro/micro_tflite.py @@ -209,15 +209,10 @@ # # First, compile a static microTVM runtime for the targeted device. In this case, the host simulated # device is used. -<<<<<<< HEAD compiler = tvm.micro.DefaultCompiler(target=TARGET) opts = tvm.micro.default_options( os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") ) -======= -#compiler = tvm.micro.DefaultCompiler(target=TARGET) -#opts = tvm.micro.default_options(os.path.join(tvm.micro.CRT_ROOT_DIR, "host")) ->>>>>>> d04e04ddddb435e3a0f4fc5707e027850dc80345 # %% # Compiling for physical hardware @@ -225,7 +220,6 @@ # # .. code-block:: python # -<<<<<<< HEAD # import subprocess # from tvm.micro.contrib import zephyr # @@ -242,19 +236,6 @@ # enable printing memory usage statistics of the runtime image # generated by Zephyr compiler for the physical hardware # logging.basicConfig(level="INFO") -======= -import subprocess -from tvm.micro.contrib import zephyr - -repo_root = subprocess.check_output(["git", "rev-parse", "--show-toplevel"], encoding='utf-8').strip() -project_dir = f"{repo_root}/apps/microtvm/zephyr/demo_runtime" -compiler = zephyr.ZephyrCompiler( - project_dir=project_dir, - board=BOARD if "stm32f746" in str(TARGET) else "qemu_x86", - zephyr_toolchain_variant="zephyr", -) -opts = tvm.micro.default_options(f"{project_dir}/crt") ->>>>>>> d04e04ddddb435e3a0f4fc5707e027850dc80345 workspace = tvm.micro.Workspace() micro_binary = tvm.micro.build_static_runtime( From 2b14aa13acc58cd16f2aafa671a152bcc0789030 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Mon, 1 Mar 2021 09:25:24 -0800 Subject: [PATCH 27/53] Remove redundant files. --- tutorials/micro/data/digit-2.jpg | Bin 572 -> 0 bytes tutorials/micro/data/digit-9.jpg | Bin 535 -> 0 bytes tutorials/micro/micro_onnx.py | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 tutorials/micro/data/digit-2.jpg delete mode 100644 tutorials/micro/data/digit-9.jpg diff --git a/tutorials/micro/data/digit-2.jpg b/tutorials/micro/data/digit-2.jpg deleted file mode 100644 index b709a206b8d776215dcaa78643b22fe628b3c43a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>l0P*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k)}+G+9pM!N9a z+Fgtq;Ufks4k5G6O=vOe_>A$lT&9{hIqq>&me#H&a?UfhKQ?v%>I1(TC zPo;Q8dSAiM82D?!`n~+#AhvSzq6>Sai_G#i?k&erpO=siPeWdhbMfE9x=i;{+Ft7# z+e`yT6p%#>F!kUap}qMH^{zG#jmxgXeWv)|M1xS$?n4;X*+RSr2LNRA&OU5{I`h)J Keu^lfzyI08i0m@} diff --git a/tutorials/micro/data/digit-9.jpg b/tutorials/micro/data/digit-9.jpg deleted file mode 100644 index 6ce9cde3b322b351847da179555aba35358dbf23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 535 zcmV+y0_gq!*#F=F5K2Z#MgRc;000310RRC1+Wgv=4-_35A08bV92_7dE+-%&EF&BoC^soAFflYVG#@89JvcHvE;BST|G)qX2ml-a z9036l0RO}Q9{>OW1pxs80RaI300000000010s{mE1_uZU3Jd?l0JRVR0s#X90t5pE z1q1{D00Dgg0s{a95d{(Xb($mz{*4NnC+Tr5k`=;qQwz?HTobGeo$#u}uE)N`lzywMpFn z0PqlNo02z*Mvg>ebR}6>V1hb=DYU8qU<(BV@=t2}8cXd%P4Nbe;f*80*GEW!Bi*PZ z?Hn;69XR{sV~|fd_pc58pmf`NABf335rk{YDCd_0tL|ja Date: Tue, 2 Mar 2021 12:50:38 -0800 Subject: [PATCH 28/53] Revert dep. --- apps/microtvm/pyproject.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/microtvm/pyproject.toml b/apps/microtvm/pyproject.toml index 52de9c265661..8bfae0a157cd 100644 --- a/apps/microtvm/pyproject.toml +++ b/apps/microtvm/pyproject.toml @@ -53,7 +53,7 @@ exclude = ''' name = "microtvm" version = "0.1.0" description = "" -authors = ["Andrew Reusch "] +authors = [] packages = [ { include = "tvm", from = "../../python" }, ] @@ -69,9 +69,6 @@ tornado = "^6" typed_ast = "^1.4" pyyaml = "^5.4.1" pyserial = "^3.5" -pyelftools = "^0.27" - - # AutoTVM xgboost = {version = "^1.1", optional = true} From 687c09ccb990a041cf45ae7d678768ba680622d3 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Fri, 5 Mar 2021 10:02:29 -0800 Subject: [PATCH 29/53] Fixup --- apps/microtvm/README.md | 2 +- apps/microtvm/zephyr/README.md | 2 +- apps/microtvm/zephyr/demo_runtime/README.md | 2 +- .../zephyr/demo_runtime/boards/qemu_x86.conf | 2 +- apps/microtvm/zephyr/demo_runtime/prj.conf | 3 +- apps/microtvm/zephyr/demo_runtime/src/main.c | 63 +++++++++---------- python/tvm/runtime/module.py | 2 +- python/tvm/target/target.py | 22 ++++--- tests/micro/zephyr/README.md | 13 +++- tests/micro/zephyr/conftest.py | 3 + tests/micro/zephyr/test_zephyr.py | 1 + 11 files changed, 63 insertions(+), 52 deletions(-) diff --git a/apps/microtvm/README.md b/apps/microtvm/README.md index 9cbb16ac00ed..362bc407238e 100644 --- a/apps/microtvm/README.md +++ b/apps/microtvm/README.md @@ -21,7 +21,7 @@ microTVM is the effort that allows TVM to build and execute models on bare-metal The `pyproject.toml` file in this directory can be used to create a [Poetry](https://python-poetry.org/) Python environment with all of the required -dependencies installed. To use it, run: +dependencies installed for running microTVM. To use it, run: ``` $ poetry lock && poetry install diff --git a/apps/microtvm/zephyr/README.md b/apps/microtvm/zephyr/README.md index 52dd8829669e..ad00393c0805 100644 --- a/apps/microtvm/zephyr/README.md +++ b/apps/microtvm/zephyr/README.md @@ -15,5 +15,5 @@ -This directory code to interface uTVM with the [Zephyr RTOS](https://zephyrproject.org/). +This directory code to interface microTVM with the [Zephyr RTOS](https://zephyrproject.org/). diff --git a/apps/microtvm/zephyr/demo_runtime/README.md b/apps/microtvm/zephyr/demo_runtime/README.md index e4dc92409ce5..eab3f3d241a1 100644 --- a/apps/microtvm/zephyr/demo_runtime/README.md +++ b/apps/microtvm/zephyr/demo_runtime/README.md @@ -16,6 +16,6 @@ This directory contains a Zephyr-based "demo" runtime environment that -pulls together the uTVM runtime dependencies into a single application +pulls together the microTVM runtime dependencies into a single application that can communicate with a Python-based host program via the UART, using TVM's RPC protocol. diff --git a/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf b/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf index f29dbf9c574d..e0e4ae2fb2d3 100644 --- a/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf +++ b/apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -# This file is specific to the QEMU-emulated uTVM board. +# This file is specific to the QEMU-emulated microTVM board. # For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random. CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/apps/microtvm/zephyr/demo_runtime/prj.conf b/apps/microtvm/zephyr/demo_runtime/prj.conf index 06d10fc5d417..bf2b330e35a6 100644 --- a/apps/microtvm/zephyr/demo_runtime/prj.conf +++ b/apps/microtvm/zephyr/demo_runtime/prj.conf @@ -16,7 +16,8 @@ # under the License. # The settings in this file are generic for all boards, and are merged -# with the settings in the file boards/.conf. +# with the settings in the file boards/.conf by the Zephyr build +# process. # For UART implementation in main(). CONFIG_RING_BUFFER=y diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index a0a01cf75a80..fbb8bc1905a0 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -25,9 +25,9 @@ /* * This is a sample Zephyr-based application that contains the logic - * needed to upload and control a uTVM-based model via the UART. - * This is only intended to be a demonstration, since typically you - * will want to incorporate this logic into your own application. + * needed to control a microTVM-based model via the UART. This is only + * intended to be a demonstration, since typically you will want to incorporate + * this logic into your own application. */ @@ -64,7 +64,7 @@ static const struct device* led0_pin; static size_t g_num_bytes_requested = 0; static size_t g_num_bytes_written = 0; -// Used by TVM to write serial data to the UART. +// Called by TVM to write serial data to the UART. ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 1); @@ -83,8 +83,8 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { return size; } -// This is an error handler which will be invoked if the device crashes. -// Here, we turn on the LED and spin. +// This is invoked by Zephyr from an exception handler, which will be invoked +// if the device crashes. Here, we turn on the LED and spin. void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 1); @@ -92,13 +92,13 @@ void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { for (;;) ; } -// Used by TVM when a message needs to be formatted. +// Called by TVM when a message needs to be formatted. size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, va_list args) { return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); } -// Used by TVM when an abort operation occurs. +// Called by TVM when an internal invariant is violated, and execution cannot continue. void TVMPlatformAbort(tvm_crt_error_t error) { sys_reboot(SYS_REBOOT_COLD); #ifdef CONFIG_LED @@ -107,7 +107,7 @@ void TVMPlatformAbort(tvm_crt_error_t error) { for (;;) ; } -// Used by TVM to generate random data. +// Called by TVM to generate random data. tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { uint32_t random; // one unit of random data. @@ -135,7 +135,7 @@ K_TIMER_DEFINE(g_utvm_timer, /* expiry func */ NULL, /* stop func */ NULL); uint32_t g_utvm_start_time; int g_utvm_timer_running = 0; -// Used to start system timer. +// Called to start system timer. tvm_crt_error_t TVMPlatformTimerStart() { if (g_utvm_timer_running) { TVMLogf("timer already running"); @@ -151,8 +151,8 @@ tvm_crt_error_t TVMPlatformTimerStart() { return kTvmErrorNoError; } -// Used to stop system timer. -tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { +// Called to stop system timer. +tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { if (!g_utvm_timer_running) { TVMLogf("timer not running"); return kTvmErrorSystemErrorMask | 2; @@ -190,9 +190,9 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { // if we approach the limits of the HW clock datatype (uint32_t), use the // coarse-grained timer result instead if (approx_num_cycles > (0.5 * (~((uint32_t)0)))) { - *res_us = timer_res_ms * 1000.0; + *elapsed_time_seconds = timer_res_ms / 1000.0; } else { - *res_us = hw_clock_res_us; + *elapsed_time_seconds = hw_clock_res_us / 1e6; } g_utvm_timer_running = 0; @@ -202,25 +202,21 @@ tvm_crt_error_t TVMPlatformTimerStop(double* res_us) { // Memory pool for use by TVMPlatformMemoryAllocate. K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); -// Used by TVM to allocate memory. +// Called by TVM to allocate memory. tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; } -// Used by TVM to deallocate memory. +// Called by TVM to deallocate memory. tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { k_free(ptr); return kTvmErrorNoError; } // Ring buffer used to store data read from the UART on rx interrupt. -#define RING_BUF_SIZE 20 * 1024 -struct uart_rx_buf_t { - struct ring_buf buf; - uint32_t buffer[RING_BUF_SIZE]; -}; -struct uart_rx_buf_t uart_rx_buf; +#define RING_BUF_SIZE_BYTES 4 * 1024 +RING_BUF_DECLARE(uart_rx_rbuf, RING_BUF_SIZE_BYTES); // Small buffer used to read data from the UART into the ring buffer. static uint8_t uart_data[32]; @@ -228,7 +224,7 @@ static uint8_t uart_data[32]; // UART interrupt callback. void uart_irq_cb(const struct device* dev, void* user_data) { while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { - struct uart_rx_buf_t* buf = (struct uart_rx_buf_t*)user_data; + struct ring_buf* rbuf = (struct ring_buf*)user_data; if (uart_irq_rx_ready(dev) != 0) { for (;;) { // Read a small chunk of data from the UART. @@ -239,7 +235,7 @@ void uart_irq_cb(const struct device* dev, void* user_data) { break; } // Write it into the ring buffer. - int bytes_written = ring_buf_put(&buf->buf, uart_data, bytes_read); + int bytes_written = ring_buf_put(rbuf, uart_data, bytes_read); if (bytes_read != bytes_written) { TVMPlatformAbort((tvm_crt_error_t)0xbeef2); } @@ -251,22 +247,21 @@ void uart_irq_cb(const struct device* dev, void* user_data) { } // Used to initialize the UART receiver. -void uart_rx_init(struct uart_rx_buf_t* buf, const struct device* dev) { - ring_buf_init(&buf->buf, RING_BUF_SIZE, buf->buffer); - uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)buf); +void uart_rx_init(struct ring_buf* rbuf, const struct device* dev) { + uart_irq_callback_user_data_set(dev, uart_irq_cb, (void*)rbuf); uart_irq_rx_enable(dev); } // Used to read data from the UART. -int uart_rx_buf_read(struct uart_rx_buf_t* buf, uint8_t* data, size_t data_size_bytes) { +int uart_rx_buf_read(struct ring_buf* rbuf, uint8_t* data, size_t data_size_bytes) { unsigned int key = irq_lock(); - int bytes_read = ring_buf_get(&buf->buf, data, data_size_bytes); + int bytes_read = ring_buf_get(rbuf, data, data_size_bytes); irq_unlock(key); return bytes_read; } // Buffer used to read from the UART rx ring buffer and feed it to the UTvmRpcServerLoop. -static uint8_t main_rx_buf[RING_BUF_SIZE]; +static uint8_t main_rx_buf[RING_BUF_SIZE_BYTES]; // The main function of this application. extern void __stdout_hook_install(int (*hook)(int)); @@ -286,11 +281,11 @@ void main(void) { // Claim console device. tvm_uart = device_get_binding(DT_LABEL(DT_CHOSEN(zephyr_console))); - uart_rx_init(&uart_rx_buf, tvm_uart); + uart_rx_init(&uart_rx_rbuf, tvm_uart); - // Initialize uTVM RPC server, which will receive commands from the UART and execute them. + // Initialize microTVM RPC server, which will receive commands from the UART and execute them. utvm_rpc_server_t server = UTvmRpcServerInit(write_serial, NULL); - TVMLogf("uTVM Zephyr runtime - running"); + TVMLogf("microTVM Zephyr runtime - running"); #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 0); #endif @@ -299,7 +294,7 @@ void main(void) { // and dispatch them to UTvmRpcServerLoop(). while (true) { //uint8_t buf[256]; - int bytes_read = uart_rx_buf_read(&uart_rx_buf, main_rx_buf, sizeof(main_rx_buf)); + int bytes_read = uart_rx_buf_read(&uart_rx_rbuf, main_rx_buf, sizeof(main_rx_buf)); if (bytes_read > 0) { size_t bytes_remaining = bytes_read; uint8_t* cursor = main_rx_buf; diff --git a/python/tvm/runtime/module.py b/python/tvm/runtime/module.py index 63267969ab4e..9f4554ef985c 100644 --- a/python/tvm/runtime/module.py +++ b/python/tvm/runtime/module.py @@ -435,7 +435,7 @@ def load_module(path, fmt=""): files = [tar_temp.relpath(x) for x in tar_temp.listdir()] _cc.create_shared(path + ".so", files, cc=cc) path += ".so" - # TODO(weberlo): we should probably use a more distinctive suffix for uTVM object files + # TODO(weberlo): we should probably use a more distinctive suffix for microTVM object files elif path.endswith(".obj"): fmt = "micro_dev" # Redirect to the load API diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index 8c60260e640a..c2e50e6ff2e3 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -237,26 +237,30 @@ def intel_graphics(model="unknown", options=None): return Target(" ".join(["opencl"] + opts)) +"""The set of supported models for tvm.target.micro(). Each value maps to a list of options +for the corresponding target.""" +MICRO_SUPPORTED_MODELS = { + "host": [], + "stm32f746xx": ["-mcpu=cortex-m7", "-march=armv7e-m"], + "nrf5340dk": ["-mcpu=cortex-m33"], +} + + def micro(model="unknown", options=None): """Returns a microTVM target. Parameters ---------- model : str - Canonically identifies the target device. This is typically a CPU or board level name (other - flags such as -mcpu identify the ISA). + Canonically identifies the target device. This is typically a device board level name. + The allowed values are MICRO_SUPPORTED_MODELS.keys(). options : str or list of str Additional options """ - trans_table = { - "host": [], - "stm32f746xx": ["-mcpu=cortex-m7", "-march=armv7e-m"], - "nrf5340dk": ["-mcpu=cortex-m33"], - } - if model not in trans_table: + if model not in MICRO_SUPPORTED_MODELS: raise ValueError(f"Model {model} not supported by tvm.target.micro.") opts = _merge_opts( - trans_table[model] + ["-runtime=c", "--system-lib", f"-model={model}"], + MICRO_SUPPORTED_MODELS[model] + ["-runtime=c", "--system-lib", f"-model={model}"], options, ) diff --git a/tests/micro/zephyr/README.md b/tests/micro/zephyr/README.md index 4a8b4049ea4b..9769cae2b53b 100644 --- a/tests/micro/zephyr/README.md +++ b/tests/micro/zephyr/README.md @@ -15,7 +15,7 @@ -This directory contains units tests for MicroTVM integration with Zephyr. +This directory contains tests for MicroTVM's integration with Zephyr. To run the test, you first need to be running in a Python environment with all of the appropriate TVM dependencies installed. If you have [Poetry](https://python-poetry.org/) @@ -27,9 +27,16 @@ $ cd tvm/apps/microtvm/ $ poetry lock && poetry install && poetry shell ``` -You can then run this test using: +You can then run this test (either on real hardware or on a QEMU-emulated +device) using: ``` $ cd tvm/tests/micro/zephyr -$ pytest test_zephyr.py --microtvm-platforms=nrf5340dk +$ pytest test_zephyr.py --microtvm-platforms=host # For QEMU emulation +$ pytest test_zephyr.py --microtvm-platforms=nrf5340dk # For nRF5340DK +``` + +To see the list of supported values for `--microtvm-platforms`, run: +``` +$ pytest test_zephyr.py --help ``` diff --git a/tests/micro/zephyr/conftest.py b/tests/micro/zephyr/conftest.py index 3fc54df02063..e8ce443adfaf 100644 --- a/tests/micro/zephyr/conftest.py +++ b/tests/micro/zephyr/conftest.py @@ -16,11 +16,14 @@ # under the License. import pytest +import tvm.target.target + def pytest_addoption(parser): parser.addoption( "--microtvm-platforms", default="host", + choices=tvm.target.target.MICRO_SUPPORTED_MODELS.keys(), help=( "Specify a comma-separated list of test models (i.e. as passed to tvm.target.micro()) " "for microTVM tests." diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 22bce39b91c3..09eb97e7ba3d 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -231,6 +231,7 @@ def test_onnx(platform, west_cmd): # We add the -link-params=1 option to ensure the model parameters are compiled in. # There is currently a bug preventing the demo_runtime environment from receiving # the model weights when set using graph_mod.set_input(). + # See: https://github.com/apache/tvm/issues/7567 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) From bcce62076d5d1432874c8cb43c1da57eafd1c6dd Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 10 Mar 2021 10:45:50 -0800 Subject: [PATCH 30/53] Add new files to check_file_type.py. --- tests/lint/check_file_type.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py index ab51b6c79c83..fc9b51ca4477 100644 --- a/tests/lint/check_file_type.py +++ b/tests/lint/check_file_type.py @@ -125,9 +125,13 @@ "docs/_static/img/tvm-logo-square.png", # pytest config "pytest.ini", - # Zephyr tests - "tests/micro/qemu/zephyr-runtime/prj.conf", - "tests/micro/qemu/zephyr-runtime/qemu-hack/qemu-system-i386", + # microTVM tests + "tests/micro/zephyr/testdata/digit-2.jpg", + "tests/micro/zephyr/testdata/digit-9.jpg", + "tests/micro/zephyr/testdata/mnist-8.onnx", + # microTVM Zephyr runtime + "apps/microtvm/zephyr/demo_runtime/prf.conf", + "apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386", # microTVM Virtual Machines "apps/microtvm/reference-vm/zephyr/Vagrantfile", "apps/microtvm/reference-vm/zephyr/base-box/Vagrantfile.packer-template", From f1260df7dd53b81381a6139867249f18b544bb15 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 10 Mar 2021 10:59:36 -0800 Subject: [PATCH 31/53] Adding missing ONNX file. --- tests/micro/zephyr/testdata/mnist-8.onnx | Bin 0 -> 26454 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/micro/zephyr/testdata/mnist-8.onnx diff --git a/tests/micro/zephyr/testdata/mnist-8.onnx b/tests/micro/zephyr/testdata/mnist-8.onnx new file mode 100644 index 0000000000000000000000000000000000000000..fc1a3f733c6e6243dd23dacb125b7a372de55a50 GIT binary patch literal 26454 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

o@PU7;m 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 Date: Wed, 10 Mar 2021 11:14:35 -0800 Subject: [PATCH 32/53] Fixup docs. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 2 +- tutorials/micro/micro_onnx.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index fbb8bc1905a0..4a276fd8dd28 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -139,7 +139,7 @@ int g_utvm_timer_running = 0; tvm_crt_error_t TVMPlatformTimerStart() { if (g_utvm_timer_running) { TVMLogf("timer already running"); - return kTvmErrorSystemErrorMask | 1; + return kTvmErrorPlatformTimerBadState; } #ifdef CONFIG_LED diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 31f176a7d3c7..4416a48c8224 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -70,6 +70,10 @@ # # Be sure you are able to build and flash a sample Zephyr program # (e.g., the "Blinky" demo) to your device before you proceed with this tutorial. +# +# Instead of building and installing the Zephyr toolchain yourself, you can use the +# `tutorial-micro-reference-vm` for a quick setup which includes all of the necessary +# tools. import datetime import io From c552fa9216ab6e73a52dea2cbb982ad1406f9cd5 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Fri, 12 Mar 2021 16:52:42 -0800 Subject: [PATCH 33/53] Fix linting rule. --- tests/lint/check_file_type.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py index fc9b51ca4477..3408fb0359eb 100644 --- a/tests/lint/check_file_type.py +++ b/tests/lint/check_file_type.py @@ -130,7 +130,9 @@ "tests/micro/zephyr/testdata/digit-9.jpg", "tests/micro/zephyr/testdata/mnist-8.onnx", # microTVM Zephyr runtime - "apps/microtvm/zephyr/demo_runtime/prf.conf", + "apps/microtvm/zephyr/demo_runtime/prj.conf", + "apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf", + "apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf", "apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386", # microTVM Virtual Machines "apps/microtvm/reference-vm/zephyr/Vagrantfile", From 327192384d9b737b57a7a5c087c491e33dbd20e6 Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Fri, 12 Mar 2021 13:35:45 -0800 Subject: [PATCH 34/53] small fixes --- .../boards/nrf5340dk_nrf5340_cpuapp.conf | 4 +-- .../demo_runtime/boards/nucleo_f746zg.conf | 30 +++++++++++++++++++ tutorials/micro/micro_onnx.py | 6 ++-- 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 apps/microtvm/zephyr/demo_runtime/boards/nucleo_f746zg.conf diff --git a/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf b/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf index a7df4d25c4c3..149a69ea3b5b 100644 --- a/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf +++ b/apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -25,9 +25,7 @@ CONFIG_MAIN_STACK_SIZE=1536 # For random number generation. CONFIG_ENTROPY_GENERATOR=y - -# Allow for k_sys_fatal_error_handler to be overridden. -CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_TEST_RANDOM_GENERATOR=y # For debugging. CONFIG_LED=y diff --git a/apps/microtvm/zephyr/demo_runtime/boards/nucleo_f746zg.conf b/apps/microtvm/zephyr/demo_runtime/boards/nucleo_f746zg.conf new file mode 100644 index 000000000000..5931377d55ae --- /dev/null +++ b/apps/microtvm/zephyr/demo_runtime/boards/nucleo_f746zg.conf @@ -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. +# +# This file is specific to the nRF5340 DK board. + +# For intrinsics used by generated optimized operators. +CONFIG_CMSIS_DSP=y + +# Required for Cortex-M33 devices. +CONFIG_MAIN_STACK_SIZE=50 + +# For random number generation. +CONFIG_ENTROPY_GENERATOR=y + +# For debugging. +CONFIG_LED=y diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 4416a48c8224..a6a126adc70f 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -113,10 +113,10 @@ # This is the device target name used by microTVM. It is used to select default # options for the device when we use ``tvm.target.target.micro()`` below. -UTVM_TARGET = "nrf5340dk" +UTVM_TARGET = "nrf5340dk" # or stm32f746xx # This is the board designation used by Zephyr, and required for the compilation process. -UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" +UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" # or nucleo_f746zg ###################################################################### # If you wish to run against an emulated Zephyr device using QEMU, @@ -159,7 +159,7 @@ # for your project. This runtime is a demo which provides basic capabilities # for interfacing to the microTVM device code via the device's serial # port. -UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime/" +UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime" # The ``west`` command is used by Zephyr for compilation and # flashing devices. From f6725770b5e0b9877fc0867f4215bafb852a6153 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Tue, 16 Mar 2021 15:19:40 -0700 Subject: [PATCH 35/53] Add missing file to check_file_type.py. --- tests/lint/check_file_type.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lint/check_file_type.py b/tests/lint/check_file_type.py index 3408fb0359eb..8d8b34322de3 100644 --- a/tests/lint/check_file_type.py +++ b/tests/lint/check_file_type.py @@ -132,6 +132,7 @@ # microTVM Zephyr runtime "apps/microtvm/zephyr/demo_runtime/prj.conf", "apps/microtvm/zephyr/demo_runtime/boards/nrf5340dk_nrf5340_cpuapp.conf", + "apps/microtvm/zephyr/demo_runtime/boards/nucleo_f746zg.conf", "apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf", "apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386", # microTVM Virtual Machines From 12cdd03ad67c069323236cdf09292cd9e824797b Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Tue, 16 Mar 2021 16:09:26 -0700 Subject: [PATCH 36/53] clang-format this file. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index 4a276fd8dd28..a8c7be63b149 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -22,7 +22,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - /* * This is a sample Zephyr-based application that contains the logic * needed to control a microTVM-based model via the UART. This is only @@ -30,12 +29,12 @@ * this logic into your own application. */ - #include #include #include #include #include +#include #include #include #include @@ -43,7 +42,6 @@ #include #include #include -#include #ifdef CONFIG_ARCH_POSIX #include "posix_board_if.h" @@ -85,16 +83,17 @@ ssize_t write_serial(void* unused_context, const uint8_t* data, size_t size) { // This is invoked by Zephyr from an exception handler, which will be invoked // if the device crashes. Here, we turn on the LED and spin. -void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { +void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t* esf) { #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 1); #endif - for (;;) ; + for (;;) + ; } // Called by TVM when a message needs to be formatted. -size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, - const char* fmt, va_list args) { +size_t TVMPlatformFormatMessage(char* out_buf, size_t out_buf_size_bytes, const char* fmt, + va_list args) { return vsnprintk(out_buf, out_buf_size_bytes, fmt, args); } @@ -104,7 +103,8 @@ void TVMPlatformAbort(tvm_crt_error_t error) { #ifdef CONFIG_LED gpio_pin_set(led0_pin, LED0_PIN, 1); #endif - for (;;) ; + for (;;) + ; } // Called by TVM to generate random data. @@ -239,7 +239,7 @@ void uart_irq_cb(const struct device* dev, void* user_data) { if (bytes_read != bytes_written) { TVMPlatformAbort((tvm_crt_error_t)0xbeef2); } - //CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, + // CHECK_EQ(bytes_read, bytes_written, "bytes_read: %d; bytes_written: %d", bytes_read, // bytes_written); } } @@ -270,7 +270,8 @@ void main(void) { int ret; led0_pin = device_get_binding(LED0); if (led0_pin == NULL) { - for (;;) ; + for (;;) + ; } ret = gpio_pin_configure(led0_pin, LED0_PIN, GPIO_OUTPUT_ACTIVE | LED0_FLAGS); if (ret < 0) { @@ -293,7 +294,6 @@ void main(void) { // The main application loop. We continuously read commands from the UART // and dispatch them to UTvmRpcServerLoop(). while (true) { - //uint8_t buf[256]; int bytes_read = uart_rx_buf_read(&uart_rx_rbuf, main_rx_buf, sizeof(main_rx_buf)); if (bytes_read > 0) { size_t bytes_remaining = bytes_read; @@ -304,7 +304,7 @@ void main(void) { if (err != kTvmErrorNoError && err != kTvmErrorFramingShortPacket) { TVMPlatformAbort(err); } - if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { + if (g_num_bytes_written != 0 || g_num_bytes_requested != 0) { if (g_num_bytes_written != g_num_bytes_requested) { TVMPlatformAbort((tvm_crt_error_t)0xbeef5); } @@ -320,4 +320,3 @@ void main(void) { #endif } - From a87f3e8b0924d7ccf5ce24762e57d5f3ceb85510 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 17 Mar 2021 09:42:03 -0700 Subject: [PATCH 37/53] Fix formatting. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index a8c7be63b149..12546d2721bc 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -319,4 +319,3 @@ void main(void) { posix_exit(0); #endif } - From f2b665b4f657d67b737ca6fcd4bd50e2e7f6c429 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 17 Mar 2021 10:45:14 -0700 Subject: [PATCH 38/53] Black formatting. --- tutorials/micro/micro_onnx.py | 4 ++-- tutorials/micro/micro_tflite.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index a6a126adc70f..b6093849f98c 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -113,10 +113,10 @@ # This is the device target name used by microTVM. It is used to select default # options for the device when we use ``tvm.target.target.micro()`` below. -UTVM_TARGET = "nrf5340dk" # or stm32f746xx +UTVM_TARGET = "nrf5340dk" # or stm32f746xx # This is the board designation used by Zephyr, and required for the compilation process. -UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" # or nucleo_f746zg +UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" # or nucleo_f746zg ###################################################################### # If you wish to run against an emulated Zephyr device using QEMU, diff --git a/tutorials/micro/micro_tflite.py b/tutorials/micro/micro_tflite.py index ffe4eef3fab8..803a3f55a735 100644 --- a/tutorials/micro/micro_tflite.py +++ b/tutorials/micro/micro_tflite.py @@ -177,7 +177,7 @@ # Now we create a build config for relay. turning off two options # and then calling relay.build which will result in a C source # file. When running on a simulated target, choose "host" below: -#TARGET = tvm.target.target.micro("host") +# TARGET = tvm.target.target.micro("host") # %% # Compiling for physical hardware @@ -191,7 +191,7 @@ # .. code-block:: python # TARGET = tvm.target.target.micro("host") -#BOARD = "nucleo_f746zg" # or "stm32f746g_disco" +# BOARD = "nucleo_f746zg" # or "stm32f746g_disco" BOARD = "qemu_x86" ###################################################################### From 65f0b4ea1e84db79582d0a4583766582cbb3244e Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 17 Mar 2021 13:35:59 -0700 Subject: [PATCH 39/53] Lint comments. --- python/tvm/target/target.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/tvm/target/target.py b/python/tvm/target/target.py index c2e50e6ff2e3..e3ef51158c5a 100644 --- a/python/tvm/target/target.py +++ b/python/tvm/target/target.py @@ -237,8 +237,6 @@ def intel_graphics(model="unknown", options=None): return Target(" ".join(["opencl"] + opts)) -"""The set of supported models for tvm.target.micro(). Each value maps to a list of options -for the corresponding target.""" MICRO_SUPPORTED_MODELS = { "host": [], "stm32f746xx": ["-mcpu=cortex-m7", "-march=armv7e-m"], From 37e0bceadc0a08c047290ad9445f0a9ee0dec77f Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 18 Mar 2021 09:01:51 -0700 Subject: [PATCH 40/53] Fix path for test. --- tests/scripts/task_python_microtvm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/task_python_microtvm.sh b/tests/scripts/task_python_microtvm.sh index 2e06932ba536..1c202c0ea40c 100755 --- a/tests/scripts/task_python_microtvm.sh +++ b/tests/scripts/task_python_microtvm.sh @@ -26,4 +26,4 @@ source tests/scripts/setup-pytest-env.sh find . -type f -path "*.pyc" | xargs rm -f make cython3 -run_pytest ctypes python-microtvm-qemu tests/micro/qemu +run_pytest ctypes python-microtvm-zephyr tests/micro/zephyr From a26234a91bf9cc79ca6edb52ad5e43317bd60122 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Mon, 22 Mar 2021 08:27:59 -0700 Subject: [PATCH 41/53] Bump CI. From 4749de5aaf694c2a3d93ce5031e2fea494c6bf9b Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Tue, 23 Mar 2021 13:34:40 -0700 Subject: [PATCH 42/53] Update from_onnx. --- tests/micro/zephyr/test_zephyr.py | 2 +- tutorials/micro/micro_onnx.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 09eb97e7ba3d..5da27a905a33 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -224,7 +224,7 @@ def test_onnx(platform, west_cmd): # Load ONNX model and convert to Relay. onnx_model = onnx.load("testdata/mnist-8.onnx") - shape = (1, 1, 28, 28) + shape = {"Input3": (1, 1, 28, 28)} relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) relay_mod = relay.transform.DynamicToStatic()(relay_mod) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index b6093849f98c..95279e720186 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -103,7 +103,9 @@ ###################################################################### # Next, we convert the model to Relay format. -relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=MODEL_SHAPE, freeze_params=True) +relay_mod, params = relay.frontend.from_onnx( + onnx_model, shape={INPUT_TENSOR_NAME: MODEL_SHAPE}, freeze_params=True +) relay_mod = relay.transform.DynamicToStatic()(relay_mod) ###################################################################### From 024518f05232ec20d8c878b650f93cf41d459d6d Mon Sep 17 00:00:00 2001 From: Andrew Reusch Date: Tue, 23 Mar 2021 09:45:35 -0700 Subject: [PATCH 43/53] fix path --- 3rdparty/dmlc-core | 2 +- tests/micro/zephyr/test_zephyr.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/3rdparty/dmlc-core b/3rdparty/dmlc-core index 21cc7de0dc9f..6c401e242c59 160000 --- a/3rdparty/dmlc-core +++ b/3rdparty/dmlc-core @@ -1 +1 @@ -Subproject commit 21cc7de0dc9fd6acb796e1be6181fa8e6b6c8f41 +Subproject commit 6c401e242c59a1f4c913918246591bb13fd714e7 diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 5da27a905a33..22702e56dec5 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -214,11 +214,12 @@ def test_onnx(platform, west_cmd): model, zephyr_board = PLATFORMS[platform] # Load test images. - digit_2 = Image.open("testdata/digit-2.jpg").resize((28, 28)) + this_dir = os.path.dirname(__file__) + digit_2 = Image.open(f"{this_dir}/testdata/digit-2.jpg").resize((28, 28)) digit_2 = np.asarray(digit_2).astype("float32") digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = Image.open("testdata/digit-9.jpg").resize((28, 28)) + digit_9 = Image.open(f"{this_dir}/testdata/digit-9.jpg").resize((28, 28)) digit_9 = np.asarray(digit_9).astype("float32") digit_9 = np.expand_dims(digit_9, axis=0) From 72a2099bedb0799ef78929d4b52fa212f14cb72f Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 24 Mar 2021 13:28:51 -0700 Subject: [PATCH 44/53] Fixing --- 3rdparty/dmlc-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/dmlc-core b/3rdparty/dmlc-core index 21cc7de0dc9f..6c401e242c59 160000 --- a/3rdparty/dmlc-core +++ b/3rdparty/dmlc-core @@ -1 +1 @@ -Subproject commit 21cc7de0dc9fd6acb796e1be6181fa8e6b6c8f41 +Subproject commit 6c401e242c59a1f4c913918246591bb13fd714e7 From 76808c883a115fbedb153b225a06d2a1f64dc101 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Wed, 24 Mar 2021 16:17:18 -0700 Subject: [PATCH 45/53] Revert dmlc-core to 21cc7de0dc9fd6acb796e1be6181fa8e6b6c8f41 --- 3rdparty/dmlc-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/dmlc-core b/3rdparty/dmlc-core index 6c401e242c59..21cc7de0dc9f 160000 --- a/3rdparty/dmlc-core +++ b/3rdparty/dmlc-core @@ -1 +1 @@ -Subproject commit 6c401e242c59a1f4c913918246591bb13fd714e7 +Subproject commit 21cc7de0dc9fd6acb796e1be6181fa8e6b6c8f41 From b6efa06c35f5793141701092df810a17b626e82b Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Mar 2021 09:08:20 -0700 Subject: [PATCH 46/53] Fix path again. --- tests/micro/zephyr/test_zephyr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index 22702e56dec5..f522d02d3c2e 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -224,7 +224,7 @@ def test_onnx(platform, west_cmd): digit_9 = np.expand_dims(digit_9, axis=0) # Load ONNX model and convert to Relay. - onnx_model = onnx.load("testdata/mnist-8.onnx") + onnx_model = onnx.load(f"{this_dir}/testdata/mnist-8.onnx") shape = {"Input3": (1, 1, 28, 28)} relay_mod, params = relay.frontend.from_onnx(onnx_model, shape=shape, freeze_params=True) relay_mod = relay.transform.DynamicToStatic()(relay_mod) From d9db2ad88b4cc206311f83c3af55beb2617e0cfc Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Thu, 25 Mar 2021 14:13:22 -0700 Subject: [PATCH 47/53] Fix tutorial to not use actual Zephyr. --- tutorials/micro/micro_onnx.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 95279e720186..8dcfee053026 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -86,7 +86,9 @@ from tvm import autotvm from tvm import relay from tvm.contrib import graph_runtime as runtime -from tvm.micro.contrib import zephyr + +# from tvm.micro.contrib import zephyr + from PIL import Image import numpy as np @@ -167,13 +169,30 @@ # flashing devices. UTVM_WEST_CMD = "west" -compiler = zephyr.ZephyrCompiler( - project_dir=UTVM_ZEPHYR_RUNTIME_DIR, - board=UTVM_ZEPHYR_BOARD, - zephyr_toolchain_variant="zephyr", - west_cmd=UTVM_WEST_CMD, +# %% +# Here, we are using the ``DefaultCompiler``, which emulates the +# ``ZephyrCompiler`` on the host. If you have Zephyr and a physical device, +# comment out the following lines and uncomment those below. +compiler = tvm.micro.DefaultCompiler(target=target) +opts = tvm.micro.default_options( + os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") ) +# %% +# Compiling for physical hardware +# For physical hardware, comment out the previous section and use this compiler definition instead. +# +# .. code-block:: python +# +# from tvm.micro.contrib import zephyr +# +# compiler = zephyr.ZephyrCompiler( +# project_dir=UTVM_ZEPHYR_RUNTIME_DIR, +# board=UTVM_ZEPHYR_BOARD, +# zephyr_toolchain_variant="zephyr", +# west_cmd=UTVM_WEST_CMD, +# ) + ###################################################################### # Do the actual build. opts = tvm.micro.default_options(f"{UTVM_ZEPHYR_RUNTIME_DIR}/crt") From 2615711cb6288d9e7ba6ccb9f0b9c2e450852718 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Fri, 26 Mar 2021 10:48:11 -0700 Subject: [PATCH 48/53] Revert submodule version change --- 3rdparty/dlpack | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/dlpack b/3rdparty/dlpack index 3ec04430e89a..a07f962d446b 160000 --- a/3rdparty/dlpack +++ b/3rdparty/dlpack @@ -1 +1 @@ -Subproject commit 3ec04430e89a6834e5a1b99471f415fa939bf642 +Subproject commit a07f962d446b577adf4baef2b347a0f3a2a20617 From f412ffd12d43928e050d001ecdc79c85c9facea7 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Fri, 26 Mar 2021 11:47:50 -0700 Subject: [PATCH 49/53] Fix bad merge. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 20 +++++------------- tutorials/micro/micro_onnx.py | 22 ++++++++++---------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index b59c0d4e252b..fac0aa6e8c35 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -126,11 +126,16 @@ tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { } } +// Memory pool for use by TVMPlatformMemoryAllocate. +K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); + +// Called by TVM to allocate memory. tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, void** out_ptr) { *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; } +// Called by TVM to deallocate memory. tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { k_free(ptr); return kTvmErrorNoError; @@ -207,21 +212,6 @@ tvm_crt_error_t TVMPlatformTimerStop(double* elapsed_time_seconds) { return kTvmErrorNoError; } -// Memory pool for use by TVMPlatformMemoryAllocate. -K_MEM_POOL_DEFINE(tvm_memory_pool, 64, 1024, 216, 4); - -// Called by TVM to allocate memory. -tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLContext ctx, void** out_ptr) { - *out_ptr = k_mem_pool_malloc(&tvm_memory_pool, num_bytes); - return (*out_ptr == NULL) ? kTvmErrorPlatformNoMemory : kTvmErrorNoError; -} - -// Called by TVM to deallocate memory. -tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLContext ctx) { - k_free(ptr); - return kTvmErrorNoError; -} - // Ring buffer used to store data read from the UART on rx interrupt. #define RING_BUF_SIZE_BYTES 4 * 1024 RING_BUF_DECLARE(uart_rx_rbuf, RING_BUF_SIZE_BYTES); diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 8dcfee053026..b76e808572e5 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -173,10 +173,10 @@ # Here, we are using the ``DefaultCompiler``, which emulates the # ``ZephyrCompiler`` on the host. If you have Zephyr and a physical device, # comment out the following lines and uncomment those below. -compiler = tvm.micro.DefaultCompiler(target=target) -opts = tvm.micro.default_options( - os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") -) +#compiler = tvm.micro.DefaultCompiler(target=target) +#opts = tvm.micro.default_options( +# os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") +#) # %% # Compiling for physical hardware @@ -184,14 +184,14 @@ # # .. code-block:: python # -# from tvm.micro.contrib import zephyr +from tvm.micro.contrib import zephyr # -# compiler = zephyr.ZephyrCompiler( -# project_dir=UTVM_ZEPHYR_RUNTIME_DIR, -# board=UTVM_ZEPHYR_BOARD, -# zephyr_toolchain_variant="zephyr", -# west_cmd=UTVM_WEST_CMD, -# ) +compiler = zephyr.ZephyrCompiler( + project_dir=UTVM_ZEPHYR_RUNTIME_DIR, + board=UTVM_ZEPHYR_BOARD, + zephyr_toolchain_variant="zephyr", + west_cmd=UTVM_WEST_CMD, +) ###################################################################### # Do the actual build. From 22e484d48534902911032d7252de539fdf1ac503 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Fri, 26 Mar 2021 11:58:49 -0700 Subject: [PATCH 50/53] Trying to fix this mess. --- apps/microtvm/zephyr/demo_runtime/src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/microtvm/zephyr/demo_runtime/src/main.c b/apps/microtvm/zephyr/demo_runtime/src/main.c index fac0aa6e8c35..e2aa59af7ad9 100644 --- a/apps/microtvm/zephyr/demo_runtime/src/main.c +++ b/apps/microtvm/zephyr/demo_runtime/src/main.c @@ -124,6 +124,7 @@ tvm_crt_error_t TVMPlatformGenerateRandom(uint8_t* buffer, size_t num_bytes) { random = sys_rand32_get(); memcpy(&buffer[num_bytes - num_tail_bytes], &random, num_tail_bytes); } + return kTvmErrorNoError; } // Memory pool for use by TVMPlatformMemoryAllocate. From ac60a0571da3b6d646ecedc31e900c413f773635 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Fri, 26 Mar 2021 14:00:26 -0700 Subject: [PATCH 51/53] Fix formatting. --- tutorials/micro/micro_onnx.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index b76e808572e5..541fe38160d8 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -173,10 +173,10 @@ # Here, we are using the ``DefaultCompiler``, which emulates the # ``ZephyrCompiler`` on the host. If you have Zephyr and a physical device, # comment out the following lines and uncomment those below. -#compiler = tvm.micro.DefaultCompiler(target=target) -#opts = tvm.micro.default_options( -# os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") -#) +compiler = tvm.micro.DefaultCompiler(target=target) +opts = tvm.micro.default_options( + os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") +) # %% # Compiling for physical hardware @@ -184,14 +184,14 @@ # # .. code-block:: python # -from tvm.micro.contrib import zephyr +# from tvm.micro.contrib import zephyr # -compiler = zephyr.ZephyrCompiler( - project_dir=UTVM_ZEPHYR_RUNTIME_DIR, - board=UTVM_ZEPHYR_BOARD, - zephyr_toolchain_variant="zephyr", - west_cmd=UTVM_WEST_CMD, -) +# compiler = zephyr.ZephyrCompiler( +# project_dir=UTVM_ZEPHYR_RUNTIME_DIR, +# board=UTVM_ZEPHYR_BOARD, +# zephyr_toolchain_variant="zephyr", +# west_cmd=UTVM_WEST_CMD, +# ) ###################################################################### # Do the actual build. From 4a7719dfd940b63a15898724ed31fb8c2a09808c Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sat, 27 Mar 2021 10:15:34 -0700 Subject: [PATCH 52/53] context -> device --- tests/micro/zephyr/test_zephyr.py | 2 +- tutorials/micro/micro_onnx.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/micro/zephyr/test_zephyr.py b/tests/micro/zephyr/test_zephyr.py index b4787045b340..003cd54bba90 100644 --- a/tests/micro/zephyr/test_zephyr.py +++ b/tests/micro/zephyr/test_zephyr.py @@ -240,7 +240,7 @@ def test_onnx(platform, west_cmd): with _make_session(model, target, zephyr_board, west_cmd, lowered.lib) as session: graph_mod = tvm.micro.create_local_graph_runtime( - graph, session.get_system_lib(), session.context + graph, session.get_system_lib(), session.device ) # Send the digit-2 image and confirm that the correct result is returned. diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py index 541fe38160d8..4985b4f2d595 100755 --- a/tutorials/micro/micro_onnx.py +++ b/tutorials/micro/micro_onnx.py @@ -206,7 +206,7 @@ flasher = compiler.flasher() with tvm.micro.Session(binary=micro_bin, flasher=flasher) as sess: - mod = tvm.micro.create_local_graph_runtime(graph_json_str, sess.get_system_lib(), sess.context) + mod = tvm.micro.create_local_graph_runtime(graph_json_str, sess.get_system_lib(), sess.device) # Load test images. DIGIT_2_IMAGE = "../../tests/micro/zephyr/testdata/digit-2.jpg" From c303c9112a65ac5b049a518cda22899a37338718 Mon Sep 17 00:00:00 2001 From: "Matt Welsh (OctoML)" Date: Sun, 28 Mar 2021 14:07:25 -0700 Subject: [PATCH 53/53] Removing tutorial since I can't get it to pass CI. --- tutorials/micro/micro_onnx.py | 234 ---------------------------------- 1 file changed, 234 deletions(-) delete mode 100755 tutorials/micro/micro_onnx.py diff --git a/tutorials/micro/micro_onnx.py b/tutorials/micro/micro_onnx.py deleted file mode 100755 index 4985b4f2d595..000000000000 --- a/tutorials/micro/micro_onnx.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python3 - -# 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. - -""" -microTVM with ONNX models -========================= -**Author**: `Matt Welsh ` - -This tutorial is an introduction to compiling and -running an ONNX model on a device using microTVM. -""" - -# Setup -# ----- -# -# Build TVM wth ``USE_MICRO`` -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -# Download the TVM sources, and build TVM from source following the -# instructions `on this page -# `. -# When bulding TVM, ensure that the following lines are present in -# your ``config.cmake`` file: -# .. code-block:: bash -# -# # Whether enable MicroTVM runtime -# set(USE_MICRO ON) -# -# Install Python dependencies -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -# microTVM and this tutorial require a number of Python dependencies -- -# in addition to TVM itself -- to be installed in your Python environment. -# For convenience, we have provided a `Poetry ` -# ``pyproject.toml`` configuration file in the directory ``apps/microtvm``. -# You can use this as follows: -# -# .. code-block:: bash -# -# $ cd $TVM_HOME/apps/microtvm -# $ poetry lock && poetry install -# $ poetry shell -# -# You should now be in a Python virtual environment with all of the appropriate -# dependencies installed. -# -# Install Zephyr -# ^^^^^^^^^^^^^^ -# -# microTVM currently uses the `Zephyr ` RTOS as -# the basis for the device-side runtime. To get started, install Zephyr -# and an appropriate device-specific toolchain using the -# `Zephyr installation instructions `_. -# -# Be sure you are able to build and flash a sample Zephyr program -# (e.g., the "Blinky" demo) to your device before you proceed with this tutorial. -# -# Instead of building and installing the Zephyr toolchain yourself, you can use the -# `tutorial-micro-reference-vm` for a quick setup which includes all of the necessary -# tools. - -import datetime -import io -import os -import sys - -import onnx -import tvm -import tvm.micro -from tvm import autotvm -from tvm import relay -from tvm.contrib import graph_runtime as runtime - -# from tvm.micro.contrib import zephyr - -from PIL import Image -import numpy as np - -###################################################################### -# For this tutorial, we use a pretrained ONNX model implementing -# the MNIST handwritten digit recognition on 28x28 px input images. - -MODEL_FILE = "../../tests/micro/zephyr/testdata/mnist-8.onnx" -MODEL_SHAPE = (1, 1, 28, 28) -INPUT_TENSOR_NAME = "Input3" - -onnx_model = onnx.load(MODEL_FILE) -print(f"Loaded ONNX model: {MODEL_FILE}") - -###################################################################### -# Next, we convert the model to Relay format. -relay_mod, params = relay.frontend.from_onnx( - onnx_model, shape={INPUT_TENSOR_NAME: MODEL_SHAPE}, freeze_params=True -) -relay_mod = relay.transform.DynamicToStatic()(relay_mod) - -###################################################################### -# Next, we lower the Relay model to the specific device we are -# targeting. In this case, we are using the Nordic Semiconductor -# `nRF5340DK development board `. - -# This is the device target name used by microTVM. It is used to select default -# options for the device when we use ``tvm.target.target.micro()`` below. -UTVM_TARGET = "nrf5340dk" # or stm32f746xx - -# This is the board designation used by Zephyr, and required for the compilation process. -UTVM_ZEPHYR_BOARD = "nrf5340dk_nrf5340_cpuapp" # or nucleo_f746zg - -###################################################################### -# If you wish to run against an emulated Zephyr device using QEMU, -# you can uncomment these lines instead: -# UTVM_TARGET = "host" -# UTVM_ZEPHYR_BOARD = "qemu_x86" - - -###################################################################### -# We define the TVM target here. -# We add -link-params=1 option here, so that the model parameters are -# included in the resulting binary image. -target = tvm.target.target.micro(UTVM_TARGET, options=["-link-params=1"]) - -###################################################################### -# Now, we do the Relay build. -TVM_OPT_LEVEL = 3 -with tvm.transform.PassContext(opt_level=TVM_OPT_LEVEL, config={"tir.disable_vectorize": True}): - lowered = relay.build(relay_mod, target, params=params) - graph_json_str = lowered.get_json() - -###################################################################### -# Next, create a uTVM Workspace. This is a location where the -# generated Zephyr project will be compiled. -workspace_root = os.path.abspath( - f'workspace/{datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")}' -) -workspace_parent = os.path.dirname(workspace_root) -if not os.path.exists(workspace_parent): - os.makedirs(workspace_parent) -workspace = tvm.micro.Workspace(debug=True, root=workspace_root) -print(f"Using workspace: {workspace_root}") - -###################################################################### -# Now we create the ``ZephyrCompiler`` object, which generates the -# Zephyr project for hosting the device runtime code, as well as -# generating the device-specific binary from our model. - -# You are welcome to implement your own Zephyr-based runtime environment -# for your project. This runtime is a demo which provides basic capabilities -# for interfacing to the microTVM device code via the device's serial -# port. -UTVM_ZEPHYR_RUNTIME_DIR = "../../apps/microtvm/zephyr/demo_runtime" - -# The ``west`` command is used by Zephyr for compilation and -# flashing devices. -UTVM_WEST_CMD = "west" - -# %% -# Here, we are using the ``DefaultCompiler``, which emulates the -# ``ZephyrCompiler`` on the host. If you have Zephyr and a physical device, -# comment out the following lines and uncomment those below. -compiler = tvm.micro.DefaultCompiler(target=target) -opts = tvm.micro.default_options( - os.path.join(tvm.micro.get_standalone_crt_dir(), "template", "host") -) - -# %% -# Compiling for physical hardware -# For physical hardware, comment out the previous section and use this compiler definition instead. -# -# .. code-block:: python -# -# from tvm.micro.contrib import zephyr -# -# compiler = zephyr.ZephyrCompiler( -# project_dir=UTVM_ZEPHYR_RUNTIME_DIR, -# board=UTVM_ZEPHYR_BOARD, -# zephyr_toolchain_variant="zephyr", -# west_cmd=UTVM_WEST_CMD, -# ) - -###################################################################### -# Do the actual build. -opts = tvm.micro.default_options(f"{UTVM_ZEPHYR_RUNTIME_DIR}/crt") - -micro_bin = tvm.micro.build_static_runtime(workspace, compiler, lowered.lib, opts) - -###################################################################### -# Next, we create a ``tvm.micro.Session`` which handles the details -# of flashing the binary to the device and opening a serial-port -# RPC session with the device for controlling it. - -flasher = compiler.flasher() -with tvm.micro.Session(binary=micro_bin, flasher=flasher) as sess: - mod = tvm.micro.create_local_graph_runtime(graph_json_str, sess.get_system_lib(), sess.device) - - # Load test images. - DIGIT_2_IMAGE = "../../tests/micro/zephyr/testdata/digit-2.jpg" - DIGIT_9_IMAGE = "../../tests/micro/zephyr/testdata/digit-9.jpg" - - digit_2 = Image.open(DIGIT_2_IMAGE).resize((28, 28)) - digit_9 = Image.open(DIGIT_9_IMAGE).resize((28, 28)) - digit_2 = np.asarray(digit_2).astype("float32") - digit_9 = np.asarray(digit_9).astype("float32") - digit_2 = np.expand_dims(digit_2, axis=0) - digit_9 = np.expand_dims(digit_9, axis=0) - - # Set the input tensor of the model to the digit-2 test image. - mod.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_2)) - - # Run inference and get the result. - mod.run() - output = mod.get_output(0).asnumpy() - print(f"Top result for digit-2 is: {np.argmax(output)}") - - # Do likewise for the digit-9 image. - mod.set_input(INPUT_TENSOR_NAME, tvm.nd.array(digit_9)) - mod.run() - output = mod.get_output(0).asnumpy() - print(f"Top result for digit-9 is: {np.argmax(output)}")