Snapdragon Neural Processing Engine SDK
Reference Guide
|
This section describes the process of creating a UDO package from a simple text specification of a user-defined operation using the snpe-udo-package-generator. From the SNPE API standpoint, a UDO package consists of a registration library and one or more implementation libraries. As such, while a user can create a UDO package independent of this prescription, this section describes the process of creating a partially defined UDO package which can be easily implemented and compiled to produce the relevant libraries.
To generate a package using SNPE tools, it is necessary to create a UDO configuration describing the operation and the package details. See Defining a UDO Package for more information. Once a configuration has been specified to adequately represent the desired UDO, it can be supplied as an argument to the SNPE UDO package generator tool described in snpe-udo-package-generator. The intention of the tool is to generate partial skeleton code to aid rapid prototyping. This section describes the usage of the package generator tool and the artifacts it generates.
In order to run the snpe-udo-package-generator, the user is expected to have followed the setup instructions at SNPE Setup. The tool also has a dependency on the Mako Template Library, which can be found here: https://www.makotemplates.org/download.html. Additionally, we need an extracted QNN-SDK (no need of QNN-SDK setup) for generating the skeleton code. For QNN-SDK details, refer to the QNN documentation at $QNN_SDK_ROOT/docs/index.html
page, where QNN_SDK_ROOT
is the location of the QNN-SDK installation. Set the $QNN_SDK_ROOT
to the unzipped QNN-SDK location. Once setup is complete, the following command can be used to generate a package:
snpe-udo-package-generator -p <my_config.json> -o <my-dir>
The above command will create a UDO package which will be a directory composed of skeleton code and build files that can be used to compile the package contents into stand-alone shared libraries. The config file referenced in UDO Tutorial has been used to generate the udo package contents below:
|-- Makefile |-- common.mk |-- config | `-- softmax.json |-- include | |-- SoftmaxUdoPackageCpuImplValidationFunctions.hpp | |-- SoftmaxUdoPackageDspImplValidationFunctions.hpp | |-- SoftmaxUdoPackageGpuImplValidationFunctions.hpp | `-- utils | |-- IUdoOpDefinition.hpp | |-- UdoDspShared.h | |-- UdoMacros.hpp | |-- UdoOperation.hpp | `-- UdoUtil.hpp `-- jni |-- Android.mk |-- Application.mk `-- src |-- CPU | |-- Makefile | |-- makefiles | | |-- Android.mk | | |-- Application.mk | | `-- Makefile.linux-x86_64 | `-- src | |-- SoftmaxUdoPackageInterface.cpp | |-- ops | | `-- Softmax.cpp | `-- utils | |-- BackendUtils.hpp | |-- CPU | | |-- CpuBackendUtils.cpp | | `-- CpuBackendUtils.hpp | `-- CustomOpUtils.hpp |-- DSP | |-- Makefile | |-- include | | `-- DspOps.hpp | `-- src | |-- SoftmaxUdoPackageInterface.cpp | `-- ops | `-- Softmax.cpp |-- DSP_V68 | |-- Makefile | |-- SoftmaxImplLibDsp.cpp | `-- SoftmaxUdoPackageImplLibDsp.cpp |-- GPU | |-- Makefile | |-- include | | |-- GpuCustomOpPackage.hpp | | `-- Operation.hpp | |-- makefiles | | |-- Android.mk | | `-- Application.mk | `-- src | |-- GpuCustomOpPackage.cpp | |-- SoftmaxUdoPackageInterface.cpp | `-- ops | `-- Softmax.cpp |-- reg | |-- Makefile | |-- SoftmaxUdoPackageCpuImplValidationFunctions.cpp | |-- SoftmaxUdoPackageDspImplValidationFunctions.cpp | |-- SoftmaxUdoPackageGpuImplValidationFunctions.cpp | `-- SoftmaxUdoPackageRegLib.cpp `-- utils `-- UdoUtil.cpp
This section and the following sub-sections cover the source code generated in a package using the package contents displayed in Generating UDO Skeleton Code. When finalized, a UDO package is expected to contain a registration library and one or more implementation libraries. To produce the registration library, the source code in jni/src/reg is compiled. The implementation library is compiled using source code from each core-type specific directory. Recall that the package created by the tool will still need to be implemented. The following subsections will address the files that need to be implemented. All generated source code will have the tag Auto-generated in the header. The source code is considered partially complete in the generation stage, and it is the user's responsibility to implement certain files as needed to ensure proper compatibility and functionality with the SNPE API. All code to be implemented will have the tag add code here in the body to indicate that it needs to be implemented. Note that all libraries link against the C++ utils source code.
As mentioned previously, the registration library is created from source code in jni/src/reg. The directory contains a Makefile to compile the package, one or more validation files separated per runtime, and the package specific file: SoftMaxUdoPackageRegLib.cpp which contains the function symbols that get resolved by the SNPE UDO API when the library is opened. The registration library file contains API calls that provide the SNPE UDO API with information about the nature of the operations in the model, as well as the implementation libraries they belong to. The user is only expected to edit the validation files, which are designed to contain any optional validation that a user would like to implement for the operations contained in the library. Users should note that implementing validation is at their discretion. A sample snippet from the validation file used in the Softmax example above is shown below:
SnpeUdo_ErrorType_t SoftmaxCpuValidationFunction::validateOperation(SnpeUdo_OpDefinition_t* def) { /** * add code here */ return SNPE_UDO_NO_ERROR; }
The implementation library is created per core-type, from source code that lives under the core-type specific directory within jni/src. Using the CPU runtime as an example, the jni/src/CPU directory contains a Makefile to build the CPU implementation library, a package-specific source file: SoftmaxUdoPackageInterface.cpp for all operations to be contained in the library, and a per operation source file: Softmax.cpp that should contain the runtime implementation. As in the registration case, the package-specific source file should not be edited in the general case. Similarly this file contains methods that return information about the operations contained in the implementation library, and methods that act as a layer of indirection above the code that is ultimately executed in the per operation file. In the CPU case, the three methods in Softmax.cpp namely: finalize, execute, and free are the user's responsibility to edit. Note these methods create the operation, execute its implementation, and free the operation respectively. As such, these are completely determined by the user. A sample generated version of the implementation library is included below:
Qnn_ErrorHandle_t execute(CustomOp* operation) { /** * Add code here **/ return QNN_SUCCESS; } Qnn_ErrorHandle_t finalize(const CustomOp* operation) { QNN_CUSTOM_BE_ENSURE_EQ(operation->numInput(), 1, QNN_OP_PACKAGE_ERROR_VALIDATION_FAILURE) QNN_CUSTOM_BE_ENSURE_EQ(operation->numOutput(), 1, QNN_OP_PACKAGE_ERROR_VALIDATION_FAILURE) /** * Add code here **/ return QNN_SUCCESS; } Qnn_ErrorHandle_t free(CustomOp& operation) { /** * Add code here **/ return QNN_SUCCESS; }