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

[BYOC][ETHOSN] Introduce the Ethos-N BYOC integration #6222

Merged
merged 37 commits into from Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
628c6a8
[BYOC][ETHOSN] Introduce the Ethos-N BYOC integration
mbaret Jul 27, 2020
a73e524
Turn off USE_ETHOSN_HW by default
mbaret Aug 7, 2020
e116823
Update capabilities file
mbaret Aug 7, 2020
5350901
Fix missing header
mbaret Aug 10, 2020
b69b7a8
Update cmake comments on ETHOSN_HW
mbaret Aug 10, 2020
1d03ac9
Add checker for case when USE_ETHOSN=OFF and USE_ETHOSN_HW=ON
mbaret Aug 10, 2020
56b3294
Fix 'available' boolean
mbaret Aug 10, 2020
73d70e6
Check availability in op registration
mbaret Aug 10, 2020
01005f8
Remove unnecessary line
mbaret Aug 10, 2020
175fcdd
Simplify getting output_size
mbaret Aug 10, 2020
587e4a5
Remove unnecessary new line
mbaret Aug 10, 2020
b8acaa6
Remove NOLINTS
mbaret Aug 10, 2020
2f2a054
Remove unused parts of PR
mbaret Aug 10, 2020
3eacad8
Fix CI Ethos-N settings
mbaret Aug 10, 2020
dc48617
Removed unnecessary line in infra
mbaret Aug 10, 2020
696a6d6
Remove unnecessary len in infra
mbaret Aug 10, 2020
80fb108
Rename 'cpu_ops' to 'host_ops'
mbaret Aug 10, 2020
df6e6d0
Added explanation on mocking
mbaret Aug 10, 2020
c81af10
IsEthosOp -> IsEthosnOp
mbaret Aug 10, 2020
d39524c
Improve documentation in ethosn_api.h
mbaret Aug 10, 2020
3014543
No longer iterate over module when compiling
mbaret Aug 10, 2020
c478165
Move EthosnCompiler implementations into codegen.cc
mbaret Aug 10, 2020
abb1066
Fix linting
mbaret Aug 10, 2020
5cb6d9c
Refactor EthosnAPI compilation functions into EthosnCompiler
mbaret Aug 10, 2020
186c92e
Improve docs for Tvm2Npu
mbaret Aug 10, 2020
6a967e7
Move more implementation out of headers
mbaret Aug 10, 2020
c1b5b96
Move implementation in ethosn_api.h
mbaret Aug 10, 2020
d12983d
Improve docs for capabilities.h
mbaret Aug 10, 2020
93c5091
Use else() in cmake
mbaret Aug 12, 2020
5e62110
Use GetDataSize
mbaret Aug 12, 2020
d9b5e22
Use const&
mbaret Aug 12, 2020
d06da5a
Fix python linting
mbaret Aug 12, 2020
c6a0c1b
Remove load/save to file
mbaret Aug 12, 2020
4fe1a6f
data->data
mbaret Aug 12, 2020
ae17572
Remove specific cpu target
mbaret Aug 12, 2020
57d1569
Test export/load module
mbaret Aug 13, 2020
3ab99dd
Fix cmake garbage
mbaret Aug 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Expand Up @@ -8,6 +8,7 @@ include(cmake/util/FindOpenCL.cmake)
include(cmake/util/FindVulkan.cmake)
include(cmake/util/FindLLVM.cmake)
include(cmake/util/FindROCM.cmake)
include(cmake/util/FindEthosN.cmake)

if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/config.cmake)
include(${CMAKE_CURRENT_BINARY_DIR}/config.cmake)
Expand Down Expand Up @@ -44,6 +45,7 @@ tvm_option(INSTALL_DEV "Install compiler infrastructure" OFF)
tvm_option(HIDE_PRIVATE_SYMBOLS "Compile with -fvisibility=hidden." OFF)
tvm_option(USE_TF_TVMDSOOP "Build with TensorFlow TVMDSOOp" OFF)
tvm_option(USE_FALLBACK_STL_MAP "Use TVM's POD compatible Map" OFF)
tvm_option(USE_ETHOSN "Build with Arm Ethos-N" OFF)

