Skip to content

Commit

Permalink
[1/3][AOT][DeviceAPI] Connecting devices structure to relevant operat…
Browse files Browse the repository at this point in the history
…ors (apache#9395)

* [AOT][DeviceAPI] Connecting devices structure to relevant operators

This patch adds support for passing the device context via the unpacked API in AOT, generating an additional struct if necessary:

```c
/*!
 * \brief Device context pointers for TVM module "default"
 */
struct tvmgen_default_devices {
  void* npu;
};
```

Which is then added as an argument to the entry function:
```c
/*!
 * \brief entrypoint function for TVM module "default"
 * \param inputs Input tensors for the module
 * \param outputs Output tensors for the module
 * \param devices Device context pointers for the module
 */
int32_t tvmgen_default_run(
  struct tvmgen_default_inputs* inputs,
  struct tvmgen_default_outputs* outputs,
  struct tvmgen_default_devices* devices
);
```

I've temporarily added the collection of external code generators to the TE compiler pending proper annotation of the eventual functions.

Co-authored-by: Grant Watson <grant.watson@arm.com>

* Correct "use_device_api" attribute name on Target

Co-authored-by: Grant Watson <grant.watson@arm.com>
  • Loading branch information
2 people authored and mehrdadh committed Dec 1, 2021
1 parent 3fc5ff0 commit 2f0c742
Show file tree
Hide file tree
Showing 39 changed files with 564 additions and 152 deletions.
4 changes: 2 additions & 2 deletions apps/microtvm/ethosu/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ RANLIB = arm-none-eabi-ranlib
PKG_CFLAGS = ${PKG_COMPILE_OPTS} \
-I${STANDALONE_CRT_PATH}/include \
-I${STANDALONE_CRT_PATH}/src/runtime/crt/include \
-Iinclude \
-I${PWD}/include \
-I${CORSTONE_300_PATH} \
-I${ETHOSU_PATH}/core_driver/include \
-I${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Include/ \
Expand Down Expand Up @@ -95,7 +95,7 @@ ${BUILD_DIR}/ethosu_core_driver/libethosu_core_driver.a:
$(QUIET)cd $(abspath $(BUILD_DIR)/ethosu_core_driver) && $(MAKE)

# Build demo application
$(BUILD_DIR)/demo: src/demo.c $(BUILD_DIR)/stack_allocator.o $(BUILD_DIR)/crt_backend_api.o ${BUILD_DIR}/libcodegen.a ${BUILD_DIR}/libcmsis_startup.a ${BUILD_DIR}/ethosu_core_driver/libethosu_core_driver.a ${BUILD_DIR}/libuart.a
$(BUILD_DIR)/demo: src/demo.c src/tvm_ethosu_runtime.c $(BUILD_DIR)/stack_allocator.o $(BUILD_DIR)/crt_backend_api.o ${BUILD_DIR}/libcodegen.a ${BUILD_DIR}/libcmsis_startup.a ${BUILD_DIR}/ethosu_core_driver/libethosu_core_driver.a ${BUILD_DIR}/libuart.a
$(QUIET)mkdir -p $(@D)
$(QUIET)$(CC) $(PKG_CFLAGS) -o $@ $^ $(PKG_LDFLAGS)

Expand Down
30 changes: 30 additions & 0 deletions apps/microtvm/ethosu/include/tvm_ethosu_runtime.h
Original file line number Diff line number Diff line change
@@ -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.
*/

#ifndef TVM_RUNTIME_CONTRIB_ETHOSU_ETHOSU_RUNTIME_H_
#define TVM_RUNTIME_CONTRIB_ETHOSU_ETHOSU_RUNTIME_H_

#include <ethosu_driver.h>
#include <stddef.h>
#include <stdint.h>

int32_t TVMEthosULaunch(struct ethosu_driver* resource_handle, void* cms_data, size_t cms_data_size,
uint64_t* base_addrs, size_t* base_addrs_size, int num_tensors);

#endif // TVM_RUNTIME_CONTRIB_ETHOSU_ETHOSU_RUNTIME_H_
7 changes: 6 additions & 1 deletion apps/microtvm/ethosu/src/demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ int main(int argc, char** argv) {
struct tvmgen_default_inputs inputs = {
.input = input,
};
tvmgen_default_run(&inputs, &outputs);
struct ethosu_driver* driver = ethosu_reserve_driver();
struct tvmgen_default_devices devices = {
.ethos_u = driver,
};
tvmgen_default_run(&inputs, &outputs, &devices);
ethosu_release_driver(driver);

// Calculate index of max value
uint8_t max_value = 0;
Expand Down
34 changes: 34 additions & 0 deletions apps/microtvm/ethosu/src/tvm_ethosu_runtime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#include "tvm_ethosu_runtime.h"

#include <ethosu_driver.h>

int32_t TVMEthosULaunch(struct ethosu_driver* driver, void* cms_data, size_t cms_data_size,
uint64_t* base_addrs, size_t* base_addrs_size, int num_tensors) {
int32_t result =
ethosu_invoke(driver, cms_data, cms_data_size, base_addrs, base_addrs_size, num_tensors);

// Map errors in invoke to TVM errors
if (result != 0) {
return -1;
}
return 0;
}
2 changes: 1 addition & 1 deletion python/tvm/driver/tvmc/composite_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"pass_pipeline": partition_for_ethosn78,
},
"ethos-u": {
"config_key": "relay.ext.ethosu.options",
"config_key": "relay.ext.ethos-u.options",
"pass_pipeline": partition_for_ethosu,
},
"bnns": {
Expand Down
7 changes: 4 additions & 3 deletions python/tvm/micro/model_library_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ class UnsupportedInModelLibraryFormatError(Exception):
"""Raised when export_model_library_format does not support the given Module tree."""


def generate_c_interface_header(module_name, inputs, outputs, include_path):
def generate_c_interface_header(module_name, inputs, outputs, devices, include_path):
"""Generate C Interface header to be included in MLF"""
mangled_name = to_c_variable_style(prefix_generated_name(module_name))
metadata_header = os.path.join(include_path, f"{mangled_name}.h")

interface_c_create = tvm._ffi.get_global_func("runtime.InterfaceCCreate")
interface_c_module = interface_c_create(module_name, inputs, outputs)
interface_c_module = interface_c_create(module_name, inputs, outputs, devices)

with open(metadata_header, "w") as header_file:
header_file.write(interface_c_module.get_source())
Expand Down Expand Up @@ -318,7 +318,8 @@ def _export_graph_model_library_format(
include_path = codegen_dir / "host" / "include"
include_path.mkdir()
inputs, outputs = _get_inputs_and_outputs_from_module(mod)
generate_c_interface_header(mod.libmod_name, inputs, outputs, include_path)
devices = mod.get_devices()
generate_c_interface_header(mod.libmod_name, inputs, outputs, devices, include_path)

parameters_dir = tempdir / "parameters"
parameters_dir.mkdir()
Expand Down
2 changes: 1 addition & 1 deletion python/tvm/relay/backend/contrib/ethosu/_ffi_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
"""FFI APIs for relay transformation passes."""
import tvm._ffi # type: ignore

tvm._ffi._init_api("relay.ext.ethosu", __name__)
tvm._ffi._init_api("relay.ext.ethos-u", __name__)
6 changes: 3 additions & 3 deletions python/tvm/relay/backend/contrib/ethosu/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from tvm.relay.backend.contrib.ethosu import util


@tvm._ffi.register_func("relay.ext.ethosu")
@tvm._ffi.register_func("relay.ext.ethos-u")
def ethosu_compiler(external_function):
"""The entry-point to a compile a external relay function of
NPU compatible operators to generated command stream.
Expand All @@ -38,11 +38,11 @@ def ethosu_compiler(external_function):
input_size = util.calculate_size_bytes(external_function.params[0])
output_size = util.calculate_size_bytes(external_function.body)
cmms, encoded_constants, scratch_size = _compile(external_function)
ethosu_runtime = tvm._ffi.get_global_func("runtime.module.ethosu.create")
ethosu_runtime = tvm._ffi.get_global_func("runtime.module.ethos-u.create")
return ethosu_runtime(func_name, cmms, encoded_constants, scratch_size, input_size, output_size)


@tvm._ffi.register_func("relay.ext.ethosu.constant_updater")
@tvm._ffi.register_func("relay.ext.ethos-u.constant_updater")
def constant_updater(expr, symbol): # pylint: disable=unused-argument
"""
The constant updater process happen after lowering in the core compiler.
Expand Down
2 changes: 1 addition & 1 deletion python/tvm/relay/backend/contrib/ethosu/legalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Conv2DRewriter(DFPatternCallback):

def __init__(self):
super().__init__(require_type=True)
self.pattern = (wildcard().has_attr({"Composite": "ethosu.qnn_conv2d"}))(wildcard())
self.pattern = (wildcard().has_attr({"Composite": "ethos-u.qnn_conv2d"}))(wildcard())

def callback(
self, pre: tvm.relay.Expr, post: tvm.relay.Expr, node_map: tvm.ir.container.Map
Expand Down
2 changes: 1 addition & 1 deletion python/tvm/relay/backend/contrib/ethosu/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def round_up(a: int, b: int) -> int:

def get_accelerator_config():
"""Get the variant of the accelerator to compile for"""
compiler_attrs = tvm.get_global_func("relay.ext.ethosu.get_compiler_attrs")()
compiler_attrs = tvm.get_global_func("relay.ext.ethos-u.get_compiler_attrs")()
return compiler_attrs.accelerator_config


Expand Down
2 changes: 1 addition & 1 deletion python/tvm/relay/backend/contrib/ethosu/vela_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def get_accelerator_config() -> vapi.NpuAccelerator:
"ethos-u55-64": vapi.NpuAccelerator.Ethos_U55_64,
"ethos-u55-32": vapi.NpuAccelerator.Ethos_U55_32,
}
compiler_attrs = tvm.get_global_func("relay.ext.ethosu.get_compiler_attrs")()
compiler_attrs = tvm.get_global_func("relay.ext.ethos-u.get_compiler_attrs")()
accel_config_str = compiler_attrs.accelerator_config
assert accel_config_str in npu_accel_str_map.keys(), f"{accel_config_str} is not supported"
return npu_accel_str_map[accel_config_str]
11 changes: 10 additions & 1 deletion python/tvm/relay/backend/executor_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,22 @@ class AOTExecutorFactoryModule(ExecutorFactoryModule):
The parameters of module
function_metadata : Map of String to FunctionInfo
This holds a map function names to their information
devices : List[str]
List of devices used in the module
"""

def __init__(self, ir_mod, target, libmod, libmod_name, params, function_metadata):
def __init__(self, ir_mod, target, libmod, libmod_name, params, function_metadata, devices):
self.ir_mod = ir_mod
self.target = target
self.lib = libmod
self.libmod_name = libmod_name
self.params = params
self.iter_cnt = 0
self.function_metadata = function_metadata
self.devices = devices

def get_devices(self):
return self.devices

def get_params(self):
return self.params
Expand Down Expand Up @@ -152,6 +158,9 @@ def __init__(
def export_library(self, file_name, fcompile=None, addons=None, **kwargs):
return self.module.export_library(file_name, fcompile, addons, **kwargs)

def get_devices(self):
return []

def get_params(self):
return self.params

Expand Down
16 changes: 14 additions & 2 deletions python/tvm/relay/build_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def __init__(self):
self._set_params_func = self.mod["set_params"]
self._get_params_func = self.mod["get_params"]
self._get_function_metadata = self.mod["get_function_metadata"]
self._get_devices = self.mod["get_devices"]

def build(
self, mod, target=None, target_host=None, params=None, executor="graph", mod_name=None
Expand Down Expand Up @@ -236,6 +237,10 @@ def get_function_metadata(self):
each PrimFunc"""
return self._get_function_metadata()

def get_devices(self):
"""Returns a list of devices configured in this module"""
return self._get_devices()

def get_params(self):
"""Return the updated weights."""
params = self._get_params_func()
Expand Down Expand Up @@ -370,14 +375,21 @@ def build(ir_mod, target=None, target_host=None, params=None, mod_name="default"
mod=ir_mod, target=target, params=params, executor=executor, mod_name=mod_name
)
func_metadata = bld_mod.get_function_metadata()
devices = bld_mod.get_devices()

if executor == "aot":
executor_factory = _executor_factory.AOTExecutorFactoryModule(
ir_mod, target, runtime_mod, mod_name, params, func_metadata
ir_mod, target, runtime_mod, mod_name, params, func_metadata, devices
)
elif executor == "graph":
executor_factory = _executor_factory.GraphExecutorFactoryModule(
ir_mod, target, executor_config, runtime_mod, mod_name, params, func_metadata
ir_mod,
target,
executor_config,
runtime_mod,
mod_name,
params,
func_metadata,
)
else:
assert False, "Executor " + executor + " not supported"
Expand Down
30 changes: 15 additions & 15 deletions python/tvm/relay/op/contrib/ethosu.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class QnnConv2DParams:
and extract quantization information of all the associated tensors.
"""

composite_name = "ethosu.qnn_conv2d"
composite_name = "ethos-u.qnn_conv2d"
# The NPU only supports padding upto the numbers as follows
padding_bounds = [31, 31, 32, 32]
activation_map = {"clip": "CLIP"}
Expand Down Expand Up @@ -275,7 +275,7 @@ class QnnDepthwiseConv2DParams(QnnConv2DParams):
and extract the parameter information.
"""

composite_name = "ethosu.depthwise_conv2d"
composite_name = "ethos-u.depthwise_conv2d"
# The hardware only supports padding upto the numbers as follows
padding_bounds = [31, 31, 32, 32]

Expand Down Expand Up @@ -343,11 +343,11 @@ def qnn_depthwise_conv2d_pattern() -> tvm.relay.dataflow_pattern.DFPattern:

class MaxPool2DParams:
"""
This class will parse a call to a ethosu.maxpool2d composite function
This class will parse a call to a ethos-u.maxpool2d composite function
and extract the parameter information.
"""

composite_name = "ethosu.maxpool2d"
composite_name = "ethos-u.maxpool2d"
# The hardware only supports padding upto the numbers as follows
padding_bounds = [127, 127, 128, 128]

Expand Down Expand Up @@ -399,11 +399,11 @@ def qnn_maxpool2d_pattern() -> tvm.relay.dataflow_pattern.DFPattern:

class AvgPool2DParams:
"""
This class will parse a call to a ethosu.avgpool2d composite function
This class will parse a call to a ethos-u.avgpool2d composite function
and extract the parameter information.
"""

composite_name = "ethosu.avgpool2d"
composite_name = "ethos-u.avgpool2d"
# The hardware only supports padding upto the numbers as follows
padding_bounds = [127, 127, 128, 128]

Expand Down Expand Up @@ -547,7 +547,7 @@ class AddParams(BinaryElementwiseParams):
and extract the parameter information.
"""

composite_name = "ethosu.add"
composite_name = "ethos-u.add"

def __init__(self, func_body: Call):
BinaryElementwiseParams.__init__(self, func_body, "ADD", True)
Expand Down Expand Up @@ -589,7 +589,7 @@ class SubParams(BinaryElementwiseParams):
and extract the parameter information.
"""

composite_name = "ethosu.sub"
composite_name = "ethos-u.sub"

def __init__(self, func_body: Call):
BinaryElementwiseParams.__init__(self, func_body, "SUB", True)
Expand Down Expand Up @@ -631,7 +631,7 @@ class MulParams(BinaryElementwiseParams):
and extract the parameter information.
"""

composite_name = "ethosu.mul"
composite_name = "ethos-u.mul"

def __init__(self, func_body: Call):
BinaryElementwiseParams.__init__(self, func_body, "MUL", True)
Expand Down Expand Up @@ -673,7 +673,7 @@ class MinParams(BinaryElementwiseParams):
and extract the parameter information.
"""

composite_name = "ethosu.min"
composite_name = "ethos-u.min"

def __init__(self, func_body: Call):
BinaryElementwiseParams.__init__(self, func_body, "MIN", False)
Expand Down Expand Up @@ -708,7 +708,7 @@ class MaxParams(BinaryElementwiseParams):
and extract the parameter information.
"""

composite_name = "ethosu.max"
composite_name = "ethos-u.max"

def __init__(self, func_body: Call):
BinaryElementwiseParams.__init__(self, func_body, "MAX", False)
Expand Down Expand Up @@ -743,7 +743,7 @@ class ShlParams(BinaryElementwiseParams):
and extract the parameter information.
"""

composite_name = "ethosu.shl"
composite_name = "ethos-u.shl"

def __init__(self, func_body: Call):
BinaryElementwiseParams.__init__(self, func_body, "SHL", False)
Expand All @@ -768,7 +768,7 @@ def shl_pattern() -> tvm.relay.dataflow_pattern.DFPattern:
return pattern


@register_pattern_table("ethosu")
@register_pattern_table("ethos-u")
def pattern_table() -> List[Tuple[str, tvm.relay.dataflow_pattern.DFPattern, Callable]]:
return [
(
Expand Down Expand Up @@ -848,10 +848,10 @@ def partition_for_ethosu(
if params:
mod["main"] = bind_params_by_name(mod["main"], params)

pattern = relay.op.contrib.get_pattern_table("ethosu")
pattern = relay.op.contrib.get_pattern_table("ethos-u")
mod = relay.transform.InferType()(mod)
mod = relay.transform.MergeComposite(pattern)(mod)
mod = relay.transform.AnnotateTarget("ethosu")(mod)
mod = relay.transform.AnnotateTarget("ethos-u")(mod)
mod = relay.transform.MergeCompilerRegions()(mod)
mod = relay.transform.InferType()(mod)
mod = relay.transform.PartitionGraph()(mod)
Expand Down
Loading

0 comments on commit 2f0c742

Please sign in to comment.