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: RISCV support for Zephyr QEMU RISCV-32/64 #7804

Merged
merged 43 commits into from
Apr 8, 2021
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b199829
working on qemu
mehrdadh Mar 25, 2021
08ea3ba
debugging
Mar 29, 2021
7844d01
riscv hacks
Mar 29, 2021
cbddb72
merge fix
Mar 29, 2021
da1390e
config added
mehrdadh Mar 29, 2021
ff3e997
change target platforms
mehrdadh Mar 29, 2021
3876fae
fix merge
areusch Mar 26, 2021
25d3135
debugging issue with zephyr 2.5
mehrdadh Mar 29, 2021
91ca321
cleanup
mehrdadh Mar 30, 2021
791e0f2
Merge branch 'main' into riscv
mehrdadh Apr 2, 2021
8a92af8
working on qemu
mehrdadh Mar 25, 2021
223bc04
debugging
Mar 29, 2021
a17f168
riscv hacks
Mar 29, 2021
5597bce
config added
mehrdadh Mar 29, 2021
d477573
change target platforms
mehrdadh Mar 29, 2021
ce6fc59
fix merge
areusch Mar 26, 2021
52c8512
debugging issue with zephyr 2.5
mehrdadh Mar 29, 2021
0d6eaa3
cleanup
mehrdadh Mar 30, 2021
ecbef7a
testing
mehrdadh Apr 2, 2021
2d6eeba
pass riscv64
mehrdadh Apr 2, 2021
8ba1bcc
fix conflict
mehrdadh Apr 2, 2021
86752ae
Merge branch 'main' into riscv
mehrdadh Apr 2, 2021
f95126f
fix conflict
mehrdadh Apr 5, 2021
140048b
fix merge
areusch Apr 1, 2021
4523447
small fix
mehrdadh Apr 3, 2021
c946585
update vm_name
mehrdadh Apr 5, 2021
39670a5
add zephyr version
mehrdadh Apr 2, 2021
cd9a701
add comment for riscv32 issue
mehrdadh Apr 6, 2021
7457b0b
remove debug messages
mehrdadh Apr 6, 2021
72528fe
Merge branch 'main' into riscv
mehrdadh Apr 6, 2021
3aef928
cleanup
mehrdadh Apr 6, 2021
3858ee2
cleanup
mehrdadh Apr 6, 2021
c5532a5
change workspace
mehrdadh Apr 7, 2021
819358b
fix zephyr version
mehrdadh Apr 7, 2021
d632129
cleanup
mehrdadh Apr 7, 2021
6225e61
change to symlink
mehrdadh Apr 7, 2021
820943a
fix flag
mehrdadh Apr 7, 2021
a3eb92b
add comment
mehrdadh Apr 7, 2021
53ab2ee
lint check
mehrdadh Apr 7, 2021
319ad3b
lint fix
mehrdadh Apr 7, 2021
65a1a59
fix format
mehrdadh Apr 7, 2021
9647097
rename debugger
mehrdadh Apr 8, 2021
25bd2fe
rework args
mehrdadh Apr 8, 2021
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
14 changes: 12 additions & 2 deletions apps/microtvm/reference-vm/base-box-tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,14 +358,18 @@ def test_command(args):


def release_command(args):
vm_name = f"tlcpack/microtvm-{args.platform}"
if args.platform == "zephyr":
vm_name = f"{vm_name}-{args.zephyr_version}"
mehrdadh marked this conversation as resolved.
Show resolved Hide resolved

if not args.skip_creating_release_version:
subprocess.check_call(
[
"vagrant",
"cloud",
"version",
"create",
f"tlcpack/microtvm-{args.platform}",
vm_name,
args.release_version,
]
)
Expand All @@ -379,7 +383,7 @@ def release_command(args):
"cloud",
"publish",
"-f",
f"tlcpack/microtvm-{args.platform}",
vm_name,
args.release_version,
provider_name,
os.path.join(
Expand Down Expand Up @@ -461,6 +465,12 @@ def parse_args():
help="For use with 'test' command. MicroTVM platfrom that are used for testing.",
)

