Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[microTVM][Zephyr][projectAPI] Minimize project build commands #12209

Merged
merged 6 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack")

set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance.

set(ENABLE_CMSIS <ENABLE_CMSIS>)
<CMAKE_ARGS>

find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
project(microtvm_autogenerated_project)
Expand Down
93 changes: 56 additions & 37 deletions apps/microtvm/zephyr/template_project/microtvm_api_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@

BOARDS = API_SERVER_DIR / "boards.json"

CMAKELIST_FILENAME = "CMakeLists.txt"

# Used to check Zephyr version installed on the host.
# We only check two levels of the version.
ZEPHYR_VERSION = 2.7
Expand Down Expand Up @@ -275,13 +277,13 @@ def _get_nrf_device_args(options):
),
server.ProjectOption(
"verbose",
optional=["build"],
optional=["generate_project"],
type="bool",
help="Run build with verbose output.",
),
server.ProjectOption(
"west_cmd",
optional=["build"],
optional=["generate_project"],
default=WEST_CMD,
type="str",
help=(
Expand All @@ -292,14 +294,14 @@ def _get_nrf_device_args(options):
server.ProjectOption(
"zephyr_base",
required=(["generate_project", "open_transport"] if not ZEPHYR_BASE else None),
optional=(["generate_project", "open_transport", "build"] if ZEPHYR_BASE else ["build"]),
optional=(["generate_project", "open_transport"] if ZEPHYR_BASE else ["build"]),
default=ZEPHYR_BASE,
type="str",
help="Path to the zephyr base directory.",
),
server.ProjectOption(
"zephyr_board",
required=["generate_project", "build", "flash", "open_transport"],
required=["generate_project"],
choices=list(BOARD_PROPERTIES),
type="str",
help="Name of the Zephyr board to build for.",
Expand Down Expand Up @@ -419,7 +421,7 @@ def _create_prj_conf(self, project_dir, options):
f.write("\n")

API_SERVER_CRT_LIBS_TOKEN = "<API_SERVER_CRT_LIBS>"
ENABLE_CMSIS_TOKEN = "<ENABLE_CMSIS>"
CMAKE_ARGS_TOKEN = "<CMAKE_ARGS>"

CRT_LIBS_BY_PROJECT_TYPE = {
"host_driven": "microtvm_rpc_server microtvm_rpc_common aot_executor_module aot_executor common",
Expand Down Expand Up @@ -457,6 +459,28 @@ def _cmsis_required(self, project_path: Union[str, pathlib.Path]) -> bool:
return True
return False

def _generate_cmake_args(self, mlf_extracted_path, options) -> str:
cmake_args = "\n# cmake args\n"
if options.get("verbose"):
cmake_args += "set(CMAKE_VERBOSE_MAKEFILE TRUE)\n"

if options.get("zephyr_base"):
cmake_args += f"set(ZEPHYR_BASE {options['zephyr_base']})\n"

if options.get("west_cmd"):
cmake_args += f"set(WEST {options['west_cmd']})\n"

if self._is_qemu(options["zephyr_board"]):
# Some boards support more than one emulator, so ensure QEMU is set.
cmake_args += f"set(EMU_PLATFORM qemu)\n"

cmake_args += f"set(BOARD {options['zephyr_board']})\n"

enable_cmsis = self._cmsis_required(mlf_extracted_path)
cmake_args += f"set(ENABLE_CMSIS {str(enable_cmsis).upper()})\n"

return cmake_args

def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options):
# Check Zephyr version
version = self._get_platform_version(get_zephyr_base(options))
Expand Down Expand Up @@ -488,7 +512,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec
os.makedirs(extract_path)
tf.extractall(path=extract_path)

if self._is_qemu(options):
if self._is_qemu(options["zephyr_board"]):
shutil.copytree(API_SERVER_DIR / "qemu-hack", project_dir / "qemu-hack")

# Populate CRT.
Expand All @@ -503,16 +527,15 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec
shutil.copy2(src_path, dst_path)

# Populate Makefile.
with open(project_dir / "CMakeLists.txt", "w") as cmake_f:
with open(API_SERVER_DIR / "CMakeLists.txt.template", "r") as cmake_template_f:
with open(project_dir / CMAKELIST_FILENAME, "w") as cmake_f:
with open(API_SERVER_DIR / f"{CMAKELIST_FILENAME}.template", "r") as cmake_template_f:
for line in cmake_template_f:
if self.API_SERVER_CRT_LIBS_TOKEN in line:
crt_libs = self.CRT_LIBS_BY_PROJECT_TYPE[options["project_type"]]
line = line.replace("<API_SERVER_CRT_LIBS>", crt_libs)

if self.ENABLE_CMSIS_TOKEN in line:
enable_cmsis = self._cmsis_required(extract_path)
line = line.replace(self.ENABLE_CMSIS_TOKEN, str(enable_cmsis).upper())
if self.CMAKE_ARGS_TOKEN in line:
line = self._generate_cmake_args(extract_path, options)

cmake_f.write(line)

Expand Down Expand Up @@ -542,23 +565,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec
def build(self, options):
BUILD_DIR.mkdir()

cmake_args = ["cmake", ".."]
if options.get("verbose"):
cmake_args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE")

if options.get("zephyr_base"):
cmake_args.append(f"-DZEPHYR_BASE:STRING={options['zephyr_base']}")

if options.get("west_cmd"):
cmake_args.append(f"-DWEST={options['west_cmd']}")

if self._is_qemu(options):
# Some boards support more than one emulator, so ensure QEMU is set.
cmake_args.append(f"-DEMU_PLATFORM=qemu")

cmake_args.append(f"-DBOARD:STRING={options['zephyr_board']}")

check_call(cmake_args, cwd=BUILD_DIR)
check_call(["cmake", ".."], cwd=BUILD_DIR)

args = ["make", "-j2"]
if options.get("verbose"):
Expand All @@ -571,22 +578,32 @@ def build(self, options):
_KNOWN_QEMU_ZEPHYR_BOARDS = ("mps2_an521", "mps3_an547")

@classmethod
def _is_qemu(cls, options):
return (
"qemu" in options["zephyr_board"]
or options["zephyr_board"] in cls._KNOWN_QEMU_ZEPHYR_BOARDS
)
def _is_qemu(cls, board: str) -> bool:
return "qemu" in board or board in cls._KNOWN_QEMU_ZEPHYR_BOARDS

@classmethod
def _has_fpu(cls, zephyr_board):
fpu_boards = [name for name, board in BOARD_PROPERTIES.items() if board["fpu"]]
return zephyr_board in fpu_boards

@classmethod
def _find_board_from_cmake_file(cls) -> str:
zephyr_board = None
with open(API_SERVER_DIR / CMAKELIST_FILENAME) as cmake_f:
for line in cmake_f:
if line.startswith("set(BOARD"):
zephyr_board = line.strip("\n").strip("set(BOARD ").strip(")")
break

if not zephyr_board:
mehrdadh marked this conversation as resolved.
Show resolved Hide resolved
raise RuntimeError(f"No Zephyr board set in the {API_SERVER_DIR / CMAKELIST_FILENAME}.")
return zephyr_board

def flash(self, options):
if self._is_qemu(options):
return # NOTE: qemu requires no flash step--it is launched from open_transport.
zephyr_board = self._find_board_from_cmake_file()

zephyr_board = options["zephyr_board"]
if self._is_qemu(zephyr_board):
return # NOTE: qemu requires no flash step--it is launched from open_transport.

# The nRF5340DK requires an additional `nrfjprog --recover` before each flash cycle.
# This is because readback protection is enabled by default when this device is flashed.
Expand All @@ -601,7 +618,9 @@ def flash(self, options):
check_call(["make", "flash"], cwd=API_SERVER_DIR / "build")

def open_transport(self, options):
if self._is_qemu(options):
zephyr_board = self._find_board_from_cmake_file()

if self._is_qemu(zephyr_board):
transport = ZephyrQemuTransport(options)
else:
transport = ZephyrSerialTransport(options)
Expand Down
14 changes: 6 additions & 8 deletions gallery/how_to/work_with_microtvm/micro_tvmc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ tvmc compile magic_wand.tflite \
#
# To generate a Zephyr project we use TVM Micro subcommand ``create``. We pass the MLF format and the path
# for the project to ``create`` subcommand along with project options. Project options for each
# platform (Zephyr/Arduino) are defined in their Project API server file. To generate Zephyr project, run:
# platform (Zephyr/Arduino) are defined in their Project API server file. To build
# Zephyr project for a different Zephyr board, change ``zephyr_board`` project option.
# To generate Zephyr project, run:
#
# bash
tvmc micro create \
Expand Down Expand Up @@ -151,20 +153,17 @@ tvmc micro create \
# bash
tvmc micro build \
project \
zephyr \
--project-option zephyr_board=qemu_x86
zephyr
# bash
# This will build the project in ``project`` directory and generates binary files under ``project/build``. To build
# Zephyr project for a different Zephyr board, change ``zephyr_board`` project option.
# This will build the project in ``project`` directory and generates binary files under ``project/build``.
#
# Next, we flash the Zephyr binary file to Zephyr device. For ``qemu_x86`` Zephyr board this step does not
# actually perform any action since QEMU will be used, however you need this step for physical hardware.
#
# bash
tvmc micro flash \
project \
zephyr \
--project-option zephyr_board=qemu_x86
zephyr
# bash

############################################################
Expand All @@ -181,7 +180,6 @@ tvmc micro flash \
tvmc run \
--device micro \
project \
--project-option zephyr_board=qemu_x86 \
--fill-mode ones \
--print-top 4
# bash
Expand Down
44 changes: 23 additions & 21 deletions tests/micro/common/test_tvmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ def test_tvmc_model_build_only(platform, board, output_dir):
cmd_result = _run_tvmc(create_project_cmd)
assert cmd_result == 0, "tvmc micro failed in step: create-project"

cmd_result = _run_tvmc(
["micro", "build", project_dir, platform, "--project-option", f"{platform}_board={board}"]
)
build_cmd = ["micro", "build", project_dir, platform]
if platform == "arduino":
build_cmd += ["--project-option", f"{platform}_board={board}"]
cmd_result = _run_tvmc(build_cmd)
assert cmd_result == 0, "tvmc micro failed in step: build"
shutil.rmtree(output_dir)

Expand Down Expand Up @@ -174,28 +175,29 @@ def test_tvmc_model_run(platform, board, output_dir):
cmd_result = _run_tvmc(create_project_cmd)
assert cmd_result == 0, "tvmc micro failed in step: create-project"

cmd_result = _run_tvmc(
["micro", "build", project_dir, platform, "--project-option", f"{platform}_board={board}"]
)
build_cmd = ["micro", "build", project_dir, platform]
if platform == "arduino":
build_cmd += ["--project-option", f"{platform}_board={board}"]
cmd_result = _run_tvmc(build_cmd)

assert cmd_result == 0, "tvmc micro failed in step: build"

cmd_result = _run_tvmc(
["micro", "flash", project_dir, platform, "--project-option", f"{platform}_board={board}"]
)
flash_cmd = ["micro", "flash", project_dir, platform]
if platform == "arduino":
flash_cmd += ["--project-option", f"{platform}_board={board}"]
cmd_result = _run_tvmc(flash_cmd)
assert cmd_result == 0, "tvmc micro failed in step: flash"

cmd_result = _run_tvmc(
[
"run",
"--device",
"micro",
project_dir,
"--project-option",
f"{platform}_board={board}",
"--fill-mode",
"random",
]
)
run_cmd = [
"run",
"--device",
"micro",
project_dir,
]
if platform == "arduino":
run_cmd += ["--project-option", f"{platform}_board={board}"]
run_cmd += ["--fill-mode", "random"]
cmd_result = _run_tvmc(run_cmd)
assert cmd_result == 0, "tvmc micro failed in step: run"
shutil.rmtree(output_dir)

Expand Down