import content from sso://googleplex-android/platform/vendor/google/libraries/neuralnetworks/hvxservice master
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..9b3f9d9
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed 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.
+#
+
+BasedOnStyle: Google
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+TabWidth: 4
+UseTab: Never
+IndentWidth: 4
diff --git a/1.0/Android.bp b/1.0/Android.bp
new file mode 100644
index 0000000..a57f169
--- /dev/null
+++ b/1.0/Android.bp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+cc_binary {
+ name: "android.hardware.neuralnetworks@1.0-service-hvx",
+ owner: "google",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ proprietary: true,
+ init_rc: ["android.hardware.neuralnetworks@1.0-service-hvx.rc"],
+ srcs: [
+ "Device.cpp",
+ "HexagonController.cpp",
+ "HexagonModel.cpp",
+ "HexagonOperationsCheck.cpp",
+ "HexagonOperationsPrepare.cpp",
+ "HexagonUtils.cpp",
+ "PreparedModel.cpp",
+ "Service.cpp",
+ ],
+ header_libs: [
+ "libneuralnetworks_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ "libdl",
+ "libhardware",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ "android.hardware.neuralnetworks@1.0",
+ "android.hardware.neuralnetworks@1.1",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ ],
+ static_libs: [
+ "libneuralnetworks_common",
+ ],
+}
diff --git a/1.0/Device.cpp b/1.0/Device.cpp
new file mode 100644
index 0000000..264223b
--- /dev/null
+++ b/1.0/Device.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "Device.h"
+#include <android-base/logging.h>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include "HexagonModel.h"
+#include "HexagonUtils.h"
+#include "PreparedModel.h"
+#include "ValidateHal.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+
+Device::Device() : mCurrentStatus(DeviceStatus::AVAILABLE) {}
+
+Device::~Device() {}
+
+static std::once_flag configure_nnlib;
+static void configureHexagon() {
+ std::call_once(configure_nnlib, []() {
+ hexagon::Controller::getInstance().config();
+ hexagon::Controller::getInstance().boost(100);
+ });
+}
+
+Return<void> Device::getCapabilities(getCapabilities_cb _hidl_cb) {
+ configureHexagon();
+
+ // These numbers are approximations for this release.
+ // TODO Change with the actual number.
+ PerformanceInfo float32Performance = {
+ .execTime = 30.0f, .powerUsage = 2.0f,
+ };
+
+ PerformanceInfo quantized8Performance = {
+ .execTime = 0.7f, .powerUsage = 0.7f,
+ };
+
+ Capabilities capabilities = {
+ .float32Performance = float32Performance, .quantized8Performance = quantized8Performance,
+ };
+
+ ErrorStatus status =
+ hexagon::isHexagonAvailable() ? ErrorStatus::NONE : ErrorStatus::DEVICE_UNAVAILABLE;
+
+ _hidl_cb(status, capabilities);
+ return Void();
+}
+
+Return<void> Device::getSupportedOperations(const Model& model,
+ getSupportedOperations_cb _hidl_cb) {
+ configureHexagon();
+
+ if (!nn::validateModel(model)) {
+ _hidl_cb(ErrorStatus::INVALID_ARGUMENT, std::vector<bool>{});
+ return Void();
+ }
+ if (!hexagon::isHexagonAvailable()) {
+ _hidl_cb(ErrorStatus::DEVICE_UNAVAILABLE, std::vector<bool>{});
+ return Void();
+ }
+
+ hexagon::Model hexagonModel(model);
+ std::vector<bool> supported = hexagonModel.supportedOperations();
+
+ _hidl_cb(ErrorStatus::NONE, supported);
+ return Void();
+}
+
+static void asyncPrepare(const Model& model, const sp<IPreparedModelCallback>& callback) {
+ std::shared_ptr<hexagon::Model> hexagonModel = std::make_shared<hexagon::Model>(model);
+
+ Return<void> ret;
+ if (hexagonModel->prepare()) {
+ ret = callback->notify(ErrorStatus::NONE, new PreparedModel(model, hexagonModel));
+ } else {
+ ret = callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
+ }
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Error in callback's return type: " << ret.description();
+ }
+}
+
+Return<ErrorStatus> Device::prepareModel(const Model& model,
+ const sp<IPreparedModelCallback>& callback) {
+ configureHexagon();
+
+ if (callback.get() == nullptr) {
+ LOG(ERROR) << "invalid callback passed to prepareModel";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ if (!nn::validateModel(model)) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ if (!hexagon::isHexagonAvailable()) {
+ callback->notify(ErrorStatus::DEVICE_UNAVAILABLE, nullptr);
+ return ErrorStatus::DEVICE_UNAVAILABLE;
+ }
+
+ // TODO: once nnlib hanging issue is resolved, make this function
+ // asynchronous again
+ asyncPrepare(model, callback);
+
+ return ErrorStatus::NONE;
+}
+
+Return<DeviceStatus> Device::getStatus() {
+ configureHexagon();
+ mCurrentStatus =
+ hexagon::isHexagonAvailable() ? DeviceStatus::AVAILABLE : DeviceStatus::OFFLINE;
+ return mCurrentStatus;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/Device.h b/1.0/Device.h
new file mode 100644
index 0000000..cae055c
--- /dev/null
+++ b/1.0/Device.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 ANDROID_HARDWARE_NEURALNETWORKS_V1_0_DEVICE_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct Device : public IDevice {
+ Device();
+ ~Device() override;
+
+ // Methods from IDevice follow.
+ Return<void> getCapabilities(getCapabilities_cb _hidl_cb) override;
+ Return<void> getSupportedOperations(const Model& model,
+ getSupportedOperations_cb _hidl_cb) override;
+ Return<ErrorStatus> prepareModel(const Model& model,
+ const sp<IPreparedModelCallback>& callback) override;
+ Return<DeviceStatus> getStatus() override;
+
+ private:
+ DeviceStatus mCurrentStatus;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_DEVICE_H
diff --git a/1.0/HexagonController.cpp b/1.0/HexagonController.cpp
new file mode 100644
index 0000000..d2753ac
--- /dev/null
+++ b/1.0/HexagonController.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "HexagonController.h"
+
+#define LOAD_HEXAGON_FUNCTION(name) \
+ mFn_##name = loadFunction<hexagon_nn_controller_##name##_fn>("hexagon_nn_controller_" #name);
+
+#define CLOSE_HEXAGON_FUNCTION(name) mFn_##name = nullptr;
+
+#define FOR_EACH_FUNCTION(MACRO) \
+ MACRO(init) \
+ MACRO(getlog) \
+ MACRO(snpprint) \
+ MACRO(set_debug_level) \
+ MACRO(prepare) \
+ MACRO(append_node) \
+ MACRO(append_const_node) \
+ MACRO(execute_new) \
+ MACRO(execute) \
+ MACRO(teardown) \
+ MACRO(get_perfinfo) \
+ MACRO(reset_perfinfo) \
+ MACRO(version) \
+ MACRO(last_execution_cycles) \
+ MACRO(GetHexagonBinaryVersion) \
+ MACRO(PrintLog) \
+ MACRO(op_name_to_id) \
+ MACRO(op_id_to_name) \
+ MACRO(disable_dcvs) \
+ MACRO(set_powersave_level) \
+ MACRO(config) \
+ MACRO(get_dsp_offset) \
+ MACRO(boost) \
+ MACRO(slow)
+
+#define CONTROLLER_CHECK(function, ...) \
+ if (mFn_##function == nullptr) { \
+ return -1; \
+ } \
+ int err = mFn_##function(__VA_ARGS__); \
+ if (err != 0) { \
+ return err; \
+ } \
+ return 0;
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+const char Controller::kFilename[] = "libhexagon_nn_controller.so";
+
+Controller::Controller() {
+ openNnlib();
+}
+
+Controller::~Controller() {
+ closeNnlib();
+}
+
+bool Controller::openNnlib() {
+ mHandle = dlopen(kFilename, RTLD_LAZY | RTLD_LOCAL);
+ HEXAGON_SOFT_ASSERT_NE(mHandle, 0,
+ "FAILED TO LOAD LIBRARY " /* << kFilename << ": " << dlerror()*/);
+ FOR_EACH_FUNCTION(LOAD_HEXAGON_FUNCTION)
+ return true;
+}
+
+bool Controller::closeNnlib() {
+ FOR_EACH_FUNCTION(CLOSE_HEXAGON_FUNCTION)
+ if (mHandle != nullptr) {
+ int err = dlclose(mHandle);
+ mHandle = nullptr;
+ HEXAGON_SOFT_ASSERT_EQ(err, 0, "FAILED TO CLOSE LIBRARY " << kFilename);
+ }
+ return true;
+}
+
+bool Controller::resetNnlib() {
+ return closeNnlib() && openNnlib();
+}
+
+Controller& Controller::getInstance() {
+ static Controller instance{};
+ return instance;
+}
+
+int Controller::init(hexagon_nn_nn_id* g) {
+ CONTROLLER_CHECK(init, g);
+}
+
+int Controller::getlog(hexagon_nn_nn_id id, unsigned char* buf, uint32_t length) {
+ CONTROLLER_CHECK(getlog, id, buf, length);
+}
+
+int Controller::snpprint(hexagon_nn_nn_id id, unsigned char* buf, uint32_t length) {
+ CONTROLLER_CHECK(snpprint, id, buf, length);
+}
+
+int Controller::set_debug_level(hexagon_nn_nn_id id, int level) {
+ CONTROLLER_CHECK(set_debug_level, id, level);
+}
+
+int Controller::prepare(hexagon_nn_nn_id id) {
+ CONTROLLER_CHECK(prepare, id);
+}
+
+int Controller::append_node(hexagon_nn_nn_id id, uint32_t node_id, op_type operation,
+ hexagon_nn_padding_type padding, const hexagon_nn_input* inputs,
+ uint32_t num_inputs, const hexagon_nn_output* outputs,
+ uint32_t num_outputs) {
+ CONTROLLER_CHECK(append_node, id, node_id, operation, padding, inputs, num_inputs, outputs,
+ num_outputs);
+}
+
+int Controller::append_const_node(hexagon_nn_nn_id id, uint32_t node_id, uint32_t batches,
+ uint32_t height, uint32_t width, uint32_t depth,
+ const uint8_t* data, uint32_t data_len) {
+ CONTROLLER_CHECK(append_const_node, id, node_id, batches, height, width, depth, data, data_len);
+}
+
+int Controller::execute_new(hexagon_nn_nn_id id, const hexagon_nn_tensordef* inputs,
+ uint32_t n_inputs, hexagon_nn_tensordef* outputs, uint32_t n_outputs) {
+ CONTROLLER_CHECK(execute_new, id, inputs, n_inputs, outputs, n_outputs);
+}
+
+int Controller::execute(hexagon_nn_nn_id id, uint32_t batches_in, uint32_t height_in,
+ uint32_t width_in, uint32_t depth_in, const uint8_t* data_in,
+ uint32_t data_len_in, uint32_t* batches_out, uint32_t* height_out,
+ uint32_t* width_out, uint32_t* depth_out, uint8_t* data_out,
+ uint32_t data_out_max, uint32_t* data_out_size) {
+ CONTROLLER_CHECK(execute, id, batches_in, height_in, width_in, depth_in, data_in, data_len_in,
+ batches_out, height_out, width_out, depth_out, data_out, data_out_max,
+ data_out_size);
+}
+
+int Controller::teardown(hexagon_nn_nn_id id) {
+ CONTROLLER_CHECK(teardown, id);
+}
+
+int Controller::get_perfinfo(hexagon_nn_nn_id id, hexagon_nn_perfinfo* info_out,
+ unsigned int info_out_len, unsigned int* n_items_out) {
+ CONTROLLER_CHECK(get_perfinfo, id, info_out, info_out_len, n_items_out);
+}
+
+int Controller::reset_perfinfo(hexagon_nn_nn_id id, uint32_t event) {
+ CONTROLLER_CHECK(reset_perfinfo, id, event);
+}
+
+int Controller::version(int* ver) {
+ CONTROLLER_CHECK(version, ver);
+}
+
+int Controller::last_execution_cycles(hexagon_nn_nn_id id, unsigned int* cycles_lo,
+ unsigned int* cycles_hi) {
+ CONTROLLER_CHECK(last_execution_cycles, id, cycles_lo, cycles_hi);
+}
+
+int Controller::GetHexagonBinaryVersion(int* ver) {
+ CONTROLLER_CHECK(GetHexagonBinaryVersion, ver);
+}
+
+int Controller::PrintLog(const uint8_t* data_in, unsigned int data_in_len) {
+ CONTROLLER_CHECK(PrintLog, data_in, data_in_len);
+}
+
+int Controller::op_name_to_id(const char* name, unsigned int* id) {
+ CONTROLLER_CHECK(op_name_to_id, name, id);
+}
+
+int Controller::op_id_to_name(const unsigned int id, char* name, int name_len) {
+ CONTROLLER_CHECK(op_id_to_name, id, name, name_len);
+}
+
+int Controller::disable_dcvs() {
+ CONTROLLER_CHECK(disable_dcvs);
+}
+
+int Controller::set_powersave_level(unsigned int level) {
+ CONTROLLER_CHECK(set_powersave_level, level);
+}
+
+int Controller::config() {
+ CONTROLLER_CHECK(config);
+}
+
+unsigned int Controller::get_dsp_offset() {
+ CONTROLLER_CHECK(get_dsp_offset);
+}
+
+int Controller::boost(int bus_usage) {
+ CONTROLLER_CHECK(boost, bus_usage);
+}
+
+int Controller::slow() {
+ CONTROLLER_CHECK(slow);
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/HexagonController.h b/1.0/HexagonController.h
new file mode 100644
index 0000000..ce7e8e7
--- /dev/null
+++ b/1.0/HexagonController.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 ANDROID_HARDWARE_V1_0_HEXAGON_CONTROLLER_H
+#define ANDROID_HARDWARE_V1_0_HEXAGON_CONTROLLER_H
+
+#include <android-base/logging.h>
+#include "HexagonUtils.h"
+#include "dlfcn.h"
+#include "hexagon_nn_controller/hexagon_nn_controller.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+// interface wrapper
+class Controller {
+ // methods
+ private:
+ Controller();
+ ~Controller();
+ Controller(const Controller&) = delete;
+ Controller(Controller&&) = delete;
+ Controller& operator=(const Controller&) = delete;
+ Controller& operator=(Controller&&) = delete;
+
+ template <typename Function>
+ Function loadFunction(const char* name) {
+ void* fn = dlsym(mHandle, name);
+ if (fn == nullptr) {
+ LOG(ERROR) << "loadFunction -- failed to load function " << name;
+ }
+ return reinterpret_cast<Function>(fn);
+ }
+
+ bool openNnlib();
+ bool closeNnlib();
+
+ public:
+ static Controller& getInstance();
+ bool resetNnlib();
+
+ int init(hexagon_nn_nn_id* g);
+
+ int getlog(hexagon_nn_nn_id id, unsigned char* buf, uint32_t length);
+
+ int snpprint(hexagon_nn_nn_id id, unsigned char* buf, uint32_t length);
+
+ int set_debug_level(hexagon_nn_nn_id id, int level);
+
+ int prepare(hexagon_nn_nn_id id);
+
+ int append_node(hexagon_nn_nn_id id, uint32_t node_id, op_type operation,
+ hexagon_nn_padding_type padding, const hexagon_nn_input* inputs,
+ uint32_t num_inputs, const hexagon_nn_output* outputs, uint32_t num_outputs);
+
+ int append_const_node(hexagon_nn_nn_id id, uint32_t node_id, uint32_t batches, uint32_t height,
+ uint32_t width, uint32_t depth, const uint8_t* data, uint32_t data_len);
+
+ int execute_new(hexagon_nn_nn_id id, const hexagon_nn_tensordef* inputs, uint32_t n_inputs,
+ hexagon_nn_tensordef* outputs, uint32_t n_outputs);
+
+ int execute(hexagon_nn_nn_id id, uint32_t batches_in, uint32_t height_in, uint32_t width_in,
+ uint32_t depth_in, const uint8_t* data_in, uint32_t data_len_in,
+ uint32_t* batches_out, uint32_t* height_out, uint32_t* width_out,
+ uint32_t* depth_out, uint8_t* data_out, uint32_t data_out_max,
+ uint32_t* data_out_size);
+
+ int teardown(hexagon_nn_nn_id id);
+
+ int get_perfinfo(hexagon_nn_nn_id id, hexagon_nn_perfinfo* info_out, unsigned int info_out_len,
+ unsigned int* n_items_out);
+
+ int reset_perfinfo(hexagon_nn_nn_id id, uint32_t event);
+
+ int version(int* ver);
+
+ int last_execution_cycles(hexagon_nn_nn_id id, unsigned int* cycles_lo,
+ unsigned int* cycles_hi);
+
+ int GetHexagonBinaryVersion(int* ver);
+
+ int PrintLog(const uint8_t* data_in, unsigned int data_in_len);
+
+ int op_name_to_id(const char* name, unsigned int* id);
+
+ int op_id_to_name(const unsigned int id, char* name, int name_len);
+
+ int disable_dcvs();
+
+ int set_powersave_level(unsigned int level);
+
+ int config();
+
+ unsigned int get_dsp_offset();
+
+ int boost(int bus_usage);
+
+ int slow();
+
+ // members
+ private:
+ static const char kFilename[];
+ void* mHandle;
+ hexagon_nn_controller_init_fn mFn_init;
+ hexagon_nn_controller_getlog_fn mFn_getlog;
+ hexagon_nn_controller_snpprint_fn mFn_snpprint;
+ hexagon_nn_controller_set_debug_level_fn mFn_set_debug_level;
+ hexagon_nn_controller_prepare_fn mFn_prepare;
+ hexagon_nn_controller_append_node_fn mFn_append_node;
+ hexagon_nn_controller_append_const_node_fn mFn_append_const_node;
+ hexagon_nn_controller_execute_new_fn mFn_execute_new;
+ hexagon_nn_controller_execute_fn mFn_execute;
+ hexagon_nn_controller_teardown_fn mFn_teardown;
+ hexagon_nn_controller_get_perfinfo_fn mFn_get_perfinfo;
+ hexagon_nn_controller_reset_perfinfo_fn mFn_reset_perfinfo;
+ hexagon_nn_controller_version_fn mFn_version;
+ hexagon_nn_controller_last_execution_cycles_fn mFn_last_execution_cycles;
+ hexagon_nn_controller_GetHexagonBinaryVersion_fn mFn_GetHexagonBinaryVersion;
+ hexagon_nn_controller_PrintLog_fn mFn_PrintLog;
+ hexagon_nn_controller_op_name_to_id_fn mFn_op_name_to_id;
+ hexagon_nn_controller_op_id_to_name_fn mFn_op_id_to_name;
+ hexagon_nn_controller_disable_dcvs_fn mFn_disable_dcvs;
+ hexagon_nn_controller_set_powersave_level_fn mFn_set_powersave_level;
+ hexagon_nn_controller_config_fn mFn_config;
+ hexagon_nn_controller_get_dsp_offset_fn mFn_get_dsp_offset;
+ hexagon_nn_controller_boost_fn mFn_boost;
+ hexagon_nn_controller_slow_fn mFn_slow;
+};
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_V1_0_HEXAGON_CONTROLLER_H
diff --git a/1.0/HexagonModel.cpp b/1.0/HexagonModel.cpp
new file mode 100644
index 0000000..859d5fd
--- /dev/null
+++ b/1.0/HexagonModel.cpp
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "HexagonModel.h"
+#include <numeric>
+#include <unordered_set>
+#include "HexagonOperations.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+static std::vector<OperandInfo> getOperandsInfo(const NeuralnetworksModel& model,
+ const std::vector<RunTimePoolInfo>& pools) {
+ std::vector<OperandInfo> info(model.operands.size());
+ for (size_t i = 0; i < model.operands.size(); ++i) {
+ const Operand& operand = model.operands[i];
+ info[i] = {
+ .type = operand.type,
+ .dimensions = operand.dimensions,
+ .scale = operand.scale,
+ .zeroPoint = operand.zeroPoint,
+ .lifetime = operand.lifetime,
+ .buffer = const_cast<uint8_t*>(getData(operand, model.operandValues, pools)),
+ .length = operand.location.length,
+ };
+ }
+ return info;
+}
+
+Model::Model(const NeuralnetworksModel& model) : mGraphId(0), mNodeCount(0), mCompiled(false) {
+ mPools = mapPools(model.pools);
+ mOperands = getOperandsInfo(model, mPools);
+ std::for_each(mPools.begin(), mPools.end(), [](RunTimePoolInfo& mem) { mem.update(); });
+
+ mOperations = model.operations;
+ mInputs = model.inputIndexes;
+ mOutputs = model.outputIndexes;
+}
+
+Model::Model(Model&& other) {
+ *this = std::move(other);
+}
+
+Model& Model::operator=(Model&& other) {
+ if (this != &other) {
+ mNodeCount = other.mNodeCount;
+ mGraphId = other.mGraphId;
+ mCompiled = other.mCompiled;
+ mOperands = std::move(other.mOperands);
+ mOperations = std::move(other.mOperations);
+ mInputs = std::move(other.mInputs);
+ mOutputs = std::move(other.mOutputs);
+ mPools = std::move(other.mPools);
+ other.mNodeCount = 0;
+ other.mGraphId = {};
+ other.mCompiled = false;
+ }
+ return *this;
+}
+
+Model::~Model() {
+ clearModel();
+}
+
+std::string Model::getLog() {
+ char buffer[16 * 1024];
+ int err = hexagon::Controller::getInstance().getlog(
+ mGraphId, reinterpret_cast<uint8_t*>(buffer), sizeof(buffer));
+ HEXAGON_SOFT_ASSERT_EQ(0, err, "failed getLog");
+ return buffer;
+}
+
+std::string Model::getGraph() {
+ char buffer[16 * 1024];
+ int err = hexagon::Controller::getInstance().snpprint(
+ mGraphId, reinterpret_cast<uint8_t*>(buffer), sizeof(buffer));
+ HEXAGON_SOFT_ASSERT_EQ(0, err, "failed getGraph");
+ return buffer;
+}
+
+uint32_t Model::getNextNode() {
+ return ++mNodeCount;
+}
+
+const int32_t* Model::getPointer(uint32_t operand) {
+ return reinterpret_cast<const int32_t*>(mOperands[operand].buffer);
+}
+
+Shape Model::getShape(uint32_t operand) {
+ return {
+ .type = mOperands[operand].type,
+ .dimensions = mOperands[operand].dimensions,
+ .scale = mOperands[operand].scale,
+ .offset = mOperands[operand].zeroPoint,
+ };
+}
+
+bool Model::setShape(uint32_t operand, const Shape& shape) {
+ const hexagon_nn_output& output = mOperands[operand].hexagon_output;
+ HEXAGON_SOFT_ASSERT_EQ(output, hexagon_nn_output{}, "Output has already been set");
+ // mOperands[operand].type = shape.type;
+ mOperands[operand].dimensions = shape.dimensions;
+ // mOperands[operand].scale = shape.scale;
+ // mOperands[operand].zeroPoint = shape.offset;
+ return true;
+}
+
+bool Model::isConstant(uint32_t operand) {
+ OperandLifeTime lifetime = mOperands[operand].lifetime;
+ return lifetime == OperandLifeTime::CONSTANT_COPY ||
+ lifetime == OperandLifeTime::CONSTANT_REFERENCE;
+}
+
+hexagon_nn_input Model::createTensorInternal(uint32_t B, uint32_t H, uint32_t W, uint32_t D,
+ const uint8_t* ptr, size_t size) {
+ uint32_t node = getNextNode();
+ bool success = hexagon::Controller::getInstance().append_const_node(mGraphId, node, B, H, W, D,
+ ptr, size) == 0;
+ HEXAGON_SOFT_ASSERT(success, "Failed to create tensor");
+ return {.src_id = node, .output_idx = 0};
+}
+
+hexagon_nn_input Model::createShape(uint32_t B, uint32_t H, uint32_t W, uint32_t D) {
+ uint32_t dump = 0;
+ return createTensorInternal(B, H, W, D, reinterpret_cast<uint8_t*>(&dump), sizeof(dump));
+}
+
+hexagon_nn_input Model::addOperand(uint32_t operandIndex) {
+ const OperandInfo& operand = mOperands[operandIndex];
+ std::vector<uint32_t> dims = getAlignedDimensions(operand.dimensions, 4);
+ HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Rank must be at most 4");
+ hexagon_nn_input result =
+ createTensorInternal(dims[0], dims[1], dims[2], dims[3], operand.buffer, operand.length);
+ HEXAGON_SOFT_ASSERT_NE(hexagon_nn_input{}, result, "Failed to add operand");
+ return result;
+}
+
+const hexagon_nn_input& Model::getTensor(uint32_t operand) {
+ hexagon_nn_input& tensor = mOperands[operand].hexagon_input;
+ if (tensor == hexagon_nn_input{}) {
+ tensor = addOperand(operand);
+ }
+ return tensor;
+}
+
+const hexagon_nn_input& Model::getQuantizationMin(uint32_t operand) {
+ OperandInfo& operandInfo = mOperands[operand];
+ if (operandInfo.hexagon_input_min == hexagon_nn_input{}) {
+ float real_value =
+ operandInfo.type == OperandType::TENSOR_QUANT8_ASYMM
+ ? (std::numeric_limits<uint8_t>::min() - operandInfo.zeroPoint) * operandInfo.scale
+ : std::numeric_limits<uint32_t>::min() * operandInfo.scale;
+ operandInfo.hexagon_input_min = createValues<float>({real_value});
+ }
+ return operandInfo.hexagon_input_min;
+}
+
+const hexagon_nn_input& Model::getQuantizationMax(uint32_t operand) {
+ OperandInfo& operandInfo = mOperands[operand];
+ if (operandInfo.hexagon_input_max == hexagon_nn_input{}) {
+ float real_value =
+ operandInfo.type == OperandType::TENSOR_QUANT8_ASYMM
+ ? (std::numeric_limits<uint8_t>::max() - operandInfo.zeroPoint) * operandInfo.scale
+ : std::numeric_limits<uint32_t>::max() * operandInfo.scale;
+ operandInfo.hexagon_input_max = createValues<float>({real_value});
+ }
+ return operandInfo.hexagon_input_max;
+}
+
+hexagon_nn_padding_type Model::getPadding(uint32_t operand) {
+ const int32_t padding = getScalar<int32_t>(operand);
+ return hexagon::getPadding(padding);
+}
+
+hexagon_nn_input Model::createQuantizationValue(uint32_t operand, int32_t quant_value) {
+ OperandInfo& operandInfo = mOperands[operand];
+ float real_value = (quant_value - operandInfo.zeroPoint) * operandInfo.scale;
+ return createValues<float>({real_value});
+}
+
+hexagon_nn_input Model::createConvFilterTensor(uint32_t operand) {
+ OperandInfo& operandInfo = mOperands[operand];
+ std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4);
+ HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions");
+ // NHWC --> HWCN
+ if (getShape(operand).type == OperandType::TENSOR_FLOAT32) {
+ std::vector<float> transposed =
+ transpose<float>(dims[0], dims[1] * dims[2] * dims[3],
+ reinterpret_cast<const float*>(operandInfo.buffer));
+ return createTensorInternal(dims[1], dims[2], dims[3], dims[0],
+ reinterpret_cast<const uint8_t*>(transposed.data()),
+ operandInfo.length);
+ } else {
+ std::vector<uint8_t> transposed =
+ transpose<uint8_t>(dims[0], dims[1] * dims[2] * dims[3],
+ reinterpret_cast<const uint8_t*>(operandInfo.buffer));
+ return createTensorInternal(dims[1], dims[2], dims[3], dims[0],
+ reinterpret_cast<const uint8_t*>(transposed.data()),
+ operandInfo.length);
+ }
+}
+
+hexagon_nn_input Model::createDepthwiseFilterTensor(uint32_t operand, int32_t depth_multiplier) {
+ OperandInfo& operandInfo = mOperands[operand];
+ std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4);
+ HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions");
+ // NHWC --> HWCN
+ return createTensorInternal(dims[1], dims[2], dims[3] / depth_multiplier,
+ dims[0] * depth_multiplier, operandInfo.buffer, operandInfo.length);
+}
+
+hexagon_nn_input Model::createFullyConnectedWeightTensor(uint32_t operand) {
+ OperandInfo& operandInfo = mOperands[operand];
+ std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4);
+ HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions");
+ // WC --> CW
+ uint32_t num_units = dims[0] * dims[1] * dims[2];
+ uint32_t input_size = dims[3];
+ if (getShape(operand).type == OperandType::TENSOR_FLOAT32) {
+ std::vector<float> transposed = transpose<float>(
+ num_units, input_size, reinterpret_cast<const float*>(operandInfo.buffer));
+ return createTensorInternal(1, 1, input_size, num_units,
+ reinterpret_cast<const uint8_t*>(transposed.data()),
+ operandInfo.length);
+ } else {
+ std::vector<uint8_t> transposed = transpose<uint8_t>(
+ num_units, input_size, reinterpret_cast<const uint8_t*>(operandInfo.buffer));
+ return createTensorInternal(1, 1, input_size, num_units,
+ reinterpret_cast<const uint8_t*>(transposed.data()),
+ operandInfo.length);
+ }
+}
+
+op_type Model::getFloatActivation(uint32_t operand) {
+ return getFloatActivationFunction(getScalar<FusedActivationFunc>(operand));
+}
+
+op_type Model::getQuantizedActivation(uint32_t operand) {
+ return getQuantizedActivationFunction(getScalar<FusedActivationFunc>(operand));
+}
+
+static bool verifyOperationInputs(const std::vector<hexagon_nn_input>& inputs) {
+ for (const hexagon_nn_input& input : inputs) {
+ if (input == hexagon_nn_input{}) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool verifyOperationOutputs(const std::vector<hexagon_nn_output>& outputs) {
+ for (const hexagon_nn_output& output : outputs) {
+ if (output == hexagon_nn_output{}) {
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t Model::addOperationInternal(op_type op, hexagon_nn_padding_type pad,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<hexagon_nn_output>& outputs) {
+ HEXAGON_SOFT_ASSERT(verifyOperationInputs(inputs),
+ "error adding operation: one or more inputs is invalid");
+ HEXAGON_SOFT_ASSERT(verifyOperationOutputs(outputs),
+ "error adding operation: one or more outputs is invalid");
+ uint32_t node = getNextNode();
+ return hexagon::Controller::getInstance().append_node(mGraphId, node, op, pad, inputs.data(),
+ inputs.size(), outputs.data(),
+ outputs.size()) == 0
+ ? node
+ : 0;
+}
+
+std::vector<hexagon_nn_output> Model::getHexagonOutputs(const std::vector<uint32_t>& operands) {
+ std::vector<hexagon_nn_output> outputs;
+ for (uint32_t index : operands) {
+ const OperandInfo& operand = mOperands[index];
+ outputs.push_back(make_hexagon_nn_output(operand.dimensions, getSize(operand.type)));
+ if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) {
+ outputs.push_back(make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)));
+ outputs.push_back(make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)));
+ }
+ }
+ return outputs;
+}
+
+bool Model::registerHexagonInputs(const std::vector<uint32_t>& operands, uint32_t node) {
+ uint32_t idx = 0;
+ for (uint32_t i = 0; i < static_cast<uint32_t>(operands.size()); ++i) {
+ OperandInfo& operand = mOperands[operands[i]];
+ HEXAGON_SOFT_ASSERT_EQ(operand.hexagon_input, hexagon_nn_input{},
+ "Error: operation output has already been registered");
+ operand.hexagon_input = {.src_id = node, .output_idx = idx++};
+ if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) {
+ operand.hexagon_input_min = {.src_id = node, .output_idx = idx++};
+ operand.hexagon_input_max = {.src_id = node, .output_idx = idx++};
+ }
+ }
+ return true;
+}
+
+bool Model::addBasicOperation(op_type op, hexagon_nn_padding_type pad,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs) {
+ std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
+ uint32_t node = addOperationInternal(op, pad, inputs, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
+ return registerHexagonInputs(outputs, node);
+}
+
+std::vector<hexagon_nn_input> Model::setupActivationArgs(op_type op) {
+ switch (op) {
+ case OP_Nop:
+ return {};
+ case OP_Relu_f:
+ FALLTHROUGH_INTENDED;
+ case OP_QuantizedRelu_8:
+ return {};
+ case OP_ReluX_f:
+ FALLTHROUGH_INTENDED;
+ case OP_QuantizedReluX_8:
+ return {createValues<float>({6.0f})};
+ case OP_Clamp_f:
+ FALLTHROUGH_INTENDED;
+ case OP_QuantizedClamp_8:
+ return {createValues<float>({-1.0f}), createValues<float>({1.0f})};
+ default:
+ HEXAGON_SOFT_ASSERT(false, "Unknown activation symbol " << op);
+ }
+}
+
+bool Model::addFloatOperationWithActivation(op_type op, hexagon_nn_padding_type pad,
+ op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs) {
+ std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
+ std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
+
+ uint32_t node = addOperationInternal(op, pad, inputs, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
+
+ std::vector<hexagon_nn_input> buffer_in = {{.src_id = node, .output_idx = 0}};
+ buffer_in.insert(buffer_in.end(), actArgs.begin(), actArgs.end());
+ node = addOperationInternal(activation, NN_PAD_NA, buffer_in, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
+
+ return registerHexagonInputs(outputs, node);
+}
+
+bool Model::addQuant8OperationWithActivation(op_type op, hexagon_nn_padding_type pad,
+ op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs) {
+ std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
+ std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
+
+ uint32_t node = addOperationInternal(op, pad, inputs, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
+
+ std::vector<hexagon_nn_input> buffer_in = {{.src_id = node, .output_idx = 0},
+ {.src_id = node, .output_idx = 1},
+ {.src_id = node, .output_idx = 2}};
+ buffer_in.insert(buffer_in.end(), actArgs.begin(), actArgs.end());
+ node = addOperationInternal(activation, NN_PAD_NA, buffer_in, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
+
+ return registerHexagonInputs(outputs, node);
+}
+
+bool Model::addFusedFloatOperation(op_type op, hexagon_nn_padding_type pad,
+ const hexagon_nn_input& bias, op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs) {
+ HEXAGON_SOFT_ASSERT_EQ(1, outputs.size(), "addFusedFloatOperation requires 1 output");
+ std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
+ std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
+ uint32_t node;
+
+ node = addOperationInternal(op, pad, inputs, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
+
+ if (bias != hexagon_nn_input{}) {
+ const hexagon_nn_input buffer1_in = {.src_id = node, .output_idx = 0};
+ node = addOperationInternal(OP_BiasAdd_f, NN_PAD_NA, {buffer1_in, bias}, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding bias operation");
+ }
+
+ if (activation != OP_Nop) {
+ std::vector<hexagon_nn_input> buffer2_in = {{.src_id = node, .output_idx = 0}};
+ buffer2_in.insert(buffer2_in.end(), actArgs.begin(), actArgs.end());
+ node = addOperationInternal(activation, NN_PAD_NA, buffer2_in, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
+ }
+
+ return registerHexagonInputs(outputs, node);
+}
+
+bool Model::addFusedQuant8Operation(op_type op, hexagon_nn_padding_type pad,
+ const std::vector<hexagon_nn_input>& bias, op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs) {
+ HEXAGON_SOFT_ASSERT_EQ(1, outputs.size(), "addFusedQuant8Operation requires 1 output");
+ std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
+ const hexagon_nn_input& new_min = getQuantizationMin(outputs[0]);
+ const hexagon_nn_input& new_max = getQuantizationMax(outputs[0]);
+ uint32_t node;
+
+ hexagon_nn_output tensor_out8 =
+ make_hexagon_nn_output(mOperands[outputs[0]].dimensions, sizeof(uint8_t));
+ hexagon_nn_output tensor_out32 =
+ make_hexagon_nn_output(mOperands[outputs[0]].dimensions, sizeof(int32_t));
+ hexagon_nn_output scalar_out32 = make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float));
+
+ std::vector<hexagon_nn_output> out8 = {tensor_out8, scalar_out32, scalar_out32};
+ std::vector<hexagon_nn_output> out32 = {tensor_out32, scalar_out32, scalar_out32};
+
+ // base operation
+ node = addOperationInternal(op, pad, inputs, out32);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
+ hexagon_nn_input previous = {.src_id = node, .output_idx = 0};
+ hexagon_nn_input previous_min = {.src_id = node, .output_idx = 1};
+ hexagon_nn_input previous_max = {.src_id = node, .output_idx = 2};
+
+ // add bias
+ if (bias.size() == 3) {
+ node = addOperationInternal(
+ OP_QuantizedBiasAdd_32p32to32, NN_PAD_NA,
+ {previous, bias[0], previous_min, previous_max, bias[1], bias[2]}, out32);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding bias operation");
+ previous.src_id = node;
+ previous_min.src_id = node;
+ previous_max.src_id = node;
+ }
+
+ // requantize
+ node = addOperationInternal(OP_Requantize_32to8, NN_PAD_NA,
+ {previous, previous_min, previous_max, new_min, new_max}, out8);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding requantize operation");
+ previous.src_id = node;
+ previous_min.src_id = node;
+ previous_max.src_id = node;
+
+ // activation
+ if (activation != OP_Nop) {
+ std::vector<hexagon_nn_input> buffer = {previous, previous_min, previous_max};
+ buffer.insert(buffer.end(), actArgs.begin(), actArgs.end());
+ node = addOperationInternal(activation, NN_PAD_NA, buffer, out8);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
+ }
+
+ return registerHexagonInputs(outputs, node);
+}
+
+bool Model::verifyOperations() {
+ std::vector<bool> supported = supportedOperations();
+ return std::all_of(supported.begin(), supported.end(), [](bool valid) { return valid; });
+}
+
+bool Model::verifyOperands() {
+ for (const OperandInfo& operand : mOperands) {
+ HEXAGON_SOFT_ASSERT_GE(4ul, operand.dimensions.size(),
+ "Operand must have at most 4 dimensions");
+ for (uint32_t dim : operand.dimensions) {
+ HEXAGON_SOFT_ASSERT_NE(0, dim, "At least one operand with unknown dimension");
+ }
+ }
+ return true;
+}
+
+bool Model::addInputs() {
+ // prepare OP_INPUT's outputs
+ std::vector<hexagon_nn_output> outs;
+ for (size_t i = 0; i < mInputs.size(); ++i) {
+ OperandInfo& operand = mOperands[mInputs[i]];
+ outs.push_back(make_hexagon_nn_output(operand.dimensions, getSize(operand.type)));
+ }
+
+ // add single input node for entire graph
+ uint32_t node = addOperationInternal(OP_INPUT, NN_PAD_NA, {}, outs);
+ HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding input operation");
+
+ // update operand information
+ for (size_t i = 0; i < mInputs.size(); ++i) {
+ OperandInfo& operand = mOperands[mInputs[i]];
+ operand.hexagon_input = {.src_id = node, .output_idx = static_cast<uint32_t>(i)};
+ }
+
+ return true;
+}
+
+bool Model::addOperations() {
+ for (const Operation& operation : mOperations) {
+ OperationType operationType = operation.type;
+
+ // For now, the operation type is always the same as its first operand
+ // parameter. If this changes in the future, this line of code will need
+ // to be updated.
+ OperandType operandType = mOperands[operation.inputs[0]].type;
+
+ OperationTuple opTuple = std::make_pair(operationType, operandType);
+ HEXAGON_SOFT_ASSERT(
+ getOperationPrepareTable().find(opTuple) != getOperationPrepareTable().end(),
+ "Operation not found");
+ bool success =
+ getOperationPrepareTable()[opTuple](operation.inputs, operation.outputs, this);
+ HEXAGON_SOFT_ASSERT(success, "error adding operation");
+ }
+ return true;
+}
+
+bool Model::addOutputs() {
+ // prepare OP_OUTPUT's inputs
+ std::vector<hexagon_nn_input> ins;
+ for (size_t out : mOutputs) {
+ OperandInfo& operand = mOperands[out];
+ HEXAGON_SOFT_ASSERT_NE(operand.hexagon_input, hexagon_nn_input{},
+ "output operand has not been registered");
+
+ if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) {
+ // Adjust quantized range of outputs
+ uint32_t dequant = addOperationInternal(
+ OP_Dequantize, NN_PAD_NA,
+ {operand.hexagon_input, operand.hexagon_input_min, operand.hexagon_input_max},
+ {make_hexagon_nn_output(operand.dimensions, sizeof(float))});
+ uint32_t quant =
+ addOperationInternal(OP_Quantize, NN_PAD_NA,
+ {{.src_id = dequant, .output_idx = 0},
+ createQuantizationValue(out, 0),
+ createQuantizationValue(out, 255)},
+ {make_hexagon_nn_output(operand.dimensions, sizeof(uint8_t)),
+ make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)),
+ make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float))});
+ ins.push_back({.src_id = quant, .output_idx = 0});
+ } else {
+ ins.push_back(operand.hexagon_input);
+ }
+ }
+
+ // add single output node for entire graph
+ bool success = addBasicOperation(OP_OUTPUT, NN_PAD_NA, ins, {});
+ HEXAGON_SOFT_ASSERT(success, "Error adding output operation");
+
+ return true;
+}
+
+void Model::clearModel() {
+ mCompiled = false;
+ for (OperandInfo& operand : mOperands) {
+ operand.hexagon_input = {};
+ operand.hexagon_input_min = {};
+ operand.hexagon_input_max = {};
+ operand.hexagon_output = {};
+ }
+ if (mGraphId != hexagon_nn_nn_id{}) {
+ hexagon::Controller::getInstance().teardown(mGraphId);
+ }
+}
+
+std::vector<bool> Model::supportedOperations() {
+ std::vector<bool> supported(mOperations.size());
+ for (size_t i = 0; i < supported.size(); ++i) {
+ const Operation& operation = mOperations[i];
+ OperationType operationType = operation.type;
+
+ // For now, the operation type is always the same as its first operand
+ // parameter. If this changes in the future, this line of code will need
+ // to be updated.
+ OperandType operandType = mOperands[operation.inputs[0]].type;
+
+ OperationTuple opTuple = std::make_pair(operationType, operandType);
+
+ auto entry = getOperationCheckTable().find(opTuple);
+ if (entry != getOperationCheckTable().end()) {
+ supported[i] = entry->second(operation.inputs, operation.outputs, this);
+ } else {
+ supported[i] = false;
+ }
+ }
+ return supported;
+}
+
+bool Model::prepare() {
+ if (!verifyOperations() || !verifyOperands()) {
+ return false;
+ }
+
+ int err = hexagon::Controller::getInstance().init(&mGraphId);
+ HEXAGON_SOFT_ASSERT_EQ(0, err, "Hexagon could not allocate new graph");
+ HEXAGON_SOFT_ASSERT_NE(0, mGraphId, "Hexagon could not allocate new graph");
+ hexagon::Controller::getInstance().set_debug_level(mGraphId, 0);
+
+ if (!addInputs() || !addOperations() || !addOutputs()) {
+ clearModel();
+ LOG(ERROR) << "Something went wrong. Clearing the model and aborting.";
+ return false;
+ }
+
+ err = hexagon::Controller::getInstance().prepare(mGraphId);
+
+ LOG(INFO) << "PrepareModel was " << (err == 0 ? "SUCCESSFUL" : "UNSUCCESSFUL");
+
+ return err == 0;
+}
+
+static hexagon_nn_tensordef convertToTensordef(const OperandInfo& operand) {
+ std::vector<uint32_t> dimensions = getAlignedDimensions(operand.dimensions, 4);
+ return {
+ .batches = dimensions[0],
+ .height = dimensions[1],
+ .width = dimensions[2],
+ .depth = dimensions[3],
+ .data = operand.buffer,
+ .dataLen = static_cast<int32_t>(operand.length),
+ .data_valid_len = operand.length, // unused?
+ .unused = 0,
+ };
+}
+
+static uint32_t getSize(const OperandInfo& operand) {
+ return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(),
+ getSize(operand.type), std::multiplies<>{});
+}
+
+static OperandInfo getUpdatedOperand(const RequestArgument& inputOutput,
+ const std::vector<RunTimePoolInfo>& pools,
+ const OperandInfo& oldInfo) {
+ OperandInfo newInfo = oldInfo;
+
+ const RunTimePoolInfo& pool = pools[inputOutput.location.poolIndex];
+ uint32_t offset = inputOutput.location.offset;
+
+ if (inputOutput.dimensions.size() > 0) {
+ newInfo.dimensions = inputOutput.dimensions;
+ }
+
+ newInfo.buffer = pool.getBuffer() + offset;
+ newInfo.length = getSize(newInfo);
+
+ return newInfo;
+}
+
+bool Model::execute(const Request& request) {
+ std::vector<RunTimePoolInfo> pools = mapPools(request.pools);
+
+ // prepare inputs
+ std::vector<hexagon_nn_tensordef> inputs;
+ for (size_t i = 0; i < request.inputs.size(); ++i) {
+ const OperandInfo& oldInfo = mOperands[mInputs[i]];
+ OperandInfo newInfo = getUpdatedOperand(request.inputs[i], pools, oldInfo);
+ inputs.push_back(convertToTensordef(newInfo));
+ }
+
+ // prepare outputs
+ std::vector<hexagon_nn_tensordef> outputs;
+ for (size_t i = 0; i < request.outputs.size(); ++i) {
+ const OperandInfo& oldInfo = mOperands[mOutputs[i]];
+ OperandInfo newInfo = getUpdatedOperand(request.outputs[i], pools, oldInfo);
+ outputs.push_back(convertToTensordef(newInfo));
+ }
+
+ // execute model
+ int err = hexagon::Controller::getInstance().execute_new(mGraphId, inputs.data(), inputs.size(),
+ outputs.data(), outputs.size());
+
+ std::for_each(pools.begin(), pools.end(), [](RunTimePoolInfo& pool) { pool.update(); });
+
+ LOG(INFO) << "EXECUTION WAS " << (err == 0 ? "SUCCESSFUL" : "UNSUCCESSFUL");
+
+ return err == 0;
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/HexagonModel.h b/1.0/HexagonModel.h
new file mode 100644
index 0000000..797620b
--- /dev/null
+++ b/1.0/HexagonModel.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 ANDROID_HARDWARE_V1_0_HEXAGON_MODEL_H
+#define ANDROID_HARDWARE_V1_0_HEXAGON_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <atomic>
+#include <string>
+#include <vector>
+#include "CpuExecutor.h"
+#include "HexagonController.h"
+#include "HexagonOperations.h"
+#include "HexagonUtils.h"
+#include "OperationsUtils.h"
+#include "hexagon_nn_controller/hexagon_nn_controller.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+using ::android::nn::RunTimePoolInfo;
+using ::android::nn::Shape;
+
+using NeuralnetworksModel = ::android::hardware::neuralnetworks::V1_0::Model;
+
+// runtime operand information
+struct OperandInfo {
+ // tensor information
+ OperandType type;
+ std::vector<uint32_t> dimensions;
+
+ // (optional) quantization paramters
+ float scale;
+ int32_t zeroPoint;
+
+ // lifetime
+ OperandLifeTime lifetime;
+
+ // data location
+ uint8_t* buffer;
+ uint32_t length;
+
+ // Hexagon nnlib identifiers
+ hexagon_nn_input hexagon_input;
+ hexagon_nn_input hexagon_input_min;
+ hexagon_nn_input hexagon_input_max;
+ hexagon_nn_output hexagon_output;
+};
+
+// interface wrapper
+class Model {
+ public:
+ // methods
+ Model() = delete;
+ Model(const Model&) = delete;
+ Model& operator=(const Model&) = delete;
+ Model(Model&& other);
+ Model& operator=(Model&& other);
+
+ Model(const NeuralnetworksModel& model);
+ ~Model();
+
+ std::string getLog();
+ std::string getGraph();
+
+ // model check
+ const int32_t* getPointer(uint32_t operand);
+ Shape getShape(uint32_t operand);
+ bool setShape(uint32_t operand, const Shape& shape);
+ bool isConstant(uint32_t operand);
+
+ // model prepare types
+ const hexagon_nn_input& getTensor(uint32_t operand);
+ const hexagon_nn_input& getQuantizationMin(uint32_t operand);
+ const hexagon_nn_input& getQuantizationMax(uint32_t operand);
+ hexagon_nn_input createQuantizationValue(uint32_t operand, int32_t quant_value);
+ hexagon_nn_input createConvFilterTensor(uint32_t operand);
+ hexagon_nn_input createDepthwiseFilterTensor(uint32_t operand, int32_t depth_multiplier);
+ hexagon_nn_input createFullyConnectedWeightTensor(uint32_t operand);
+ template <typename Type>
+ Type getScalar(uint32_t operand);
+ op_type getFloatActivation(uint32_t operand);
+ op_type getQuantizedActivation(uint32_t operand);
+ hexagon_nn_padding_type getPadding(uint32_t operand);
+
+ template <typename Type>
+ hexagon_nn_input createTensor(uint32_t B, uint32_t H, uint32_t W, uint32_t D,
+ const std::vector<Type>& values);
+ hexagon_nn_input createShape(uint32_t B, uint32_t H, uint32_t W, uint32_t D);
+ template <typename Type>
+ hexagon_nn_input createValues(const std::vector<Type>& values);
+ template <typename Type>
+ hexagon_nn_input createScalar(Type value);
+
+ // model prepare operations
+ bool addBasicOperation(op_type op, hexagon_nn_padding_type pad,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs);
+ bool addFloatOperationWithActivation(op_type op, hexagon_nn_padding_type pad,
+ op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs);
+ bool addQuant8OperationWithActivation(op_type op, hexagon_nn_padding_type pad,
+ op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs);
+ bool addFusedFloatOperation(op_type op, hexagon_nn_padding_type pad,
+ const hexagon_nn_input& bias, op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs);
+ bool addFusedQuant8Operation(op_type op, hexagon_nn_padding_type pad,
+ const std::vector<hexagon_nn_input>& bias, op_type activation,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<uint32_t>& outputs);
+
+ std::vector<bool> supportedOperations();
+ bool prepare();
+ bool execute(const Request& request);
+
+ private:
+ uint32_t getNextNode();
+ uint32_t addOperationInternal(op_type op, hexagon_nn_padding_type pad,
+ const std::vector<hexagon_nn_input>& inputs,
+ const std::vector<hexagon_nn_output>& outputs);
+ hexagon_nn_input createTensorInternal(uint32_t B, uint32_t H, uint32_t W, uint32_t D,
+ const uint8_t* ptr, size_t size);
+ std::vector<hexagon_nn_input> setupActivationArgs(op_type op);
+ hexagon_nn_input addOperand(uint32_t operand);
+ std::vector<hexagon_nn_output> getHexagonOutputs(const std::vector<uint32_t>& operands);
+ bool registerHexagonInputs(const std::vector<uint32_t>& operands, uint32_t node);
+
+ bool verifyOperations();
+ bool verifyOperands();
+ bool addInputs();
+ bool addOperations();
+ bool addOutputs();
+
+ void clearModel();
+
+ // members
+ hexagon_nn_nn_id mGraphId;
+ uint32_t mNodeCount;
+ bool mCompiled;
+ std::vector<OperandInfo> mOperands;
+ std::vector<Operation> mOperations;
+ std::vector<uint32_t> mInputs;
+ std::vector<uint32_t> mOutputs;
+ std::vector<RunTimePoolInfo> mPools;
+};
+
+// template implementations
+
+template <typename Type>
+Type Model::getScalar(uint32_t operand) {
+ return *reinterpret_cast<const Type*>(mOperands[operand].buffer);
+}
+
+template <typename Type>
+hexagon_nn_input Model::createTensor(uint32_t B, uint32_t H, uint32_t W, uint32_t D,
+ const std::vector<Type>& values) {
+ return createTensorInternal(B, H, W, D, reinterpret_cast<const uint8_t*>(values.data()),
+ values.size() * sizeof(Type));
+}
+
+template <typename Type>
+hexagon_nn_input Model::createValues(const std::vector<Type>& values) {
+ return createTensor(1, 1, 1, values.size(), values);
+}
+
+template <typename Type>
+hexagon_nn_input Model::createScalar(Type value) {
+ return createTensorInternal(1, 1, 1, 1, reinterpret_cast<uint8_t*>(&value), sizeof(Type));
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_V1_0_HEXAGON_MODEL_H
diff --git a/1.0/HexagonOperations.h b/1.0/HexagonOperations.h
new file mode 100644
index 0000000..a19a236
--- /dev/null
+++ b/1.0/HexagonOperations.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 ANDROID_HARDWARE_V1_0_HEXAGON_OPERATIONS_H
+#define ANDROID_HARDWARE_V1_0_HEXAGON_OPERATIONS_H
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <functional>
+#include <map>
+#include "HexagonUtils.h"
+#include "hexagon_nn_controller/hexagon_nn_controller.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+class Model;
+using HexagonModel = ::android::hardware::neuralnetworks::V1_0::implementation::hexagon::Model;
+
+using ::android::hardware::neuralnetworks::V1_0::Operand;
+
+using OperationTuple = std::pair<OperationType, OperandType>;
+
+using HexagonOperationFn =
+ std::function<bool(const std::vector<uint32_t>& /* ins */,
+ const std::vector<uint32_t>& /* outs */, HexagonModel* /* model */)>;
+
+using OperationTable = std::map<OperationTuple, HexagonOperationFn>;
+OperationTable& getOperationPrepareTable();
+OperationTable& getOperationCheckTable();
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_V1_0_HEXAGON_OPERATIONS_H
diff --git a/1.0/HexagonOperationsCheck.cpp b/1.0/HexagonOperationsCheck.cpp
new file mode 100644
index 0000000..d900c2b
--- /dev/null
+++ b/1.0/HexagonOperationsCheck.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "HexagonModel.h"
+#include "HexagonOperations.h"
+#include "OperationsUtils.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+using android::nn::Shape;
+
+namespace {
+
+bool addMul(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model, OperationType op) {
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for " << toString(op));
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << toString(op));
+
+ // get output size
+ const Shape in1Shape = model->getShape(ins[0]);
+ const Shape in2Shape = model->getShape(ins[1]);
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(addMulPrepare(in1Shape, in2Shape, &outShape), "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool add(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
+ return addMul(ins, outs, model, OperationType::ADD);
+}
+
+bool mul(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
+ return addMul(ins, outs, model, OperationType::MUL);
+}
+
+bool pool(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model,
+ OperationType op) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for " << toString(op));
+
+ // get parameters
+ const Shape inShape = model->getShape(ins[0]);
+
+ // setup parameters
+ int32_t padding_left;
+ int32_t padding_right;
+ int32_t padding_top;
+ int32_t padding_bottom;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t filter_width;
+ int32_t filter_height;
+
+ // get parameters
+ if (ins.size() == 10) {
+ padding_left = model->getScalar<int32_t>(ins[1]);
+ padding_right = model->getScalar<int32_t>(ins[2]);
+ padding_top = model->getScalar<int32_t>(ins[3]);
+ padding_bottom = model->getScalar<int32_t>(ins[4]);
+ stride_width = model->getScalar<int32_t>(ins[5]);
+ stride_height = model->getScalar<int32_t>(ins[6]);
+ filter_width = model->getScalar<int32_t>(ins[7]);
+ filter_height = model->getScalar<int32_t>(ins[8]);
+
+ HEXAGON_SOFT_ASSERT_NE(getPadding(inShape.dimensions[2], inShape.dimensions[1],
+ stride_width, stride_height, filter_width, filter_height,
+ padding_left, padding_right, padding_top, padding_bottom),
+ NN_PAD_NA, "Unknown padding");
+ } else {
+ const int32_t padding_implicit = model->getScalar<int32_t>(ins[1]);
+ stride_width = model->getScalar<int32_t>(ins[2]);
+ stride_height = model->getScalar<int32_t>(ins[3]);
+ filter_width = model->getScalar<int32_t>(ins[4]);
+ filter_height = model->getScalar<int32_t>(ins[5]);
+
+ nn::calculateExplicitPadding(inShape.dimensions[2], stride_width, filter_width,
+ padding_implicit, &padding_left, &padding_right);
+ nn::calculateExplicitPadding(inShape.dimensions[1], stride_height, filter_height,
+ padding_implicit, &padding_top, &padding_bottom);
+ }
+
+ // get output size
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(
+ genericPoolingPrepare(inShape, padding_left, padding_right, padding_top, padding_bottom,
+ stride_width, stride_height, filter_width, filter_height, &outShape),
+ "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool average_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return pool(ins, outs, model, OperationType::AVERAGE_POOL_2D);
+}
+
+bool l2_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return pool(ins, outs, model, OperationType::L2_POOL_2D);
+}
+
+bool max_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return pool(ins, outs, model, OperationType::MAX_POOL_2D);
+}
+
+bool concatenation(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::CONCATENATION);
+ HEXAGON_SOFT_ASSERT_LE(3, ins.size(), "Need at least 3 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ const size_t numInputTensors = ins.size() - 1;
+
+ const int32_t axis = model->getScalar<int32_t>(ins[numInputTensors]);
+
+ // get output size
+ std::vector<Shape> inShapes(numInputTensors);
+ for (size_t i = 0; i < numInputTensors; ++i) {
+ inShapes[i] = model->getShape(ins[i]);
+ }
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(concatenationPrepare(inShapes, axis, &outShape), "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::CONV_2D);
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7, "Need 7 or 10 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // setup shapes
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape filterShape = model->getShape(ins[1]);
+ const Shape biasShape = model->getShape(ins[2]);
+
+ // setup parameters
+ int32_t padding_left;
+ int32_t padding_right;
+ int32_t padding_top;
+ int32_t padding_bottom;
+ int32_t stride_width;
+ int32_t stride_height;
+
+ // get parameters
+ if (ins.size() == 10) {
+ padding_left = model->getScalar<int32_t>(ins[3]);
+ padding_right = model->getScalar<int32_t>(ins[4]);
+ padding_top = model->getScalar<int32_t>(ins[5]);
+ padding_bottom = model->getScalar<int32_t>(ins[6]);
+ stride_width = model->getScalar<int32_t>(ins[7]);
+ stride_height = model->getScalar<int32_t>(ins[8]);
+
+ HEXAGON_SOFT_ASSERT_NE(
+ getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
+ padding_left, padding_right, padding_top, padding_bottom),
+ NN_PAD_NA, "Unknown padding");
+ } else {
+ const int32_t padding_implicit = model->getScalar<int32_t>(ins[3]);
+ stride_width = model->getScalar<int32_t>(ins[4]);
+ stride_height = model->getScalar<int32_t>(ins[5]);
+
+ nn::calculateExplicitPadding(inputShape.dimensions[2], stride_width,
+ filterShape.dimensions[2], padding_implicit, &padding_left,
+ &padding_right);
+ nn::calculateExplicitPadding(inputShape.dimensions[1], stride_height,
+ filterShape.dimensions[1], padding_implicit, &padding_top,
+ &padding_bottom);
+ }
+
+ // get output size
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(
+ convPrepare(inputShape, filterShape, biasShape, padding_left, padding_right, padding_top,
+ padding_bottom, stride_width, stride_height, &outShape),
+ "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ // enforce filter is a constant
+ HEXAGON_SOFT_ASSERT(model->isConstant(ins[1]), name << "requires filter to be constant data");
+
+ return true;
+}
+
+bool depthwise_conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::DEPTHWISE_CONV_2D);
+ HEXAGON_SOFT_ASSERT(ins.size() == 8 || ins.size() == 11, "Need 8 or 11 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // setup shapes
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape filterShape = model->getShape(ins[1]);
+ const Shape biasShape = model->getShape(ins[2]);
+
+ // setup parameters
+ int32_t padding_left;
+ int32_t padding_right;
+ int32_t padding_top;
+ int32_t padding_bottom;
+ int32_t stride_width;
+ int32_t stride_height;
+
+ // get parameters
+ if (ins.size() == 11) {
+ padding_left = model->getScalar<int32_t>(ins[3]);
+ padding_right = model->getScalar<int32_t>(ins[4]);
+ padding_top = model->getScalar<int32_t>(ins[5]);
+ padding_bottom = model->getScalar<int32_t>(ins[6]);
+ stride_width = model->getScalar<int32_t>(ins[7]);
+ stride_height = model->getScalar<int32_t>(ins[8]);
+
+ HEXAGON_SOFT_ASSERT_NE(
+ getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
+ padding_left, padding_right, padding_top, padding_bottom),
+ NN_PAD_NA, "Unknown padding");
+
+ } else {
+ const int32_t padding_implicit = model->getScalar<int32_t>(ins[3]);
+ stride_width = model->getScalar<int32_t>(ins[4]);
+ stride_height = model->getScalar<int32_t>(ins[5]);
+
+ nn::calculateExplicitPadding(inputShape.dimensions[2], stride_width,
+ filterShape.dimensions[2], padding_implicit, &padding_left,
+ &padding_right);
+ nn::calculateExplicitPadding(inputShape.dimensions[1], stride_height,
+ filterShape.dimensions[1], padding_implicit, &padding_top,
+ &padding_bottom);
+ }
+
+ // get output size
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(
+ depthwiseConvPrepare(inputShape, filterShape, biasShape, padding_left, padding_right,
+ padding_top, padding_bottom, stride_width, stride_height, &outShape),
+ "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ // enforce filter is a constant
+ HEXAGON_SOFT_ASSERT(model->isConstant(ins[1]), name << " requires filter to be constant data");
+
+ return true;
+}
+
+bool dequantize(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::DEQUANTIZE);
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // get output size
+ const Shape inputShape = model->getShape(ins[0]);
+ Shape outShape = model->getShape(outs[0]);
+
+ HEXAGON_SOFT_ASSERT(dequantizePrepare(inputShape, &outShape), "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool fully_connected(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::FULLY_CONNECTED);
+ HEXAGON_SOFT_ASSERT_EQ(4, ins.size(), "Need 4 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // get output size
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape weightsShape = model->getShape(ins[1]);
+ const Shape biasShape = model->getShape(ins[2]);
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(fullyConnectedPrepare(inputShape, weightsShape, biasShape, &outShape),
+ "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ // enforce weight is a constant
+ HEXAGON_SOFT_ASSERT(model->isConstant(ins[1]), name << "requires weight to be constant data");
+
+ return true;
+}
+
+bool local_response_normalization(const std::vector<uint32_t>& ins,
+ const std::vector<uint32_t>& outs, HexagonModel* model) {
+ std::string name = toString(OperationType::LOCAL_RESPONSE_NORMALIZATION);
+ HEXAGON_SOFT_ASSERT_EQ(5, ins.size(), "Need 5 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // get output size
+ const Shape inShape = model->getShape(ins[0]);
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(genericNormalizationPrepare(inShape, &outShape), "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool activation(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model, uint32_t numInputs, OperationType op) {
+ HEXAGON_SOFT_ASSERT_EQ(numInputs, ins.size(),
+ "Need " << numInputs << " input for " << toString(op));
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << toString(op));
+
+ // get output size
+ const Shape inShape = model->getShape(ins[0]);
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(genericActivationPrepare(inShape, &outShape), "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool logistic(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return activation(ins, outs, model, 1, OperationType::LOGISTIC);
+}
+
+bool relu(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return activation(ins, outs, model, 1, OperationType::RELU);
+}
+
+bool relu1(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return activation(ins, outs, model, 1, OperationType::RELU1);
+}
+
+bool relu6(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return activation(ins, outs, model, 1, OperationType::RELU6);
+}
+
+bool softmax(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return activation(ins, outs, model, 2, OperationType::SOFTMAX);
+}
+
+bool tanh(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ return activation(ins, outs, model, 1, OperationType::TANH);
+}
+
+bool reshape(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::RESHAPE);
+ HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // get output size
+ const Shape inShape = model->getShape(ins[0]);
+ const Shape targetShape = model->getShape(ins[1]);
+ const int32_t* targetShapePtr = model->getPointer(ins[1]);
+ int32_t targetShapeNumElem = ::android::nn::getNumberOfElements(targetShape);
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(targetShapePtr != nullptr, "pointer value is currently nullptr");
+
+ HEXAGON_SOFT_ASSERT(reshapePrepare(inShape, targetShapePtr, targetShapeNumElem, &outShape),
+ "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+bool resize_bilinear(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ std::string name = toString(OperationType::RESIZE_BILINEAR);
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for " << name);
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for " << name);
+
+ // get parameters
+ const int32_t width = model->getScalar<int32_t>(ins[1]);
+ const int32_t height = model->getScalar<int32_t>(ins[2]);
+
+ // get output size
+ const Shape inShape = model->getShape(ins[0]);
+ Shape outShape = model->getShape(outs[0]);
+ HEXAGON_SOFT_ASSERT(resizeBilinearPrepare(inShape, width, height, &outShape),
+ "Error getting shape");
+ HEXAGON_SOFT_ASSERT(model->setShape(outs[0], outShape), "Error setting shape");
+
+ return true;
+}
+
+} // namespace
+
+OperationTable& getOperationCheckTable() {
+ static OperationTable table = {
+ // NOTE: the operations that are commented out via inline represent
+ // operations that are valid for the Android O NNAPI release, but are
+ // currently not implemented in HVX.
+
+ // -------------------------- 32-BIT FLOAT ----------------------------
+ // HVX is only performant when running on quantized values. Further, as
+ // an optimization, the current HVX driver will convert some floating
+ // point tensors into quantized values, perform the operation, and then
+ // convert them back to floating point. This results in a loss in
+ // precision causing some tests to fail. For these reasons, the FLOAT32
+ // operations are being temporarily disabled.
+ /*
+ {{OperationType::ADD, OperandType::TENSOR_FLOAT32}, add},
+ {{OperationType::AVERAGE_POOL_2D, OperandType::TENSOR_FLOAT32}, average_pool_2d},
+ {{OperationType::CONCATENATION, OperandType::TENSOR_FLOAT32}, concatenation},
+ {{OperationType::CONV_2D, OperandType::TENSOR_FLOAT32}, conv_2d},
+ {{OperationType::DEPTHWISE_CONV_2D, OperandType::TENSOR_FLOAT32}, depthwise_conv_2d},
+ //{{OperationType::DEPTH_TO_SPACE, OperandType::TENSOR_FLOAT32}, depth_to_space},
+ //{{OperationType::EMBEDDING_LOOKUP, OperandType::TENSOR_FLOAT32}, embedding_lookup},
+ //{{OperationType::FLOOR, OperandType::TENSOR_FLOAT32}, floor},
+ {{OperationType::FULLY_CONNECTED, OperandType::TENSOR_FLOAT32}, fully_connected},
+ //{{OperationType::HASHTABLE_LOOKUP, OperandType::TENSOR_FLOAT32}, hashtable_lookup},
+ //{{OperationType::L2_NORMALIZATION, OperandType::TENSOR_FLOAT32}, l2_normalization},
+ {{OperationType::L2_POOL_2D, OperandType::TENSOR_FLOAT32}, l2_pool_2d},
+ {{OperationType::LOCAL_RESPONSE_NORMALIZATION, OperandType::TENSOR_FLOAT32},
+ local_response_normalization},
+ {{OperationType::LOGISTIC, OperandType::TENSOR_FLOAT32}, logistic},
+ //{{OperationType::LSH_PROJECTION, OperandType::TENSOR_FLOAT32}, lsh_projection},
+ //{{OperationType::LSTM, OperandType::TENSOR_FLOAT32}, lstm },
+ {{OperationType::MAX_POOL_2D, OperandType::TENSOR_FLOAT32}, max_pool_2d},
+ {{OperationType::MUL, OperandType::TENSOR_FLOAT32}, mul},
+ {{OperationType::RELU, OperandType::TENSOR_FLOAT32}, relu},
+ {{OperationType::RELU1, OperandType::TENSOR_FLOAT32}, relu1},
+ {{OperationType::RELU6, OperandType::TENSOR_FLOAT32}, relu6},
+ {{OperationType::RESHAPE, OperandType::TENSOR_FLOAT32}, reshape},
+ {{OperationType::RESIZE_BILINEAR, OperandType::TENSOR_FLOAT32}, resize_bilinear},
+ //{{OperationType::RNN, OperandType::TENSOR_FLOAT32}, rnn},
+ {{OperationType::SOFTMAX, OperandType::TENSOR_FLOAT32}, softmax},
+ //{{OperationType::SPACE_TO_DEPTH, OperandType::TENSOR_FLOAT32}, space_to_depth},
+ //{{OperationType::SVDF, OperandType::TENSOR_FLOAT32}, svdf },
+ {{OperationType::TANH, OperandType::TENSOR_FLOAT32}, tanh},
+ */
+
+ // -------------------- QUANTIZED 8-BIT ASYMMETRICAL ------------------
+ {{OperationType::ADD, OperandType::TENSOR_QUANT8_ASYMM}, add},
+ {{OperationType::AVERAGE_POOL_2D, OperandType::TENSOR_QUANT8_ASYMM}, average_pool_2d},
+ {{OperationType::CONCATENATION, OperandType::TENSOR_QUANT8_ASYMM}, concatenation},
+ {{OperationType::CONV_2D, OperandType::TENSOR_QUANT8_ASYMM}, conv_2d},
+ {{OperationType::DEPTHWISE_CONV_2D, OperandType::TENSOR_QUANT8_ASYMM}, depthwise_conv_2d},
+ //{{OperationType::DEPTH_TO_SPACE, OperandType::TENSOR_QUANT8_ASYMM}, depth_to_space},
+ {{OperationType::DEQUANTIZE, OperandType::TENSOR_QUANT8_ASYMM}, dequantize},
+ //{{OperationType::EMBEDDING_LOOKUP, OperandType::TENSOR_QUANT8_ASYMM}, embedding_lookup},
+ {{OperationType::FULLY_CONNECTED, OperandType::TENSOR_QUANT8_ASYMM}, fully_connected},
+ //{{OperationType::HASHTABLE_LOOKUP, OperandType::TENSOR_QUANT8_ASYMM}, hashtable_lookup},
+ {{OperationType::LOGISTIC, OperandType::TENSOR_QUANT8_ASYMM}, logistic},
+ //{{OperationType::LSH_PROJECTION, OperandType::TENSOR_QUANT8_ASYMM}, lsh_projection},
+ {{OperationType::MAX_POOL_2D, OperandType::TENSOR_QUANT8_ASYMM}, max_pool_2d},
+ {{OperationType::MUL, OperandType::TENSOR_QUANT8_ASYMM}, mul},
+ {{OperationType::RELU, OperandType::TENSOR_QUANT8_ASYMM}, relu},
+ {{OperationType::RELU1, OperandType::TENSOR_QUANT8_ASYMM}, relu1},
+ {{OperationType::RELU6, OperandType::TENSOR_QUANT8_ASYMM}, relu6},
+ {{OperationType::RESHAPE, OperandType::TENSOR_QUANT8_ASYMM}, reshape},
+ {{OperationType::SOFTMAX, OperandType::TENSOR_QUANT8_ASYMM}, softmax},
+ //{{OperationType::SPACE_TO_DEPTH, OperandType::TENSOR_QUANT8_ASYMM}, space_to_depth},
+ };
+
+ // The following functions are normally used by float32, but those
+ // operations have been temporarily disabled. Void explicitly marks them as
+ // unused, and prevents the compiler from throwing an error.
+ (void)l2_pool_2d;
+ (void)local_response_normalization;
+ (void)tanh;
+ (void)resize_bilinear;
+
+ return table;
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/HexagonOperationsPrepare.cpp b/1.0/HexagonOperationsPrepare.cpp
new file mode 100644
index 0000000..3b8238f
--- /dev/null
+++ b/1.0/HexagonOperationsPrepare.cpp
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "HexagonModel.h"
+#include "HexagonOperations.h"
+#include "OperationsUtils.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+using android::nn::Shape;
+
+namespace {
+namespace float32 {
+
+bool add(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for float32::add");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::add");
+
+ // get parameters
+ const hexagon_nn_input& in1 = model->getTensor(ins[0]);
+ const hexagon_nn_input& in2 = model->getTensor(ins[1]);
+
+ const op_type act = model->getFloatActivation(ins[2]);
+
+ // add node to graph
+ return model->addFusedFloatOperation(OP_Add_f, NN_PAD_NA, {}, act, {in1, in2}, outs);
+}
+
+bool average_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for float32::average_pool_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::average_pool_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t filter_width;
+ int32_t filter_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
+ stride_width = model->getScalar<int32_t>(ins[5]);
+ stride_height = model->getScalar<int32_t>(ins[6]);
+ filter_width = model->getScalar<int32_t>(ins[7]);
+ filter_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getFloatActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filter_width, filter_height, padding_left, padding_right,
+ padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[1]);
+ stride_width = model->getScalar<int32_t>(ins[2]);
+ stride_height = model->getScalar<int32_t>(ins[3]);
+ filter_width = model->getScalar<int32_t>(ins[4]);
+ filter_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getFloatActivation(ins[6]);
+ }
+
+ const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFloatOperationWithActivation(OP_AvgPool_f, pad, act, {input, window, stride},
+ outs);
+}
+
+bool concatenation(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_LE(3, ins.size(), "Need at least 3 inputs for float32::concatenation");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::concatenation");
+
+ const size_t numInputTensors = ins.size() - 1;
+
+ // get parameters
+ std::vector<hexagon_nn_input> inputs(numInputTensors + 1);
+ for (size_t i = 0; i < numInputTensors; ++i) {
+ inputs[i + 1] = model->getTensor(ins[i]);
+ }
+
+ // axis being concatenated
+ const int32_t axis = model->getScalar<int32_t>(ins[numInputTensors]);
+ const int32_t dims = model->getShape(ins[0]).dimensions.size();
+ inputs[0] = model->createScalar<int32_t>(axis + (4 - dims));
+
+ // add node to graph
+ return model->addBasicOperation(OP_Concat_f, NN_PAD_NA, inputs, outs);
+}
+
+bool conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for float32::conv_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::conv_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input filter = model->createConvFilterTensor(ins[1]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
+ stride_width = model->getScalar<int32_t>(ins[7]);
+ stride_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getFloatActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape filterShape = model->getShape(ins[1]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
+ padding_left, padding_right, padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[3]);
+ stride_width = model->getScalar<int32_t>(ins[4]);
+ stride_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getFloatActivation(ins[6]);
+ }
+
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFusedFloatOperation(OP_Conv2d_f, pad, bias, act, {input, filter, stride},
+ outs);
+}
+
+bool depthwise_conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 11 || ins.size() == 8,
+ "Need 8 or 11 inputs for float32::depthwise_conv_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::depthwise_conv_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+
+ const Shape filterShape = model->getShape(ins[1]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t depth_multiplier;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 11) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
+ stride_width = model->getScalar<int32_t>(ins[7]);
+ stride_height = model->getScalar<int32_t>(ins[8]);
+ depth_multiplier = model->getScalar<int32_t>(ins[9]);
+ act = model->getFloatActivation(ins[10]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape filterShape = model->getShape(ins[1]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
+ padding_left, padding_right, padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[3]);
+ stride_width = model->getScalar<int32_t>(ins[4]);
+ stride_height = model->getScalar<int32_t>(ins[5]);
+ depth_multiplier = model->getScalar<int32_t>(ins[6]);
+ act = model->getFloatActivation(ins[7]);
+ }
+
+ const hexagon_nn_input filter = model->createDepthwiseFilterTensor(ins[1], depth_multiplier);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFusedFloatOperation(OP_DepthwiseConv2d_f, pad, bias, act,
+ {input, filter, stride}, outs);
+}
+
+bool fully_connected(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(4, ins.size(), "Need 4 inputs for float32::fully_connected");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::fully_connected");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& weights = model->createFullyConnectedWeightTensor(ins[1]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+
+ const op_type act = model->getFloatActivation(ins[3]);
+
+ // add node to graph
+ return model->addFusedFloatOperation(OP_MatMul_f, NN_PAD_NA, bias, act, {input, weights}, outs);
+}
+
+bool l2_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for float32::l2_pool_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::l2_pool_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t filter_width;
+ int32_t filter_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
+ stride_width = model->getScalar<int32_t>(ins[5]);
+ stride_height = model->getScalar<int32_t>(ins[6]);
+ filter_width = model->getScalar<int32_t>(ins[7]);
+ filter_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getFloatActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filter_width, filter_height, padding_left, padding_right,
+ padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[1]);
+ stride_width = model->getScalar<int32_t>(ins[2]);
+ stride_height = model->getScalar<int32_t>(ins[3]);
+ filter_width = model->getScalar<int32_t>(ins[4]);
+ filter_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getFloatActivation(ins[6]);
+ }
+
+ const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFloatOperationWithActivation(OP_L2Pool_f, pad, act, {input, window, stride},
+ outs);
+}
+
+bool local_response_normalization(const std::vector<uint32_t>& ins,
+ const std::vector<uint32_t>& outs, HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(5, ins.size(),
+ "Need 5 inputs for float32::local_response_normalization");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(),
+ "Need 1 output for float32::local_response_normalization");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+ const hexagon_nn_input& alpha = model->getTensor(ins[3]);
+ const hexagon_nn_input& beta = model->getTensor(ins[4]);
+
+ // create value that's [1, 1, 1, radius] with value of 1.0f
+ const int32_t radius = model->getScalar<int32_t>(ins[1]);
+ const hexagon_nn_input window = model->createTensor<float>(1, 1, 1, radius * 2 + 1, {1.0f});
+
+ // add node to graph
+ return model->addBasicOperation(OP_LRN_f, NN_PAD_NA, {input, window, bias, alpha, beta}, outs);
+}
+
+bool logistic(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::logistic");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::logistic");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Sigmoid_f, NN_PAD_NA, {input}, outs);
+}
+
+bool max_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for float32::max_pool_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::max_pool_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t filter_width;
+ int32_t filter_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
+ stride_width = model->getScalar<int32_t>(ins[5]);
+ stride_height = model->getScalar<int32_t>(ins[6]);
+ filter_width = model->getScalar<int32_t>(ins[7]);
+ filter_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getFloatActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filter_width, filter_height, padding_left, padding_right,
+ padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[1]);
+ stride_width = model->getScalar<int32_t>(ins[2]);
+ stride_height = model->getScalar<int32_t>(ins[3]);
+ filter_width = model->getScalar<int32_t>(ins[4]);
+ filter_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getFloatActivation(ins[6]);
+ }
+
+ const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFloatOperationWithActivation(OP_MaxPool_f, pad, act, {input, window, stride},
+ outs);
+}
+
+bool mul(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for float32::mul");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::mul");
+
+ // get parameters
+ const hexagon_nn_input& in1 = model->getTensor(ins[0]);
+ const hexagon_nn_input& in2 = model->getTensor(ins[1]);
+
+ const op_type act = model->getFloatActivation(ins[2]);
+
+ // add node to graph
+ return model->addFusedFloatOperation(OP_Mul_f, NN_PAD_NA, {}, act, {in1, in2}, outs);
+}
+
+bool relu(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::relu");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::relu");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Relu_f, NN_PAD_NA, {input}, outs);
+}
+
+bool relu1(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::relu1");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::relu1");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input min = model->createScalar(-1.0f);
+ const hexagon_nn_input max = model->createScalar(1.0f);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Clamp_f, NN_PAD_NA, {input, min, max}, outs);
+}
+
+bool relu6(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::relu6");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::relu6");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input max = model->createScalar(6.0f);
+
+ // add node to graph
+ return model->addBasicOperation(OP_ReluX_f, NN_PAD_NA, {input, max}, outs);
+}
+
+bool reshape(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for float32::reshape");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::reshape");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& newdims = model->getTensor(ins[1]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Reshape, NN_PAD_NA, {input, newdims}, outs);
+}
+
+bool resize_bilinear(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for float32::resize_bilinear");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::resize_bilinear");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ const int32_t width = model->getScalar<int32_t>(ins[1]);
+ const int32_t height = model->getScalar<int32_t>(ins[2]);
+
+ const hexagon_nn_input newdim = model->createValues<int32_t>({height, width});
+
+ // add node to graph
+ return model->addBasicOperation(OP_ResizeBilinear_f, NN_PAD_NA, {input, newdim}, outs);
+}
+
+bool softmax(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for float32::softmax");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::softmax");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& beta = model->getTensor(ins[1]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Softmax_f, NN_PAD_NA, {input, beta}, outs);
+}
+
+bool tanh(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::tanh");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::tanh");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Tanh_f, NN_PAD_NA, {input}, outs);
+}
+
+} // namespace float32
+
+namespace quant8_asym {
+
+bool add(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for quant8_asym::add");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::add");
+
+ // get parameters
+ const hexagon_nn_input& in1 = model->getTensor(ins[0]);
+ const hexagon_nn_input& in2 = model->getTensor(ins[1]);
+
+ const op_type act = model->getQuantizedActivation(ins[2]);
+
+ const hexagon_nn_input& in1_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& in1_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input& in2_min = model->getQuantizationMin(ins[1]);
+ const hexagon_nn_input& in2_max = model->getQuantizationMax(ins[1]);
+
+ // add node to graph
+ return model->addFusedQuant8Operation(OP_QuantizedAdd_8p8to32, NN_PAD_NA, {}, act,
+ {in1, in2, in1_min, in1_max, in2_min, in2_max}, outs);
+}
+
+bool average_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for quant8_asym::average_pool_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::average_pool_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t filter_width;
+ int32_t filter_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
+ stride_width = model->getScalar<int32_t>(ins[5]);
+ stride_height = model->getScalar<int32_t>(ins[6]);
+ filter_width = model->getScalar<int32_t>(ins[7]);
+ filter_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getQuantizedActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filter_width, filter_height, padding_left, padding_right,
+ padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[1]);
+ stride_width = model->getScalar<int32_t>(ins[2]);
+ stride_height = model->getScalar<int32_t>(ins[3]);
+ filter_width = model->getScalar<int32_t>(ins[4]);
+ filter_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getQuantizedActivation(ins[6]);
+ }
+
+ const hexagon_nn_input& in_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& in_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addQuant8OperationWithActivation(OP_QuantizedAvgPool_8, pad, act,
+ {input, in_min, in_max, window, stride}, outs);
+}
+
+bool concatenation(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_LE(3, ins.size(), "Need at least 3 inputs for quant8_asym::concatenation");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::concatenation");
+
+ const size_t numInputTensors = ins.size() - 1;
+
+ // get parameters
+ std::vector<hexagon_nn_input> inputs(numInputTensors * 3 + 1);
+ for (size_t i = 0; i < numInputTensors; ++i) {
+ inputs[i + 1 + numInputTensors * 0] = model->getTensor(ins[i]);
+ inputs[i + 1 + numInputTensors * 1] = model->getQuantizationMin(ins[i]);
+ inputs[i + 1 + numInputTensors * 2] = model->getQuantizationMax(ins[i]);
+ }
+
+ // axis being concatenated
+ const int32_t axis = model->getScalar<int32_t>(ins[numInputTensors]);
+ const int32_t dims = model->getShape(ins[0]).dimensions.size();
+ inputs[0] = model->createScalar<int32_t>(axis + (4 - dims));
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedConcat_8, NN_PAD_NA, inputs, outs);
+}
+
+bool conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for quant8_asym::conv_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::conv_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input filter = model->createConvFilterTensor(ins[1]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
+ stride_width = model->getScalar<int32_t>(ins[7]);
+ stride_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getQuantizedActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape filterShape = model->getShape(ins[1]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
+ padding_left, padding_right, padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[3]);
+ stride_width = model->getScalar<int32_t>(ins[4]);
+ stride_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getQuantizedActivation(ins[6]);
+ }
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input& filter_min = model->getQuantizationMin(ins[1]);
+ const hexagon_nn_input& filter_max = model->getQuantizationMax(ins[1]);
+ const hexagon_nn_input& bias_min = model->getQuantizationMin(ins[2]);
+ const hexagon_nn_input& bias_max = model->getQuantizationMax(ins[2]);
+
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFusedQuant8Operation(
+ OP_QuantizedConv2d_8x8to32, pad, {bias, bias_min, bias_max}, act,
+ {input, filter, input_min, input_max, filter_min, filter_max, stride}, outs);
+}
+
+bool depthwise_conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 11 || ins.size() == 8,
+ "Need 8 to 11 inputs for quant8_asym::depthwise_conv_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::depthwise_conv_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t depth_multiplier;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 11) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
+ stride_width = model->getScalar<int32_t>(ins[7]);
+ stride_height = model->getScalar<int32_t>(ins[8]);
+ depth_multiplier = model->getScalar<int32_t>(ins[9]);
+ act = model->getQuantizedActivation(ins[10]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ const Shape filterShape = model->getShape(ins[1]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
+ padding_left, padding_right, padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[3]);
+ stride_width = model->getScalar<int32_t>(ins[4]);
+ stride_height = model->getScalar<int32_t>(ins[5]);
+ depth_multiplier = model->getScalar<int32_t>(ins[6]);
+ act = model->getQuantizedActivation(ins[7]);
+ }
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input& filter_min = model->getQuantizationMin(ins[1]);
+ const hexagon_nn_input& filter_max = model->getQuantizationMax(ins[1]);
+ const hexagon_nn_input& bias_min = model->getQuantizationMin(ins[2]);
+ const hexagon_nn_input& bias_max = model->getQuantizationMax(ins[2]);
+
+ const hexagon_nn_input filter = model->createDepthwiseFilterTensor(ins[1], depth_multiplier);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addFusedQuant8Operation(
+ OP_QuantizedDepthwiseConv2d_8x8to32, pad, {bias, bias_min, bias_max}, act,
+ {input, filter, input_min, input_max, filter_min, filter_max, stride}, outs);
+}
+
+bool dequantize(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::dequantize");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::dequantize");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_Dequantize, NN_PAD_NA, {input, input_min, input_max}, outs);
+}
+
+bool fully_connected(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(4, ins.size(), "Need 4 inputs for quant8::fully_connected");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8::fully_connected");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& weights = model->createFullyConnectedWeightTensor(ins[1]);
+ const hexagon_nn_input& bias = model->getTensor(ins[2]);
+
+ const op_type act = model->getQuantizedActivation(ins[3]);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input& weights_min = model->getQuantizationMin(ins[1]);
+ const hexagon_nn_input& weights_max = model->getQuantizationMax(ins[1]);
+ const hexagon_nn_input& bias_min = model->getQuantizationMin(ins[2]);
+ const hexagon_nn_input& bias_max = model->getQuantizationMax(ins[2]);
+
+ // add node to graph
+ return model->addFusedQuant8Operation(
+ OP_QuantizedMatMul_8x8to32, NN_PAD_NA, {bias, bias_min, bias_max}, act,
+ {input, weights, input_min, input_max, weights_min, weights_max}, outs);
+}
+
+bool logistic(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::logistic");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::logistic");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+
+ // TFLite uses different max value
+ const hexagon_nn_input input_max = model->createQuantizationValue(ins[0], 256);
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedSigmoid_8, NN_PAD_NA, {input, input_min, input_max},
+ outs);
+}
+
+bool max_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
+ "Need 7 or 10 inputs for quant8_asym::max_pool_2d");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::max_pool_2d");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ // setup parameters
+ hexagon_nn_padding_type pad;
+ int32_t stride_width;
+ int32_t stride_height;
+ int32_t filter_width;
+ int32_t filter_height;
+ op_type act;
+
+ // get parameters
+ if (ins.size() == 10) {
+ const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
+ const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
+ const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
+ const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
+ stride_width = model->getScalar<int32_t>(ins[5]);
+ stride_height = model->getScalar<int32_t>(ins[6]);
+ filter_width = model->getScalar<int32_t>(ins[7]);
+ filter_height = model->getScalar<int32_t>(ins[8]);
+ act = model->getQuantizedActivation(ins[9]);
+
+ const Shape inputShape = model->getShape(ins[0]);
+ pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
+ stride_height, filter_width, filter_height, padding_left, padding_right,
+ padding_top, padding_bottom);
+ HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
+ } else {
+ pad = model->getPadding(ins[1]);
+ stride_width = model->getScalar<int32_t>(ins[2]);
+ stride_height = model->getScalar<int32_t>(ins[3]);
+ filter_width = model->getScalar<int32_t>(ins[4]);
+ filter_height = model->getScalar<int32_t>(ins[5]);
+ act = model->getQuantizedActivation(ins[6]);
+ }
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
+ const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
+
+ // add node to graph
+ return model->addQuant8OperationWithActivation(
+ OP_QuantizedMaxPool_8, pad, act, {input, input_min, input_max, window, stride}, outs);
+}
+
+bool mul(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for quant8_asym::mul");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::mul");
+
+ // get parameters
+ const hexagon_nn_input& in1 = model->getTensor(ins[0]);
+ const hexagon_nn_input& in2 = model->getTensor(ins[1]);
+
+ const op_type act = model->getQuantizedActivation(ins[2]);
+
+ const hexagon_nn_input& in1_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& in1_max = model->getQuantizationMax(ins[0]);
+ const hexagon_nn_input& in2_min = model->getQuantizationMin(ins[1]);
+ const hexagon_nn_input& in2_max = model->getQuantizationMax(ins[1]);
+
+ // add node to graph
+ return model->addFusedQuant8Operation(OP_QuantizedMul_8x8to32, NN_PAD_NA, {}, act,
+ {in1, in2, in1_min, in1_max, in2_min, in2_max}, outs);
+}
+
+bool relu(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::relu");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::relu");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedRelu_8, NN_PAD_NA, {input, input_min, input_max},
+ outs);
+}
+
+bool relu1(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::relu1");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::relu1");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input min = model->createScalar(-1.0f);
+ const hexagon_nn_input max = model->createScalar(1.0f);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedClamp_8, NN_PAD_NA,
+ {input, input_min, input_max, min, max}, outs);
+}
+
+bool relu6(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::relu6");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::relu6");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input max = model->createScalar(6.0f);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedReluX_8, NN_PAD_NA,
+ {input, input_min, input_max, max}, outs);
+}
+
+bool reshape(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for quant8_asym::reshape");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::reshape");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& newdims = model->getTensor(ins[1]);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedReshape, NN_PAD_NA,
+ {input, newdims, input_min, input_max}, outs);
+}
+
+bool softmax(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
+ HexagonModel* model) {
+ HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for quant8_asym::softmax");
+ HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::softmax");
+
+ // get parameters
+ const hexagon_nn_input& input = model->getTensor(ins[0]);
+ const hexagon_nn_input& beta = model->getTensor(ins[1]);
+
+ const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
+ const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
+
+ // add node to graph
+ return model->addBasicOperation(OP_QuantizedSoftmax_8, NN_PAD_NA,
+ {input, input_min, input_max, beta}, outs);
+}
+
+} // namespace quant8_asym
+
+} // namespace
+
+OperationTable& getOperationPrepareTable() {
+ static OperationTable table = {
+ // NOTE: the operations that are commented out via inline represent
+ // operations that are valid for the Android O NNAPI release, but are
+ // currently not implemented in HVX.
+
+ // -------------------------- 32-BIT FLOAT ----------------------------
+ // HVX is only performant when running on quantized values. Further, as
+ // an optimization, the current HVX driver will convert some floating
+ // point tensors into quantized values, perform the operation, and then
+ // convert them back to floating point. This results in a loss in
+ // precision causing some tests to fail. For these reasons, the FLOAT32
+ // operations are being temporarily disabled.
+ /*
+ {{OperationType::ADD, OperandType::TENSOR_FLOAT32}, float32::add},
+ {{OperationType::AVERAGE_POOL_2D, OperandType::TENSOR_FLOAT32}, float32::average_pool_2d},
+ {{OperationType::CONCATENATION, OperandType::TENSOR_FLOAT32}, float32::concatenation},
+ {{OperationType::CONV_2D, OperandType::TENSOR_FLOAT32}, float32::conv_2d},
+ {{OperationType::DEPTHWISE_CONV_2D, OperandType::TENSOR_FLOAT32},
+ float32::depthwise_conv_2d},
+ //{{OperationType::DEPTH_TO_SPACE, OperandType::TENSOR_FLOAT32}, float32::depth_to_space},
+ //{{OperationType::EMBEDDING_LOOKUP, OperandType::TENSOR_FLOAT32},
+ // float32::embedding_lookup},
+ //{{OperationType::FLOOR, OperandType::TENSOR_FLOAT32}, float32::floor},
+ {{OperationType::FULLY_CONNECTED, OperandType::TENSOR_FLOAT32}, float32::fully_connected},
+ //{{OperationType::HASHTABLE_LOOKUP, OperandType::TENSOR_FLOAT32},
+ // float32::hashtable_lookup},
+ //{{OperationType::L2_NORMALIZATION, OperandType::TENSOR_FLOAT32},
+ // float32::l2_normalization},
+ {{OperationType::L2_POOL_2D, OperandType::TENSOR_FLOAT32}, float32::l2_pool_2d},
+ {{OperationType::LOCAL_RESPONSE_NORMALIZATION, OperandType::TENSOR_FLOAT32},
+ float32::local_response_normalization},
+ {{OperationType::LOGISTIC, OperandType::TENSOR_FLOAT32}, float32::logistic},
+ //{{OperationType::LSH_PROJECTION, OperandType::TENSOR_FLOAT32}, float32::lsh_projection},
+ //{{OperationType::LSTM, OperandType::TENSOR_FLOAT32}, float32::lstm },
+ {{OperationType::MAX_POOL_2D, OperandType::TENSOR_FLOAT32}, float32::max_pool_2d},
+ {{OperationType::MUL, OperandType::TENSOR_FLOAT32}, float32::mul},
+ {{OperationType::RELU, OperandType::TENSOR_FLOAT32}, float32::relu},
+ {{OperationType::RELU1, OperandType::TENSOR_FLOAT32}, float32::relu1},
+ {{OperationType::RELU6, OperandType::TENSOR_FLOAT32}, float32::relu6},
+ {{OperationType::RESHAPE, OperandType::TENSOR_FLOAT32}, float32::reshape},
+ {{OperationType::RESIZE_BILINEAR, OperandType::TENSOR_FLOAT32}, float32::resize_bilinear},
+ //{{OperationType::RNN, OperandType::TENSOR_FLOAT32}, float32::rnn},
+ {{OperationType::SOFTMAX, OperandType::TENSOR_FLOAT32}, float32::softmax},
+ //{{OperationType::SPACE_TO_DEPTH, OperandType::TENSOR_FLOAT32}, float32::space_to_depth},
+ //{{OperationType::SVDF, OperandType::TENSOR_FLOAT32}, float32::svdf },
+ {{OperationType::TANH, OperandType::TENSOR_FLOAT32}, float32::tanh},
+ */
+
+ // -------------------- QUANTIZED 8-BIT ASYMMETRICAL ------------------
+ {{OperationType::ADD, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::add},
+ {{OperationType::AVERAGE_POOL_2D, OperandType::TENSOR_QUANT8_ASYMM},
+ quant8_asym::average_pool_2d},
+ {{OperationType::CONCATENATION, OperandType::TENSOR_QUANT8_ASYMM},
+ quant8_asym::concatenation},
+ {{OperationType::CONV_2D, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::conv_2d},
+ {{OperationType::DEPTHWISE_CONV_2D, OperandType::TENSOR_QUANT8_ASYMM},
+ quant8_asym::depthwise_conv_2d},
+ //{{OperationType::DEPTH_TO_SPACE, OperandType::TENSOR_QUANT8_ASYMM},
+ // quant8_asym::depth_to_space},
+ {{OperationType::DEQUANTIZE, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::dequantize},
+ //{{OperationType::EMBEDDING_LOOKUP, OperandType::TENSOR_QUANT8_ASYMM},
+ // quant8_asym::embedding_lookup},
+ {{OperationType::FULLY_CONNECTED, OperandType::TENSOR_QUANT8_ASYMM},
+ quant8_asym::fully_connected},
+ //{{OperationType::HASHTABLE_LOOKUP, OperandType::TENSOR_QUANT8_ASYMM},
+ // quant8_asym::hashtable_lookup},
+ {{OperationType::LOGISTIC, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::logistic},
+ //{{OperationType::LSH_PROJECTION, OperandType::TENSOR_QUANT8_ASYMM},
+ // quant8_asym::lsh_projection},
+ {{OperationType::MAX_POOL_2D, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::max_pool_2d},
+ {{OperationType::MUL, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::mul},
+ {{OperationType::RELU, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::relu},
+ {{OperationType::RELU1, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::relu1},
+ {{OperationType::RELU6, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::relu6},
+ {{OperationType::RESHAPE, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::reshape},
+ {{OperationType::SOFTMAX, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::softmax},
+ //{{OperationType::SPACE_TO_DEPTH, OperandType::TENSOR_QUANT8_ASYMM},
+ // quant8_asym::space_to_depth},
+ };
+
+ // The following functions are normally used by float32, but those
+ // operations have been temporarily disabled. Void explicitly marks them as
+ // unused, and prevents the compiler from throwing an error.
+ (void)float32::add;
+ (void)float32::average_pool_2d;
+ (void)float32::concatenation;
+ (void)float32::conv_2d;
+ (void)float32::depthwise_conv_2d;
+ (void)float32::fully_connected;
+ (void)float32::l2_pool_2d;
+ (void)float32::local_response_normalization;
+ (void)float32::logistic;
+ (void)float32::max_pool_2d;
+ (void)float32::mul;
+ (void)float32::relu;
+ (void)float32::relu1;
+ (void)float32::relu6;
+ (void)float32::reshape;
+ (void)float32::resize_bilinear;
+ (void)float32::softmax;
+ (void)float32::tanh;
+
+ return table;
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/HexagonUtils.cpp b/1.0/HexagonUtils.cpp
new file mode 100644
index 0000000..bab6a5e
--- /dev/null
+++ b/1.0/HexagonUtils.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "HexagonUtils.h"
+#include <hidlmemory/mapping.h>
+#include <algorithm>
+#include <numeric>
+#include <vector>
+#include "OperationsUtils.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+bool isHexagonAvailable() {
+ int version = -1;
+ Controller::getInstance().version(&version);
+ if (version != 92) {
+ LOG(INFO) << "ATTEMPTING TO RESTART NNLIB";
+ Controller::getInstance().resetNnlib();
+ Controller::getInstance().version(&version);
+ }
+ return version == 92;
+}
+
+hexagon_nn_padding_type getPadding(uint32_t pad) {
+ switch (pad) {
+ case ::android::nn::kPaddingSame:
+ return NN_PAD_SAME;
+ case ::android::nn::kPaddingValid:
+ return NN_PAD_VALID;
+ case ::android::nn::kPaddingUnknown:
+ default:
+ return NN_PAD_NA;
+ };
+}
+
+hexagon_nn_padding_type getPadding(int32_t inWidth, int32_t inHeight, int32_t strideWidth,
+ int32_t strideHeight, int32_t filterWidth, int32_t filterHeight,
+ int32_t paddingLeft, int32_t paddingRight, int32_t paddingTop,
+ int32_t paddingBottom) {
+ return getPadding(::android::nn::getPaddingScheme(inWidth, inHeight, strideWidth, strideHeight,
+ filterWidth, filterHeight, paddingLeft,
+ paddingRight, paddingTop, paddingBottom));
+}
+
+op_type getFloatActivationFunction(FusedActivationFunc act) {
+ switch (act) {
+ case FusedActivationFunc::RELU:
+ return OP_Relu_f;
+ case FusedActivationFunc::RELU1:
+ return OP_Clamp_f;
+ case FusedActivationFunc::RELU6:
+ return OP_ReluX_f;
+ case FusedActivationFunc::NONE:
+ FALLTHROUGH_INTENDED;
+ default:
+ return OP_Nop;
+ };
+}
+
+op_type getQuantizedActivationFunction(FusedActivationFunc act) {
+ switch (act) {
+ case FusedActivationFunc::RELU:
+ return OP_QuantizedRelu_8;
+ case FusedActivationFunc::RELU1:
+ return OP_QuantizedClamp_8;
+ case FusedActivationFunc::RELU6:
+ return OP_QuantizedReluX_8;
+ case FusedActivationFunc::NONE:
+ FALLTHROUGH_INTENDED;
+ default:
+ return OP_Nop;
+ };
+}
+
+uint32_t getSize(OperandType type) {
+ static const uint32_t sizes[] = {
+ 4, // FLOAT32
+ 4, // INT32
+ 4, // UINT32
+ 4, // TENSOR_FLOAT32
+ 4, // TENSOR_INT32
+ 1, // TENSOR_SYMMETRICAL_QUANT8
+ };
+ HEXAGON_SOFT_ASSERT(static_cast<uint32_t>(type) < sizeof(sizes) / sizeof(*sizes),
+ "Error: type exceeds max enum value");
+ return sizes[static_cast<uint32_t>(type)];
+}
+
+std::vector<uint32_t> getAlignedDimensions(const std::vector<uint32_t>& dims, uint32_t N) {
+ HEXAGON_SOFT_ASSERT_GE(
+ N, dims.size(),
+ "Error: constant data dimensions " << dims.size() << " exceeds alignment of " << N);
+ std::vector<uint32_t> dimensions(N - dims.size(), 1);
+ dimensions.insert(dimensions.end(), dims.begin(), dims.end());
+ return dimensions;
+}
+
+std::vector<RunTimePoolInfo> mapPools(const hidl_vec<hidl_memory>& pools) {
+ std::vector<RunTimePoolInfo> poolInfos;
+ poolInfos.reserve(pools.size());
+ bool fail = false;
+ for (const auto& pool : pools) {
+ poolInfos.emplace_back(pool, &fail);
+ }
+ HEXAGON_SOFT_ASSERT(!fail, "Error setting pools");
+ return poolInfos;
+}
+
+std::unordered_set<uint32_t> getPoolIndexes(const std::vector<RequestArgument>& inputsOutputs) {
+ std::unordered_set<uint32_t> indexes;
+ for (const RequestArgument& inputOutput : inputsOutputs) {
+ indexes.insert(inputOutput.location.poolIndex);
+ }
+ return indexes;
+}
+
+namespace {
+const uint8_t* getDataFromBlock(const hidl_vec<uint8_t>& block, uint32_t offset, uint32_t length) {
+ HEXAGON_SOFT_ASSERT_LE(offset + length, block.size(),
+ "Error: trying to copy data from outside of block bounds");
+ return block.data() + offset;
+}
+
+const uint8_t* getDataFromPool(const RunTimePoolInfo& pool, uint32_t offset,
+ [[maybe_unused]] uint32_t length) {
+ // HEXAGON_SOFT_ASSERT_LE(offset + length, pool->getSize(),
+ // "Error: trying to copy data from outside of pool bounds");
+ return pool.getBuffer() + offset;
+}
+} // anonymous namespace
+
+const uint8_t* getData(const Operand& operand, const hidl_vec<uint8_t>& block,
+ const std::vector<RunTimePoolInfo>& pools) {
+ switch (operand.lifetime) {
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ return nullptr;
+ case OperandLifeTime::MODEL_INPUT:
+ case OperandLifeTime::MODEL_OUTPUT:
+ HEXAGON_SOFT_ASSERT(false,
+ "Error: trying to retrieve data that is only known at runtime");
+ case OperandLifeTime::CONSTANT_COPY:
+ return getDataFromBlock(block, operand.location.offset, operand.location.length);
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ return getDataFromPool(pools[operand.location.poolIndex], operand.location.offset,
+ operand.location.length);
+ default:
+ HEXAGON_SOFT_ASSERT(false, "Error: unrecognized operand lifetime");
+ }
+}
+
+bool operator==(const hexagon_nn_input& lhs, const hexagon_nn_input& rhs) {
+ return lhs.src_id == rhs.src_id && lhs.output_idx == rhs.output_idx;
+}
+
+bool operator!=(const hexagon_nn_input& lhs, const hexagon_nn_input& rhs) {
+ return !(lhs == rhs);
+}
+
+bool operator==(const hexagon_nn_output& lhs, const hexagon_nn_output& rhs) {
+ return lhs.rank == rhs.rank && lhs.max_sizes[0] == rhs.max_sizes[0] &&
+ lhs.max_sizes[1] == rhs.max_sizes[1] && lhs.max_sizes[2] == rhs.max_sizes[2] &&
+ lhs.max_sizes[3] == rhs.max_sizes[3] && lhs.max_sizes[4] == rhs.max_sizes[4] &&
+ lhs.max_sizes[5] == rhs.max_sizes[5] && lhs.max_sizes[6] == rhs.max_sizes[6] &&
+ lhs.max_sizes[7] == rhs.max_sizes[7] && lhs.elementsize == rhs.elementsize &&
+ lhs.zero_offset == rhs.zero_offset && lhs.stepsize == rhs.stepsize;
+}
+
+bool operator!=(const hexagon_nn_output& lhs, const hexagon_nn_output& rhs) {
+ return !(lhs == rhs);
+}
+
+hexagon_nn_output make_hexagon_nn_output(const std::vector<uint32_t>& dims, uint32_t size) {
+ std::vector<uint32_t> alignedDims = getAlignedDimensions(dims, 4);
+ hexagon_nn_output output = {
+ .rank = std::min(8u, static_cast<uint32_t>(alignedDims.size())),
+ .max_sizes = {0, 0, 0, 0, 0, 0, 0, 0},
+ .elementsize = size,
+ .zero_offset = 0,
+ .stepsize = 0.0f,
+ };
+ for (size_t i = 0; i < alignedDims.size() && i < 8; ++i) {
+ output.max_sizes[i] = alignedDims[i];
+ }
+ return output;
+}
+
+// printers
+std::string toString(uint32_t val) {
+ return std::to_string(val);
+}
+
+std::string toString(float val) {
+ return std::to_string(val);
+}
+
+std::string toString(hexagon_nn_nn_id id) {
+ return std::to_string(static_cast<int32_t>(id));
+}
+
+std::string toString(op_type op) {
+ static const char* opText[] = {
+#define DEF_OP(NAME, ...) "OP_" #NAME,
+#include "hexagon_nn_controller/ops.def"
+#undef DEF_OP
+ };
+ return static_cast<size_t>(op) < sizeof(opText) / sizeof(char*)
+ ? opText[static_cast<size_t>(op)]
+ : "<invalid op_type>";
+}
+
+std::string toString(hexagon_nn_padding_type padding) {
+ static const char* paddingText[] = {
+ "NN_PAD_NA",
+ "NN_PAD_SAME",
+ "NN_PAD_VALID",
+ "NN_PAD_MIRROR_REFLECT",
+ "NN_PAD_MIRROR_SYMMETRIC",
+ "NN_PAD_SAME_CAFFE",
+ };
+ return static_cast<size_t>(padding) < sizeof(paddingText) / sizeof(char*)
+ ? paddingText[static_cast<size_t>(padding)]
+ : "<invalid hexagon_nn_padding_type>";
+}
+
+std::string toString(const hexagon_nn_input& input) {
+ return "hexagon_nn_input{.src_id: " + std::to_string(input.src_id) +
+ ", .output_idx: " + std::to_string(input.output_idx) + "}";
+}
+
+std::string toString(const hexagon_nn_output& output) {
+ return "hexagon_nn_output{.rank: " + std::to_string(output.rank) + ", .max_sizes: [" +
+ std::to_string(output.max_sizes[0]) + ", " + std::to_string(output.max_sizes[1]) + ", " +
+ std::to_string(output.max_sizes[2]) + ", " + std::to_string(output.max_sizes[3]) + ", " +
+ std::to_string(output.max_sizes[4]) + ", " + std::to_string(output.max_sizes[5]) + ", " +
+ std::to_string(output.max_sizes[6]) + ", " + std::to_string(output.max_sizes[7]) + "]" +
+ ", .elementsize: " + std::to_string(output.elementsize) +
+ ", .zero_offset: " + std::to_string(output.zero_offset) +
+ ", .stepsize: " + std::to_string(output.stepsize) + "}";
+}
+
+std::string toString(const hexagon_nn_tensordef& tensordef) {
+ return "hexagon_nn_tensordef{.batches: " + std::to_string(tensordef.batches) +
+ ", .height: " + std::to_string(tensordef.height) +
+ ", .width: " + std::to_string(tensordef.width) +
+ ", .depth: " + std::to_string(tensordef.depth) +
+ ", .data: " + std::to_string(reinterpret_cast<uintptr_t>(tensordef.data)) +
+ ", .dataLen: " + std::to_string(tensordef.dataLen) +
+ ", .data_valid_len: " + std::to_string(tensordef.data_valid_len) +
+ ", .unused: " + std::to_string(tensordef.unused) + "}";
+}
+
+std::string toString(const hexagon_nn_perfinfo& perfinfo) {
+ return "hexagon_nn_perfinfo{.node_id: " + std::to_string(perfinfo.node_id) +
+ ", .executions: " + std::to_string(perfinfo.executions) +
+ ", .counter_lo: " + std::to_string(perfinfo.counter_lo) +
+ ", .counter_hi: " + std::to_string(perfinfo.counter_hi) + "}";
+}
+
+std::string toString(const ::android::nn::Shape& shape) {
+ return "Shape{.type: " + toString(shape.type) +
+ ", .dimensions: " + toString(shape.dimensions.data(), shape.dimensions.size()) +
+ ", .scale: " + std::to_string(shape.scale) +
+ ", .zeroPoint: " + std::to_string(shape.offset) + "}";
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/HexagonUtils.h b/1.0/HexagonUtils.h
new file mode 100644
index 0000000..a50bb6f
--- /dev/null
+++ b/1.0/HexagonUtils.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 ANDROID_HARDWARE_V1_0_UTILS_H
+#define ANDROID_HARDWARE_V1_0_UTILS_H
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <string>
+#include <unordered_set>
+#include <vector>
+#include "CpuExecutor.h"
+#include "HexagonController.h"
+#include "OperationsUtils.h"
+#include "hexagon_nn_controller/hexagon_nn_controller.h"
+
+#define HEXAGON_SOFT_ASSERT(condition, message) \
+ if (!(condition)) { \
+ LOG(DEBUG) << __FILE__ << "::" << __LINE__ << " -- " << message; \
+ return {}; \
+ }
+
+#define HEXAGON_SOFT_ASSERT_CMP(cmp, lhs, rhs, message) \
+ HEXAGON_SOFT_ASSERT(((lhs)cmp(rhs)), "failed " #lhs " " #cmp " " #rhs " (" \
+ << (lhs) << " " #cmp " " << (rhs) << "): " message)
+
+#define HEXAGON_SOFT_ASSERT_EQ(lhs, rhs, message) HEXAGON_SOFT_ASSERT_CMP(==, lhs, rhs, message)
+#define HEXAGON_SOFT_ASSERT_NE(lhs, rhs, message) HEXAGON_SOFT_ASSERT_CMP(!=, lhs, rhs, message)
+#define HEXAGON_SOFT_ASSERT_LT(lhs, rhs, message) HEXAGON_SOFT_ASSERT_CMP(<, lhs, rhs, message)
+#define HEXAGON_SOFT_ASSERT_LE(lhs, rhs, message) HEXAGON_SOFT_ASSERT_CMP(<=, lhs, rhs, message)
+#define HEXAGON_SOFT_ASSERT_GT(lhs, rhs, message) HEXAGON_SOFT_ASSERT_CMP(>, lhs, rhs, message)
+#define HEXAGON_SOFT_ASSERT_GE(lhs, rhs, message) HEXAGON_SOFT_ASSERT_CMP(>=, lhs, rhs, message)
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+namespace hexagon {
+
+using ::android::sp;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::neuralnetworks::V1_0::FusedActivationFunc;
+using ::android::hardware::neuralnetworks::V1_0::Operand;
+using ::android::nn::RunTimePoolInfo;
+
+bool isHexagonAvailable();
+
+hexagon_nn_padding_type getPadding(uint32_t pad);
+hexagon_nn_padding_type getPadding(int32_t inWidth, int32_t inHeight, int32_t strideWidth,
+ int32_t strideHeight, int32_t filterWidth, int32_t filterHeight,
+ int32_t paddingLeft, int32_t paddingRight, int32_t paddingTop,
+ int32_t paddingBottom);
+op_type getFloatActivationFunction(FusedActivationFunc act);
+op_type getQuantizedActivationFunction(FusedActivationFunc act);
+
+uint32_t getSize(OperandType type);
+std::vector<uint32_t> getAlignedDimensions(const std::vector<uint32_t>& dims, uint32_t N);
+
+std::vector<RunTimePoolInfo> mapPools(const hidl_vec<hidl_memory>& pools);
+
+std::unordered_set<uint32_t> getPoolIndexes(const std::vector<RequestArgument>& inputsOutputs);
+
+const uint8_t* getData(const Operand& operand, const hidl_vec<uint8_t>& block,
+ const std::vector<RunTimePoolInfo>& pools);
+
+template <typename Type>
+std::vector<Type> transpose(uint32_t height, uint32_t width, const Type* input) {
+ std::vector<Type> output(height * width);
+ for (uint32_t i = 0; i < height; ++i) {
+ for (uint32_t j = 0; j < width; ++j) {
+ output[j * height + i] = input[i * width + j];
+ }
+ }
+ return output;
+}
+
+hexagon_nn_output make_hexagon_nn_output(const std::vector<uint32_t>& dims, uint32_t size);
+
+bool operator==(const hexagon_nn_input& lhs, const hexagon_nn_input& rhs);
+bool operator!=(const hexagon_nn_input& lhs, const hexagon_nn_input& rhs);
+bool operator==(const hexagon_nn_output& lhs, const hexagon_nn_output& rhs);
+bool operator!=(const hexagon_nn_output& lhs, const hexagon_nn_output& rhs);
+
+// printers
+std::string toString(uint32_t val);
+std::string toString(float val);
+std::string toString(hexagon_nn_nn_id id);
+std::string toString(op_type op);
+std::string toString(hexagon_nn_padding_type padding);
+std::string toString(const hexagon_nn_input& input);
+std::string toString(const hexagon_nn_output& output);
+std::string toString(const hexagon_nn_tensordef& tensordef);
+std::string toString(const hexagon_nn_perfinfo& perfinfo);
+std::string toString(const ::android::nn::Shape& input);
+
+template <typename Type>
+std::string toString(const Type* buffer, uint32_t count) {
+ std::string os = "[";
+ for (uint32_t i = 0; i < count; ++i) {
+ os += (i == 0 ? "" : ", ") + toString(buffer[i]);
+ }
+ return os += "]";
+}
+
+template <typename CharT, typename Traits>
+std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ const hexagon_nn_input& obj) {
+ return os << toString(obj);
+}
+
+template <typename CharT, typename Traits>
+std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ const hexagon_nn_output& obj) {
+ return os << toString(obj);
+}
+
+template <typename CharT, typename Traits>
+std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ const hexagon_nn_tensordef& obj) {
+ return os << toString(obj);
+}
+
+template <typename CharT, typename Traits>
+std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ const hexagon_nn_perfinfo& obj) {
+ return os << toString(obj);
+}
+
+template <typename CharT, typename Traits>
+std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ const ::android::nn::Shape& obj) {
+ return os << toString(obj);
+}
+
+template <typename CharT, typename Traits>
+std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
+ ErrorStatus status) {
+ return os << toString(status);
+}
+
+} // namespace hexagon
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_V1_0_UTILS_H
diff --git a/1.0/PreparedModel.cpp b/1.0/PreparedModel.cpp
new file mode 100644
index 0000000..c95d56f
--- /dev/null
+++ b/1.0/PreparedModel.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
+
+#include "PreparedModel.h"
+#include <android-base/logging.h>
+#include <thread>
+#include "HexagonUtils.h"
+#include "ValidateHal.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+
+PreparedModel::PreparedModel(const Model& neuralNetworksModel,
+ const std::shared_ptr<hexagon::Model>& hexagonModel)
+ : mNeuralNetworksModel(neuralNetworksModel), mHexagonModel(hexagonModel) {}
+
+PreparedModel::~PreparedModel() {}
+
+static void asyncExecute(const std::shared_ptr<hexagon::Model>& model, const Request& request,
+ const sp<IExecutionCallback>& callback) {
+ ErrorStatus status =
+ model->execute(request) == true ? ErrorStatus::NONE : ErrorStatus::GENERAL_FAILURE;
+ Return<void> ret = callback->notify(status);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Error in callback's return type: " << ret.description();
+ }
+}
+
+Return<ErrorStatus> PreparedModel::execute(const Request& request,
+ const sp<IExecutionCallback>& callback) {
+ if (callback.get() == nullptr) {
+ LOG(ERROR) << "invalid callback passed to execute";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+
+ if (!nn::validateRequest(request, mNeuralNetworksModel)) {
+ Return<void> ret = callback->notify(ErrorStatus::INVALID_ARGUMENT);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Error in callback's return type: " << ret.description();
+ }
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ if (!hexagon::isHexagonAvailable()) {
+ Return<void> ret = callback->notify(ErrorStatus::DEVICE_UNAVAILABLE);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Error in callback's return type: " << ret.description();
+ }
+ return ErrorStatus::DEVICE_UNAVAILABLE;
+ }
+
+ // TODO: once nnlib hanging issue is resolved, make this function
+ // asynchronous again
+ asyncExecute(mHexagonModel, request, callback);
+
+ return ErrorStatus::NONE;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/1.0/PreparedModel.h b/1.0/PreparedModel.h
new file mode 100644
index 0000000..c2edd99
--- /dev/null
+++ b/1.0/PreparedModel.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 ANDROID_HARDWARE_NEURALNETWORKS_V1_0_PREPAREDMODEL_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_PREPAREDMODEL_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include "HexagonModel.h"
+#include "hexagon_nn_controller/hexagon_nn_controller.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct PreparedModel : public IPreparedModel {
+ private:
+ PreparedModel() = delete;
+ PreparedModel(const PreparedModel&) = delete;
+ PreparedModel(PreparedModel&&) = delete;
+ PreparedModel& operator=(const PreparedModel&) = delete;
+ PreparedModel& operator=(PreparedModel&&) = delete;
+
+ public:
+ PreparedModel(const Model& neuralNetworksModel,
+ const std::shared_ptr<hexagon::Model>& hexagonModel);
+ ~PreparedModel() override;
+
+ // Methods from IPreparedModel follow.
+ Return<ErrorStatus> execute(const Request& request,
+ const sp<IExecutionCallback>& callback) override;
+
+ private:
+ Model mNeuralNetworksModel;
+ std::shared_ptr<hexagon::Model> mHexagonModel;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_PREPAREDMODEL_H
diff --git a/1.0/Service.cpp b/1.0/Service.cpp
new file mode 100644
index 0000000..f1f74e3
--- /dev/null
+++ b/1.0/Service.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+#define LOG_TAG "android.hardware.neuralnetworks@1.0-service-hvx"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <hidl/HidlTransportSupport.h>
+#include "Device.h"
+
+// Generated HIDL files
+using android::hardware::neuralnetworks::V1_0::IDevice;
+using android::hardware::neuralnetworks::V1_0::implementation::Device;
+
+int main() {
+ android::sp<IDevice> device = new Device();
+ android::hardware::configureRpcThreadpool(4, true /* will join */);
+ if (device->registerAsService("hvx") != android::OK) {
+ LOG(ERROR) << "Could not register service";
+ return 1;
+ }
+ android::hardware::joinRpcThreadpool();
+ LOG(ERROR) << "Hvx service exited!";
+ return 1;
+}
diff --git a/1.0/android.hardware.neuralnetworks@1.0-service-hvx.rc b/1.0/android.hardware.neuralnetworks@1.0-service-hvx.rc
new file mode 100644
index 0000000..538b869
--- /dev/null
+++ b/1.0/android.hardware.neuralnetworks@1.0-service-hvx.rc
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed 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.
+#
+
+service neuralnetworks_hal_service_hvx /vendor/bin/hw/android.hardware.neuralnetworks@1.0-service-hvx
+ class hal
+ user system
+ group system
diff --git a/1.0/hexagon_nn_controller/hexagon_nn_controller.h b/1.0/hexagon_nn_controller/hexagon_nn_controller.h
new file mode 100644
index 0000000..cc1aada
--- /dev/null
+++ b/1.0/hexagon_nn_controller/hexagon_nn_controller.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 HEXAGON_NN_CONTROLLER_H
+#define HEXAGON_NN_CONTROLLER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// includes
+#include "hexagon_nn_ops.h"
+
+// hexagon types
+
+typedef struct hexagon_nn_input {
+ unsigned int src_id;
+ unsigned int output_idx;
+} hexagon_nn_input;
+
+typedef struct hexagon_nn_output {
+ unsigned int rank;
+ unsigned int max_sizes[8];
+ unsigned int elementsize;
+ int zero_offset;
+ float stepsize;
+} hexagon_nn_output;
+
+typedef struct hexagon_nn_perfinfo {
+ unsigned int node_id;
+ unsigned int executions;
+ unsigned int counter_lo;
+ unsigned int counter_hi;
+} hexagon_nn_perfinfo;
+
+typedef int hexagon_nn_nn_id;
+
+typedef 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
+} hexagon_nn_padding_type;
+
+typedef 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;
+} hexagon_nn_tensordef;
+
+// interface types
+
+typedef int (*hexagon_nn_controller_init_fn)(hexagon_nn_nn_id* g);
+
+typedef int (*hexagon_nn_controller_getlog_fn)(hexagon_nn_nn_id id, unsigned char* buf,
+ unsigned int length);
+
+typedef int (*hexagon_nn_controller_snpprint_fn)(hexagon_nn_nn_id id, unsigned char* buf,
+ unsigned int length);
+
+typedef int (*hexagon_nn_controller_set_debug_level_fn)(hexagon_nn_nn_id id, int level);
+
+typedef int (*hexagon_nn_controller_prepare_fn)(hexagon_nn_nn_id id);
+
+typedef int (*hexagon_nn_controller_append_node_fn)(
+ hexagon_nn_nn_id id, unsigned int node_id, op_type operation, hexagon_nn_padding_type padding,
+ const hexagon_nn_input* inputs, unsigned int num_inputs, const hexagon_nn_output* outputs,
+ unsigned int num_outputs);
+
+typedef int (*hexagon_nn_controller_append_const_node_fn)(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,
+ unsigned int data_len);
+
+typedef int (*hexagon_nn_controller_execute_new_fn)(hexagon_nn_nn_id id,
+ const hexagon_nn_tensordef* inputs,
+ unsigned int n_inputs,
+ hexagon_nn_tensordef* outputs,
+ unsigned int n_outputs);
+
+typedef int (*hexagon_nn_controller_execute_fn)(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,
+ unsigned int data_len_in, unsigned int* batches_out,
+ unsigned int* height_out, unsigned int* width_out,
+ unsigned int* depth_out, unsigned char* data_out,
+ unsigned int data_out_max,
+ unsigned int* data_out_size);
+
+typedef int (*hexagon_nn_controller_teardown_fn)(hexagon_nn_nn_id id);
+
+typedef int (*hexagon_nn_controller_get_perfinfo_fn)(hexagon_nn_nn_id id,
+ hexagon_nn_perfinfo* info_out,
+ unsigned int info_out_len,
+ unsigned int* n_items_out);
+
+typedef int (*hexagon_nn_controller_reset_perfinfo_fn)(hexagon_nn_nn_id id, unsigned int event);
+
+typedef int (*hexagon_nn_controller_version_fn)(int* ver);
+
+typedef int (*hexagon_nn_controller_last_execution_cycles_fn)(hexagon_nn_nn_id id,
+ unsigned int* cycles_lo,
+ unsigned int* cycles_hi);
+
+typedef int (*hexagon_nn_controller_GetHexagonBinaryVersion_fn)(int* ver);
+
+typedef int (*hexagon_nn_controller_PrintLog_fn)(const unsigned char* data_in,
+ unsigned int data_in_len);
+
+typedef int (*hexagon_nn_controller_op_name_to_id_fn)(const char* name, unsigned int* id);
+
+typedef int (*hexagon_nn_controller_op_id_to_name_fn)(const unsigned int id, char* name,
+ int name_len);
+
+typedef int (*hexagon_nn_controller_disable_dcvs_fn)();
+
+typedef int (*hexagon_nn_controller_set_powersave_level_fn)(unsigned int level);
+
+typedef int (*hexagon_nn_controller_config_fn)();
+
+typedef unsigned int (*hexagon_nn_controller_get_dsp_offset_fn)();
+
+typedef int (*hexagon_nn_controller_boost_fn)(int bus_usage);
+
+typedef int (*hexagon_nn_controller_slow_fn)();
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // HEXAGON_NN_CONTROLLER_H
diff --git a/1.0/hexagon_nn_controller/hexagon_nn_ops.h b/1.0/hexagon_nn_controller/hexagon_nn_ops.h
new file mode 100644
index 0000000..282095b
--- /dev/null
+++ b/1.0/hexagon_nn_controller/hexagon_nn_ops.h
@@ -0,0 +1,58 @@
+
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef HEXAGON_NN_GRAPH_OPS_H
+#define HEXAGON_NN_GRAPH_OPS_H 1
+/*
+ *
+ * Now that that's out of the way, let's get to the good stuff.
+ *
+ * This defines common types used.
+ */
+
+/**
+ * This file comes from:
+ * https://source.codeaurora.org/quic/hexagon_nn/nnlib/tree/interface/hexagon_nn_ops.h
+ */
+
+#define DEF_OP(NAME,...) OP_##NAME,
+typedef enum op_type_enum {
+#include "ops.def"
+ NN_OPS_MAX
+} op_type;
+#undef DEF_OP
+
+#endif
+
diff --git a/1.0/hexagon_nn_controller/ops.def b/1.0/hexagon_nn_controller/ops.def
new file mode 100644
index 0000000..28e0b8f
--- /dev/null
+++ b/1.0/hexagon_nn_controller/ops.def
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * '
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * You probably want to
+ *
+ * ## ##### #####
+ * # # # # # #
+ * # # # # # #
+ * ###### # # # #
+ * # # # # # #
+ * # # ##### #####
+ *
+ *
+ * # # #### ##### ###### ####
+ * ## # # # # # # #
+ * # # # # # # # ##### ####
+ * # # # # # # # # #
+ * # ## # # # # # # #
+ * # # #### ##### ###### ####
+ *
+ *
+ * ## #####
+ * # # #
+ * # # #
+ * ###### #
+ * # # #
+ * # # #
+ *
+ *
+ * ##### # # ######
+ * # # # #
+ * # ###### #####
+ * # # # #
+ * # # # #
+ * # # # ######
+ *
+ *
+ * ###### # # #####
+ * # ## # # #
+ * ##### # # # # #
+ * # # # # # #
+ * # # ## # #
+ * ###### # # #####
+ *
+ * otherwise the interface becomes incompatible.
+ */
+
+/**
+ * This file comes from:
+ * https://source.codeaurora.org/quic/hexagon_nn/nnlib/tree/interface/ops.def
+ */
+
+DEF_OP(INPUT)
+DEF_OP(OUTPUT)
+DEF_OP(Nop)
+DEF_OP(Const)
+DEF_OP(Check)
+DEF_OP(Close_f)
+DEF_OP(Close_quint8)
+DEF_OP(Close_q_quint8)
+DEF_OP(Close_int32)
+DEF_OP(Close_qint32)
+DEF_OP(PPrint_8)
+DEF_OP(PPrint_32)
+DEF_OP(PPrint_f)
+DEF_OP(PreFree)
+DEF_OP(Flatten)
+
+#ifndef DEF_OP_WREF
+#define DEF_OP_WREF(NAME) DEF_OP(NAME) DEF_OP(NAME##_ref)
+#define __SELF_DEF_OP_WREF
+#endif
+
+DEF_OP_WREF(QuantizedConv2d_8x8to32)
+DEF_OP_WREF(QuantizedMatMul_8x8to32)
+DEF_OP_WREF(QuantizeDownAndShrinkRange_32to8)
+DEF_OP_WREF(QuantizedRelu_8)
+DEF_OP_WREF(QuantizedReluX_8)
+DEF_OP_WREF(QuantizedMaxPool_8)
+DEF_OP_WREF(QuantizedAvgPool_8)
+DEF_OP_WREF(QuantizedL2Pool_8)
+DEF_OP_WREF(QuantizedConcat_8)
+DEF_OP_WREF(QuantizedBiasAdd_8p8to32)
+DEF_OP_WREF(Min_f)
+DEF_OP_WREF(Max_f)
+DEF_OP_WREF(Quantize)
+DEF_OP_WREF(Dequantize)
+DEF_OP_WREF(Supernode_8x8p8to8)
+
+DEF_OP(QuantizedFlatten)
+DEF_OP(Softmax_f)
+DEF_OP(Conv2d_f)
+DEF_OP(MatMul_f)
+DEF_OP(Relu_f)
+DEF_OP(ReluX_f)
+DEF_OP(AvgPool_f)
+DEF_OP(L2Pool_f)
+DEF_OP(MaxPool_f)
+DEF_OP(Concat_f)
+DEF_OP(BiasAdd_f)
+DEF_OP(LRN_f)
+
+DEF_OP(Variable)
+DEF_OP(Assign)
+DEF_OP(Reshape)
+DEF_OP(QuantizedReshape)
+DEF_OP(Tanh_f)
+DEF_OP(Sigmoid_f)
+DEF_OP(Slice_8)
+DEF_OP(Slice_f)
+DEF_OP(QuantizedSlice_8)
+DEF_OP(Add_f)
+DEF_OP(Mul_f)
+DEF_OP(Minimum_f)
+DEF_OP(Maximum_f)
+
+DEF_OP_WREF(Requantize_32to8)
+DEF_OP_WREF(RequantizationRange_32)
+
+DEF_OP(Neg_f)
+DEF_OP(Sub_f)
+DEF_OP(AddN_f)
+DEF_OP(Range_int32)
+DEF_OP(Rank_int32)
+DEF_OP(Transpose_int32)
+DEF_OP(Transpose_f)
+DEF_OP(InstanceNorm_f)
+DEF_OP_WREF(QuantizedInstanceNorm_8)
+DEF_OP(Sub_int32)
+DEF_OP(Add_int32)
+DEF_OP(Split_f)
+DEF_OP(Dequantize_qint32_f)
+DEF_OP(PRelu_f)
+DEF_OP_WREF(QuantizedPRelu_8)
+DEF_OP(Sum_f)
+DEF_OP(Prod_f)
+DEF_OP(Mul_int32)
+DEF_OP(LogicalAnd_int32)
+DEF_OP(LogicalOr_int32)
+DEF_OP(LogicalXor_int32)
+DEF_OP(Shape_int32)
+DEF_OP(Pack_int32)
+DEF_OP(MirrorPad_f)
+DEF_OP(ResizeNearestNeighbor_f)
+DEF_OP(StridedSlice_int32)
+DEF_OP(StridedSlice_f)
+DEF_OP(ExpandDims_int32)
+DEF_OP(ExpandDims_f)
+
+DEF_OP(LogSoftmax_f)
+DEF_OP(Split_int32)
+DEF_OP(QuantizedSplit_8)
+
+DEF_OP(Deconv_f)
+DEF_OP_WREF(QuantizedDeconv_8x8to32)
+
+DEF_OP_WREF(QuantizedMul_8x8to32)
+DEF_OP_WREF(QuantizedAdd_8p8to32)
+DEF_OP_WREF(QuantizedSigmoid_8)
+DEF_OP_WREF(QuantizedTanh_8)
+DEF_OP_WREF(QuantizedSoftmax_8)
+DEF_OP_WREF(QuantizedLRN_8)
+DEF_OP_WREF(Quantizedpad2d_frame_8p)
+DEF_OP_WREF(QuantizedSub_8p8to32)
+DEF_OP_WREF(QuantizedMaximum_8)
+DEF_OP_WREF(QuantizedMinimum_8)
+
+DEF_OP(Pad_f)
+DEF_OP(SpaceToBatchND_f)
+DEF_OP(BatchToSpaceND_f)
+DEF_OP(QuantizedPad_8)
+DEF_OP(ResizeBilinear_f)
+DEF_OP(ConcatV2_f)
+DEF_OP(ConcatV2_int32)
+DEF_OP(Prod_int32)
+DEF_OP(Slice_int32)
+
+DEF_OP(QuantizedAdd_8p8to8)
+DEF_OP(QuantizedResizeBilinear_8)
+DEF_OP(Supernode_8x8p8to8_d32)
+DEF_OP(Convert_to_d32)
+DEF_OP(Convert_from_d32)
+DEF_OP_WREF(QuantizedMaxPool_8_d32)
+DEF_OP_WREF(QuantizedConcat_8_d32)
+DEF_OP_WREF(QuantizedAvgPool_8_d32)
+
+DEF_OP(Sink)
+
+DEF_OP_WREF(QuantizedPRelu_8_d32)
+DEF_OP_WREF(AutoQuantize)
+DEF_OP_WREF(QuantizedDepthwiseConv2d_8x8to32)
+DEF_OP(DepthwiseConv2d_f)
+DEF_OP(DepthwiseSupernode_8x8p8to8)
+DEF_OP(DepthwiseSupernode_8x8p8to8_d32)
+
+DEF_OP_WREF(QuantizedMul_8x8to8_d32)
+
+DEF_OP(FullyConnected_u8)
+#if 0
+DEF_OP_WREF(QuantizedFC_8x8p8to8)
+#endif
+
+DEF_OP_WREF(QuantizedAdd_8p8to8_d32)
+
+DEF_OP_WREF(QuantizedClamp_8)
+DEF_OP(Clamp_f)
+DEF_OP(QuantizeForTest_d32)
+DEF_OP(Close_d32)
+DEF_OP_WREF(QuantizedSub_8p8to8_d32)
+
+DEF_OP(InputSupernode_8x8p8to8_outd32)
+DEF_OP(QuantizedLRN_8_d32)
+DEF_OP_WREF(QuantizedBiasAdd_32p32to32)
+DEF_OP_WREF(Quantize_int32)
+
+DEF_OP(Supernode_8x8p32to8)
+DEF_OP(DepthwiseSupernode_8x8p32to8)
+DEF_OP(Supernode_8x8p32to8_d32)
+DEF_OP(DepthwiseSupernode_8x8p32to8_d32)
+DEF_OP(InputSupernode_8x8p32to8_outd32)
+
+DEF_OP(PPrint_8_d32)
+DEF_OP(PPrintWithPadding_8_d32)
+DEF_OP_WREF(AutoQuantize_d32)
+
+DEF_OP_WREF(QuantizedTanh_8_d32)
+DEF_OP_WREF(QuantizedSigmoid_8_d32)
+DEF_OP_WREF(QuantizedSoftmax_8_d32)
+
+
+DEF_OP_WREF(QuantizedL2Pool_8_d32)
+
+DEF_OP(Gather_f)
+DEF_OP(Gather_int32)
+DEF_OP(Gather_8)
+DEF_OP(Table_f)
+DEF_OP(Table_int32)
+DEF_OP(Table_8)
+
+DEF_OP(FillPadding_8_d32)
+DEF_OP(QuantizedResizeBilinear_8_d32)
+#ifdef __SELF_DEF_OP_WREF
+#undef __SELF_DEF_OP_WREF
+#undef DEF_OP_WREF
+#endif
+
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..eba2a93
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+subdirs=["neuralnetworks/hvxservice/1.0"]
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..14b5e8b
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed 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.
+#
+
+[Options]
+ignore_merged_commits = true
+
+[Builtin Hooks]
+clang_format = true