parser.add_argument(
"--zephyr-version",
default="2.5",
help="Zephyr RTOS version to release, in the form 'x.y'. Must be specified with release.",
)

return parser.parse_args()


Expand Down
2 changes: 1 addition & 1 deletion apps/microtvm/reference-vm/zephyr/base-box/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ wget --no-verbose -O $ZEPHYR_SDK_FILE \
https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/zephyr-sdk-${ZEPHYR_SDK_VERSION}-x86_64-linux-setup.run
chmod +x $ZEPHYR_SDK_FILE
"./$ZEPHYR_SDK_FILE" -- -d ~/zephyr-sdk -y
rm -rf ZEPHYR_SDK_FILE
rm -rf "${ZEPHYR_SDK_FILE}"

# GDB for Zephyr SDK depends on python3.8
sudo add-apt-repository ppa:deadsnakes/ppa
Expand Down
29 changes: 29 additions & 0 deletions apps/microtvm/zephyr/demo_runtime/boards/qemu_riscv32.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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 RISCV32 microTVM board.

# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random.
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_TIMER_RANDOM_GENERATOR=y

# Default 512, for operations with large floating point data.
mehrdadh marked this conversation as resolved.
Show resolved Hide resolved
CONFIG_MAIN_STACK_SIZE=2048

# For floating point operations. It has exception on floating point operations
# without this flag.
CONFIG_FPU_SHARING=y
25 changes: 25 additions & 0 deletions apps/microtvm/zephyr/demo_runtime/boards/qemu_riscv64.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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 RISCV64 microTVM board.

# For TVMPlatformGenerateRandom(). Remember, these values do not need to be truly random.
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_TIMER_RANDOM_GENERATOR=y

# Default 512, for operations with large floating point data.
CONFIG_MAIN_STACK_SIZE=2048
5 changes: 5 additions & 0 deletions apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ while [ "$#" -gt 0 ]; do
shift
done

# For debugging
if [ "${TVM_QEMU_DEBUG}" != "" ]; then
ARGS=( "${ARGS[@]}" -s -S )
fi

"${ARGS[@]}"
6 changes: 6 additions & 0 deletions apps/microtvm/zephyr/demo_runtime/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ static uint8_t main_rx_buf[RING_BUF_SIZE_BYTES];
// The main function of this application.
extern void __stdout_hook_install(int (*hook)(int));
void main(void) {
// TODO (mehrdadh): Update this when zephyr version has updated to 2.6.
// Update zephyr to latest version to use with qemu_riscv32.
#ifdef CONFIG_BOARD_QEMU_RISCV32
k_float_enable(_current, 0);
#endif

#ifdef CONFIG_LED
int ret;
led0_pin = device_get_binding(LED0);
Expand Down
61 changes: 58 additions & 3 deletions python/tvm/micro/contrib/zephyr.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
"""Defines a compiler integration that uses an externally-supplied Zephyr project."""

import collections
import copy
import logging
import multiprocessing
import os
import pathlib
import re
import tempfile
import textwrap
Expand Down Expand Up @@ -428,12 +430,28 @@ def _get_device_args(self, cmake_entries):
f"runner {flash_runner}"
)

def _zephyr_transport(self, micro_binary):
qemu_debugger = None
if self._debug_rpc_session:
qemu_debugger = debugger.RpcDebugger(
self._debug_rpc_session,
debugger.DebuggerFactory(
QemuGdbDebugger,
(micro_binary.abspath(micro_binary.debug_files[0]),),
{},
),
)

return ZephyrQemuTransport(
micro_binary.base_dir, startup_timeout_sec=30.0, qemu_debugger=qemu_debugger
)

def flash(self, micro_binary):
cmake_entries = read_cmake_cache(
micro_binary.abspath(micro_binary.labelled_files["cmake_cache"][0])
)
if "qemu" in cmake_entries["BOARD"]:
return ZephyrQemuTransport(micro_binary.base_dir, startup_timeout_sec=30.0)
return self._zephyr_transport(micro_binary)

build_dir = os.path.dirname(
micro_binary.abspath(micro_binary.labelled_files["cmake_cache"][0])
Expand Down Expand Up @@ -532,6 +550,26 @@ def transport(self, micro_binary):
)


class QemuGdbDebugger(debugger.GdbDebugger):
def __init__(self, elf_file):
super(QemuGdbDebugger, self).__init__()
self._elf_file = elf_file

def popen_kwargs(self):
# expect self._elf file to follow the form .../zephyr/zephyr.elf
cmake_cache_path = pathlib.Path(self._elf_file).parent.parent / "CMakeCache.txt"
cmake_cache = read_cmake_cache(cmake_cache_path)
return {
"args": [
cmake_cache["CMAKE_GDB"],
"-ex",
"target remote localhost:1234",
"-ex",
f"file {self._elf_file}",
],
}


class QemuStartupFailureError(Exception):
"""Raised when the qemu pipe is not present within startup_timeout_sec."""

Expand Down Expand Up @@ -571,33 +609,47 @@ def write(self, data, timeout_sec):
class ZephyrQemuTransport(Transport):
"""The user-facing Zephyr QEMU transport class."""

def __init__(self, base_dir, startup_timeout_sec=5.0, **kwargs):
def __init__(self, base_dir, startup_timeout_sec=5.0, qemu_debugger=None, **kwargs):
self.base_dir = base_dir
self.startup_timeout_sec = startup_timeout_sec
self.kwargs = kwargs
self.proc = None
self.fd_transport = None
self.pipe_dir = None
self.qemu_debugger = qemu_debugger

def timeouts(self):
return TransportTimeouts(
session_start_retry_timeout_sec=2.0,
session_start_timeout_sec=self.startup_timeout_sec,
session_established_timeout_sec=5.0,
session_established_timeout_sec=5.0 if self.qemu_debugger is None else 0,
)

def open(self):
self.pipe_dir = tempfile.mkdtemp()
self.pipe = os.path.join(self.pipe_dir, "fifo")
self.write_pipe = os.path.join(self.pipe_dir, "fifo.in")
self.read_pipe = os.path.join(self.pipe_dir, "fifo.out")

os.mkfifo(self.write_pipe)
os.mkfifo(self.read_pipe)
if self.qemu_debugger is not None:
if "env" in self.kwargs:
self.kwargs["env"] = copy.copy(self.kwargs["env"])
else:
self.kwargs["env"] = os.environ.copy()

self.kwargs["env"]["TVM_QEMU_DEBUG"] = "1"

self.proc = subprocess.Popen(
["make", "run", f"QEMU_PIPE={self.pipe}"],
cwd=self.base_dir,
**self.kwargs,
)

if self.qemu_debugger is not None:
self.qemu_debugger.start()

# NOTE: although each pipe is unidirectional, open both as RDWR to work around a select
# limitation on linux. Without this, non-blocking I/O can't use timeouts because named
# FIFO are always considered ready to read when no one has opened them for writing.
Expand All @@ -612,6 +664,9 @@ def open(self):
self.fd_transport.open()

def close(self):
if self.qemu_debugger is not None:
self.qemu_debugger.stop()

if self.fd_transport is not None:
self.fd_transport.child_transport.write_monitor_quit()
self.proc.wait()
Expand Down
6 changes: 5 additions & 1 deletion tests/lint/check_file_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,15 @@
"tests/micro/zephyr/testdata/mnist-8.onnx",
# microTVM Zephyr runtime
"apps/microtvm/zephyr/demo_runtime/prj.conf",
"apps/microtvm/zephyr/demo_runtime/boards/qemu_x86.conf",
"apps/microtvm/zephyr/demo_runtime/boards/qemu_riscv32.conf",
"apps/microtvm/zephyr/demo_runtime/boards/qemu_riscv64.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/boards/stm32f746g_disco.conf",
"apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-i386",
"apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-riscv32",
"apps/microtvm/zephyr/demo_runtime/qemu-hack/qemu-system-riscv64",
# microTVM Virtual Machines
"apps/microtvm/reference-vm/zephyr/Vagrantfile",
"apps/microtvm/reference-vm/zephyr/base-box/Vagrantfile.packer-template",
Expand Down
12 changes: 11 additions & 1 deletion tests/micro/zephyr/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,22 @@

import tvm.target.target

# The models that should pass this configuration. Maps a short, identifying platform string to
# (model, zephyr_board).
PLATFORMS = {
"host": ("host", "qemu_x86"),
"host_riscv32": ("host", "qemu_riscv32"),
"host_riscv64": ("host", "qemu_riscv64"),
"stm32f746xx": ("stm32f746xx", "nucleo_f746zg"),
"nrf5340dk": ("nrf5340dk", "nrf5340dk_nrf5340_cpuapp"),
}


def pytest_addoption(parser):
parser.addoption(
"--microtvm-platforms",
default="host",
choices=tvm.target.target.MICRO_SUPPORTED_MODELS.keys(),
choices=PLATFORMS.keys(),
help=(
"Specify a comma-separated list of test models (i.e. as passed to tvm.target.micro()) "
"for microTVM tests."
Expand Down
18 changes: 8 additions & 10 deletions tests/micro/zephyr/test_zephyr.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import os
import subprocess
import sys
import logging

import pytest
import numpy as np
Expand All @@ -39,6 +40,8 @@
from tvm.relay.expr_functor import ExprMutator
from tvm.relay.op.annotation import compiler_begin, compiler_end

import conftest

# If set, build the uTVM binary from scratch on each test.
# Otherwise, reuses the build from the previous test run.
BUILD = True
Expand All @@ -48,6 +51,10 @@
# python -m tvm.exec.microtvm_debug_shell
DEBUG = False

_LOG = logging.getLogger(__name__)

PLATFORMS = conftest.PLATFORMS


def _make_sess_from_op(model, zephyr_board, west_cmd, op_name, sched, arg_bufs):
target = tvm.target.target.micro(model)
Expand All @@ -59,7 +66,7 @@ def _make_sess_from_op(model, zephyr_board, west_cmd, op_name, sched, arg_bufs):


def _make_session(model, target, zephyr_board, west_cmd, mod):
test_name = f"{os.path.splitext(os.path.abspath(__file__))[0]}_{model}"
test_name = f"{os.path.splitext(os.path.abspath(__file__))[0]}_{zephyr_board}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change? seems like we should put both in the path, if anything

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@areusch so we distinguish the test based on the zephyr_board. For ex. qemu_x86, qemu_riscv32 and qemu_riscv64 all use the same model which is host, but they have different zephyr_board. zephyr_board will always be unique.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh sorry, I was misreading. this looks good

prev_build = f"{test_name}-last-build.micro-binary"
workspace_root = (
f'{test_name}_workspace/{datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")}'
Expand Down Expand Up @@ -123,15 +130,6 @@ def _make_add_sess(model, zephyr_board, west_cmd):
return _make_sess_from_op(model, zephyr_board, west_cmd, "add", sched, [A, B, C])


# The models that should pass this configuration. Maps a short, identifying platform string to
# (model, zephyr_board).
PLATFORMS = {
"host": ("host", "qemu_x86"),
"stm32f746xx": ("stm32f746xx", "nucleo_f746zg"),
"nrf5340dk": ("nrf5340dk", "nrf5340dk_nrf5340_cpuapp"),
}


# The same test code can be executed on both the QEMU simulation and on real hardware.
def test_compile_runtime(platform, west_cmd):
"""Test compiling the on-device runtime."""
Expand Down