summaryrefslogtreecommitdiff
path: root/Blink/Drivers/CMSIS/NN/Include
diff options
context:
space:
mode:
Diffstat (limited to 'Blink/Drivers/CMSIS/NN/Include')
-rw-r--r--Blink/Drivers/CMSIS/NN/Include/arm_nn_tables.h59
-rw-r--r--Blink/Drivers/CMSIS/NN/Include/arm_nnfunctions.h1010
-rw-r--r--Blink/Drivers/CMSIS/NN/Include/arm_nnsupportfunctions.h202
3 files changed, 1271 insertions, 0 deletions
diff --git a/Blink/Drivers/CMSIS/NN/Include/arm_nn_tables.h b/Blink/Drivers/CMSIS/NN/Include/arm_nn_tables.h
new file mode 100644
index 0000000..9357424
--- /dev/null
+++ b/Blink/Drivers/CMSIS/NN/Include/arm_nn_tables.h
@@ -0,0 +1,59 @@
+/* ----------------------------------------------------------------------
+ * Project: CMSIS NN Library
+ * Title: arm_nn_tables.h
+ * Description: Extern declaration for NN tables
+ *
+ * $Date: 17. January 2018
+ * $Revision: V.1.0.0
+ *
+ * Target Processor: Cortex-M cores
+ * -------------------------------------------------------------------- */
+/*
+ * Copyright (C) 2010-2018 Arm Limited or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ *
+ * 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 _ARM_NN_TABLES_H
+#define _ARM_NN_TABLES_H
+
+#include "arm_math.h"
+
+/**
+* @brief tables for various activation functions
+*
+*/
+
+extern const q15_t sigmoidTable_q15[256];
+extern const q7_t sigmoidTable_q7[256];
+
+extern const q7_t tanhTable_q7[256];
+extern const q15_t tanhTable_q15[256];
+
+ /**
+ * @brief 2-way tables for various activation functions
+ *
+ * 2-way table, H table for value larger than 1/4
+ * L table for value smaller than 1/4, H table for remaining
+ * We have this only for the q15_t version. It does not make
+ * sense to have it for q7_t type
+ */
+extern const q15_t sigmoidHTable_q15[192];
+extern const q15_t sigmoidLTable_q15[128];
+
+extern const q15_t sigmoidLTable_q15[128];
+extern const q15_t sigmoidHTable_q15[192];
+
+#endif /* ARM_NN_TABLES_H */
diff --git a/Blink/Drivers/CMSIS/NN/Include/arm_nnfunctions.h b/Blink/Drivers/CMSIS/NN/Include/arm_nnfunctions.h
new file mode 100644
index 0000000..96c59c2
--- /dev/null
+++ b/Blink/Drivers/CMSIS/NN/Include/arm_nnfunctions.h
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2010-2018 Arm Limited or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ *
+ * 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.
+ */
+
+/* ----------------------------------------------------------------------
+ * Project: CMSIS NN Library
+ * Title: arm_nnfunctions.h
+ * Description: Public header file for CMSIS NN Library
+ *
+ * $Date: 13. July 2018
+ * $Revision: V.1.0.0
+ *
+ * Target Processor: Cortex-M cores
+ * -------------------------------------------------------------------- */
+
+/**
+ \mainpage CMSIS NN Software Library
+ *
+ * Introduction
+ * ------------
+ *
+ * This user manual describes the CMSIS NN software library,
+ * a collection of efficient neural network kernels developed to maximize the
+ * performance and minimize the memory footprint of neural networks on Cortex-M processor cores.
+ *
+ * The library is divided into a number of functions each covering a specific category:
+ * - Neural Network Convolution Functions
+ * - Neural Network Activation Functions
+ * - Fully-connected Layer Functions
+ * - Neural Network Pooling Functions
+ * - Softmax Functions
+ * - Neural Network Support Functions
+ *
+ * The library has separate functions for operating on different weight and activation data
+ * types including 8-bit integers (q7_t) and 16-bit integers (q15_t). The descrition of the
+ * kernels are included in the function description. The implementation details are also
+ * described in this paper [1].
+ *
+ * Block Diagram
+ * --------
+ * \image html CMSIS-NN-OVERVIEW.PNG
+ *
+ * Examples
+ * --------
+ *
+ * The library ships with a number of examples which demonstrate how to use the library functions.
+ *
+ * Pre-processor Macros
+ * ------------
+ *
+ * Each library project have differant pre-processor macros.
+ *
+ * - ARM_MATH_DSP:
+ *
+ * Define macro ARM_MATH_DSP, If the silicon supports DSP instructions.
+ *
+ * - ARM_MATH_BIG_ENDIAN:
+ *
+ * Define macro ARM_MATH_BIG_ENDIAN to build the library for big endian targets. By default library builds for little endian targets.
+ *
+ * - ARM_NN_TRUNCATE:
+ *
+ * Define macro ARM_NN_TRUNCATE to use floor instead of round-to-the-nearest-int for the computation.
+ *
+ * Copyright Notice
+ * ------------
+ *
+ * Copyright (C) 2010-2018 Arm Limited. All rights reserved.
+ *
+ * [1] CMSIS-NN: Efficient Neural Network Kernels for Arm Cortex-M CPUs https://arxiv.org/abs/1801.06601
+ */
+
+/**
+ * @defgroup groupNN Neural Network Functions
+ * These functions perform basic operations for neural network layers.
+ */
+
+#ifndef _ARM_NNFUNCTIONS_H
+#define _ARM_NNFUNCTIONS_H
+
+#include "arm_nnsupportfunctions.h"
+#include "arm_nn_tables.h"
+
+#define USE_INTRINSIC
+
+//#define ARM_NN_TRUNCATE /* This config the rounding model to floor or round to the nearest int */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @defgroup NNConv Neural Network Convolution Functions
+ *
+ * Perform convolution layer
+ *
+ * The convolution is implemented in 2 steps: im2col and GEMM
+ *
+ * im2col is a process of converting each patch of image data into
+ * a column. After im2col, the convolution is computed as matrix-matrix
+ * multiplication.
+ *
+ * To reduce the memory footprint, the im2col is performed partially.
+ * Each iteration, only a few column (i.e., patches) are generated and
+ * computed with GEMM kernels similar to CMSIS-DSP arm_mat_mult functions.
+ *
+ */
+
+ /**
+ * @brief Basic Q7 convolution function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_convolve_HWC_q7_basic(const q7_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Basic Q7 convolution function (non-sqaure shape)
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in_x input tensor dimention x
+ * @param[in] dim_im_in_y input tensor dimention y
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel_x filter kernel size x
+ * @param[in] dim_kernel_y filter kernel size y
+ * @param[in] padding_x padding size x
+ * @param[in] padding_y padding size y
+ * @param[in] stride_x convolution stride x
+ * @param[in] stride_y convolution stride y
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out_x output tensor dimension x
+ * @param[in] dim_im_out_y output tensor dimension y
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ */
+
+ arm_status arm_convolve_HWC_q7_basic_nonsquare(const q7_t * Im_in,
+ const uint16_t dim_im_in_x,
+ const uint16_t dim_im_in_y,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel_x,
+ const uint16_t dim_kernel_y,
+ const uint16_t padding_x,
+ const uint16_t padding_y,
+ const uint16_t stride_x,
+ const uint16_t stride_y,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out_x,
+ const uint16_t dim_im_out_y,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Basic Q15 convolution function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_convolve_HWC_q15_basic(const q15_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const q15_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const q15_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q15_t * Im_out,
+ const uint16_t dim_im_out,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Fast Q7 convolution function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This function is the version with full list of optimization tricks, but with
+ * some contraints:
+ * ch_im_in is multiple of 4
+ * ch_im_out is multiple of 2
+ */
+
+ arm_status arm_convolve_HWC_q7_fast(const q7_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Fast Q7 convolution function (non-sqaure shape)
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in_x input tensor dimention x
+ * @param[in] dim_im_in_y input tensor dimention y
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel_x filter kernel size x
+ * @param[in] dim_kernel_y filter kernel size y
+ * @param[in] padding_x padding size x
+ * @param[in] padding_y padding size y
+ * @param[in] stride_x convolution stride x
+ * @param[in] stride_y convolution stride y
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out_x output tensor dimension x
+ * @param[in] dim_im_out_y output tensor dimension y
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This function is the version with full list of optimization tricks, but with
+ * some contraints:
+ * ch_im_in is multiple of 4
+ * ch_im_out is multiple of 2
+ */
+
+ arm_status arm_convolve_HWC_q7_fast_nonsquare(const q7_t * Im_in,
+ const uint16_t dim_im_in_x,
+ const uint16_t dim_im_in_y,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel_x,
+ const uint16_t dim_kernel_y,
+ const uint16_t padding_x,
+ const uint16_t padding_y,
+ const uint16_t stride_x,
+ const uint16_t stride_y,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out_x,
+ const uint16_t dim_im_out_y,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Fast Q7 version of 1x1 convolution (non-sqaure shape)
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in_x input tensor dimention x
+ * @param[in] dim_im_in_y input tensor dimention y
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel_x filter kernel size x
+ * @param[in] dim_kernel_y filter kernel size y
+ * @param[in] padding_x padding size x
+ * @param[in] padding_y padding size y
+ * @param[in] stride_x convolution stride x
+ * @param[in] stride_y convolution stride y
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out_x output tensor dimension x
+ * @param[in] dim_im_out_y output tensor dimension y
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This function implement convolution with 1x1 kernel size (i.e., dim_kernel_x=1
+ * and dim_kernel_y=1). It can be used for
+ * second half of MobileNets after depthwise separable convolution.
+ *
+ * This function is the version with full list of optimization tricks, but with
+ * some contraints:
+ * ch_im_in is multiple of 4
+ * ch_im_out is multiple of 2
+ */
+ arm_status arm_convolve_1x1_HWC_q7_fast_nonsquare(const q7_t * Im_in,
+ const uint16_t dim_im_in_x,
+ const uint16_t dim_im_in_y,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel_x,
+ const uint16_t dim_kernel_y,
+ const uint16_t padding_x,
+ const uint16_t padding_y,
+ const uint16_t stride_x,
+ const uint16_t stride_y,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out_x,
+ const uint16_t dim_im_out_y,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Q7 version of convolution for RGB image
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This kernel is written exclusively for convolution with ch_im_in
+ * equals 3. This applies on the first layer of CNNs which has input
+ * image with RGB format.
+ */
+
+ arm_status arm_convolve_HWC_q7_RGB(const q7_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Fast Q15 convolution function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This function is the version with full list of optimization tricks, but with
+ * some contraints:
+ * ch_im_in is multiple of 2
+ * ch_im_out is multiple of 2
+ */
+
+ arm_status arm_convolve_HWC_q15_fast(const q15_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const q15_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const q15_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q15_t * Im_out,
+ const uint16_t dim_im_out,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Fast Q15 convolution function (non-sqaure shape)
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in_x input tensor dimention x
+ * @param[in] dim_im_in_y input tensor dimention y
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel_x filter kernel size x
+ * @param[in] dim_kernel_y filter kernel size y
+ * @param[in] padding_x padding size x
+ * @param[in] padding_y padding size y
+ * @param[in] stride_x convolution stride x
+ * @param[in] stride_y convolution stride y
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out_x output tensor dimension x
+ * @param[in] dim_im_out_y output tensor dimension y
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * @details
+ *
+ * <b>Buffer size:</b>
+ *
+ * bufferA size: 2*ch_im_in*dim_kernel*dim_kernel
+ *
+ * bufferB size: 0
+ *
+ * <b>Input dimension constraints:</b>
+ *
+ * ch_im_in is multiple of 2
+ *
+ * ch_im_out is multipe of 2
+ *
+ */
+
+ arm_status
+ arm_convolve_HWC_q15_fast_nonsquare(const q15_t * Im_in,
+ const uint16_t dim_im_in_x,
+ const uint16_t dim_im_in_y,
+ const uint16_t ch_im_in,
+ const q15_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel_x,
+ const uint16_t dim_kernel_y,
+ const uint16_t padding_x,
+ const uint16_t padding_y,
+ const uint16_t stride_x,
+ const uint16_t stride_y,
+ const q15_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q15_t * Im_out,
+ const uint16_t dim_im_out_x,
+ const uint16_t dim_im_out_y,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Q7 depthwise separable convolution function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This function is the version with full list of optimization tricks, but with
+ * some contraints:
+ * ch_im_in is multiple of 2
+ * ch_im_out is multiple of 2
+ */
+
+ arm_status arm_depthwise_separable_conv_HWC_q7(const q7_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+ /**
+ * @brief Q7 depthwise separable convolution function (non-square shape)
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in_x input tensor dimention x
+ * @param[in] dim_im_in_y input tensor dimention y
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] wt pointer to kernel weights
+ * @param[in] ch_im_out number of filters, i.e., output tensor channels
+ * @param[in] dim_kernel_x filter kernel size x
+ * @param[in] dim_kernel_y filter kernel size y
+ * @param[in] padding_x padding sizes x
+ * @param[in] padding_y padding sizes y
+ * @param[in] stride_x convolution stride x
+ * @param[in] stride_y convolution stride y
+ * @param[in] bias pointer to bias
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in,out] Im_out pointer to output tensor
+ * @param[in] dim_im_out_x output tensor dimension x
+ * @param[in] dim_im_out_y output tensor dimension y
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] bufferB pointer to buffer space for output
+ * @return The function returns either
+ * <code>ARM_MATH_SIZE_MISMATCH</code> or <code>ARM_MATH_SUCCESS</code> based on the outcome of size checking.
+ *
+ * This function is the version with full list of optimization tricks, but with
+ * some contraints:
+ * ch_im_in is multiple of 2
+ * ch_im_out is multiple of 2
+ */
+ arm_status arm_depthwise_separable_conv_HWC_q7_nonsquare(const q7_t * Im_in,
+ const uint16_t dim_im_in_x,
+ const uint16_t dim_im_in_y,
+ const uint16_t ch_im_in,
+ const q7_t * wt,
+ const uint16_t ch_im_out,
+ const uint16_t dim_kernel_x,
+ const uint16_t dim_kernel_y,
+ const uint16_t padding_x,
+ const uint16_t padding_y,
+ const uint16_t stride_x,
+ const uint16_t stride_y,
+ const q7_t * bias,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ q7_t * Im_out,
+ const uint16_t dim_im_out_x,
+ const uint16_t dim_im_out_y,
+ q15_t * bufferA,
+ q7_t * bufferB);
+
+
+/**
+ * @defgroup FC Fully-connected Layer Functions
+ *
+ * Perform fully-connected layer
+ *
+ * Fully-connected layer is basically a matrix-vector multiplication
+ * with bias. The matrix is the weights and the input/output vectors
+ * are the activation values. Supported {weight, activation} precisions
+ * include {8-bit, 8-bit}, {16-bit, 16-bit}, and {8-bit, 16-bit}.
+ *
+ * Here we have two types of kernel functions. The basic function
+ * implements the function using regular GEMV approach. The opt functions
+ * operates with weights in interleaved formats.
+ *
+ */
+
+ /**
+ * @brief Q7 basic fully-connected layer function
+ * @param[in] pV pointer to input vector
+ * @param[in] pM pointer to matrix weights
+ * @param[in] dim_vec length of the vector
+ * @param[in] num_of_rows number of rows in weight matrix
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias pointer to bias
+ * @param[in,out] pOut pointer to output vector
+ * @param[in,out] vec_buffer pointer to buffer space for input
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_fully_connected_q7(const q7_t * pV,
+ const q7_t * pM,
+ const uint16_t dim_vec,
+ const uint16_t num_of_rows,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q7_t * bias,
+ q7_t * pOut,
+ q15_t * vec_buffer);
+
+ /**
+ * @brief Q7 opt fully-connected layer function
+ * @param[in] pV pointer to input vector
+ * @param[in] pM pointer to matrix weights
+ * @param[in] dim_vec length of the vector
+ * @param[in] num_of_rows number of rows in weight matrix
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias pointer to bias
+ * @param[in,out] pOut pointer to output vector
+ * @param[in,out] vec_buffer pointer to buffer space for input
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_fully_connected_q7_opt(const q7_t * pV,
+ const q7_t * pM,
+ const uint16_t dim_vec,
+ const uint16_t num_of_rows,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q7_t * bias,
+ q7_t * pOut,
+ q15_t * vec_buffer);
+
+ /**
+ * @brief Q15 basic fully-connected layer function
+ * @param[in] pV pointer to input vector
+ * @param[in] pM pointer to matrix weights
+ * @param[in] dim_vec length of the vector
+ * @param[in] num_of_rows number of rows in weight matrix
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias pointer to bias
+ * @param[in,out] pOut pointer to output vector
+ * @param[in,out] vec_buffer pointer to buffer space for input
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_fully_connected_q15(const q15_t * pV,
+ const q15_t * pM,
+ const uint16_t dim_vec,
+ const uint16_t num_of_rows,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q15_t * bias,
+ q15_t * pOut,
+ q15_t * vec_buffer);
+
+ /**
+ * @brief Q15 opt fully-connected layer function
+ * @param[in] pV pointer to input vector
+ * @param[in] pM pointer to matrix weights
+ * @param[in] dim_vec length of the vector
+ * @param[in] num_of_rows number of rows in weight matrix
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias pointer to bias
+ * @param[in,out] pOut pointer to output vector
+ * @param[in,out] vec_buffer pointer to buffer space for input
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_fully_connected_q15_opt(const q15_t * pV,
+ const q15_t * pM,
+ const uint16_t dim_vec,
+ const uint16_t num_of_rows,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q15_t * bias,
+ q15_t * pOut,
+ q15_t * vec_buffer);
+
+ /**
+ * @brief Mixed Q15-Q7 fully-connected layer function
+ * @param[in] pV pointer to input vector
+ * @param[in] pM pointer to matrix weights
+ * @param[in] dim_vec length of the vector
+ * @param[in] num_of_rows number of rows in weight matrix
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias pointer to bias
+ * @param[in,out] pOut pointer to output vector
+ * @param[in,out] vec_buffer pointer to buffer space for input
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_fully_connected_mat_q7_vec_q15(const q15_t * pV,
+ const q7_t * pM,
+ const uint16_t dim_vec,
+ const uint16_t num_of_rows,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q7_t * bias,
+ q15_t * pOut,
+ q15_t * vec_buffer);
+
+ /**
+ * @brief Mixed Q15-Q7 opt fully-connected layer function
+ * @param[in] pV pointer to input vector
+ * @param[in] pM pointer to matrix weights
+ * @param[in] dim_vec length of the vector
+ * @param[in] num_of_rows number of rows in weight matrix
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias pointer to bias
+ * @param[in,out] pOut pointer to output vector
+ * @param[in,out] vec_buffer pointer to buffer space for input
+ * @return The function returns <code>ARM_MATH_SUCCESS</code>
+ *
+ */
+
+ arm_status arm_fully_connected_mat_q7_vec_q15_opt(const q15_t * pV,
+ const q7_t * pM,
+ const uint16_t dim_vec,
+ const uint16_t num_of_rows,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q7_t * bias,
+ q15_t * pOut,
+ q15_t * vec_buffer);
+
+/**
+ * @brief Matrix-Multiplication Kernels for Convolution
+ *
+ * These functions are used within convolution layer functions for
+ * matrix multiplication.
+ *
+ * The implementation is similar to CMSIS-DSP arm_mat_mult functions
+ * with one Q7 and one Q15 operands. The Q15 operand is the im2col
+ * output which is always with 2 columns.
+ *
+ */
+
+ /**
+ * @brief Matrix-multiplication function for convolution
+ * @param[in] pA pointer to operand A
+ * @param[in] pInBuffer pointer to operand B, always conssists of 2 vectors
+ * @param[in] ch_im_out numRow of A
+ * @param[in] numCol_A numCol of A
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias the bias
+ * @param[in,out] pOut pointer to output
+ * @return The function returns the incremented output pointer
+ */
+
+ q7_t *arm_nn_mat_mult_kernel_q7_q15(const q7_t * pA,
+ const q15_t * pInBuffer,
+ const uint16_t ch_im_out,
+ const uint16_t numCol_A,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q7_t * bias,
+ q7_t * pOut);
+
+ /**
+ * @brief Matrix-multiplication function for convolution with reordered columns
+ * @param[in] pA pointer to operand A
+ * @param[in] pInBuffer pointer to operand B, always conssists of 2 vectors
+ * @param[in] ch_im_out numRow of A
+ * @param[in] numCol_A numCol of A
+ * @param[in] bias_shift amount of left-shift for bias
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] bias the bias
+ * @param[in,out] pOut pointer to output
+ * @return The function returns the incremented output pointer
+ */
+
+ q7_t *arm_nn_mat_mult_kernel_q7_q15_reordered(const q7_t * pA,
+ const q15_t * pInBuffer,
+ const uint16_t ch_im_out,
+ const uint16_t numCol_A,
+ const uint16_t bias_shift,
+ const uint16_t out_shift,
+ const q7_t * bias,
+ q7_t * pOut);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * Other functions
+ * These layers are typically not timing critical
+ * Basic implementation is supported here
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @defgroup Acti Neural Network Activation Functions
+ *
+ * Perform activation layers, including ReLU (Rectified Linear Unit),
+ * sigmoid and tanh
+ *
+ */
+
+ /**
+ * @brief Q7 RELU function
+ * @param[in,out] data pointer to input
+ * @param[in] size number of elements
+ * @return none.
+ */
+
+ void arm_relu_q7(q7_t * data, uint16_t size);
+
+ /**
+ * @brief Q15 RELU function
+ * @param[in,out] data pointer to input
+ * @param[in] size number of elements
+ * @return none.
+ */
+
+ void arm_relu_q15(q15_t * data, uint16_t size);
+
+ /**
+ * @brief Q7 neural network activation function using direct table look-up
+ * @param[in,out] data pointer to input
+ * @param[in] size number of elements
+ * @param[in] int_width bit-width of the integer part, assume to be smaller than 3
+ * @param[in] type type of activation functions
+ * @return none.
+ */
+
+ void arm_nn_activations_direct_q7(q7_t * data, uint16_t size, uint16_t int_width,
+ arm_nn_activation_type type);
+
+ /**
+ * @brief Q15 neural network activation function using direct table look-up
+ * @param[in,out] data pointer to input
+ * @param[in] size number of elements
+ * @param[in] int_width bit-width of the integer part, assume to be smaller than 3
+ * @param[in] type type of activation functions
+ * @return none.
+ */
+
+ void arm_nn_activations_direct_q15(q15_t * data, uint16_t size, uint16_t int_width,
+ arm_nn_activation_type type);
+
+/**
+ * @defgroup Pooling Neural Network Pooling Functions
+ *
+ * Perform pooling functions, including max pooling and average pooling
+ *
+ */
+
+ /**
+ * @brief Q7 max pooling function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] Im_out pointer to output tensor
+ * @return none.
+ *
+ */
+
+ void arm_maxpool_q7_HWC(q7_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const uint16_t dim_im_out,
+ q7_t * bufferA,
+ q7_t * Im_out);
+
+ /**
+ * @brief Q7 average pooling function
+ * @param[in] Im_in pointer to input tensor
+ * @param[in] dim_im_in input tensor dimention
+ * @param[in] ch_im_in number of input tensor channels
+ * @param[in] dim_kernel filter kernel size
+ * @param[in] padding padding sizes
+ * @param[in] stride convolution stride
+ * @param[in] dim_im_out output tensor dimension
+ * @param[in,out] bufferA pointer to buffer space for input
+ * @param[in,out] Im_out pointer to output tensor
+ * @return none.
+ *
+ */
+
+ void arm_avepool_q7_HWC(q7_t * Im_in,
+ const uint16_t dim_im_in,
+ const uint16_t ch_im_in,
+ const uint16_t dim_kernel,
+ const uint16_t padding,
+ const uint16_t stride,
+ const uint16_t dim_im_out,
+ q7_t * bufferA,
+ q7_t * Im_out);
+
+/**
+ * @defgroup Softmax Softmax Functions
+ *
+ * EXP(2) based softmax function
+ *
+ */
+
+ /**
+ * @brief Q7 softmax function
+ * @param[in] vec_in pointer to input vector
+ * @param[in] dim_vec input vector dimention
+ * @param[out] p_out pointer to output vector
+ * @return none.
+ *
+ */
+
+ void arm_softmax_q7(const q7_t * vec_in, const uint16_t dim_vec, q7_t * p_out);
+
+ /**
+ * @brief Q15 softmax function
+ * @param[in] vec_in pointer to input vector
+ * @param[in] dim_vec input vector dimention
+ * @param[out] p_out pointer to output vector
+ * @return none.
+ *
+ */
+
+ void arm_softmax_q15(const q15_t * vec_in, const uint16_t dim_vec, q15_t * p_out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Blink/Drivers/CMSIS/NN/Include/arm_nnsupportfunctions.h b/Blink/Drivers/CMSIS/NN/Include/arm_nnsupportfunctions.h
new file mode 100644
index 0000000..05a239d
--- /dev/null
+++ b/Blink/Drivers/CMSIS/NN/Include/arm_nnsupportfunctions.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010-2018 Arm Limited or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ *
+ * 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.
+ */
+
+/* ----------------------------------------------------------------------
+ * Project: CMSIS NN Library
+ * Title: arm_nnsupportfunctions.h
+ * Description: Public header file of support functions for CMSIS NN Library
+ *
+ * $Date: 13. July 2018
+ * $Revision: V.1.0.0
+ *
+ * Target Processor: Cortex-M cores
+ * -------------------------------------------------------------------- */
+
+#ifndef _ARM_NNSUPPORTFUNCTIONS_H_
+#define _ARM_NNSUPPORTFUNCTIONS_H_
+
+#include "arm_math.h"
+#include "arm_common_tables.h"
+//#include <cstring>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @brief Union for SIMD access of Q31/Q15/Q7 types
+ */
+union arm_nnword
+{
+ q31_t word;
+ /**< Q31 type */
+ q15_t half_words[2];
+ /**< Q15 type */
+ q7_t bytes[4];
+ /**< Q7 type */
+};
+
+/**
+ * @brief Struct for specifying activation function types
+ *
+ */
+typedef enum
+{
+ ARM_SIGMOID = 0,
+ /**< Sigmoid activation function */
+ ARM_TANH = 1,
+ /**< Tanh activation function */
+} arm_nn_activation_type;
+
+/**
+ * @defgroup nndata_convert Neural Network Data Conversion Functions
+ *
+ * Perform data type conversion in-between neural network operations
+ *
+ */
+
+/**
+ * @brief Converts the elements of the Q7 vector to Q15 vector without left-shift
+ * @param[in] *pSrc points to the Q7 input vector
+ * @param[out] *pDst points to the Q15 output vector
+ * @param[in] blockSize length of the input vector
+ * @return none.
+ *
+ */
+
+void arm_q7_to_q15_no_shift(const q7_t * pSrc, q15_t * pDst, uint32_t blockSize);
+
+/**
+ * @brief Converts the elements of the Q7 vector to reordered Q15 vector without left-shift
+ * @param[in] *pSrc points to the Q7 input vector
+ * @param[out] *pDst points to the Q15 output vector
+ * @param[in] blockSize length of the input vector
+ * @return none.
+ *
+ */
+
+void arm_q7_to_q15_reordered_no_shift(const q7_t * pSrc, q15_t * pDst, uint32_t blockSize);
+
+#if defined (ARM_MATH_DSP)
+
+/**
+ * @brief read and expand one Q7 word into two Q15 words
+ */
+
+__STATIC_FORCEINLINE void *read_and_pad(void *source, q31_t * out1, q31_t * out2)
+{
+ q31_t inA = *__SIMD32(source)++;
+ q31_t inAbuf1 = __SXTB16(__ROR(inA, 8));
+ q31_t inAbuf2 = __SXTB16(inA);
+
+#ifndef ARM_MATH_BIG_ENDIAN
+ *out2 = __PKHTB(inAbuf1, inAbuf2, 16);
+ *out1 = __PKHBT(inAbuf2, inAbuf1, 16);
+#else
+ *out1 = __PKHTB(inAbuf1, inAbuf2, 16);
+ *out2 = __PKHBT(inAbuf2, inAbuf1, 16);
+#endif
+
+ return source;
+}
+
+/**
+ * @brief read and expand one Q7 word into two Q15 words with reordering
+ */
+
+__STATIC_FORCEINLINE void *read_and_pad_reordered(void *source, q31_t * out1, q31_t * out2)
+{
+ q31_t inA = *__SIMD32(source)++;
+#ifndef ARM_MATH_BIG_ENDIAN
+ *out2 = __SXTB16(__ROR(inA, 8));
+ *out1 = __SXTB16(inA);
+#else
+ *out1 = __SXTB16(__ROR(inA, 8));
+ *out2 = __SXTB16(inA);
+#endif
+
+ return source;
+}
+#endif
+
+/**
+ * @defgroup NNBasicMath Basic Math Functions for Neural Network Computation
+ *
+ * Basic Math Functions for Neural Network Computation
+ *
+ */
+
+/**
+ * @brief Q7 vector multiplication with variable output shifts
+ * @param[in] *pSrcA pointer to the first input vector
+ * @param[in] *pSrcB pointer to the second input vector
+ * @param[out] *pDst pointer to the output vector
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] blockSize number of samples in each vector
+ * @return none.
+ *
+ * <b>Scaling and Overflow Behavior:</b>
+ * \par
+ * The function uses saturating arithmetic.
+ * Results outside of the allowable Q15 range [0x8000 0x7FFF] will be saturated.
+ */
+
+void arm_nn_mult_q15(
+ q15_t * pSrcA,
+ q15_t * pSrcB,
+ q15_t * pDst,
+ const uint16_t out_shift,
+ uint32_t blockSize);
+
+/**
+ * @brief Q7 vector multiplication with variable output shifts
+ * @param[in] *pSrcA pointer to the first input vector
+ * @param[in] *pSrcB pointer to the second input vector
+ * @param[out] *pDst pointer to the output vector
+ * @param[in] out_shift amount of right-shift for output
+ * @param[in] blockSize number of samples in each vector
+ * @return none.
+ *
+ * <b>Scaling and Overflow Behavior:</b>
+ * \par
+ * The function uses saturating arithmetic.
+ * Results outside of the allowable Q7 range [0x80 0x7F] will be saturated.
+ */
+
+void arm_nn_mult_q7(
+ q7_t * pSrcA,
+ q7_t * pSrcB,
+ q7_t * pDst,
+ const uint16_t out_shift,
+ uint32_t blockSize);
+
+/**
+ * @brief defition to adding rouding offset
+ */
+#ifndef ARM_NN_TRUNCATE
+ #define NN_ROUND(out_shift) ( 0x1 << (out_shift - 1) )
+#else
+ #define NN_ROUND(out_shift) 0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif