blob: dd388109466f05ec4a5d9ba103fa917c2eedba92 [file]
# 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.
import pytest
pytest.importorskip("ethosu.vela")
import tvm
from tvm import relay
from tvm.relay.testing import run_opt_pass
from tvm.relay.backend.contrib.ethosu.tir import spec
from tvm.relay.backend.contrib.ethosu.tir.compiler import _lower_to_tir
from .infra import make_ethosu_binary_elementwise, get_binary_elementwise_args
@pytest.mark.parametrize(
"ifm_shape, ifm2_shape, ifm_channels, ifm2_channels, ifm_layout, ofm_layout, rounding_mode",
[
((1, 5, 9, 3), (1, 5, 9, 3), 3, 3, "NHWC", "NHWC", "TFL"),
((1, 8, 3, 9, 16), (1, 8, 3, 9, 16), 40, 40, "NHCWB16", "NHCWB16", "NATURAL"),
((1, 8, 3, 9, 16), (1, 8, 3, 9, 16), 40, 40, "NHCWB16", "NHWC", "TRUNCATE"),
((1, 8, 9, 40), (1, 8, 9, 40), 40, 40, "NHWC", "NHCWB16", "TFL"),
# Broadcast
((1, 5, 9, 3), (1, 1, 9, 1), 3, 1, "NHWC", "NHWC", "NATURAL"),
((1, 8, 9, 40), (1, 1, 1, 1), 40, 1, "NHWC", "NHCWB16", "TRUNCATE"),
],
)
@pytest.mark.parametrize("operator_type", ["ADD", "SUB", "MUL", "MIN", "MAX"])
@pytest.mark.parametrize("activation", ["NONE", "CLIP"])
def test_binary_elementwise_single(
ifm_shape,
ifm2_shape,
ifm_channels,
ifm2_channels,
ifm_layout,
ofm_layout,
rounding_mode,
operator_type,
activation,
):
dtype = "int8"
ifm = relay.var("ifm", shape=ifm_shape, dtype=dtype)
ifm2 = relay.var("ifm2", shape=ifm2_shape, dtype=dtype)
binary_elementwise = make_ethosu_binary_elementwise(
ifm,
ifm2,
ifm_channels,
ifm2_channels,
operator_type,
dtype,
False,
activation,
ifm_layout,
ifm_layout,
ofm_layout,
rounding_mode,
)
func = relay.Function(relay.analysis.free_vars(binary_elementwise), binary_elementwise)
func = run_opt_pass(func, relay.transform.InferType())
mod, _ = _lower_to_tir(func)
data = []
def _visit(stmt):
if isinstance(stmt, tvm.tir.Call):
data.append(get_binary_elementwise_args(stmt))
tvm.tir.stmt_functor.post_order_visit(mod["main"].body, _visit)
if ifm_layout == "NHWC":
ifm_stride_c = 1
ifm_stride_w = ifm_shape[3] if ifm_shape[2] != 1 else 1
ifm_stride_h = ifm_shape[2] * ifm_shape[3] if ifm_shape[1] != 1 else 1
ifm2_stride_c = 1
ifm2_stride_w = ifm2_shape[3] if ifm2_shape[2] != 1 else 1
ifm2_stride_h = ifm2_shape[2] * ifm2_shape[3] if ifm2_shape[1] != 1 else 1
ofm_height = ifm_shape[1]
ofm_width = ifm_shape[2]
else:
ifm_stride_w = 16
ifm_stride_c = 16 * ifm_shape[3]
ifm_stride_h = 16 * ifm_shape[2] * ifm_shape[3]
ifm2_stride_w = 16
ifm2_stride_c = 16 * ifm2_shape[3]
ifm2_stride_h = 16 * ifm2_shape[2] * ifm2_shape[3]
ofm_height = ifm_shape[1]
ofm_width = ifm_shape[3]
if ofm_layout == "NHWC":
ofm_stride_c = 1
ofm_stride_w = ifm_channels if ofm_width > 1 else 1
ofm_stride_h = ifm_channels * ofm_width if ofm_height > 1 else 1
else:
ofm_stride_w = 16
ofm_stride_c = 16 * ofm_width
ofm_stride_h = 16 * ofm_width * ((ifm_channels - 1) // 16 + 1)
serial_binary_elementwise = spec.SerialBinaryElementwise(
ifm=spec.SerialFeatureMap(
data_type=dtype,
height=ifm_shape[1],
width=ifm_shape[2] if ifm_layout == "NHWC" else ifm_shape[3],
channels=ifm_channels,
tile_height_0=ifm_shape[1],
tile_height_1=0,
tile_width_0=ifm_shape[2] if ifm_layout == "NHWC" else ifm_shape[3],
tile_address_0=0,
tile_address_1=0,
tile_address_2=0,
tile_address_3=0,
scale=1.0,
zero_point=0,
layout=ifm_layout,
stride_h=ifm_stride_h,
stride_w=ifm_stride_w,
stride_c=ifm_stride_c,
),
ifm2=spec.SerialFeatureMap(
data_type=dtype,
height=ifm2_shape[1],
width=ifm2_shape[2] if ifm_layout == "NHWC" else ifm2_shape[3],
channels=ifm2_channels,
tile_height_0=ifm2_shape[1],
tile_height_1=0,
tile_width_0=ifm2_shape[2] if ifm_layout == "NHWC" else ifm2_shape[3],
tile_address_0=0,
tile_address_1=0,
tile_address_2=0,
tile_address_3=0,
scale=1.0,
zero_point=0,
layout=ifm_layout,
stride_h=ifm2_stride_h,
stride_w=ifm2_stride_w,
stride_c=ifm2_stride_c,
),
ofm=spec.SerialFeatureMap(
data_type=dtype,
height=ofm_height,
width=ofm_width,
channels=ifm_channels,
tile_height_0=ofm_height,
tile_height_1=0,
tile_width_0=ofm_width,
tile_address_0=0,
tile_address_1=0,
tile_address_2=0,
tile_address_3=0,
scale=1.0,
zero_point=0,
layout=ofm_layout,
stride_h=ofm_stride_h,
stride_w=ofm_stride_w,
stride_c=ofm_stride_c,
),
operator_type=operator_type,
reversed_operands=False,
activation=spec.SerialActivation(
op=activation,
clip_min=10 if activation == "CLIP" else 0,
clip_max=100 if activation == "CLIP" else 0,
),
rounding_mode=rounding_mode,
block_config=spec.SerialBlockConfig(0, 0, 0),
rescale_config=spec.SerialRescaleConfig(False, 0, 0),
)
assert data[0] == ["ethosu_binary_elementwise"] + list(serial_binary_elementwise)
@pytest.mark.parametrize(
"ifm_shape, ifm2_shape, ifm_channels, ifm2_channels, ifm_layout, ofm_layout",
[
((1, 5, 9, 3), (1, 5, 9, 3), 3, 3, "NHWC", "NHWC"),
((1, 8, 3, 9, 16), (1, 8, 3, 9, 16), 40, 40, "NHCWB16", "NHCWB16"),
((1, 8, 3, 9, 16), (1, 8, 3, 9, 16), 40, 40, "NHCWB16", "NHWC"),
((1, 8, 9, 40), (1, 8, 9, 40), 40, 40, "NHWC", "NHCWB16"),
# Broadcast
((1, 5, 9, 3), (1, 1, 9, 1), 3, 1, "NHWC", "NHWC"),
((1, 8, 9, 40), (1, 1, 1, 1), 40, 1, "NHWC", "NHCWB16"),
],
)
@pytest.mark.parametrize("operator_type", ["SHR", "SHL"])
@pytest.mark.parametrize("rounding_mode", ["TFL", "NATURAL", "TRUNCATE"])
def test_shift_binary_elementwise_single(
ifm_shape,
ifm2_shape,
ifm_channels,
ifm2_channels,
ifm_layout,
ofm_layout,
operator_type,
rounding_mode,
):
dtype = "int32"
activation = "NONE" # Only NONE is available if the activation type is int32
ifm = relay.var("ifm", shape=ifm_shape, dtype=dtype)
ifm2 = relay.var("ifm2", shape=ifm2_shape, dtype=dtype)
binary_elementwise = make_ethosu_binary_elementwise(
ifm,
ifm2,
ifm_channels,
ifm2_channels,
operator_type,
dtype,
False,
"NONE",
ifm_layout,
ifm_layout,
ofm_layout,
rounding_mode,
)
func = relay.Function(relay.analysis.free_vars(binary_elementwise), binary_elementwise)
func = run_opt_pass(func, relay.transform.InferType())
mod, _ = _lower_to_tir(func)
data = []
def _visit(stmt):
if isinstance(stmt, tvm.tir.Call):
data.append(get_binary_elementwise_args(stmt))
tvm.tir.stmt_functor.post_order_visit(mod["main"].body, _visit)
if ifm_layout == "NHWC":
ifm_stride_c = 1
ifm_stride_w = ifm_shape[3] if ifm_shape[2] != 1 else 1
ifm_stride_h = ifm_shape[2] * ifm_shape[3] if ifm_shape[1] != 1 else 1
ifm2_stride_c = 1
ifm2_stride_w = ifm2_shape[3] if ifm2_shape[2] != 1 else 1
ifm2_stride_h = ifm2_shape[2] * ifm2_shape[3] if ifm2_shape[1] != 1 else 1
ofm_height = ifm_shape[1]
ofm_width = ifm_shape[2]
else:
ifm_stride_w = 16
ifm_stride_c = 16 * ifm_shape[3]
ifm_stride_h = 16 * ifm_shape[2] * ifm_shape[3]
ifm2_stride_w = 16
ifm2_stride_c = 16 * ifm2_shape[3]
ifm2_stride_h = 16 * ifm2_shape[2] * ifm2_shape[3]
ofm_height = ifm_shape[1]
ofm_width = ifm_shape[3]
if ofm_layout == "NHWC":
ofm_stride_c = 1
ofm_stride_w = ifm_channels if ofm_width > 1 else 1
ofm_stride_h = ifm_channels * ofm_width if ofm_height > 1 else 1
else:
ofm_stride_w = 16
ofm_stride_c = 16 * ofm_width
ofm_stride_h = 16 * ofm_width * ((ifm_channels - 1) // 16 + 1)
serial_binary_elementwise = spec.SerialBinaryElementwise(
ifm=spec.SerialFeatureMap(
data_type=dtype,
height=ifm_shape[1],
width=ifm_shape[2] if ifm_layout == "NHWC" else ifm_shape[3],
channels=ifm_channels,
tile_height_0=ifm_shape[1],
tile_height_1=0,
tile_width_0=ifm_shape[2] if ifm_layout == "NHWC" else ifm_shape[3],
tile_address_0=0,
tile_address_1=0,
tile_address_2=0,
tile_address_3=0,
scale=1.0,
zero_point=0,
layout=ifm_layout,
stride_h=ifm_stride_h,
stride_w=ifm_stride_w,
stride_c=ifm_stride_c,
),
ifm2=spec.SerialFeatureMap(
data_type=dtype,
height=ifm2_shape[1],
width=ifm2_shape[2] if ifm_layout == "NHWC" else ifm2_shape[3],
channels=ifm2_channels,
tile_height_0=ifm2_shape[1],
tile_height_1=0,
tile_width_0=ifm2_shape[2] if ifm_layout == "NHWC" else ifm2_shape[3],
tile_address_0=0,
tile_address_1=0,
tile_address_2=0,
tile_address_3=0,
scale=1.0,
zero_point=0,
layout=ifm_layout,
stride_h=ifm2_stride_h,
stride_w=ifm2_stride_w,
stride_c=ifm2_stride_c,
),
ofm=spec.SerialFeatureMap(
data_type=dtype,
height=ofm_height,
width=ofm_width,
channels=ifm_channels,
tile_height_0=ofm_height,
tile_height_1=0,
tile_width_0=ofm_width,
tile_address_0=0,
tile_address_1=0,
tile_address_2=0,
tile_address_3=0,
scale=1.0,
zero_point=0,
layout=ofm_layout,
stride_h=ofm_stride_h,
stride_w=ofm_stride_w,
stride_c=ofm_stride_c,
),
operator_type=operator_type,
reversed_operands=False,
activation=spec.SerialActivation(
op=activation,
clip_min=0,
clip_max=0,
),
rounding_mode=rounding_mode,
block_config=spec.SerialBlockConfig(0, 0, 0),
rescale_config=spec.SerialRescaleConfig(False, 0, 0),
)
assert data[0] == ["ethosu_binary_elementwise"] + list(serial_binary_elementwise)
if __name__ == "__main__":
tvm.testing.main()