diff --git a/CMakeLists.txt b/CMakeLists.txt index 24f0653b3a78..bb5cfdfbd373 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,6 +413,8 @@ endif(USE_PIPELINE_EXECUTOR) # Module rules include(cmake/modules/VTA.cmake) include(cmake/modules/StandaloneCrt.cmake) +include(cmake/modules/Zephyr.cmake) +include(cmake/modules/Arduino.cmake) include(cmake/modules/CUDA.cmake) include(cmake/modules/Hexagon.cmake) include(cmake/modules/OpenCL.cmake) @@ -500,6 +502,8 @@ if(USE_MICRO) # Unix Makefiles generator, need to add these explicit target-level dependency) add_dependencies(tvm host_standalone_crt) add_dependencies(tvm_runtime host_standalone_crt) + add_dependencies(tvm_runtime zephyr) + add_dependencies(tvm_runtime arduino) endif() if(USE_CPP_RPC) diff --git a/apps/microtvm/arduino/template_project/microtvm_api_server.py b/apps/microtvm/arduino/template_project/microtvm_api_server.py index 18c1b659dafd..832f6d2a0435 100644 --- a/apps/microtvm/arduino/template_project/microtvm_api_server.py +++ b/apps/microtvm/arduino/template_project/microtvm_api_server.py @@ -31,6 +31,7 @@ import tempfile import time from string import Template +import re import serial import serial.tools.list_ports @@ -287,11 +288,11 @@ def _find_modified_include_path(self, project_dir, file_path, include_path): return include_path def _get_platform_version(self, arduino_cli_path: str) -> float: + # sample output of this command: + # 'arduino-cli alpha Version: 0.18.3 Commit: d710b642 Date: 2021-05-14T12:36:58Z\n' version_output = subprocess.check_output([arduino_cli_path, "version"], encoding="utf-8") - version_output = ( - version_output.replace("\n", "").replace("\r", "").replace(":", "").lower().split(" ") - ) - full_version = version_output[version_output.index("version") + 1].split(".") + full_version = re.findall("version: ([\.0-9]*)", version_output.lower()) + full_version = full_version[0].split(".") version = float(f"{full_version[0]}.{full_version[1]}") return version diff --git a/cmake/modules/Arduino.cmake b/cmake/modules/Arduino.cmake new file mode 100644 index 000000000000..54c144081efa --- /dev/null +++ b/cmake/modules/Arduino.cmake @@ -0,0 +1,78 @@ +# 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. + +if(USE_MICRO) + message(STATUS "Add Arduino for microTVM") + + function(microtvm_add_arduino) + list( + APPEND + ARDUINO_FILE_COPY_JOBS + "apps/microtvm/arduino/template_project microtvm_api_server.py -> arduino" + "apps/microtvm/arduino/template_project boards.json -> arduino" + "apps/microtvm/arduino/template_project/src/example_project *.c -> arduino/src/example_project" + "apps/microtvm/arduino/template_project/src/example_project *.h -> arduino/src/example_project" + "apps/microtvm/arduino/template_project/src/example_project *.ino -> arduino/src/example_project" + "apps/microtvm/arduino/template_project/src/host_driven *.c -> arduino/src/host_driven" + "apps/microtvm/arduino/template_project/src/host_driven *.ino -> arduino/src/host_driven" + "apps/microtvm/arduino/template_project/crt_config *.h -> arduino/crt_config" + ) + + foreach(job_spec IN LISTS ARDUINO_FILE_COPY_JOBS) + string(REPLACE " " ";" job_spec "${job_spec}") + list(LENGTH job_spec job_spec_length) + math(EXPR job_spec_length_mod "${job_spec_length} % 3") + if(NOT "${job_spec_length_mod}" EQUAL 1) + message( + FATAL_ERROR + "Arduino copy job spec list length is ${job_spec_length}; parsed job spec is ${job_spec}" + ) + endif() + math(EXPR job_spec_stop "${job_spec_length} - 3") + + list(GET job_spec 0 job_src_base) + set(job_src_base "${CMAKE_SOURCE_DIR}/${job_src_base}") + foreach(copy_pattern_index RANGE 1 "${job_spec_stop}" 3) + list(GET job_spec ${copy_pattern_index} copy_pattern) + math(EXPR copy_dest_index "${copy_pattern_index} + 2") + list(GET job_spec ${copy_dest_index} copy_dest) + + file( + GLOB_RECURSE copy_files + RELATIVE "${job_src_base}" + "${job_src_base}/${copy_pattern}") + list(LENGTH copy_files copy_files_length) + if("${copy_files_length}" EQUAL 0) + message( + FATAL_ERROR + "Arduino copy job matched 0 files: ${job_src_base}/${copy_pattern} -> ${copy_dest}" + ) + endif() + foreach(copy_src IN LISTS copy_files) + get_filename_component( + dest_path "${MICROTVM_TEMPLATE_PROJECTS}/${copy_dest}/${copy_src}" + ABSOLUTE) + tvm_micro_add_copy_file(arduino_template_deps + ${job_src_base}/${copy_src} ${dest_path}) + endforeach() + endforeach() + endforeach() + + add_custom_target(arduino DEPENDS ${arduino_template_deps}) + endfunction() + + microtvm_add_arduino() + +endif(USE_MICRO) diff --git a/cmake/modules/StandaloneCrt.cmake b/cmake/modules/StandaloneCrt.cmake index 9f79c7da3cdf..5d822844ae34 100644 --- a/cmake/modules/StandaloneCrt.cmake +++ b/cmake/modules/StandaloneCrt.cmake @@ -16,20 +16,9 @@ # under the License. if(USE_MICRO) - message(STATUS "Build standalone CRT for micro TVM") + message(STATUS "Build standalone CRT for microTVM") file(GLOB crt_srcs src/runtime/crt/**) - function(tvm_crt_add_copy_file var src dest) - get_filename_component(basename "${src}" NAME) - get_filename_component(dest_parent_dir "${dest}" DIRECTORY) - add_custom_command( - OUTPUT "${dest}" - COMMAND "${CMAKE_COMMAND}" -E copy "${src}" "${dest}" - DEPENDS "${src}") - list(APPEND "${var}" "${dest}") - set("${var}" "${${var}}" PARENT_SCOPE) - endfunction(tvm_crt_add_copy_file) - function(tvm_crt_define_targets) # Build an isolated build directory, separate from the TVM tree. list(APPEND CRT_FILE_COPY_JOBS @@ -83,7 +72,7 @@ if(USE_MICRO) endif() foreach(copy_src IN LISTS copy_files) get_filename_component(dest_path "${standalone_crt_base}/${copy_dest}/${copy_src}" ABSOLUTE) - tvm_crt_add_copy_file(host_isolated_build_deps ${job_src_base}/${copy_src} ${dest_path}) + tvm_micro_add_copy_file(host_isolated_build_deps ${job_src_base}/${copy_src} ${dest_path}) endforeach() endforeach() endforeach() diff --git a/cmake/modules/Zephyr.cmake b/cmake/modules/Zephyr.cmake new file mode 100644 index 000000000000..048240375cd6 --- /dev/null +++ b/cmake/modules/Zephyr.cmake @@ -0,0 +1,78 @@ +# 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. + +if(USE_MICRO) + message(STATUS "Add Zephyr for microTVM") + + function(microtvm_add_zephyr) + list( + APPEND + ZEPHYR_FILE_COPY_JOBS + "apps/microtvm/zephyr/template_project microtvm_api_server.py -> zephyr" + "apps/microtvm/zephyr/template_project boards.json -> zephyr" + "apps/microtvm/zephyr/template_project CMakeLists.txt.template -> zephyr" + "apps/microtvm/zephyr/template_project/src/aot_demo *.c -> zephyr/src/aot_demo" + "apps/microtvm/zephyr/template_project/src/aot_demo *.h -> zephyr/src/aot_demo" + "apps/microtvm/zephyr/template_project/src/host_driven *.c -> zephyr/src/host_driven" + "apps/microtvm/zephyr/template_project/qemu-hack * -> zephyr/qemu-hack" + "apps/microtvm/zephyr/template_project/crt_config *.h -> zephyr/crt_config" + ) + + foreach(job_spec IN LISTS ZEPHYR_FILE_COPY_JOBS) + string(REPLACE " " ";" job_spec "${job_spec}") + list(LENGTH job_spec job_spec_length) + math(EXPR job_spec_length_mod "${job_spec_length} % 3") + if(NOT "${job_spec_length_mod}" EQUAL 1) + message( + FATAL_ERROR + "Zephyr copy job spec list length is ${job_spec_length}; parsed job spec is ${job_spec}" + ) + endif() + math(EXPR job_spec_stop "${job_spec_length} - 3") + + list(GET job_spec 0 job_src_base) + set(job_src_base "${CMAKE_SOURCE_DIR}/${job_src_base}") + foreach(copy_pattern_index RANGE 1 "${job_spec_stop}" 3) + list(GET job_spec ${copy_pattern_index} copy_pattern) + math(EXPR copy_dest_index "${copy_pattern_index} + 2") + list(GET job_spec ${copy_dest_index} copy_dest) + + file( + GLOB_RECURSE copy_files + RELATIVE "${job_src_base}" + "${job_src_base}/${copy_pattern}") + list(LENGTH copy_files copy_files_length) + if("${copy_files_length}" EQUAL 0) + message( + FATAL_ERROR + "Zephyr copy job matched 0 files: ${job_src_base}/${copy_pattern} -> ${copy_dest}" + ) + endif() + foreach(copy_src IN LISTS copy_files) + get_filename_component( + dest_path "${MICROTVM_TEMPLATE_PROJECTS}/${copy_dest}/${copy_src}" + ABSOLUTE) + tvm_micro_add_copy_file(zephyr_template_deps + ${job_src_base}/${copy_src} ${dest_path}) + endforeach() + endforeach() + endforeach() + + add_custom_target(zephyr DEPENDS ${zephyr_template_deps}) + endfunction() + + microtvm_add_zephyr() + +endif(USE_MICRO) diff --git a/cmake/utils/Utils.cmake b/cmake/utils/Utils.cmake index 4e6762b14894..44f622126abb 100644 --- a/cmake/utils/Utils.cmake +++ b/cmake/utils/Utils.cmake @@ -75,6 +75,19 @@ function(assign_source_group group) endforeach() endfunction(assign_source_group) +function(tvm_micro_add_copy_file var src dest) + get_filename_component(basename "${src}" NAME) + get_filename_component(dest_parent_dir "${dest}" DIRECTORY) + add_custom_command( + OUTPUT "${dest}" + COMMAND "${CMAKE_COMMAND}" -E copy "${src}" "${dest}" + DEPENDS "${src}") + list(APPEND "${var}" "${dest}") + set("${var}" "${${var}}" PARENT_SCOPE) +endfunction(tvm_micro_add_copy_file) + +set(MICROTVM_TEMPLATE_PROJECTS "${CMAKE_CURRENT_BINARY_DIR}/microtvm_template_projects") + # From cmake documentation: # True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number. # False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in the suffix -NOTFOUND. diff --git a/gallery/how_to/work_with_microtvm/micro_autotune.py b/gallery/how_to/work_with_microtvm/micro_autotune.py index e7a1fa84a110..d3106712aa99 100644 --- a/gallery/how_to/work_with_microtvm/micro_autotune.py +++ b/gallery/how_to/work_with_microtvm/micro_autotune.py @@ -113,12 +113,9 @@ # choose other options by choosing from `PLATFORM` list. # -repo_root = pathlib.Path( - subprocess.check_output(["git", "rev-parse", "--show-toplevel"], encoding="utf-8").strip() -) module_loader = tvm.micro.AutoTvmModuleLoader( - template_project_dir=repo_root / "src" / "runtime" / "crt" / "host", + template_project_dir=pathlib.Path(tvm.micro.get_microtvm_template_projects("crt")), project_options={"verbose": False}, ) builder = tvm.autotvm.LocalBuilder( @@ -134,7 +131,7 @@ # Compiling for physical hardware # -------------------------------------------------------------------------- # module_loader = tvm.micro.AutoTvmModuleLoader( -# template_project_dir=repo_root / "apps" / "microtvm" / "zephyr" / "template_project", +# template_project_dir=pathlib.Path(tvm.micro.get_microtvm_template_projects("zephyr")), # project_options={ # "zephyr_board": BOARD, # "west_cmd": "west", @@ -183,7 +180,7 @@ temp_dir = tvm.contrib.utils.tempdir() project = tvm.micro.generate_project( - str(repo_root / "src" / "runtime" / "crt" / "host"), + str(tvm.micro.get_microtvm_template_projects("crt")), lowered, temp_dir / "project", {"verbose": False}, @@ -192,7 +189,7 @@ # Compiling for physical hardware # -------------------------------------------------------------------------- # project = tvm.micro.generate_project( -# str(repo_root / "apps" / "microtvm" / "zephyr" / "template_project"), +# str(tvm.micro.get_microtvm_template_projects("zephyr")), # lowered, # temp_dir / "project", # { @@ -226,7 +223,7 @@ temp_dir = tvm.contrib.utils.tempdir() project = tvm.micro.generate_project( - str(repo_root / "src" / "runtime" / "crt" / "host"), + str(tvm.micro.get_microtvm_template_projects("crt")), lowered_tuned, temp_dir / "project", {"verbose": False}, @@ -235,7 +232,7 @@ # Compiling for physical hardware # -------------------------------------------------------------------------- # project = tvm.micro.generate_project( -# str(repo_root / "apps" / "microtvm" / "zephyr" / "template_project"), +# str(tvm.micro.get_microtvm_template_projects("zephyr")), # lowered_tuned, # temp_dir / "project", # { diff --git a/gallery/how_to/work_with_microtvm/micro_tflite.py b/gallery/how_to/work_with_microtvm/micro_tflite.py index cab105cb450f..35b08d87b9ee 100644 --- a/gallery/how_to/work_with_microtvm/micro_tflite.py +++ b/gallery/how_to/work_with_microtvm/micro_tflite.py @@ -269,10 +269,7 @@ import subprocess import pathlib -repo_root = pathlib.Path( - subprocess.check_output(["git", "rev-parse", "--show-toplevel"], encoding="utf-8").strip() -) -template_project_path = repo_root / "src" / "runtime" / "crt" / "host" +template_project_path = pathlib.Path(tvm.micro.get_microtvm_template_projects("crt")) project_options = {} # You can use options to provide platform-specific options through TVM. # Compiling for physical hardware (or an emulated board, like the mps_an521) @@ -280,7 +277,7 @@ # For physical hardware, you can try out the Zephyr platform by using a different template project # and options: # -# template_project_path = repo_root / "apps" / "microtvm" / "zephyr" / "template_project" +# template_project_path = pathlib.Path(tvm.micro.get_microtvm_template_projects("zephyr")) # project_options = {"project_type": "host_driven", zephyr_board": "nucleo_f746zg"}} # Create a temporary directory diff --git a/python/setup.py b/python/setup.py index 1b2a9d3ee965..5d21af6b5878 100644 --- a/python/setup.py +++ b/python/setup.py @@ -62,6 +62,13 @@ def get_lib_path(): libs.append(candidate_path) break + # Add microTVM template projects + for name in lib_path: + candidate_path = os.path.join(os.path.dirname(name), "microtvm_template_projects") + if os.path.isdir(candidate_path): + libs.append(candidate_path) + break + else: libs = None diff --git a/python/tvm/micro/__init__.py b/python/tvm/micro/__init__.py index 2aea9d3fd61d..ba966d3791bb 100644 --- a/python/tvm/micro/__init__.py +++ b/python/tvm/micro/__init__.py @@ -19,6 +19,7 @@ from .build import autotvm_build_func from .build import AutoTvmModuleLoader from .build import get_standalone_crt_dir +from .build import get_microtvm_template_projects from .model_library_format import export_model_library_format, UnsupportedInModelLibraryFormatError from .project import generate_project, GeneratedProject, TemplateProject from .session import ( diff --git a/python/tvm/micro/build.py b/python/tvm/micro/build.py index 9e278081933c..795a61edcbb3 100644 --- a/python/tvm/micro/build.py +++ b/python/tvm/micro/build.py @@ -22,6 +22,7 @@ import os import pathlib import contextlib +import enum from typing import Union from .._ffi import libinfo @@ -34,10 +35,24 @@ STANDALONE_CRT_DIR = None +class MicroTVMTemplateProject(enum.Enum): + ZEPHYR = "zephyr" + ARDUINO = "arduino" + CRT = "crt" + + @classmethod + def list(cls): + return list(map(lambda c: c.value, cls)) + + class CrtNotFoundError(Exception): """Raised when the standalone CRT dirtree cannot be found.""" +class MicroTVMTemplateProjectNotFoundError(Exception): + """Raised when the microTVM template project dirtree cannot be found.""" + + def get_standalone_crt_dir() -> str: """Find the standalone_crt directory. @@ -64,6 +79,37 @@ def get_standalone_crt_dir() -> str: return STANDALONE_CRT_DIR +def get_microtvm_template_projects(platform: str) -> str: + """Find microTVM template project directory for specific platform. + + Parameters + ---------- + platform : str + Platform type which should be defined in MicroTVMTemplateProject. + + Returns + ------- + str : + Path to template project directory for platform. + """ + if platform not in MicroTVMTemplateProject.list(): + raise ValueError(f"platform {platform} is not supported.") + + if platform == MicroTVMTemplateProject.CRT.value: + return os.path.join(get_standalone_crt_dir(), "template", "host") + + microtvm_template_projects = None + for path in libinfo.find_lib_path(): + template_path = os.path.join(os.path.dirname(path), "microtvm_template_projects") + if os.path.isdir(template_path): + microtvm_template_projects = template_path + break + else: + raise MicroTVMTemplateProjectNotFoundError() + + return os.path.join(microtvm_template_projects, platform) + + class AutoTvmModuleLoader: """MicroTVM AutoTVM Module Loader diff --git a/tests/micro/arduino/conftest.py b/tests/micro/arduino/conftest.py index 73361774821b..8625b4a45364 100644 --- a/tests/micro/arduino/conftest.py +++ b/tests/micro/arduino/conftest.py @@ -24,16 +24,10 @@ from tvm.micro import project from tvm import micro, relay -TEMPLATE_PROJECT_DIR = ( - pathlib.Path(__file__).parent - / ".." - / ".." - / ".." - / "apps" - / "microtvm" - / "arduino" - / "template_project" -).resolve() +TEMPLATE_PROJECT_DIR = pathlib.Path(tvm.micro.get_microtvm_template_projects("arduino")) + + +BOARDS = TEMPLATE_PROJECT_DIR / "boards.json" BOARDS = TEMPLATE_PROJECT_DIR / "boards.json" diff --git a/tests/micro/zephyr/test_utils.py b/tests/micro/zephyr/test_utils.py index c27c869509d7..c89e6c805498 100644 --- a/tests/micro/zephyr/test_utils.py +++ b/tests/micro/zephyr/test_utils.py @@ -31,16 +31,7 @@ import tvm.micro -TEMPLATE_PROJECT_DIR = ( - pathlib.Path(__file__).parent - / ".." - / ".." - / ".." - / "apps" - / "microtvm" - / "zephyr" - / "template_project" -).resolve() +TEMPLATE_PROJECT_DIR = pathlib.Path(tvm.micro.get_microtvm_template_projects("zephyr")) BOARDS = TEMPLATE_PROJECT_DIR / "boards.json"