From 6a397912706baca8ea3b0f7ea523e4149968677c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AF=85?= Date: Thu, 9 Aug 2018 17:53:24 +0800 Subject: [PATCH] Support hexagon_nn version 2.1 in Hexagon SDK 3.4.0 --- .../hexagon/hexagon_control_wrapper.cc | 28 ++- third_party/nnlib/hexagon_nn.h | 147 +++++++++++- .../nnlib/libhexagon_controller_2_1.so | Bin 0 -> 15708 bytes third_party/nnlib/ops.h | 226 +++++++++++++++++- tools/bazel.rc | 1 + 5 files changed, 391 insertions(+), 11 deletions(-) create mode 100755 third_party/nnlib/libhexagon_controller_2_1.so diff --git a/mace/core/runtime/hexagon/hexagon_control_wrapper.cc b/mace/core/runtime/hexagon/hexagon_control_wrapper.cc index 4e435ebf..986fe761 100644 --- a/mace/core/runtime/hexagon/hexagon_control_wrapper.cc +++ b/mace/core/runtime/hexagon/hexagon_control_wrapper.cc @@ -51,10 +51,9 @@ int HexagonControlWrapper::GetVersion() { bool HexagonControlWrapper::Config() { LOG(INFO) << "Hexagon config"; - if (hexagon_nn_set_powersave_level(0) != 0) { - return false; - } - return hexagon_nn_config() == 0; + MACE_CHECK(hexagon_nn_set_powersave_level(0) == 0, "hexagon power error"); + MACE_CHECK(hexagon_nn_config() == 0, "hexagon config error"); + return true; } bool HexagonControlWrapper::Init() { @@ -80,7 +79,10 @@ bool HexagonControlWrapper::SetupGraph(const NetDef &net_def, int64_t t0 = NowMicros(); // const node - std::thread const_thread([&]() { +#if defined(MACE_USE_NNLIB_CAF) || defined(MACE_USE_NNLIB_OLD) + std::thread const_thread([&]() +#endif + { std::vector const_node_list; for (const ConstTensor &const_tensor : net_def.tensors()) { std::vector tensor_shape(const_tensor.dims().begin(), @@ -124,10 +126,16 @@ bool HexagonControlWrapper::SetupGraph(const NetDef &net_def, "append const node error"); } const_node_list.clear(); - }); + } +#if defined(MACE_USE_NNLIB_CAF) || defined(MACE_USE_NNLIB_OLD) + ); // NOLINT +#endif // op node - std::thread op_thread([&]() { +#if defined(MACE_USE_NNLIB_CAF) || defined(MACE_USE_NNLIB_OLD) + std::thread op_thread([&]() +#endif + { OpMap op_map; op_map.Init(); std::vector op_node_list; @@ -197,10 +205,12 @@ bool HexagonControlWrapper::SetupGraph(const NetDef &net_def, op_node_list.clear(); cached_inputs.clear(); cached_outputs.clear(); - }); - + } +#if defined(MACE_USE_NNLIB_CAF) || defined(MACE_USE_NNLIB_OLD) + ); // NOLINT const_thread.join(); op_thread.join(); +#endif // input info num_inputs_ = 0; diff --git a/third_party/nnlib/hexagon_nn.h b/third_party/nnlib/hexagon_nn.h index 84933ddb..f2aaaa88 100644 --- a/third_party/nnlib/hexagon_nn.h +++ b/third_party/nnlib/hexagon_nn.h @@ -225,7 +225,152 @@ __QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_execute_new)( } #endif -#else // nnlib version +#elif defined(MACE_USE_NNLIB_2_1) // nnlib version + +#ifndef __QAIC_HEADER +#define __QAIC_HEADER(ff) ff +#endif //__QAIC_HEADER + +#ifndef __QAIC_HEADER_EXPORT +#define __QAIC_HEADER_EXPORT +#endif // __QAIC_HEADER_EXPORT + +#ifndef __QAIC_HEADER_ATTRIBUTE +#define __QAIC_HEADER_ATTRIBUTE +#endif // __QAIC_HEADER_ATTRIBUTE + +#ifndef __QAIC_IMPL +#define __QAIC_IMPL(ff) ff +#endif //__QAIC_IMPL + +#ifndef __QAIC_IMPL_EXPORT +#define __QAIC_IMPL_EXPORT +#endif // __QAIC_IMPL_EXPORT + +#ifndef __QAIC_IMPL_ATTRIBUTE +#define __QAIC_IMPL_ATTRIBUTE +#endif // __QAIC_IMPL_ATTRIBUTE +#ifdef __cplusplus +extern "C" { +#endif +#if !defined(__QAIC_STRING1_OBJECT_DEFINED__) && !defined(__STRING1_OBJECT__) +#define __QAIC_STRING1_OBJECT_DEFINED__ +#define __STRING1_OBJECT__ +typedef struct _cstring1_s { + char* data; + int dataLen; +} _cstring1_t; + +#endif /* __QAIC_STRING1_OBJECT_DEFINED__ */ +typedef struct hexagon_nn_input hexagon_nn_input; +struct hexagon_nn_input { + unsigned int src_id; + unsigned int output_idx; +}; +typedef struct hexagon_nn_output hexagon_nn_output; +struct hexagon_nn_output { + unsigned int rank; + unsigned int max_sizes[8]; + unsigned int elementsize; + int zero_offset; + float stepsize; +}; +typedef struct hexagon_nn_perfinfo hexagon_nn_perfinfo; +struct hexagon_nn_perfinfo { + unsigned int node_id; + unsigned int node_type; + unsigned int executions; + unsigned int unused; + unsigned int counter_lo; + unsigned int counter_hi; +}; +typedef int hexagon_nn_nn_id; +enum hexagon_nn_padding_type { + NN_PAD_NA, + NN_PAD_SAME, + NN_PAD_VALID, + NN_PAD_MIRROR_REFLECT, + NN_PAD_MIRROR_SYMMETRIC, + NN_PAD_SAME_CAFFE, + _32BIT_PLACEHOLDER_hexagon_nn_padding_type = 0x7fffffff +}; +typedef enum hexagon_nn_padding_type hexagon_nn_padding_type; +enum hexagon_nn_corner_type { + NN_CORNER_RELEASE, + NN_CORNER_TURBO, + NN_CORNER_NOMPLUS, + NN_CORNER_NOMINAL, + NN_CORNER_SVSPLUS, + NN_CORNER_SVS, + NN_CORNER_SVS2, + _32BIT_PLACEHOLDER_hexagon_nn_corner_type = 0x7fffffff +}; +typedef enum hexagon_nn_corner_type hexagon_nn_corner_type; +enum hexagon_nn_dcvs_type { + NN_DCVS_DEFAULT, + NN_DCVS_ENABLE, + NN_DCVS_DISABLE, + _32BIT_PLACEHOLDER_hexagon_nn_dcvs_type = 0x7fffffff +}; +typedef enum hexagon_nn_dcvs_type hexagon_nn_dcvs_type; +typedef struct hexagon_nn_tensordef hexagon_nn_tensordef; +struct hexagon_nn_tensordef { + unsigned int batches; + unsigned int height; + unsigned int width; + unsigned int depth; + unsigned char* data; + int dataLen; + unsigned int data_valid_len; + unsigned int unused; +}; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_config)(void) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_get_dsp_offset)(unsigned int* libhexagon_addr, unsigned int* fastrpc_shell_addr) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_init)(hexagon_nn_nn_id* g) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_set_debug_level)(hexagon_nn_nn_id id, int level) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_snpprint)(hexagon_nn_nn_id id, unsigned char* buf, int bufLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_getlog)(hexagon_nn_nn_id id, unsigned char* buf, int bufLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_append_node)(hexagon_nn_nn_id id, unsigned int node_id, unsigned int operation, hexagon_nn_padding_type padding, const hexagon_nn_input* inputs, int inputsLen, const hexagon_nn_output* outputs, int outputsLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_append_const_node)(hexagon_nn_nn_id id, unsigned int node_id, unsigned int batches, unsigned int height, unsigned int width, unsigned int depth, const unsigned char* data, int dataLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_append_empty_const_node)(hexagon_nn_nn_id id, unsigned int node_id, unsigned int batches, unsigned int height, unsigned int width, unsigned int depth, unsigned int size) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_populate_const_node)(hexagon_nn_nn_id id, unsigned int node_id, const unsigned char* data, int dataLen, unsigned int target_offset) __QAIC_HEADER_ATTRIBUTE; +typedef struct hexagon_nn_op_node hexagon_nn_op_node; +struct hexagon_nn_op_node { + unsigned int node_id; + unsigned int operation; + hexagon_nn_padding_type padding; + hexagon_nn_input* inputs; + int inputsLen; + hexagon_nn_output* outputs; + int outputsLen; +}; +typedef struct hexagon_nn_const_node hexagon_nn_const_node; +struct hexagon_nn_const_node { + unsigned int node_id; + hexagon_nn_tensordef tensor; +}; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_append_node_list)(hexagon_nn_nn_id id, const hexagon_nn_op_node* ops, int opsLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_append_const_node_list)(hexagon_nn_nn_id id, const hexagon_nn_const_node* consts, int constsLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_prepare)(hexagon_nn_nn_id id) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_execute)(hexagon_nn_nn_id id, unsigned int batches_in, unsigned int height_in, unsigned int width_in, unsigned int depth_in, const unsigned char* data_in, int data_inLen, unsigned int* batches_out, unsigned int* height_out, unsigned int* width_out, unsigned int* depth_out, unsigned char* data_out, int data_outLen, unsigned int* data_len_out) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_teardown)(hexagon_nn_nn_id id) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_set_powersave_level)(unsigned int level) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_set_powersave_details)(hexagon_nn_corner_type corner, hexagon_nn_dcvs_type dcvs, unsigned int latency) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_get_perfinfo)(hexagon_nn_nn_id id, hexagon_nn_perfinfo* info_out, int info_outLen, unsigned int* n_items) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_reset_perfinfo)(hexagon_nn_nn_id id, unsigned int event) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_last_execution_cycles)(hexagon_nn_nn_id id, unsigned int* cycles_lo, unsigned int* cycles_hi) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_version)(int* ver) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_op_name_to_id)(const char* name, unsigned int* node_id) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_op_id_to_name)(unsigned int node_id, char* name, int nameLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_disable_dcvs)(void) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_GetHexagonBinaryVersion)(int* ver) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_PrintLog)(const unsigned char* buf, int bufLen) __QAIC_HEADER_ATTRIBUTE; +__QAIC_HEADER_EXPORT int __QAIC_HEADER(hexagon_nn_execute_new)(hexagon_nn_nn_id id, const hexagon_nn_tensordef* inputs, int inputsLen, hexagon_nn_tensordef* outputs, int outputsLen) __QAIC_HEADER_ATTRIBUTE; +#ifdef __cplusplus +} +#endif + +#else // nnlib version : MACE_USE_NNLIB_CAF #ifndef __QAIC_HEADER #define __QAIC_HEADER(ff) ff diff --git a/third_party/nnlib/libhexagon_controller_2_1.so b/third_party/nnlib/libhexagon_controller_2_1.so new file mode 100755 index 0000000000000000000000000000000000000000..3d592e01856d276bbfbcbfd1d768307719fd00ed GIT binary patch literal 15708 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 Xr<;X=H|-r6rFXPMh#sVw!PNUVzIZ