# 3rdparty libraries
tvm_option(DLPACK_PATH "Path to DLPACK" "3rdparty/dlpack/include")
Expand Down Expand Up @@ -307,6 +309,7 @@ include(cmake/modules/Metal.cmake)
include(cmake/modules/ROCM.cmake)
include(cmake/modules/LLVM.cmake)
include(cmake/modules/Micro.cmake)
include(cmake/modules/contrib/EthosN.cmake)
include(cmake/modules/contrib/BLAS.cmake)
include(cmake/modules/contrib/CODEGENC.cmake)
include(cmake/modules/contrib/DNNL.cmake)
Expand Down
10 changes: 10 additions & 0 deletions cmake/config.cmake
Expand Up @@ -210,6 +210,16 @@ set(USE_DNNL_CODEGEN OFF)
set(USE_ARM_COMPUTE_LIB OFF)
set(USE_ARM_COMPUTE_LIB_GRAPH_RUNTIME OFF)

# Whether to build with Arm Ethos-N support
# Possible values:
# - OFF: disable Arm Ethos-N support
# - path/to/arm-ethos-N-stack: use a specific version of the
# Ethos-N driver stack
set(USE_ETHOSN OFF)
# If USE_ETHOSN is enabled, use ETHOSN_HW (ON) if Ethos-N hardware is available on this machine
# otherwise use ETHOSN_HW (OFF) to use the software test infrastructure
set(USE_ETHOSN_HW OFF)

# Build ANTLR parser for Relay text format
# Possible values:
# - ON: enable ANTLR by searching default locations (cmake find_program for antlr4 and /usr/local for jar)
Expand Down
57 changes: 57 additions & 0 deletions cmake/modules/contrib/EthosN.cmake
@@ -0,0 +1,57 @@
# 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.

# Arm Ethos-N rules

if(NOT USE_ETHOSN STREQUAL "OFF")
find_ethosn(${USE_ETHOSN})

if(NOT ETHOSN_FOUND)
message(FATAL_ERROR "Cannot find Ethos-N, USE_ETHOSN=" ${USE_ETHOSN})

else()
include_directories(${ETHOSN_INCLUDE_DIRS})
add_definitions(${ETHOSN_DEFINITIONS})

message(STATUS "Build with Ethos-N ${ETHOSN_PACKAGE_VERSION}")

file(GLOB ETHOSN_RUNTIME_CONTRIB_SRC
CONFIGURE_DEPENDS src/runtime/contrib/ethosn/ethosn_runtime.cc
CONFIGURE_DEPENDS src/runtime/contrib/ethosn/ethosn_device.cc)
list(APPEND RUNTIME_SRCS ${ETHOSN_RUNTIME_CONTRIB_SRC})

file(GLOB COMPILER_ETHOSN_SRCS
CONFIGURE_DEPENDS src/relay/backend/contrib/ethosn/*)
list(APPEND COMPILER_SRCS ${COMPILER_ETHOSN_SRCS})

list(APPEND TVM_LINKER_LIBS ${ETHOSN_COMPILER_LIBRARY}
${ETHOSN_RUNTIME_LIBRARY})
list(APPEND TVM_RUNTIME_LINKER_LIBS ${ETHOSN_COMPILER_LIBRARY}
${ETHOSN_RUNTIME_LIBRARY})

if(NOT MSVC)
set_source_files_properties(${COMPILER_ETHOSN_SRCS}
PROPERTIES COMPILE_DEFINITIONS "DMLC_ENABLE_RTTI=0")
set_source_files_properties(${COMPILER_ETHOSN_SRCS}
PROPERTIES COMPILE_FLAGS "-fno-rtti")
endif()
endif(NOT ETHOSN_FOUND)
else()
if(USE_ETHOSN_HW)
message(FATAL_ERROR "Cannot enable Ethos-N HW if USE_ETHOSN=OFF")
endif()
endif(NOT USE_ETHOSN STREQUAL "OFF")
94 changes: 94 additions & 0 deletions cmake/util/FindEthosN.cmake
@@ -0,0 +1,94 @@
# 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.

#######################################################
# Find Arm Ethos-N libraries
#
# Usage:
# find_ethosn(${USE_ETHOSN})
#
# - When USE_ETHOSN=/path/to/ethos-sdk-path, use the path from USE_ETHOSN
# - Else, when environment variable ETHOSN_STACK is set, use that path
# - When USE_ETHOSN=ON, use auto search
#
# Provide variables:
#
# - ETHOSN_FOUND
# - ETHOSN_PACKAGE_VERSION
# - ETHOSN_DEFINITIONS
# - ETHOSN_INCLUDE_DIRS
# - ETHOSN_COMPILER_LIBRARY
# - ETHOSN_RUNTIME_LIBRARY

macro(find_ethosn use_ethosn)
set(__use_ethosn ${use_ethosn})
if(IS_DIRECTORY ${__use_ethosn})
set(__ethosn_stack ${__use_ethosn})
message(STATUS "Arm Ethos-N driver stack PATH=" ${__use_ethosn})
elseif(IS_DIRECTORY $ENV{ETHOSN_STACK})
set(__ethosn_stack $ENV{ETHOSN_STACK})
message(STATUS "Arm Ethos-N driver stack from env=" ${__use_ethosn})
else()
set(__ethosn_stack "")
endif()

if(__ethosn_stack)
set(ETHOSN_INCLUDE_DIRS "")
# Compile-time support
find_path(_SL_DIR NAMES Support.hpp
PATHS ${__ethosn_stack}/include/ethosn_support_library)
string(REGEX REPLACE "/ethosn_support_library" "" _SL_DIR2 ${_SL_DIR})
list(APPEND ETHOSN_INCLUDE_DIRS "${_SL_DIR2}")

find_library(ETHOSN_COMPILER_LIBRARY NAMES EthosNSupport
PATHS ${__ethosn_stack}/lib)
find_library(ETHOSN_COMPILER_LIBRARY NAMES EthosNSupport)

set(ETHOSN_PACKAGE_VERSION "0.1.1")

if(USE_ETHOSN_HW STREQUAL "ON")
# Runtime hardware support
find_path(_DL_DIR NAMES Network.hpp
PATHS ${__ethosn_stack}/include/ethosn_driver_library)
string(REGEX REPLACE "/ethosn_driver_library" "" _DL_DIR2 ${_DL_DIR})
list(APPEND ETHOSN_INCLUDE_DIRS "${_DL_DIR2}")

find_library(ETHOSN_RUNTIME_LIBRARY NAMES EthosNDriver
PATHS ${__ethosn_stack}/lib)
find_library(ETHOSN_RUNTIME_LIBRARY NAMES EthosNDriver)
set(ETHOSN_DEFINITIONS -DETHOSN_HW)
endif ()

if(ETHOSN_COMPILER_LIBRARY)
set(ETHOSN_FOUND TRUE)
endif()
endif(__ethosn_stack)

if(NOT ETHOSN_FOUND)
if(__use_ethosn STREQUAL "ON")
message(WARNING "No cmake find_package available for Arm Ethos-N")
endif()

# additional libraries
else()
message(STATUS "Found ETHOSN_DEFINITIONS=${ETHOSN_DEFINITIONS}")
message(STATUS "Found ETHOSN_INCLUDE_DIRS=${ETHOSN_INCLUDE_DIRS}")
message(STATUS "Found ETHOSN_COMPILER_LIBRARY=${ETHOSN_COMPILER_LIBRARY}")
message(STATUS "Found ETHOSN_RUNTIME_LIBRARY=${ETHOSN_RUNTIME_LIBRARY}")
endif(NOT ETHOSN_FOUND)

endmacro(find_ethosn)
1 change: 1 addition & 0 deletions python/tvm/relay/op/contrib/__init__.py
Expand Up @@ -21,3 +21,4 @@
from .arm_compute_lib import *
from .dnnl import *
from .coreml import *
from .ethosn import *
22 changes: 22 additions & 0 deletions python/tvm/relay/op/contrib/_ethosn.py
@@ -0,0 +1,22 @@
# 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.

"""Expose 'is supported' functions to Python."""

import tvm._ffi

tvm._ffi._init_api("relay.ethos-n.support", __name__)
89 changes: 89 additions & 0 deletions python/tvm/relay/op/contrib/ethosn.py
@@ -0,0 +1,89 @@
# 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.
# pylint: disable=invalid-name, unused-argument
"""Arm(R) Ethos(TM) -N NPU supported operators."""
from enum import Enum
import tvm.ir
from ... import qnn as _qnn
from . import _ethosn as support


class Available(Enum):
UNAVAILABLE = 0
SW_ONLY = 1
SW_AND_HW = 2

def __bool__(self):
return self != Available.UNAVAILABLE


def ethosn_available():
"""Return whether Ethos-N software and hardware support is available"""
if not tvm.get_global_func("relay.ethos-n.query", True):
print("skip because Ethos-N module is not available")
return Available.UNAVAILABLE
hw = tvm.get_global_func("relay.ethos-n.query")()
return Available.SW_AND_HW if hw else Available.SW_ONLY


@tvm.ir.register_op_attr("qnn.concatenate", "target.ethos-n")
def qnn_concatenate(attrs, args):
"""Check if a concatenate is supported by Ethos-N."""
if not ethosn_available():
return False

conc = _qnn.op.concatenate(*args, **attrs)
if not support.concatenate(conc):
mbaret marked this conversation as resolved.
Show resolved Hide resolved
return False

# Support library has some unenforced restrictions on qnn params
min_range = 1e9
max_range = -1e9
qnn_params = []
for i in range(len(args[1].fields)):
scale = args[1].fields[i].data.asnumpy()
zero_point = args[2].fields[i].data.asnumpy()
min_range = min(-1 * zero_point * scale, min_range)
max_range = max((255 - zero_point) * scale, max_range)
qnn_params.append((scale, zero_point))

scale = (max_range - min_range) / 255
zero_point = int(-min_range/scale)
if (scale, zero_point) in qnn_params:
return True

return False


@tvm.ir.register_op_attr("split", "target.ethos-n")
def split(attrs, args):
Copy link
Member

Choose a reason for hiding this comment

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

Can you elaborate a bit why we currently chose these two ops other than more common ones like conv2d, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Conv2D is coming in the next PR. We split it up like this so that we could focus initially on the mechanics of the integration itself. Split/Concat motivate the tuple handling in the codegen which is why they were introduced now. Conv2D has a lot of other complexity to do with conversion between TVM and Support Library and so we thought that would be best handled separately.

"""Check if a split is supported by Ethos-N."""
if not ethosn_available():
return False

if isinstance(attrs["indices_or_sections"], tvm.tir.IntImm):
sp = tvm.relay.split(*args,
indices_or_sections=attrs["indices_or_sections"].value,
axis=attrs["axis"])
else:
sp = tvm.relay.split(*args,
indices_or_sections=attrs["indices_or_sections"],
axis=attrs["axis"])
if not support.split(sp.astuple()):
return False

return True
81 changes: 81 additions & 0 deletions src/relay/backend/contrib/ethosn/capabilities.h
@@ -0,0 +1,81 @@
/*
* 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.
*/

/*!
* \file src/relay/backend/contrib/ethosn/capabilities.h
* \brief The Ethos-N processor series has four variants, the Ethos-N37, Ethos-N57, Ethos-N77
* and the Ethos-N78. This release of the integration supports the first three variants.
* Configuration information for each variant is stored as a blob in this file. These blobs
* are passed into the Ethos-N support library, which in turn uses them to optimize the
* generated command-stream appropriately for the specified variant.
*/

#ifndef TVM_RELAY_BACKEND_CONTRIB_ETHOSN_CAPABILITIES_H_
#define TVM_RELAY_BACKEND_CONTRIB_ETHOSN_CAPABILITIES_H_

#include <vector>

namespace tvm {
namespace relay {
namespace contrib {
namespace ethosn {

/* Ethos-N variants (N77, N57 and N37)
Copy link
Member

Choose a reason for hiding this comment

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

just curious, what would need to change later for the support of N78? Is this backward compatible?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the same architecture/software stack, so we anticipate it is just an extension of what is already here.

* variant[0] - N77
* variant[1] - N57
* variant[2] - N37
*/
static std::vector<char> variants[3] = {
mbaret marked this conversation as resolved.
Show resolved Hide resolved
{
0x02, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
{
0x02, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
{
0x02, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}};

} // namespace ethosn
} // namespace contrib
} // namespace relay
} // namespace tvm

#endif // TVM_RELAY_BACKEND_CONTRIB_ETHOSN_CAPABILITIES_H_