mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-08 04:26:58 +03:00
Rolling v10.6.1
This commit is contained in:
@@ -5,9 +5,7 @@ set(c_srcs
|
||||
"src/basic_math/esp_nn_add_ansi.c"
|
||||
"src/basic_math/esp_nn_mul_ansi.c"
|
||||
"src/convolution/esp_nn_conv_ansi.c"
|
||||
"src/convolution/esp_nn_conv_opt.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_ansi.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_opt.c"
|
||||
"src/fully_connected/esp_nn_fully_connected_ansi.c"
|
||||
"src/softmax/esp_nn_softmax_ansi.c"
|
||||
"src/softmax/esp_nn_softmax_opt.c"
|
||||
@@ -25,7 +23,7 @@ if(CONFIG_IDF_TARGET_ESP32S3)
|
||||
"src/convolution/esp_nn_conv_esp32s3.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c"
|
||||
"src/convolution/esp_nn_conv_s16_mult8_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s8_mult8_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s16_mult8_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s16_mult4_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_esp32s3.S"
|
||||
|
||||
@@ -6,8 +6,8 @@ choice NN_OPTIMIZATIONS
|
||||
help
|
||||
Use ANSI-C versions for verification and debug purpose.
|
||||
Optimisations are automatically picked up for a chipset.
|
||||
For ESP32-S3, assembly optimisations are selected.
|
||||
For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
|
||||
For ESP32-S3, assembly Optimisations are selected.
|
||||
For ESP32, just the ANSI C versions are selected for now.
|
||||
|
||||
config NN_ANSI_C
|
||||
bool "ANSI C"
|
||||
@@ -17,8 +17,8 @@ config NN_OPTIMIZED
|
||||
bool "Optimized versions"
|
||||
help
|
||||
Optimisations are automatically picked up for a chipset.
|
||||
For ESP32-S3, assembly optimisations are selected.
|
||||
For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
|
||||
For ESP32-S3, assembly Optimisations are selected.
|
||||
For ESP32, just the ANSI C versions are selected for now.
|
||||
endchoice
|
||||
|
||||
config NN_OPTIMIZATIONS
|
||||
|
||||
@@ -7,8 +7,7 @@ The library contains optimised NN (Neural Network) functions for various Espress
|
||||
|
||||
* Supported ESP chipsets include:
|
||||
* ESP32-S3 (Assembly versions optimised to benefit from vector instructions of ESP32-S3)
|
||||
* ESP32 (Generic optimisations)
|
||||
* ESP32-C3 (Generic optimisations)
|
||||
* ESP32 (ANSI C versions)
|
||||
|
||||
## Performance
|
||||
|
||||
@@ -40,8 +39,8 @@ The library contains optimised NN (Neural Network) functions for various Espress
|
||||
* Optimized versions
|
||||
* ANSI C
|
||||
|
||||
* Default selection is for `Optimized versions`. For ESP32-S3, assembly versions are automatically selected, whereas for other chipsets (viz., ESP32, ESP32-C3), generic optimisations are selected.
|
||||
* For debugging purposes, you may want to select `ANSI C` reference versions.
|
||||
* Default selection is for `Optimized versions`. For ESP32-S3, assembly versions are automatically selected, whereas for ESP32, ANSI-C versions are selected by default.
|
||||
* For debugging purposes, you may want to select `ANSI C`
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(CONFIG_NN_OPTIMIZED)
|
||||
// select apt optimisations
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
#define ARCH_ESP32_S3 1
|
||||
#endif
|
||||
@@ -32,11 +31,12 @@ extern "C" {
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#if defined(CONFIG_NN_OPTIMIZED)
|
||||
#if defined(ARCH_ESP32_S3)
|
||||
#ifdef ARCH_ESP32_S3
|
||||
#include "esp_nn_esp32s3.h"
|
||||
#else // for other platforms use generic optimisations
|
||||
#include "esp_nn_generic_opt.h"
|
||||
#endif // #if defined(ARCH_ESP32_S3)
|
||||
#endif
|
||||
#ifdef ARCH_ESP32
|
||||
#include "esp_nn_esp32.h"
|
||||
#endif
|
||||
#else
|
||||
#include "esp_nn_ansi_c.h"
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
* @file Header definitions to include for esp_nn reference functions
|
||||
*/
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/************************** Basic math functions ****************************/
|
||||
|
||||
/**
|
||||
@@ -80,15 +81,28 @@ void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_depthwise_conv_s8_ansi(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
/**
|
||||
* @brief 2d-convolution channelwise
|
||||
@@ -98,26 +112,43 @@ void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_conv_s8_ansi(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
int esp_nn_get_conv_scratch_size_ansi(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_ch,
|
||||
const uint16_t out_ch,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht);
|
||||
void esp_nn_set_conv_scratch_buf_ansi(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t ch_mult,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf);
|
||||
|
||||
/************************** Activation functions *****************************/
|
||||
@@ -221,6 +252,9 @@ int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t h
|
||||
*/
|
||||
void esp_nn_set_softmax_scratch_buf_ansi(void *buffer);
|
||||
|
||||
/* ANSI C function to be hooked up when optimised version needed */
|
||||
void esp_nn_set_softmax_scratch_buf_opt(void *buffer);
|
||||
|
||||
/**
|
||||
* @brief reference softmax function
|
||||
*
|
||||
@@ -234,66 +268,6 @@ void esp_nn_softmax_s8_ansi(const int8_t *input_data,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data);
|
||||
|
||||
|
||||
//////////////////////////// Generic optimisations /////////////////////////////
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief 2d-convolution channelwise optimized version
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel optimized version
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_opt(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf);
|
||||
|
||||
/* ANSI C function to be hooked up when optimised version needed */
|
||||
void esp_nn_set_softmax_scratch_buf_opt(void *buffer);
|
||||
|
||||
/**
|
||||
* @brief optimised version of softmax function
|
||||
*
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief structure to club data dims
|
||||
* this structure can be used for input, output and filter
|
||||
*/
|
||||
typedef struct data_dims {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t channels;
|
||||
|
||||
int32_t extra; // can be used as batch or any other param
|
||||
} data_dims_t;
|
||||
|
||||
/**
|
||||
* @brief 2d data structure (width, height)
|
||||
*
|
||||
*/
|
||||
typedef struct data_2d {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} data_2d_t;
|
||||
|
||||
/**
|
||||
* @brief min/max activation
|
||||
*/
|
||||
typedef struct act_params {
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
} act_params_t;
|
||||
|
||||
/**
|
||||
* @brief per channel quant data
|
||||
*
|
||||
* @note number of shift and mult elements are equal to output channels
|
||||
*/
|
||||
typedef struct quant_data {
|
||||
int32_t *shift;
|
||||
int32_t *mult;
|
||||
} quant_data_t;
|
||||
|
||||
/**
|
||||
* @brief params specific to convolution 2d
|
||||
*
|
||||
*/
|
||||
typedef struct conv_params {
|
||||
int32_t in_offset;
|
||||
int32_t out_offset;
|
||||
data_2d_t stride;
|
||||
data_2d_t padding;
|
||||
data_2d_t dilation;
|
||||
act_params_t activation;
|
||||
} conv_params_t;
|
||||
|
||||
/**
|
||||
* @brief params specific to depthwise convolution 2d
|
||||
*
|
||||
*/
|
||||
typedef struct dw_conv_params {
|
||||
int32_t in_offset;
|
||||
int32_t out_offset;
|
||||
int32_t ch_mult; // channel multiplier. (in_ch * ch_mult = out_ch)
|
||||
data_2d_t stride;
|
||||
data_2d_t padding;
|
||||
data_2d_t dilation;
|
||||
act_params_t activation;
|
||||
} dw_conv_params_t;
|
||||
@@ -13,27 +13,28 @@
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn generic optimisations
|
||||
* For functions which not having optimisations, _ansi versions are picked.
|
||||
* @file Header definitions to include for esp_nn optimized functions for
|
||||
* the ESP32 platform.
|
||||
* We are hooking up just the C versions for now.
|
||||
* The file hence is exactly same as `esp_nn_ansi_c.h`
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_opt
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_ansi
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_opt
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_ansi
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_opt
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_opt
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_ansi
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_ansi
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_opt
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_opt
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_ansi
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_ansi
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include <stdint.h>
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
/************************** Basic math functions *****************************/
|
||||
@@ -85,15 +85,28 @@ void esp_nn_mul_elementwise_s8_esp32s3(const int8_t *input1_data,
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *output_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
/**
|
||||
* @brief 2d - convolution channelwise
|
||||
@@ -103,26 +116,43 @@ void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_conv_s8_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *output_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_ch,
|
||||
const uint16_t out_ch,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht);
|
||||
void esp_nn_set_conv_scratch_buf_esp32s3(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t ch_mult,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(const void *buf);
|
||||
|
||||
/************************** Pooling functions *****************************/
|
||||
|
||||
@@ -41,52 +41,8 @@
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_clz32(uint32_t in)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
__asm__ volatile("nsau %0, %0" : "+r" (in));
|
||||
return in;
|
||||
#elif defined(__GNUC__)
|
||||
return __builtin_clz(in);
|
||||
#else
|
||||
int32_t count = 32;
|
||||
uint32_t x = in, y = in >> 16;
|
||||
if (y != 0) {
|
||||
count -= 16;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 8;
|
||||
if (y != 0) {
|
||||
count -= 8;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 4;
|
||||
if (y != 0) {
|
||||
count -= 4;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 2;
|
||||
if (y != 0) {
|
||||
count -= 2;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 1;
|
||||
if (y != 0) {
|
||||
return count - 2;
|
||||
}
|
||||
return count - x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Signed saturate a 32 bit value to 8 bits keeping output in 32 bit variable.
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_saturate8(int32_t in)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
__asm__ volatile("clamps %0, %0, 7" : "+a"(in));
|
||||
return in;
|
||||
#else
|
||||
return max(INT8_MIN, min(in, INT8_MAX));
|
||||
#endif
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_pick_sat_high32_of64(int64_t val64)
|
||||
@@ -96,6 +52,15 @@ __NN_FORCE_INLINE__ int32_t esp_nn_pick_sat_high32_of64(int64_t val64)
|
||||
return (int32_t) ((int64_t) (val64 + to_add) >> 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signed saturate a 32 bit value to 8 bits keeping output in 32 bit variable.
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_saturate8(int32_t in)
|
||||
{
|
||||
__asm__ volatile("clamps %0, %0, 7" : "+a"(in));
|
||||
return in;
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_sat_round_doubling_high_mul(int32_t in0, int32_t in1)
|
||||
{
|
||||
int32_t result;
|
||||
@@ -179,7 +144,7 @@ static void esp_nn_aligned_s8_pad_with_value(const int8_t *src, int8_t *dst,
|
||||
const uint16_t pad_ht)
|
||||
{
|
||||
/* memset with pad_val */
|
||||
memset(dst, pad_val, ((input_wd + 2 * pad_wd) * (input_ht + 2 * pad_ht)) * channels);
|
||||
memset(dst, pad_val, ((input_wd + 2 * pad_wd) * (input_ht + 2 * pad_ht)) * channels * 2);
|
||||
dst += (pad_wd + input_wd + pad_wd) * channels;
|
||||
|
||||
for (int i = 0; i < input_ht; i++) {
|
||||
@@ -191,6 +156,7 @@ static void esp_nn_aligned_s8_pad_with_value(const int8_t *src, int8_t *dst,
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void esp_nn_aligned_s8_pad_end_with_value(const int8_t *src, int8_t *dst,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
@@ -203,16 +169,13 @@ static void esp_nn_aligned_s8_pad_end_with_value(const int8_t *src, int8_t *dst,
|
||||
for (int j = 0; j < input_wd * channels; j++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
if (pad_wd) {
|
||||
memset(dst, pad_val, pad_wd * channels);
|
||||
dst += pad_wd * channels;
|
||||
}
|
||||
memset(dst, pad_val, pad_wd * channels);
|
||||
dst += pad_wd * channels;
|
||||
}
|
||||
/* pad end `pad_ht` lines at end */
|
||||
if (pad_ht) {
|
||||
memset(dst, pad_val, (input_wd + pad_wd) * pad_ht * channels);
|
||||
}
|
||||
memset(dst, pad_val, (input_wd + pad_wd) * pad_ht * channels);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief convert 8 bit input data to 16 bit
|
||||
|
||||
@@ -12,14 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
int esp_nn_get_conv_scratch_size_ansi(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_ch,
|
||||
const uint16_t out_ch,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -106,35 +108,29 @@ void esp_nn_conv_u8_ansi(const uint8_t *input_data,
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_conv_s8_ansi(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
|
||||
@@ -12,30 +12,30 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
static int16_t *scratch_buffer = NULL;
|
||||
|
||||
extern void esp_nn_conv_s8_mult8_1x1_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const int8_t *filter_aligned,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
void *buffer /* scratch buffer */);
|
||||
extern void esp_nn_conv_s16_mult8_1x1_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
void *buffer /* scratch buffer */);
|
||||
|
||||
extern void esp_nn_conv_s16_mult4_1x1_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
@@ -81,40 +81,34 @@ extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int1
|
||||
|
||||
extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
|
||||
|
||||
static void esp_nn_conv_s8_unrolled(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
static void esp_nn_conv_s8_unrolled(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_ch = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_ch = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_ch; out_ch_idx++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
@@ -130,10 +124,10 @@ static void esp_nn_conv_s8_unrolled(const data_dims_t *input_dims,
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_ch;
|
||||
int32_t filter_base_offset = out_ch_idx * in_ch * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_ch;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_ch; in_ch_idx++) {
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out +=
|
||||
(input_data[input_base_offset + in_ch_idx] + input_offset) *
|
||||
filter_data[filter_base_offset + in_ch_idx];
|
||||
@@ -338,35 +332,18 @@ static void esp_nn_conv_s8_pad_valid_ch3_3x3(const int8_t *input_data,
|
||||
}
|
||||
}
|
||||
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_ch,
|
||||
const uint16_t out_ch,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_ch = input_dims->channels;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_ch = output_dims->channels;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * in_ch * out_ch;
|
||||
int input_size = input_wd * input_ht * in_ch;
|
||||
|
||||
int transpose_buf_size = 2 * (8 * in_ch); /* to store intermediate data */
|
||||
if (input_wd * input_ht < 8) {
|
||||
transpose_buf_size = 0; // not using this for leftover
|
||||
}
|
||||
int transpose_buf_size = 8 * in_ch; /* to store intermediate data */
|
||||
int align_buf_size = 32; /* extra buffer for alignment */
|
||||
if (in_ch % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
return filter_size + transpose_buf_size + align_buf_size;
|
||||
}
|
||||
return 2 * (filter_size + input_size) + transpose_buf_size + align_buf_size;
|
||||
return 2 * (filter_size + input_size + transpose_buf_size) + align_buf_size;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_esp32s3(void *buf)
|
||||
@@ -374,35 +351,29 @@ void esp_nn_set_conv_scratch_buf_esp32s3(void *buf)
|
||||
scratch_buffer = (int16_t *) buf;
|
||||
}
|
||||
|
||||
void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_conv_s8_esp32s3(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * out_channels;
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
int align_len = 16 - (filter_size & 15);
|
||||
@@ -416,16 +387,15 @@ void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
|
||||
if (channels % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int scratch_offset = (int) (filter_aligned + filter_size);
|
||||
int scratch_offset = (int) (filter_data16 + filter_size);
|
||||
void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
|
||||
memcpy(filter_aligned, filter_data, filter_size); // copy to aligned address
|
||||
esp_nn_conv_s8_mult8_1x1_esp32s3(
|
||||
input, input_wd, input_ht, channels, input_offset, filter_aligned,
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_conv_s16_mult8_1x1_esp32s3(
|
||||
input, input_wd, input_ht, channels, input_offset, filter_data16,
|
||||
bias, out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max, scratch_buf);
|
||||
} else if (channels % 4 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
(input_wd * input_ht) % 4 == 0 && /* TODO: remove this check */
|
||||
(input_wd * input_ht) % 16 == 0 && /* TODO: remove this check */
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
int scratch_offset = (int) (input_data16 + input_size);
|
||||
void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
|
||||
@@ -457,7 +427,10 @@ void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
}
|
||||
} else {
|
||||
/* Basic unrolled version */
|
||||
esp_nn_conv_s8_unrolled(input_dims, input, filter_dims, filter_data,
|
||||
bias, output_dims, out_data, conv_params, quant_data);
|
||||
esp_nn_conv_s8_unrolled(input, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht,
|
||||
filter_data, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_opt(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static void esp_nn_conv_s8_1x1(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
for (int32_t in_row = 0; in_row < out_ht * stride_ht; in_row += stride_ht) {
|
||||
for (int32_t in_col = 0; in_col < out_wd * stride_wd; in_col += stride_wd) {
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int8_t *filter_ptr = filter_data;
|
||||
const int8_t *input_base_ptr = input_data + (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t out_ch_idx = 0;
|
||||
for (; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int8_t *input_ptr = input_base_ptr;
|
||||
|
||||
int32_t in_ch_idx = 0;
|
||||
for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
for (; in_ch_idx < in_channels; in_ch_idx ++) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
|
||||
if (filter_wd == 1 && filter_ht == 1) {
|
||||
esp_nn_conv_s8_1x1(input_dims, input_data, filter_data, bias,
|
||||
output_dims, out_data, conv_params, quant_data);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
|
||||
const int8_t *input_ptr = input_data +
|
||||
(in_row * input_wd + in_col) * in_channels;
|
||||
const int8_t *filter_ptr = filter_data +
|
||||
out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
int32_t in_ch_idx = 0;
|
||||
for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
for (; in_ch_idx < in_channels; in_ch_idx ++) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t ch_mult,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -28,35 +31,29 @@ void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf)
|
||||
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_depthwise_conv_s8_ansi(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <esp_nn_defs.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* common channel multiplier == 1 case */
|
||||
__attribute__ ((noinline))
|
||||
static void esp_nn_depthwise_conv_s8_ch_mult_1(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
int ch_idx = 0;
|
||||
for (; ch_idx < channels - 3; ch_idx += 4) {//channel_loop
|
||||
int32_t result0 = 0;
|
||||
int32_t result1 = 0;
|
||||
int32_t result2 = 0;
|
||||
int32_t result3 = 0;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
|
||||
int32_t input_val0 = input_data[input_index + 0] + input_offset;
|
||||
int32_t input_val1 = input_data[input_index + 1] + input_offset;
|
||||
int32_t input_val2 = input_data[input_index + 2] + input_offset;
|
||||
int32_t input_val3 = input_data[input_index + 3] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val0 * filter_val0;
|
||||
result1 += input_val1 * filter_val1;
|
||||
result2 += input_val2 * filter_val2;
|
||||
result3 += input_val3 * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[ch_idx + 0];
|
||||
result1 += bias[ch_idx + 1];
|
||||
result2 += bias[ch_idx + 2];
|
||||
result3 += bias[ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
|
||||
result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
|
||||
result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
|
||||
result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
for (; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
if (ch_mult == 1) {
|
||||
esp_nn_depthwise_conv_s8_ch_mult_1(input_dims, input_data, filter_dims, filter_data,
|
||||
bias, output_dims, out_data, conv_params, quant_data);
|
||||
return;
|
||||
}
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int ch_mult_idx = 0;
|
||||
for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
|
||||
int32_t result0 = 0;
|
||||
int32_t result1 = 0;
|
||||
int32_t result2 = 0;
|
||||
int32_t result3 = 0;
|
||||
const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val * filter_val0;
|
||||
result1 += input_val * filter_val1;
|
||||
result2 += input_val * filter_val2;
|
||||
result3 += input_val * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[out_ch_idx + 0];
|
||||
result1 += bias[out_ch_idx + 1];
|
||||
result2 += bias[out_ch_idx + 2];
|
||||
result3 += bias[out_ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
|
||||
result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
|
||||
result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
|
||||
result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
@@ -353,59 +353,17 @@ void esp_nn_depthwise_conv_s8_ch_mult1(const int8_t *input_data,
|
||||
}
|
||||
}
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t ch_mult,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult;
|
||||
int pad_width = 0, pad_height = 0;
|
||||
|
||||
if ((ch_mult == 1) && (channels % 8 == 0) && (filter_wd == 3) && (filter_ht == 3)) {
|
||||
if (channels % 16 == 0) {
|
||||
if (pad_wd || pad_ht) {
|
||||
pad_width = pad_wd * 2;
|
||||
pad_height = pad_ht * 2;
|
||||
} else {
|
||||
// check if we need to pad additionally
|
||||
pad_width = (out_wd * stride_wd + filter_wd - 1) - input_wd;
|
||||
pad_height = (out_ht * stride_ht + filter_ht - 1) - input_ht;
|
||||
// printf("in(%d %d %d), out(%d %d), filter (%d %d) stride (%d %d), pad (%d %d)",
|
||||
// input_wd, input_ht, channels, out_wd, out_ht, filter_wd, filter_ht,
|
||||
// stride_wd, stride_ht, pad_wd, pad_ht);
|
||||
}
|
||||
if (pad_width || pad_height) {
|
||||
int input_size = (input_wd + pad_width) * (input_ht + pad_height) * channels;
|
||||
// printf("ask1 %d\n", filter_size + input_size + 16);
|
||||
return filter_size + input_size + 16; // 16 for alignment
|
||||
} else {
|
||||
// printf("ask2 %d\n", filter_size + 16);
|
||||
return filter_size + 16; // 16 for alignment
|
||||
}
|
||||
} else {
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
// printf("ask3 %d\n", 2 * (filter_size + input_size) + 16);
|
||||
return 2 * (filter_size + input_size) + 16; // 16 for alignment
|
||||
}
|
||||
} else if (ch_mult % 4 == 0) {
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
// printf("ask4 %d\n", 2 * (filter_size + input_size) + 16);
|
||||
return 2 * (filter_size + input_size) + 16; // 16 for alignment
|
||||
}
|
||||
return 32; // just few bytes
|
||||
int padding_used = ((filter_wd == 3) && (filter_ht == 3)) * 2;
|
||||
int input_size = (input_wd + padding_used) * (input_ht + padding_used) * channels;
|
||||
return 2 * (filter_size + input_size) + 16; //16 for alignment
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(void *buf)
|
||||
@@ -418,38 +376,29 @@ void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(void *buf)
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult;
|
||||
int align_len = 16 - (filter_size & 15);
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
@@ -474,27 +423,18 @@ void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
stride_wd, stride_ht, filter_aligned, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else if ((channels % 16 == 0) && (pad_wd == 0) && (pad_ht == 0)) {
|
||||
} else if ((pad_wd == 0) && (pad_ht == 0) &&
|
||||
// because this does not handle padding offset cases yet, run just for stride (1, 1).
|
||||
// end padding of input with `-input_offset` should solve this
|
||||
(stride_wd == 1) && (stride_ht == 1)) {
|
||||
/* process in 8 bits */
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
|
||||
|
||||
// check if we need to pad additionally
|
||||
int pad_right = (out_wd * stride_wd + filter_wd - 1) - input_wd;
|
||||
int pad_bottom = (out_ht * stride_ht + filter_ht - 1) - input_ht;
|
||||
if (pad_right || pad_bottom) { // pad right and bottom
|
||||
esp_nn_aligned_s8_pad_end_with_value(input_data, input_padded, input_wd, input_ht,
|
||||
channels, -input_offset, pad_right, pad_bottom);
|
||||
} else {
|
||||
input_padded = (int8_t *) input_data;
|
||||
}
|
||||
memcpy(filter_aligned, filter_data, filter_size);
|
||||
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + pad_right,
|
||||
input_ht + pad_bottom, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_aligned, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_data, input_wd, input_ht, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_aligned,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else { /* (channels % 8) == 0 */
|
||||
} else { /* (channels % 8) == 0 && pad_wd == 1 && pad_ht == 1 */
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Default configurations for ESP32-S3
|
||||
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
|
||||
@@ -23,9 +23,7 @@
|
||||
#include "test_utils.h"
|
||||
|
||||
#if CONFIG_IDF_CMAKE
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define IDF_HEAP_CAPS 1
|
||||
#endif
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
#include "esp_heap_caps.h"
|
||||
@@ -140,11 +138,6 @@ void esp_nn_add_elementwise_s8_test()
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
|
||||
out_opt_orig == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto elementwise_add_test_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input1[i] = rand() % 256 - 128;
|
||||
@@ -201,10 +194,10 @@ elementwise_add_test_cleanup:
|
||||
if (input2_orig) {
|
||||
free(input2_orig);
|
||||
}
|
||||
if (out_c_orig) {
|
||||
if (out_data_c) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_opt_orig) {
|
||||
if (out_data_opt) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
}
|
||||
@@ -289,11 +282,6 @@ void esp_nn_mul_elementwise_s8_test()
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
|
||||
out_opt_orig == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto elementwise_mult_test_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input1[i] = rand() % 256 - 128;
|
||||
@@ -345,10 +333,10 @@ elementwise_mult_test_cleanup:
|
||||
if (input2_orig) {
|
||||
free(input2_orig);
|
||||
}
|
||||
if (out_c_orig) {
|
||||
if (out_data_c) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_opt_orig) {
|
||||
if (out_data_opt) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,8 @@
|
||||
#include "test_utils.h"
|
||||
|
||||
#if CONFIG_IDF_CMAKE
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define IDF_HEAP_CAPS 1
|
||||
#endif
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
@@ -45,8 +44,8 @@ void esp_nn_depthwise_conv_s8_test()
|
||||
uint16_t filter_ht, filter_wd, ch_mult;
|
||||
uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
|
||||
|
||||
// run for 15 iterations
|
||||
for (int itr = 0; itr < 15; itr++) {
|
||||
// run for 10 iterations
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
/* prepare data */
|
||||
switch (itr) {
|
||||
case 0: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0)
|
||||
@@ -145,52 +144,22 @@ void esp_nn_depthwise_conv_s8_test()
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 8: // same as case 7, with large parameters
|
||||
input_wd = 58;
|
||||
input_ht = 58;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 128;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 9: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
default:
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
input_wd = 4;
|
||||
input_ht = 4;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
stride_wd = rand() % 2 + 1;
|
||||
stride_ht = stride_wd;
|
||||
pad_wd = stride_wd == 1 ? 0 : rand() % 2;
|
||||
pad_ht = pad_wd;
|
||||
printf("stride(%d), pad (%d)\t", stride_wd, pad_wd);
|
||||
ch_mult = 4;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t out_wd = (input_wd - filter_wd + 1) / stride_wd;
|
||||
uint16_t out_ht = (input_ht - filter_ht + 1) / stride_ht;
|
||||
if (itr == 9) {
|
||||
// expect the function to handle this gracefully
|
||||
out_wd += 1;
|
||||
out_ht += 1;
|
||||
}
|
||||
int in_size = input_wd * input_ht * channels;
|
||||
int out_size = out_wd * out_ht * channels * ch_mult;
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult + 4;
|
||||
@@ -241,16 +210,9 @@ void esp_nn_depthwise_conv_s8_test()
|
||||
out_mult[i] = 0x7eb0e200 + rand() % 50;
|
||||
}
|
||||
|
||||
data_dims_t input_dims = {.width = input_wd, .height = input_ht, .channels = channels, 1};
|
||||
data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = channels * ch_mult, 1};
|
||||
data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
|
||||
dw_conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset, .ch_mult = ch_mult,
|
||||
.stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
|
||||
.dilation = {0, 0}, .activation = {activation_min, activation_max}};
|
||||
quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(&input_dims, &filter_dims,
|
||||
&output_dims, &conv_params);
|
||||
int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(input_wd, input_ht,
|
||||
channels, ch_mult,
|
||||
filter_wd, filter_ht);
|
||||
if (scratch_buf_size > 0) {
|
||||
#if IDF_HEAP_CAPS
|
||||
scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
@@ -272,8 +234,11 @@ void esp_nn_depthwise_conv_s8_test()
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_depthwise_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 4,
|
||||
bias + 1, &output_dims, out_data_c, &conv_params, &quant_data);
|
||||
esp_nn_depthwise_conv_s8_ansi(input, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data + 4, filter_wd, filter_ht,
|
||||
bias + 1, out_data_c, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
@@ -281,8 +246,11 @@ void esp_nn_depthwise_conv_s8_test()
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_depthwise_conv_s8(&input_dims, input, &filter_dims, filter_data + 4,
|
||||
bias + 1, &output_dims, out_data_opt, &conv_params, &quant_data);
|
||||
esp_nn_depthwise_conv_s8(input, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data + 4, filter_wd, filter_ht,
|
||||
bias + 1, out_data_opt, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
@@ -511,16 +479,8 @@ void esp_nn_conv_s8_test()
|
||||
out_mult[i] = 0x7f67f4f8 + rand() % 50;
|
||||
}
|
||||
|
||||
data_dims_t input_dims = {.width = in_wd, .height = in_ht, .channels = in_channels, 1};
|
||||
data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = out_channels, 1};
|
||||
data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
|
||||
conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset,
|
||||
.stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
|
||||
.dilation = {0, 0}, .activation = {activation_min, activation_max}};
|
||||
quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_conv_scratch_size(&input_dims, &filter_dims,
|
||||
&output_dims, &conv_params);
|
||||
int scratch_buf_size = esp_nn_get_conv_scratch_size(in_wd, in_ht, in_channels,
|
||||
out_channels, filter_wd, filter_ht);
|
||||
if (scratch_buf_size > 0) {
|
||||
#if IDF_HEAP_CAPS
|
||||
void *scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
@@ -542,8 +502,11 @@ void esp_nn_conv_s8_test()
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 2,
|
||||
bias, &output_dims, out_data_c, &conv_params, &quant_data);
|
||||
esp_nn_conv_s8_ansi(input, in_wd, in_ht, in_channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht,
|
||||
filter_data + 2, filter_wd, filter_ht, bias,
|
||||
out_data_c, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
@@ -551,8 +514,11 @@ void esp_nn_conv_s8_test()
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_conv_s8(&input_dims, input, &filter_dims, filter_data + 2,
|
||||
bias, &output_dims, out_data_opt, &conv_params, &quant_data);
|
||||
esp_nn_conv_s8(input, in_wd, in_ht, in_channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht,
|
||||
filter_data + 2, filter_wd, filter_ht, bias,
|
||||
out_data_opt, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
|
||||
BIN
code/components/esp-nn_20220724.zip
Normal file
BIN
code/components/esp-nn_20220724.zip
Normal file
Binary file not shown.
@@ -8,37 +8,26 @@ on:
|
||||
jobs:
|
||||
build-master:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
idf_target: ["esp32", "esp32s2", "esp32s3"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
uses: espressif/esp-idf-ci-action@latest
|
||||
with:
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_4:
|
||||
name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }}
|
||||
build-release-v4_0:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["v4.4"]
|
||||
idf_target: ["esp32", "esp32s2", "esp32s3"]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
uses: espressif/esp-idf-ci-action@release-v4.0
|
||||
with:
|
||||
esp_idf_version: ${{ matrix.idf_ver }}
|
||||
target: ${{ matrix.idf_target }}
|
||||
path: 'examples'
|
||||
|
||||
build-release-v4_1:
|
||||
@@ -76,3 +65,15 @@ jobs:
|
||||
uses: espressif/esp-idf-ci-action@release-v4.3
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
build-release-v3_3:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@release-v3.3
|
||||
with:
|
||||
path: 'examples'
|
||||
|
||||
@@ -10,10 +10,12 @@ jobs:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Upload component to the component registry
|
||||
uses: espressif/github-actions/upload_components@master
|
||||
with:
|
||||
name: "esp32-camera"
|
||||
version: "git"
|
||||
namespace: "espressif"
|
||||
version: ${{ github.ref_name }}
|
||||
service_url: ${{ secrets.IDF_COMPONENT_API_URL }}
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
||||
@@ -1,29 +1,5 @@
|
||||
# get IDF version for comparison
|
||||
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||
|
||||
# set conversion sources
|
||||
set(COMPONENT_SRCS
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
conversions/jpge.cpp
|
||||
conversions/esp_jpg_decode.c
|
||||
)
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS
|
||||
conversions/private_include
|
||||
)
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
driver/include
|
||||
conversions/include
|
||||
)
|
||||
|
||||
set(COMPONENT_REQUIRES driver)
|
||||
|
||||
# set driver sources only for supported platforms
|
||||
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3")
|
||||
list(APPEND COMPONENT_SRCS
|
||||
set(COMPONENT_SRCS
|
||||
driver/esp_camera.c
|
||||
driver/cam_hal.c
|
||||
driver/sccb.c
|
||||
@@ -38,14 +14,22 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
sensors/gc2145.c
|
||||
sensors/gc032a.c
|
||||
sensors/bf3005.c
|
||||
sensors/bf20a6.c
|
||||
sensors/sc101iot.c
|
||||
sensors/sc030iot.c
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
conversions/jpge.cpp
|
||||
conversions/esp_jpg_decode.c
|
||||
)
|
||||
|
||||
list(APPEND COMPONENT_PRIV_INCLUDEDIRS
|
||||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
driver/include
|
||||
conversions/include
|
||||
)
|
||||
|
||||
set(COMPONENT_PRIV_INCLUDEDIRS
|
||||
driver/private_include
|
||||
sensors/private_include
|
||||
conversions/private_include
|
||||
target/private_include
|
||||
)
|
||||
|
||||
@@ -74,13 +58,8 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
)
|
||||
endif()
|
||||
|
||||
set(COMPONENT_REQUIRES driver)
|
||||
set(COMPONENT_PRIV_REQUIRES freertos nvs_flash)
|
||||
|
||||
set(min_version_for_esp_timer "4.2")
|
||||
if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer)
|
||||
list(APPEND COMPONENT_PRIV_REQUIRES esp_timer)
|
||||
endif()
|
||||
|
||||
register_component()
|
||||
endif()
|
||||
|
||||
register_component()
|
||||
|
||||
@@ -69,45 +69,6 @@ menu "Camera configuration"
|
||||
help
|
||||
Enable this option if you want to use the BF3005.
|
||||
Disable this option to save memory.
|
||||
|
||||
config BF20A6_SUPPORT
|
||||
bool "Support BF20A6(BYD20A6) VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the BF20A6.
|
||||
Disable this option to save memory.
|
||||
|
||||
config SC101IOT_SUPPORT
|
||||
bool "Support SC101IOT HD"
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use the SC101IOT.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SC101_REGS_SELECT
|
||||
prompt "SC101iot default regs"
|
||||
default SC101IOT_720P_15FPS_ENABLED
|
||||
depends on SC101IOT_SUPPORT
|
||||
help
|
||||
Currently SC010iot has several register sets available.
|
||||
Select the one that matches your needs.
|
||||
|
||||
config SC101IOT_720P_15FPS_ENABLED
|
||||
bool "xclk20M_720p_15fps"
|
||||
help
|
||||
Select this option means that when xclk is 20M, the frame rate is 15fps at 720p resolution.
|
||||
config SC101IOT_VGA_25FPS_ENABLED
|
||||
bool "xclk20M_VGA_25fps"
|
||||
help
|
||||
Select this option means that when xclk is 20M, the frame rate is 25fps at VGA resolution.
|
||||
endchoice
|
||||
|
||||
config SC030IOT_SUPPORT
|
||||
bool "Support SC030IOT VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the SC030IOT.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SCCB_HARDWARE_I2C_PORT
|
||||
bool "I2C peripheral to use for SCCB"
|
||||
@@ -164,24 +125,5 @@ menu "Camera configuration"
|
||||
help
|
||||
Maximum value of DMA buffer
|
||||
Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent
|
||||
|
||||
config CAMERA_CONVERTER_ENABLED
|
||||
bool "Enable camera RGB/YUV converter"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use RGB565/YUV422/YUV420/YUV411 format conversion.
|
||||
|
||||
choice CAMERA_CONV_PROTOCOL
|
||||
bool "Camera converter protocol"
|
||||
depends on CAMERA_CONVERTER_ENABLED
|
||||
default LCD_CAM_CONV_BT601_ENABLED
|
||||
help
|
||||
Supports format conversion under both BT601 and BT709 standards.
|
||||
|
||||
config LCD_CAM_CONV_BT601_ENABLED
|
||||
bool "BT601"
|
||||
config LCD_CAM_CONV_BT709_ENABLED
|
||||
bool "BT709"
|
||||
endchoice
|
||||
endmenu
|
||||
|
||||
@@ -25,9 +25,6 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
|
||||
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/6.5" |
|
||||
| GC2145 | 1600 x 1200 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/5" |
|
||||
| BF3005 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/4" |
|
||||
| BF20A6 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer | 1/10" |
|
||||
| SC101IOT| 1280 x 720 | color | YUV/YCbCr422<br/>Raw RGB | 1/4.2" |
|
||||
| SC030IOT| 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer | 1/6.5" |
|
||||
|
||||
## Important to Remember
|
||||
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
#include "tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/tjpgd.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/tjpgd.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
@@ -61,7 +57,7 @@ static const char * jd_errors[] = {
|
||||
"Not supported JPEG standard"
|
||||
};
|
||||
|
||||
static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
{
|
||||
uint16_t x = rect->left;
|
||||
uint16_t y = rect->top;
|
||||
@@ -77,7 +73,7 @@ static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len)
|
||||
static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
|
||||
{
|
||||
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
|
||||
if (jpeg->len && len > (jpeg->len - jpeg->index)) {
|
||||
|
||||
@@ -29,12 +29,7 @@ namespace jpge {
|
||||
if(b){
|
||||
return b;
|
||||
}
|
||||
// check if SPIRAM is enabled and allocate on SPIRAM if allocatable
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
static inline void jpge_free(void *p) { free(p); }
|
||||
|
||||
|
||||
@@ -21,6 +21,19 @@
|
||||
#include "esp_jpg_decode.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/spiram.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "esp_spiram.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -59,12 +72,7 @@ typedef struct {
|
||||
|
||||
static void *_malloc(size_t size)
|
||||
{
|
||||
// check if SPIRAM is enabled and allocate on SPIRAM if allocatable
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
// try allocating in internal memory
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
//output buffer and image width
|
||||
@@ -160,7 +168,7 @@ static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16
|
||||
}
|
||||
|
||||
//input buffer
|
||||
static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
|
||||
{
|
||||
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
|
||||
if(buf) {
|
||||
|
||||
@@ -21,6 +21,21 @@
|
||||
#include "jpge.h"
|
||||
#include "yuv.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/spiram.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/spiram.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "esp_spiram.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define TAG ""
|
||||
@@ -35,12 +50,7 @@ static void *_malloc(size_t size)
|
||||
if(res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// check if SPIRAM is enabled and is allocatable
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static IRAM_ATTR void convert_line_format(uint8_t * src, pixformat_t format, uint8_t * dst, size_t width, size_t in_channels, size_t line)
|
||||
|
||||
@@ -18,21 +18,8 @@
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include "esp_timer.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/ets_sys.h"
|
||||
#endif
|
||||
#endif // ESP_IDF_VERSION_MAJOR
|
||||
#define ESP_CAMERA_ETS_PRINTF ets_printf
|
||||
|
||||
static const char *TAG = "cam_hal";
|
||||
|
||||
static cam_obj_t *cam_obj = NULL;
|
||||
|
||||
static const uint32_t JPEG_SOI_MARKER = 0xFFD8FF; // written in little-endian for esp32
|
||||
@@ -106,7 +93,7 @@ void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType
|
||||
if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
|
||||
ll_cam_stop(cam);
|
||||
cam->state = CAM_STATE_IDLE;
|
||||
ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC"));
|
||||
ESP_EARLY_LOGE(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,15 +57,6 @@
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
#include "bf3005.h"
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
#include "bf20a6.h"
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
#include "sc101iot.h"
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
#include "sc030iot.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -128,15 +119,6 @@ static const sensor_func_t g_sensors[] = {
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
{bf3005_detect, bf3005_init},
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
{bf20a6_detect, bf20a6_init},
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
{sc101iot_detect, sc101iot_init},
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
{sc030iot_detect, sc030iot_init},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
||||
@@ -236,23 +218,6 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
|
||||
{
|
||||
pixformat_t format = PIXFORMAT_RGB565;
|
||||
switch (conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
format = PIXFORMAT_YUV420;
|
||||
break;
|
||||
case YUV422_TO_RGB565: // default format is RGB565
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Convert to %d format enabled", format);
|
||||
return format;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
{
|
||||
esp_err_t err;
|
||||
@@ -291,7 +256,6 @@ esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
|
||||
s_state->sensor.status.framesize = frame_size;
|
||||
s_state->sensor.pixformat = pix_format;
|
||||
|
||||
ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
|
||||
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to set frame size");
|
||||
@@ -299,11 +263,6 @@ esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
goto fail;
|
||||
}
|
||||
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if(config->conv_mode) {
|
||||
s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s_state->sensor.id.PID == OV2640_PID) {
|
||||
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
#include "driver/ledc.h"
|
||||
#include "sensor.h"
|
||||
#include "sys/time.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -92,19 +91,6 @@ typedef enum {
|
||||
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
|
||||
} camera_fb_location_t;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
/**
|
||||
* @brief Camera RGB\YUV conversion mode
|
||||
*/
|
||||
typedef enum {
|
||||
CONV_DISABLE,
|
||||
RGB565_TO_YUV422,
|
||||
|
||||
YUV422_TO_RGB565,
|
||||
YUV422_TO_YUV420
|
||||
} camera_conv_mode_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
@@ -138,9 +124,6 @@ typedef struct {
|
||||
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
|
||||
camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
|
||||
camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
|
||||
#endif
|
||||
} camera_config_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,9 +27,6 @@ typedef enum {
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
BF3005_PID = 0x30,
|
||||
BF20A6_PID = 0x20a6,
|
||||
SC101IOT_PID = 0xda4a,
|
||||
SC030IOT_PID = 0x9a46,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -43,9 +40,6 @@ typedef enum {
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_BF3005,
|
||||
CAMERA_BF20A6,
|
||||
CAMERA_SC101IOT,
|
||||
CAMERA_SC030IOT,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
@@ -61,15 +55,11 @@ typedef enum {
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
BF3005_SCCB_ADDR = 0x6E,
|
||||
BF20A6_SCCB_ADDR = 0x6E,
|
||||
SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
PIXFORMAT_YUV422, // 2BPP/YUV422
|
||||
PIXFORMAT_YUV420, // 1.5BPP/YUV420
|
||||
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
|
||||
PIXFORMAT_JPEG, // JPEG/COMPRESSED
|
||||
PIXFORMAT_RGB888, // 3BPP/RGB888
|
||||
|
||||
@@ -25,11 +25,6 @@ static const char* TAG = "sccb";
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
|
||||
@@ -13,9 +13,6 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
|
||||
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false},
|
||||
{CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false},
|
||||
};
|
||||
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
#define BOARD_WROVER_KIT 1
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
|
||||
url: https://github.com/espressif/esp32-camera
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sccb.h"
|
||||
#include "bf20a6.h"
|
||||
#include "bf20a6_regs.h"
|
||||
#include "bf20a6_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "bf20a6";
|
||||
#endif
|
||||
|
||||
#define H8(v) ((v)>>8)
|
||||
#define L8(v) ((v)&0xff)
|
||||
|
||||
//#define REG_DEBUG_ON
|
||||
|
||||
static int read_reg(uint8_t slv_addr, const uint16_t reg)
|
||||
{
|
||||
int ret = SCCB_Read(slv_addr, reg);
|
||||
// ESP_LOGI(TAG, "READ Register 0x%02x VALUE: 0x%02x", reg, ret);
|
||||
#ifdef REG_DEBUG_ON
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
|
||||
{
|
||||
int ret = SCCB_Write(slv_addr, reg, value);
|
||||
#ifdef REG_DEBUG_ON
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PRINT_REG
|
||||
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
|
||||
{
|
||||
return (read_reg(slv_addr, reg) & mask) == mask;
|
||||
}
|
||||
|
||||
static void print_regs(uint8_t slv_addr)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ESP_LOGI(TAG, "REG list look ======================");
|
||||
for (size_t i = 0xf0; i <= 0xfe; i++) {
|
||||
ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
ESP_LOGI(TAG, "\npage 0 ===");
|
||||
write_reg(slv_addr, 0xfe, 0x00); // page 0
|
||||
for (size_t i = 0x03; i <= 0x24; i++) {
|
||||
ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
for (size_t i = 0x40; i <= 0x95; i++) {
|
||||
ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
ESP_LOGI(TAG, "\npage 3 ===");
|
||||
write_reg(slv_addr, 0xfe, 0x03); // page 3
|
||||
for (size_t i = 0x01; i <= 0x43; i++) {
|
||||
ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
|
||||
}
|
||||
}
|
||||
|
||||
static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
ret = read_reg(slv_addr, regs[i][0]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (!ret && regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret;
|
||||
// Software Reset: clear all registers and reset them to their default values
|
||||
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_YUV422:
|
||||
set_reg_bits(sensor, 0x12, 0, 1, 0);
|
||||
break;
|
||||
case PIXFORMAT_RAW:
|
||||
set_reg_bits(sensor, 0x12, 0, 1, 0x1);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "set_pix unsupport format");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sensor->pixformat = pixformat;
|
||||
ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret = 0;
|
||||
if (framesize > FRAMESIZE_VGA) {
|
||||
return -1;
|
||||
}
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
|
||||
// Write MSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, w >> 2);
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, h >> 2);
|
||||
|
||||
// Write LSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1b, 0);
|
||||
|
||||
if ((w <= 320) && (h <= 240)) {
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 4));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 4));
|
||||
|
||||
} else if ((w <= 640) && (h <= 480)) {
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 8));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 8));
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->status.hmirror = enable;
|
||||
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->status.vflip = enable;
|
||||
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = write_reg(sensor->slv_addr, 0xb6, value);
|
||||
if (ret == 0) {
|
||||
sensor->status.colorbar = value;
|
||||
ESP_LOGD(TAG, "Set colorbar to: %d", value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x70, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set sharpness to: %d", level);
|
||||
sensor->status.sharpness = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mask > 0xFF) {
|
||||
ESP_LOGE(TAG, "mask should not more than 0xff");
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if (ret > 0) {
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mask > 0xFF) {
|
||||
ESP_LOGE(TAG, "mask should not more than 0xff");
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
value = (ret & ~mask) | (value & mask);
|
||||
|
||||
if (mask > 0xFF) {
|
||||
|
||||
} else {
|
||||
ret = write_reg(sensor->slv_addr, reg, value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
// write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x6f);
|
||||
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0xd6);
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
|
||||
sensor->status.denoise = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x13);
|
||||
sensor->status.awb = 0;
|
||||
sensor->status.dcw = 0;
|
||||
sensor->status.agc = 0;
|
||||
sensor->status.aec = 0;
|
||||
sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
|
||||
sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
|
||||
sensor->status.colorbar = 0;
|
||||
sensor->status.bpc = 0;
|
||||
sensor->status.wpc = 0;
|
||||
sensor->status.raw_gma = 0;
|
||||
sensor->status.lenc = 0;
|
||||
sensor->status.quality = 0;
|
||||
sensor->status.special_effect = 0;
|
||||
sensor->status.wb_mode = 0;
|
||||
sensor->status.awb_gain = 0;
|
||||
sensor->status.agc_gain = 0;
|
||||
sensor->status.aec_value = 0;
|
||||
sensor->status.aec2 = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val)
|
||||
{
|
||||
ESP_LOGW(TAG, "dummy Unsupported");
|
||||
return -1;
|
||||
}
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
|
||||
{
|
||||
ESP_LOGW(TAG, "gainceiling Unsupported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int bf20a6_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (BF20A6_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (BF20A6_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bf20a6_init(sensor_t *sensor)
|
||||
{
|
||||
sensor->init_status = init_status;
|
||||
sensor->reset = reset;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_contrast = set_dummy;
|
||||
sensor->set_brightness = set_dummy;
|
||||
sensor->set_saturation = set_dummy;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_gainceiling = set_gainceiling_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_whitebal = set_dummy;
|
||||
sensor->set_gain_ctrl = set_dummy;
|
||||
sensor->set_exposure_ctrl = set_dummy;
|
||||
sensor->set_hmirror = set_hmirror; // set_hmirror;
|
||||
sensor->set_vflip = set_vflip; // set_vflip;
|
||||
|
||||
sensor->set_aec2 = set_dummy;
|
||||
sensor->set_awb_gain = set_dummy;
|
||||
sensor->set_agc_gain = set_dummy;
|
||||
sensor->set_aec_value = set_dummy;
|
||||
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
sensor->set_dcw = set_dummy;
|
||||
sensor->set_bpc = set_dummy;
|
||||
sensor->set_wpc = set_dummy;
|
||||
|
||||
sensor->set_raw_gma = set_dummy;
|
||||
sensor->set_lenc = set_dummy;
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_res_raw = NULL;
|
||||
sensor->set_pll = NULL;
|
||||
sensor->set_xclk = NULL;
|
||||
|
||||
ESP_LOGD(TAG, "BF20A6 Attached");
|
||||
return 0;
|
||||
}
|
||||
@@ -88,10 +88,10 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
|
||||
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
while (!ret && (i < regs_size)) {
|
||||
while (!ret && regs[i][0] != REGLIST_TAIL) {
|
||||
if (regs[i][0] == REG_DLY) {
|
||||
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
|
||||
} else {
|
||||
@@ -132,12 +132,11 @@ static int reset(sensor_t *sensor)
|
||||
ESP_LOGE(TAG, "Software Reset FAILED!");
|
||||
return ret;
|
||||
}
|
||||
|
||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs, sizeof(gc0308_sensor_default_regs)/(sizeof(uint8_t) * 2));
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Camera defaults loaded");
|
||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
write_reg(sensor->slv_addr, 0xfe, 0x00);
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1); //frequency division for esp32, ensure pclk <= 15MHz
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
#ifndef __BF20A6_H__
|
||||
#define __BF20A6_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int bf20a6_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int bf20a6_init(sensor_t *sensor);
|
||||
|
||||
#endif // __BF20A6_H__
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* BF20A6 register definitions.
|
||||
*/
|
||||
#ifndef __BF20A6_REG_REGS_H__
|
||||
#define __BF20A6_REG_REGS_H__
|
||||
|
||||
#define SENSOR_ID_HIGH 0XFC
|
||||
#define SENSOR_ID_LOW 0XFD
|
||||
#define RESET_RELATED 0XF2
|
||||
|
||||
|
||||
#endif //__BF20A6_REG_REGS_H__
|
||||
@@ -1,158 +0,0 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0xffff /* Array end token */
|
||||
|
||||
static const uint16_t bf20a6_default_init_regs[][2] = {
|
||||
{0xf2,0x01},
|
||||
{0x12,0x20},
|
||||
{0x3a,0x00},
|
||||
{0xe1,0x92},
|
||||
{0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
|
||||
{0xe0,0x00},
|
||||
{0x2a,0x98},
|
||||
{0xcd,0x17},
|
||||
{0xc0,0x10},
|
||||
{0xc6,0x1d},
|
||||
{0x10,0x35},
|
||||
{0xe2,0x09},
|
||||
{0xe4,0x72},
|
||||
{0xe5,0x22},
|
||||
{0xe6,0x24},
|
||||
{0xe7,0x64},
|
||||
{0xe8,0xa2}, // DVP:a2}, SPI:f2 VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
|
||||
{0x4a,0x00},
|
||||
{0x00,0x03},
|
||||
{0x1f,0x02},
|
||||
{0x22,0x02},
|
||||
{0x0c,0x31},
|
||||
|
||||
{0x00,0x00},
|
||||
{0x60,0x81},
|
||||
{0x61,0x81},
|
||||
|
||||
{0xa0,0x08},
|
||||
{0x01,0x1a},
|
||||
// {0x01,0x1a},
|
||||
// {0x01,0x1a},
|
||||
// {0x02,0x15},
|
||||
// {0x02,0x15},
|
||||
{0x02,0x15},
|
||||
{0x13,0x08},
|
||||
{0x8a,0x96},
|
||||
{0x8b,0x06},
|
||||
{0x87,0x18},
|
||||
|
||||
|
||||
{0x34,0x48}, // lens
|
||||
{0x35,0x40},
|
||||
{0x36,0x40},
|
||||
|
||||
{0x71,0x44},
|
||||
{0x72,0x48},
|
||||
{0x74,0xa2},
|
||||
{0x75,0xa9},
|
||||
{0x78,0x12},
|
||||
{0x79,0xa0},
|
||||
{0x7a,0x94},
|
||||
{0x7c,0x97},
|
||||
{0x40,0x30},
|
||||
{0x41,0x30},
|
||||
{0x42,0x28},
|
||||
{0x43,0x1f},
|
||||
{0x44,0x1c},
|
||||
{0x45,0x16},
|
||||
{0x46,0x13},
|
||||
{0x47,0x10},
|
||||
{0x48,0x0D},
|
||||
{0x49,0x0C},
|
||||
{0x4B,0x0A},
|
||||
{0x4C,0x0B},
|
||||
{0x4E,0x09},
|
||||
{0x4F,0x08},
|
||||
{0x50,0x08},
|
||||
|
||||
|
||||
{0x5f,0x29},
|
||||
{0x23,0x33},
|
||||
{0xa1,0x10}, // AWB
|
||||
{0xa2,0x0d},
|
||||
{0xa3,0x30},
|
||||
{0xa4,0x06},
|
||||
{0xa5,0x22},
|
||||
{0xa6,0x56},
|
||||
{0xa7,0x18},
|
||||
{0xa8,0x1a},
|
||||
{0xa9,0x12},
|
||||
{0xaa,0x12},
|
||||
{0xab,0x16},
|
||||
{0xac,0xb1},
|
||||
{0xba,0x12},
|
||||
{0xbb,0x12},
|
||||
{0xad,0x12},
|
||||
{0xae,0x56},
|
||||
{0xaf,0x0a},
|
||||
{0x3b,0x30},
|
||||
{0x3c,0x12},
|
||||
{0x3d,0x22},
|
||||
{0x3e,0x3f},
|
||||
{0x3f,0x28},
|
||||
{0xb8,0xc3},
|
||||
{0xb9,0xa3},
|
||||
{0x39,0x47}, // pure color threshold
|
||||
{0x26,0x13},
|
||||
{0x27,0x16},
|
||||
{0x28,0x14},
|
||||
{0x29,0x18},
|
||||
{0xee,0x0d},
|
||||
|
||||
|
||||
{0x13,0x05},
|
||||
{0x24,0x3C},
|
||||
{0x81,0x20},
|
||||
{0x82,0x40},
|
||||
{0x83,0x30},
|
||||
{0x84,0x58},
|
||||
{0x85,0x30},
|
||||
{0x92,0x08},
|
||||
{0x86,0x80},
|
||||
{0x8a,0x96},
|
||||
{0x91,0xff},
|
||||
{0x94,0x62},
|
||||
{0x9a,0x18}, // outdoor threshold
|
||||
{0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
|
||||
{0x51,0x17}, // color normal
|
||||
{0x52,0x03},
|
||||
{0x53,0x5F},
|
||||
{0x54,0x47},
|
||||
{0x55,0x66},
|
||||
{0x56,0x0F},
|
||||
{0x7e,0x14},
|
||||
{0x57,0x36}, // color
|
||||
{0x58,0x2A},
|
||||
{0x59,0xAA},
|
||||
{0x5a,0xA8},
|
||||
{0x5b,0x43},
|
||||
{0x5c,0x10},
|
||||
{0x5d,0x00},
|
||||
{0x7d,0x36},
|
||||
{0x5e,0x10},
|
||||
|
||||
{0xd6,0x88}, // contrast
|
||||
{0xd5,0x20}, // bright
|
||||
{0xb0,0x84}, // low light ctrl in gray section
|
||||
{0xb5,0x08}, // the threshold of GLB_GAIN
|
||||
{0xb1,0xc8}, // saturation
|
||||
{0xb2,0xc0},
|
||||
{0xb3,0xd0},
|
||||
{0xb4,0xB0},
|
||||
|
||||
{0x32,0x10},
|
||||
// {0x8a,0x00},
|
||||
// {0x8b,0x10},
|
||||
{0xa0,0x09},
|
||||
{0x00,0x03},
|
||||
{0x0b,0x02},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define REG_DLY 0xff
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000 /* Array end token */
|
||||
|
||||
static const uint8_t gc0308_sensor_default_regs[][2] = {
|
||||
static const uint16_t gc0308_sensor_default_regs[][2] = {
|
||||
{0xfe, 0x00},
|
||||
{0xec, 0x20},
|
||||
{0x05, 0x00},
|
||||
@@ -238,21 +239,7 @@ static const uint8_t gc0308_sensor_default_regs[][2] = {
|
||||
{0x65, 0xd3},
|
||||
{0x66, 0x60},
|
||||
{0xfe, 0x00},
|
||||
|
||||
{0x01, 0x32}, //frame setting
|
||||
{0x02, 0x0c},
|
||||
{0x0f, 0x01},
|
||||
{0xe2, 0x00},
|
||||
{0xe3, 0x78},
|
||||
{0xe4, 0x00},
|
||||
{0xe5, 0xfe},
|
||||
{0xe6, 0x01},
|
||||
{0xe7, 0xe0},
|
||||
{0xe8, 0x01},
|
||||
{0xe9, 0xe0},
|
||||
{0xea, 0x01},
|
||||
{0xeb, 0xe0},
|
||||
{0xfe, 0x00},
|
||||
{REGLIST_TAIL, 0x00},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,8 +42,7 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
|
||||
|
||||
//sys reset
|
||||
{0x3000, 0x20}, // reset MCU
|
||||
{REG_DLY, 10}, // delay 10ms
|
||||
{0x3000, 0x00},
|
||||
{0x3002, 0x1c},
|
||||
|
||||
//clock enable
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* SC030IOT DVP driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SC030IOT_H__
|
||||
#define __SC030IOT_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int sc030iot_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int sc030iot_init(sensor_t *sensor);
|
||||
|
||||
#endif // __SC030IOT_H__
|
||||
@@ -1,491 +0,0 @@
|
||||
//version: V01P00_20220303
|
||||
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
|
||||
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
|
||||
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
|
||||
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
|
||||
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
|
||||
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
|
||||
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
|
||||
//pin :BIT0 pwdn// BIT1:reset
|
||||
//avdd 0:3.3V// 1:2.5V// 2:1.8V
|
||||
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
|
||||
|
||||
/*
|
||||
[DataBase]
|
||||
DBName=Dothinkey
|
||||
|
||||
[Vendor]
|
||||
VendorName=SmartSens
|
||||
|
||||
[Sensor]
|
||||
SensorName=SC031IOT
|
||||
width=640
|
||||
height=480
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xfa
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x46
|
||||
outformat=0
|
||||
mclk=20
|
||||
avdd=2.80000
|
||||
dovdd=2.800000
|
||||
dvdd=1.5
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.0000
|
||||
VPP=0.000000
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sc030iot_default_init_regs[][2] = {
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xff},
|
||||
{0x22, 0x07},
|
||||
{0x19, 0xff},
|
||||
{0x3f, 0x82},
|
||||
{0x30, 0x02},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0x72, 0x20},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x75, 0x10},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x88},
|
||||
{0x78, 0xe1},
|
||||
{0x79, 0x01},
|
||||
{0xf5, 0x01},
|
||||
{0xf4, 0x0a},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x79},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x33},
|
||||
{0x02, 0x12},
|
||||
{0x7c, 0x02},
|
||||
{0x7d, 0x0e},
|
||||
{0xa2, 0x04},
|
||||
{0x5e, 0x06},
|
||||
{0x5f, 0x0a},
|
||||
{0x0b, 0x58},
|
||||
{0x06, 0x38},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x1c},
|
||||
{0xf0, 0x37},
|
||||
{0x22, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xb1, 0x80},
|
||||
{0x34, 0x40},
|
||||
{0x0b, 0x54},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x01, 0x10},
|
||||
{0x0b, 0x6c},
|
||||
{0x06, 0x24},
|
||||
{0xf0, 0x36},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x40},
|
||||
{0xa7, 0x47},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x51},
|
||||
{0xa9, 0x44},
|
||||
{0xe9, 0x36},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x64, 0x17},
|
||||
{0x90, 0x01},
|
||||
{0x91, 0x03},
|
||||
{0x92, 0x07},
|
||||
{0x01, 0x10},
|
||||
{0x93, 0x10},
|
||||
{0x94, 0x10},
|
||||
{0x95, 0x10},
|
||||
{0x96, 0x01},
|
||||
{0x97, 0x07},
|
||||
{0x98, 0x1f},
|
||||
{0x99, 0x10},
|
||||
{0x9a, 0x20},
|
||||
{0x9b, 0x28},
|
||||
{0x9c, 0x28},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0x54},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x41},
|
||||
{0xb8, 0x43},
|
||||
{0xb9, 0x47},
|
||||
{0xba, 0x4f},
|
||||
{0xb0, 0x8b},
|
||||
{0xb1, 0x8b},
|
||||
{0xb2, 0x8b},
|
||||
{0xb3, 0x9b},
|
||||
{0xb4, 0xb8},
|
||||
{0xb5, 0xf0},
|
||||
{0x7e, 0x41},
|
||||
{0x7f, 0x47},
|
||||
{0x77, 0x80},
|
||||
{0x78, 0x84},
|
||||
{0x79, 0x8a},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x43},
|
||||
{0x97, 0x44},
|
||||
{0x98, 0x54},
|
||||
{0xf0, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x74},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0xa1},
|
||||
{0xf0, 0x36},//cvbs_off
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x21},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x00},
|
||||
{0xea, 0x09},
|
||||
{0xeb, 0x03},
|
||||
{0xec, 0x19},
|
||||
{0xed, 0x38},
|
||||
{0xe9, 0x30},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0xb1, 0x00},
|
||||
{0xf0, 0x00},
|
||||
{0xe0, 0x04},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x32, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x3e, 0xe0},
|
||||
{0x70, 0x56},
|
||||
{0x7c, 0x43},
|
||||
{0x7d, 0x47},
|
||||
{0x74, 0x00},
|
||||
{0x75, 0x00},
|
||||
{0x76, 0x00},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x22},
|
||||
{0x97, 0x22},
|
||||
{0x98, 0x22},
|
||||
{0xf0, 0x00},
|
||||
{0x72, 0x38},
|
||||
{0x7a, 0x80},
|
||||
{0x85, 0x18},
|
||||
{0x9b, 0x35},
|
||||
{0x9e, 0x20},
|
||||
{0xd0, 0x66},
|
||||
{0xd1, 0x34},
|
||||
{0Xd3, 0x44},
|
||||
{0xd6, 0x44},
|
||||
{0xb0, 0x41},
|
||||
{0xb2, 0x48},
|
||||
{0xb3, 0xf4},
|
||||
{0xb4, 0x0b},
|
||||
{0xb5, 0x78},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xc0},
|
||||
{0xbc, 0x90},
|
||||
{0xbd, 0x3a},
|
||||
{0xc1, 0x67},
|
||||
{0xf0, 0x01},
|
||||
{0x20, 0x11},
|
||||
{0x23, 0x90},
|
||||
{0x24, 0x15},
|
||||
{0x25, 0x87},
|
||||
{0xbc, 0x9f},
|
||||
{0xbd, 0x3a},
|
||||
{0x48, 0xe6},
|
||||
{0x49, 0xc0},
|
||||
{0x4a, 0xd0},
|
||||
{0x4b, 0x48},
|
||||
|
||||
// [cvbs_on]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xf1},
|
||||
|
||||
// [cvbs_off]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
};
|
||||
|
||||
/*
|
||||
[Sensor]
|
||||
SensorName=SC031IOT
|
||||
width=640
|
||||
height=480
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xfa
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x46
|
||||
outformat=0
|
||||
mclk=27
|
||||
avdd=2.80000
|
||||
dovdd=2.800000
|
||||
dvdd=1.5
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.0000
|
||||
VPP=0.000000
|
||||
*/
|
||||
/* 27M MCLK, 30fps
|
||||
static const uint8_t sc030iot_default_init_regs[][2] = {
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xff},
|
||||
{0x22, 0x07},
|
||||
{0x19, 0xff},
|
||||
{0x3f, 0x82},
|
||||
{0x30, 0x02},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0x72, 0x20},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x75, 0x10},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x88},
|
||||
{0x78, 0xe1},
|
||||
{0x79, 0x01},
|
||||
{0xf5, 0x01},
|
||||
{0xf4, 0x0a},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x79},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x33},
|
||||
{0x02, 0x12},
|
||||
{0x7c, 0x02},
|
||||
{0x7d, 0x0e},
|
||||
{0xa2, 0x04},
|
||||
{0x5e, 0x06},
|
||||
{0x5f, 0x0a},
|
||||
{0x0b, 0x58},
|
||||
{0x06, 0x38},
|
||||
{0xf0, 0x32},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x1c},
|
||||
{0xf0, 0x37},
|
||||
{0x22, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xb1, 0x80},
|
||||
{0x34, 0x40},
|
||||
{0x0b, 0x54},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x01, 0x10},
|
||||
{0x0b, 0x6c},
|
||||
{0x06, 0x24},
|
||||
{0xf0, 0x36},
|
||||
{0x31, 0x82},
|
||||
{0x3e, 0x60},
|
||||
{0x30, 0xf0},
|
||||
{0x33, 0x33},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x40},
|
||||
{0xa7, 0x47},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x51},
|
||||
{0xa9, 0x44},
|
||||
{0xe9, 0x36},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x51},
|
||||
{0x64, 0x17},
|
||||
{0x90, 0x01},
|
||||
{0x91, 0x03},
|
||||
{0x92, 0x07},
|
||||
{0x01, 0x10},
|
||||
{0x93, 0x10},
|
||||
{0x94, 0x10},
|
||||
{0x95, 0x10},
|
||||
{0x96, 0x01},
|
||||
{0x97, 0x07},
|
||||
{0x98, 0x1f},
|
||||
{0x99, 0x10},
|
||||
{0x9a, 0x20},
|
||||
{0x9b, 0x28},
|
||||
{0x9c, 0x28},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0x54},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x41},
|
||||
{0xb8, 0x43},
|
||||
{0xb9, 0x47},
|
||||
{0xba, 0x4f},
|
||||
{0xb0, 0x8b},
|
||||
{0xb1, 0x8b},
|
||||
{0xb2, 0x8b},
|
||||
{0xb3, 0x9b},
|
||||
{0xb4, 0xb8},
|
||||
{0xb5, 0xf0},
|
||||
{0x7e, 0x41},
|
||||
{0x7f, 0x47},
|
||||
{0x77, 0x80},
|
||||
{0x78, 0x84},
|
||||
{0x79, 0x8a},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x43},
|
||||
{0x97, 0x44},
|
||||
{0x98, 0x54},
|
||||
{0xf0, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0x74},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0x93},
|
||||
{0xf0, 0x36},//cvbs_off
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x21},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x00},
|
||||
{0xe9, 0x2c},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0xb1, 0x00},
|
||||
{0xf0, 0x00},
|
||||
{0xe0, 0x04},
|
||||
{0xf0, 0x01},
|
||||
{0x73, 0x00},
|
||||
{0x74, 0xe0},
|
||||
{0x70, 0x00},
|
||||
{0x71, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x32, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x3e, 0xe0},
|
||||
{0x70, 0x56},
|
||||
{0x7c, 0x43},
|
||||
{0x7d, 0x47},
|
||||
{0x74, 0x00},
|
||||
{0x75, 0x00},
|
||||
{0x76, 0x00},
|
||||
{0xa0, 0x47},
|
||||
{0xa1, 0x5f},
|
||||
{0x96, 0x22},
|
||||
{0x97, 0x22},
|
||||
{0x98, 0x22},
|
||||
{0xf0, 0x00},
|
||||
{0x72, 0x38},
|
||||
{0x7a, 0x80},
|
||||
{0x85, 0x18},
|
||||
{0x9b, 0x35},
|
||||
{0x9e, 0x20},
|
||||
{0xd0, 0x66},
|
||||
{0xd1, 0x34},
|
||||
{0Xd3, 0x44},
|
||||
{0xd6, 0x44},
|
||||
{0xb0, 0x41},
|
||||
{0xb2, 0x48},
|
||||
{0xb3, 0xf4},
|
||||
{0xb4, 0x0b},
|
||||
{0xb5, 0x78},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xc0},
|
||||
{0xbc, 0x90},
|
||||
{0xbd, 0x3a},
|
||||
{0xc1, 0x67},
|
||||
{0xf0, 0x01},
|
||||
{0x20, 0x11},
|
||||
{0x23, 0x90},
|
||||
{0x24, 0x15},
|
||||
{0x25, 0x87},
|
||||
{0xbc, 0x9f},
|
||||
{0xbd, 0x3a},
|
||||
{0x48, 0xe6},
|
||||
{0x49, 0xc0},
|
||||
{0x4a, 0xd0},
|
||||
{0x4b, 0x48},
|
||||
|
||||
// [cvbs_on]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x00},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xf1},
|
||||
|
||||
// [cvbs_off]
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x01},
|
||||
{0x79, 0xc1},
|
||||
};
|
||||
|
||||
*/
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* SC101IOT DVP driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SC101IOT_H__
|
||||
#define __SC101IOT_H__
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int sc101iot_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int sc101iot_init(sensor_t *sensor);
|
||||
|
||||
#endif // __SC101IOT_H__
|
||||
@@ -1,257 +0,0 @@
|
||||
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
|
||||
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
|
||||
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
|
||||
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
|
||||
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
|
||||
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
|
||||
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
|
||||
//pin :BIT0 pwdn// BIT1:reset
|
||||
//avdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
|
||||
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
|
||||
/*
|
||||
[DataBase]
|
||||
DBName=DemoSens
|
||||
|
||||
[Vendor]
|
||||
VendorName=SmartSens
|
||||
I2C_CRC=0
|
||||
|
||||
[Sensor]
|
||||
SensorName=SC101AP_raw
|
||||
width=1280
|
||||
height=720
|
||||
port=1
|
||||
type=2
|
||||
pin=3
|
||||
SlaveID=0xd0
|
||||
mode=0
|
||||
FlagReg=0xf7
|
||||
FlagMask=0xff
|
||||
FlagData=0xda
|
||||
FlagReg1=0xf8
|
||||
FlagMask1=0xff
|
||||
FlagData1=0x4a
|
||||
outformat=0
|
||||
mclk=20
|
||||
avdd=2.800000
|
||||
dovdd=2.800000
|
||||
dvdd=1.200000
|
||||
|
||||
Ext0=0
|
||||
Ext1=0
|
||||
Ext2=0
|
||||
AFVCC=0.00
|
||||
VPP=0.000000
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sc101iot_default_init_regs[][2] = {
|
||||
#if CONFIG_SC101IOT_720P_15FPS_ENABLED // 720P+YUV422+15FPS sensor default regs
|
||||
/* Here are some test results:
|
||||
# size xclk fps pic pclk
|
||||
# ------- ------- ------ --------- ------- --- --- --- --- ---
|
||||
# 720p 4 3 err
|
||||
# 720p 8 5 normal 15
|
||||
# 720p 10 7.8 normal 19
|
||||
# 720p 20 15 warning 37.5
|
||||
# VGA 8 6 normal
|
||||
# VGA 20 16 normal
|
||||
|
||||
*/
|
||||
{0xf0, 0x30},
|
||||
{0x01, 0xff},
|
||||
{0x02, 0xe0},
|
||||
{0x30, 0x10},
|
||||
{0x3f, 0x81},
|
||||
{0xf0, 0x00},
|
||||
{0x70, 0x6b},
|
||||
{0x72, 0x30},
|
||||
{0x84, 0xb4},
|
||||
{0x8b, 0x00},
|
||||
{0x8c, 0x20},
|
||||
{0x8d, 0x02},
|
||||
{0x8e, 0xec},
|
||||
{0x9e, 0x10},
|
||||
{0xb0, 0xc1},
|
||||
{0xc8, 0x10},
|
||||
{0xc9, 0x10},
|
||||
{0xc6, 0x00},
|
||||
{0xe0, 0x0f},
|
||||
{0xb5, 0xf0},
|
||||
{0xde, 0x80},
|
||||
{0xb5, 0xf0},
|
||||
{0xde, 0x80},
|
||||
{0xb2, 0x50},
|
||||
{0xb3, 0xfc},
|
||||
{0xb4, 0x40},
|
||||
{0xb5, 0xc0},
|
||||
{0xb6, 0x50},
|
||||
{0xb7, 0xfc},
|
||||
{0xb8, 0x40},
|
||||
{0xb9, 0xc0},
|
||||
{0xba, 0xff},
|
||||
{0xbb, 0xcc},
|
||||
{0xbc, 0xa9},
|
||||
{0xbd, 0x7d},
|
||||
{0xc1, 0x77},
|
||||
{0xf0, 0x01},
|
||||
{0x70, 0x02},
|
||||
{0x71, 0x02},
|
||||
{0x72, 0x50},
|
||||
{0x73, 0x02},
|
||||
{0x74, 0xd2},
|
||||
{0x75, 0x20},
|
||||
{0x76, 0x81},
|
||||
{0x77, 0x8c},
|
||||
{0x78, 0x81},
|
||||
{0xf4, 0x01},
|
||||
{0xf5, 0x00},
|
||||
{0xf6, 0x00},
|
||||
{0xf0, 0x36},
|
||||
{0x40, 0x03},
|
||||
{0x41, 0x01},
|
||||
{0xf0, 0x39},
|
||||
{0x02, 0x70},
|
||||
{0xf0, 0x32},
|
||||
{0x41, 0x00},
|
||||
{0x43, 0x01},
|
||||
{0x48, 0x02},
|
||||
{0xf0, 0x45},
|
||||
{0x09, 0x20},
|
||||
{0xf0, 0x33},
|
||||
{0x33, 0x10},
|
||||
{0xf0, 0x30},
|
||||
{0x38, 0x44},
|
||||
{0xf0, 0x39},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x19},
|
||||
{0x47, 0x00},
|
||||
{0x48, 0x00},
|
||||
{0xf0, 0x37},
|
||||
{0x24, 0x31},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x02},
|
||||
{0xa6, 0x51},
|
||||
{0xa7, 0x57},
|
||||
{0xe8, 0x5f},
|
||||
{0xa8, 0x50},
|
||||
{0xa9, 0x50},
|
||||
{0xe9, 0x50},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x58},
|
||||
{0xb2, 0x78},
|
||||
{0xf0, 0x34},
|
||||
{0x9f, 0x03},
|
||||
{0xa6, 0x51},
|
||||
{0xa7, 0x57},
|
||||
{0xaa, 0x01},
|
||||
{0xab, 0x28},
|
||||
{0xac, 0x01},
|
||||
{0xad, 0x38},
|
||||
{0xf0, 0x33},
|
||||
{0x0a, 0x01},
|
||||
{0x0b, 0x28},
|
||||
{0xf0, 0x33},
|
||||
{0x64, 0x0f},
|
||||
{0xec, 0x51},
|
||||
{0xed, 0x57},
|
||||
{0x06, 0x58},
|
||||
{0xe9, 0x58},
|
||||
{0xeb, 0x68},
|
||||
{0xf0, 0x33},
|
||||
{0x64, 0x0f},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0xb6, 0x40},
|
||||
{0xb7, 0x51},
|
||||
{0xb8, 0x53},
|
||||
{0xb9, 0x57},
|
||||
{0xba, 0x5f},
|
||||
{0xb0, 0x84},
|
||||
{0xb1, 0x82},
|
||||
{0xb2, 0x84},
|
||||
{0xb3, 0x88},
|
||||
{0xb4, 0x90},
|
||||
{0xb5, 0x90},
|
||||
{0xf0, 0x36},
|
||||
{0x7e, 0x50},
|
||||
{0x7f, 0x51},
|
||||
{0x77, 0x81},
|
||||
{0x78, 0x86},
|
||||
{0x79, 0x89},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0x9c, 0x51},
|
||||
{0x9d, 0x57},
|
||||
{0x90, 0x54},
|
||||
{0x91, 0x54},
|
||||
{0x92, 0x56},
|
||||
{0xf0, 0x36},
|
||||
{0xa0, 0x51},
|
||||
{0xa1, 0x57},
|
||||
{0x96, 0x33},
|
||||
{0x97, 0x43},
|
||||
{0x98, 0x43},
|
||||
{0xf0, 0x36},
|
||||
{0x70, 0xdf},
|
||||
{0x7c, 0x40},
|
||||
{0x7d, 0x53},
|
||||
{0x74, 0xd0},
|
||||
{0x75, 0xf0},
|
||||
{0x76, 0xf0},
|
||||
{0xf0, 0x37},
|
||||
{0x0f, 0xd5},
|
||||
{0x7a, 0x40},
|
||||
{0x7b, 0x57},
|
||||
{0x71, 0x09},
|
||||
{0x72, 0x09},
|
||||
{0x73, 0x05},
|
||||
{0xf0, 0x33},
|
||||
{0x01, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0xfb},
|
||||
{0xf0, 0x36},
|
||||
{0x3c, 0x0d},
|
||||
{0xf0, 0x33},
|
||||
{0x14, 0x95},
|
||||
{0xf0, 0x33},
|
||||
{0x8f, 0x80},
|
||||
{0xf0, 0x37},
|
||||
{0x27, 0x14},
|
||||
{0x28, 0x03},
|
||||
{0xf0, 0x36},
|
||||
{0x37, 0xf4},
|
||||
{0xf0, 0x33},
|
||||
{0x01, 0x44},
|
||||
{0xf0, 0x36},
|
||||
{0x79, 0x89},
|
||||
{0xf0, 0x34},
|
||||
{0xac, 0x01},
|
||||
{0xad, 0x40},
|
||||
{0xf0, 0x33},
|
||||
{0xeb, 0x70},
|
||||
{0xf0, 0x34},
|
||||
{0xa8, 0x50},
|
||||
{0xa9, 0x50},
|
||||
{0xf0, 0x33},
|
||||
{0xb3, 0x58},
|
||||
{0xf0, 0x36},
|
||||
{0x11, 0x80},
|
||||
{0xf0, 0x36},
|
||||
{0x41, 0x51},
|
||||
{0xf0, 0x3f},
|
||||
{0x03, 0x09},
|
||||
{0xf0, 0x32},
|
||||
{0x0c, 0x06},
|
||||
{0x0d, 0x82},
|
||||
{0x0e, 0x02},
|
||||
{0x0f, 0xee},
|
||||
{0xf0, 0x36},
|
||||
{0xea, 0x09},
|
||||
{0xeb, 0xf5},
|
||||
{0xec, 0x11},
|
||||
{0xed, 0x27},
|
||||
{0xe9, 0x20},
|
||||
#endif
|
||||
};
|
||||
@@ -1,335 +0,0 @@
|
||||
/*
|
||||
* SC030IOT driver.
|
||||
*
|
||||
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "sc030iot.h"
|
||||
#include "sc030iot_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "sc030";
|
||||
#endif
|
||||
|
||||
#define SC030_SENSOR_ID_HIGH_REG 0XF7
|
||||
#define SC030_SENSOR_ID_LOW_REG 0XF8
|
||||
#define SC030_MAX_FRAME_WIDTH (640)
|
||||
#define SC030_MAX_FRAME_HIGH (480)
|
||||
|
||||
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg_low);
|
||||
if(ret > 0){
|
||||
ret &= reg_value_mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
|
||||
{
|
||||
int i=0, res = 0;
|
||||
while (i<regs_entry_len) {
|
||||
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = get_reg(sensor, reg, 0xff);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
|
||||
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
|
||||
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable test pattern mode
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
|
||||
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
|
||||
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
|
||||
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
|
||||
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
|
||||
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
|
||||
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_aec_value(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
|
||||
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
|
||||
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
|
||||
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_saturation(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
|
||||
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
|
||||
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
|
||||
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = set_regs(sensor, sc030iot_default_init_regs, sizeof(sc030iot_default_init_regs)/(sizeof(uint8_t) * 2));
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
|
||||
// ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "reset fail");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
|
||||
{
|
||||
int ret = 0;
|
||||
//sc:H_start={0x0172[1:0],0x0170},H_end={0x0172[5:4],0x0171},
|
||||
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x03) | (((offset_x+w)>>4)&0x30));
|
||||
|
||||
//sc:V_start={0x0175[1:0],0x0173},H_end={0x0175[5:4],0x0174},
|
||||
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x03) | (((offset_y+h)>>4)&0x30));
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
if(w>SC030_MAX_FRAME_WIDTH || h > SC030_MAX_FRAME_HIGH) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (640-w) /2;
|
||||
uint16_t offset_y = (480-h) /2;
|
||||
|
||||
if(set_window(sensor, offset_x, offset_y, w, h)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
return 0;
|
||||
err:
|
||||
ESP_LOGE(TAG, "frame size err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
case PIXFORMAT_RAW:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
ESP_LOGE(TAG, "Not support");
|
||||
break;
|
||||
case PIXFORMAT_YUV422: // For now, sc030/sc031 sensor only support YUV422.
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc030iot_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (SC030IOT_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SC030_SENSOR_ID_LOW_REG);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SC030_SENSOR_ID_HIGH_REG);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (SC030IOT_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc030iot_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
|
||||
sensor->set_saturation= set_saturation;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
sensor->set_aec_value = set_aec_value;
|
||||
sensor->set_awb_gain = set_awb_gain;
|
||||
sensor->set_contrast = set_contrast;
|
||||
//not supported
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "sc030iot Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
/*
|
||||
* SC101IOT driver.
|
||||
*
|
||||
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "sc101iot.h"
|
||||
#include "sc101iot_settings.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "sc101";
|
||||
#endif
|
||||
|
||||
#define SC101_SENSOR_ID_HIGH_REG 0XF7
|
||||
#define SC101_SENSOR_ID_LOW_REG 0XF8
|
||||
#define SC101_MAX_FRAME_WIDTH (1280)
|
||||
#define SC101_MAX_FRAME_HIGH (720)
|
||||
|
||||
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Read(sensor->slv_addr, reg_low);
|
||||
if(ret > 0){
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
|
||||
// For more information please refer to the Technical Reference Manual.
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t reg_high = (reg>>8) & 0xFF;
|
||||
uint8_t reg_low = reg & 0xFF;
|
||||
|
||||
if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
|
||||
{
|
||||
int i=0, res = 0;
|
||||
while (i<regs_entry_len) {
|
||||
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = get_reg(sensor, reg, 0xff);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
|
||||
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
|
||||
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // enable mirror
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // disable mirror
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if(enable) {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
|
||||
} else {
|
||||
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable colorbar mode
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_raw_gma(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 1, 1, enable & 0xff); // enable gamma compensation
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
|
||||
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
|
||||
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int gain)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
|
||||
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
|
||||
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
|
||||
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
|
||||
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_aec_value(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
|
||||
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
|
||||
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
|
||||
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_saturation(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
|
||||
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
|
||||
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
|
||||
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int ret = set_regs(sensor, sc101iot_default_init_regs, sizeof(sc101iot_default_init_regs)/(sizeof(uint8_t) * 2));
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
// ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
|
||||
// ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "reset fail");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
|
||||
{
|
||||
int ret = 0;
|
||||
//sc:H_start={0x0172[3:0],0x0170},H_end={0x0172[7:4],0x0171},
|
||||
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x0f) | (((offset_x+w)>>4)&0xf0));
|
||||
|
||||
//sc:V_start={0x0175[3:0],0x0173},H_end={0x0175[7:4],0x0174},
|
||||
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
|
||||
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x0f) | (((offset_y+h)>>4)&0xf0));
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
if(w>SC101_MAX_FRAME_WIDTH || h > SC101_MAX_FRAME_HIGH) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (SC101_MAX_FRAME_WIDTH-w) /2;
|
||||
uint16_t offset_y = (SC101_MAX_FRAME_HIGH-h) /2;
|
||||
|
||||
if(set_window(sensor, offset_x, offset_y, w, h)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
return 0;
|
||||
err:
|
||||
ESP_LOGE(TAG, "frame size err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
case PIXFORMAT_RAW:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
ESP_LOGE(TAG, "Not support");
|
||||
break;
|
||||
case PIXFORMAT_YUV422: // For now, sc101 sensor only support YUV422.
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc101iot_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (SC101IOT_SCCB_ADDR == slv_addr) {
|
||||
uint8_t MIDL = SCCB_Read(slv_addr, SC101_SENSOR_ID_LOW_REG);
|
||||
uint8_t MIDH = SCCB_Read(slv_addr, SC101_SENSOR_ID_HIGH_REG);
|
||||
uint16_t PID = MIDH << 8 | MIDL;
|
||||
if (SC101IOT_PID == PID) {
|
||||
id->PID = PID;
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc101iot_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
sensor->set_raw_gma = set_raw_gma;
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
sensor->set_aec_value = set_aec_value;
|
||||
sensor->set_awb_gain = set_awb_gain;
|
||||
sensor->set_saturation= set_saturation;
|
||||
sensor->set_contrast = set_contrast;
|
||||
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "sc101iot Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,14 +34,10 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
|
||||
#include "esp_rom_gpio.h"
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp32 ll_cam";
|
||||
@@ -237,7 +233,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
//DBG_PIN_SET(0);
|
||||
}
|
||||
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
I2S_ISR_DISABLE(in_suc_eof);
|
||||
|
||||
@@ -21,15 +21,10 @@
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
|
||||
#include "esp_rom_gpio.h"
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s2 ll_cam";
|
||||
@@ -75,7 +70,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
I2S0.conf.rx_start = 0;
|
||||
|
||||
|
||||
@@ -22,15 +22,10 @@
|
||||
#include "soc/gdma_reg.h"
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s3 ll_cam";
|
||||
@@ -79,7 +74,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
|
||||
bool ll_cam_stop(cam_obj_t *cam)
|
||||
{
|
||||
if (cam->jpeg_mode || !cam->psram_mode) {
|
||||
GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
|
||||
@@ -175,7 +170,6 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
}
|
||||
|
||||
GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
|
||||
// GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2;
|
||||
|
||||
GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
|
||||
//GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
|
||||
@@ -184,52 +178,8 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
switch (config->conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
if (config->pixel_format != PIXFORMAT_YUV422) {
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "YUV422 to YUV420 mode");
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 1;
|
||||
}
|
||||
break;
|
||||
case YUV422_TO_RGB565:
|
||||
if (config->pixel_format != PIXFORMAT_YUV422) {
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "YUV422 to RGB565 mode");
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#if CONFIG_LCD_CAM_CONV_BT709_ENABLED
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 1;
|
||||
#else
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
|
||||
#endif
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
|
||||
LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 1;
|
||||
cam->conv_mode = config->conv_mode;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
|
||||
REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
|
||||
REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
|
||||
@@ -265,21 +215,15 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
|
||||
|
||||
LCD_CAM.cam_rgb_yuv.val = 0;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if (config->conv_mode) {
|
||||
ret = ll_cam_converter_config(cam, config);
|
||||
if(ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_update = 1;
|
||||
LCD_CAM.cam_ctrl1.cam_start = 1;
|
||||
|
||||
ret = ll_cam_dma_init(cam);
|
||||
esp_err_t err = ll_cam_dma_init(cam);
|
||||
if(err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
|
||||
@@ -473,7 +417,6 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in,
|
||||
}
|
||||
return len / 2;
|
||||
}
|
||||
|
||||
|
||||
// just memcpy
|
||||
memcpy(out, in, len);
|
||||
@@ -490,22 +433,8 @@ esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_
|
||||
}
|
||||
cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
|
||||
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
switch (cam->conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
cam->in_bytes_per_pixel = 1.5; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 1.5; // frame buffer stores YUV420
|
||||
break;
|
||||
case YUV422_TO_RGB565:
|
||||
default:
|
||||
cam->in_bytes_per_pixel = 2; // for DMA receive
|
||||
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
break;
|
||||
}
|
||||
#else
|
||||
cam->in_bytes_per_pixel = 2; // for DMA receive
|
||||
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
|
||||
#endif
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
cam->in_bytes_per_pixel = 1;
|
||||
cam->fb_bytes_per_pixel = 1;
|
||||
|
||||
@@ -116,14 +116,8 @@ typedef struct {
|
||||
//for RGB/YUV modes
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
float in_bytes_per_pixel;
|
||||
float fb_bytes_per_pixel;
|
||||
camera_conv_mode_t conv_mode;
|
||||
#else
|
||||
uint8_t in_bytes_per_pixel;
|
||||
uint8_t fb_bytes_per_pixel;
|
||||
#endif
|
||||
uint32_t fb_size;
|
||||
|
||||
cam_state_t state;
|
||||
|
||||
BIN
code/components/esp32-camera-master_20220724.zip
Normal file
BIN
code/components/esp32-camera-master_20220724.zip
Normal file
Binary file not shown.
@@ -20,7 +20,7 @@ struct RefInfo {
|
||||
int fastalg_max = -1;
|
||||
float fastalg_SAD = -1;
|
||||
float fastalg_SAD_criteria = -1;
|
||||
int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccurity" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar)
|
||||
int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccuracy" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ list(REMOVE_ITEM srcs_kernels
|
||||
"${tfmicro_kernels_dir}/depthwise_conv.cc"
|
||||
"${tfmicro_kernels_dir}/fully_connected.cc"
|
||||
"${tfmicro_kernels_dir}/mul.cc"
|
||||
"${tfmicro_kernels_dir}/pooling.cc"
|
||||
"${tfmicro_kernels_dir}/softmax.cc")
|
||||
"${tfmicro_kernels_dir}/pooling.cc")
|
||||
|
||||
FILE(GLOB esp_nn_kernels
|
||||
"${tfmicro_kernels_dir}/esp_nn/*.cc")
|
||||
@@ -39,8 +38,6 @@ set(lib_srcs
|
||||
"${tflite_dir}/kernels/kernel_util.cc"
|
||||
"${tflite_dir}/micro/memory_planner/greedy_memory_planner.cc"
|
||||
"${tflite_dir}/micro/memory_planner/linear_memory_planner.cc"
|
||||
"${tflite_dir}/micro/arena_allocator/recording_simple_memory_allocator.cc"
|
||||
"${tflite_dir}/micro/arena_allocator/simple_memory_allocator.cc"
|
||||
"${tflite_dir}/c/common.cc"
|
||||
"${tflite_dir}/core/api/error_reporter.cc"
|
||||
"${tflite_dir}/core/api/flatbuffer_conversions.cc"
|
||||
|
||||
@@ -179,8 +179,6 @@ typedef enum {
|
||||
kTfLiteBuiltinMultinomial = 149,
|
||||
kTfLiteBuiltinGelu = 150,
|
||||
kTfLiteBuiltinDynamicUpdateSlice = 151,
|
||||
kTfLiteBuiltinRelu0To1 = 152,
|
||||
kTfLiteBuiltinUnsortedSegmentProd = 153,
|
||||
} TfLiteBuiltinOperator;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -518,9 +518,6 @@ typedef struct {
|
||||
bool approximate;
|
||||
} TfLiteGeluParams;
|
||||
|
||||
typedef struct {
|
||||
int num_segments;
|
||||
} TfLiteUnsortedSegmentProdParams;
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
@@ -113,13 +113,7 @@ typedef struct TfLiteQuantizationParams {
|
||||
} TfLiteQuantizationParams;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Opaque types used by c_api.h, c_api_opaque.h and common.h.
|
||||
|
||||
// TfLiteOpaqueContext is an opaque version of TfLiteContext;
|
||||
typedef struct TfLiteOpaqueContext TfLiteOpaqueContext;
|
||||
|
||||
// TfLiteOpaqueNode is an opaque version of TfLiteNode;
|
||||
typedef struct TfLiteOpaqueNode TfLiteOpaqueNode;
|
||||
// Opaque types used by c_api_opaque.h.
|
||||
|
||||
// TfLiteOpaqueTensor is an opaque version of TfLiteTensor;
|
||||
typedef struct TfLiteOpaqueTensor TfLiteOpaqueTensor;
|
||||
|
||||
@@ -14,33 +14,13 @@ limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
#include <string>
|
||||
|
||||
#include "tensorflow/lite/core/macros.h"
|
||||
#include "tensorflow/lite/tensorflow_profiler_logger.h"
|
||||
#endif
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif // TF_LITE_STATIC_MEMORY
|
||||
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
namespace tflite {
|
||||
// Use weak symbols here (even though they are guarded by macros) to avoid
|
||||
// build breakage when building a benchmark requires TFLite runs. The main
|
||||
// benchmark library should have tensor_profiler_logger dependency.
|
||||
TFLITE_ATTRIBUTE_WEAK void OnTfLiteTensorAlloc(TfLiteTensor* tensor,
|
||||
size_t num_bytes);
|
||||
|
||||
TFLITE_ATTRIBUTE_WEAK void OnTfLiteTensorDealloc(TfLiteTensor* tensor);
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TF_LITE_TENSORFLOW_PROFILER
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t TfLiteIntArrayGetSizeInBytes(int size) {
|
||||
@@ -119,12 +99,7 @@ void TfLiteFloatArrayFree(TfLiteFloatArray* a) { free(a); }
|
||||
void TfLiteTensorDataFree(TfLiteTensor* t) {
|
||||
if (t->allocation_type == kTfLiteDynamic ||
|
||||
t->allocation_type == kTfLitePersistentRo) {
|
||||
if (t->data.raw) {
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorDealloc(t);
|
||||
#endif
|
||||
free(t->data.raw);
|
||||
}
|
||||
free(t->data.raw);
|
||||
}
|
||||
t->data.raw = nullptr;
|
||||
}
|
||||
@@ -186,7 +161,7 @@ void TfLiteTensorFree(TfLiteTensor* t) {
|
||||
t->dims = nullptr;
|
||||
|
||||
if (t->dims_signature) {
|
||||
TfLiteIntArrayFree((TfLiteIntArray*)t->dims_signature);
|
||||
TfLiteIntArrayFree((TfLiteIntArray *) t->dims_signature);
|
||||
}
|
||||
t->dims_signature = nullptr;
|
||||
|
||||
@@ -216,12 +191,16 @@ void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
|
||||
}
|
||||
|
||||
TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst) {
|
||||
if (!src || !dst) return kTfLiteOk;
|
||||
if (src->bytes != dst->bytes) return kTfLiteError;
|
||||
if (src == dst) return kTfLiteOk;
|
||||
if (!src || !dst)
|
||||
return kTfLiteOk;
|
||||
if (src->bytes != dst->bytes)
|
||||
return kTfLiteError;
|
||||
if (src == dst)
|
||||
return kTfLiteOk;
|
||||
|
||||
dst->type = src->type;
|
||||
if (dst->dims) TfLiteIntArrayFree(dst->dims);
|
||||
if (dst->dims)
|
||||
TfLiteIntArrayFree(dst->dims);
|
||||
dst->dims = TfLiteIntArrayCopy(src->dims);
|
||||
memcpy(dst->data.raw, src->data.raw, src->bytes);
|
||||
dst->buffer_handle = src->buffer_handle;
|
||||
@@ -239,17 +218,8 @@ void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
|
||||
// TODO(b/145340303): Tensor data should be aligned.
|
||||
if (!tensor->data.raw) {
|
||||
tensor->data.raw = (char*)malloc(num_bytes);
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorAlloc(tensor, num_bytes);
|
||||
#endif
|
||||
} else if (num_bytes > tensor->bytes) {
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorDealloc(tensor);
|
||||
#endif
|
||||
tensor->data.raw = (char*)realloc(tensor->data.raw, num_bytes);
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorAlloc(tensor, num_bytes);
|
||||
#endif
|
||||
}
|
||||
tensor->bytes = num_bytes;
|
||||
}
|
||||
|
||||
@@ -173,9 +173,9 @@ void TfLiteFloatArrayFree(TfLiteFloatArray* a);
|
||||
} \
|
||||
} while (false)
|
||||
#else // TF_LITE_STRIP_ERROR_STRINGS
|
||||
#define ARGS_UNUSED(...) (void)sizeof(#__VA_ARGS__)
|
||||
#define TF_LITE_KERNEL_LOG(context, ...) ARGS_UNUSED(__VA_ARGS__)
|
||||
#define TF_LITE_MAYBE_KERNEL_LOG(context, ...) ARGS_UNUSED(__VA_ARGS__)
|
||||
#define UNUSED(...) (void)sizeof(#__VA_ARGS__)
|
||||
#define TF_LITE_KERNEL_LOG(context, ...) UNUSED(__VA_ARGS__)
|
||||
#define TF_LITE_MAYBE_KERNEL_LOG(context, ...) UNUSED(__VA_ARGS__)
|
||||
#endif // TF_LITE_STRIP_ERROR_STRINGS
|
||||
|
||||
// Check whether value is true, and if not return kTfLiteError from
|
||||
@@ -842,32 +842,6 @@ typedef struct TfLiteContext {
|
||||
size_t* bytes);
|
||||
} TfLiteContext;
|
||||
|
||||
// `TfLiteRegistrationExternal` is an external version of `TfLiteRegistration`
|
||||
// for C API which doesn't use internal types (such as `TfLiteContext`) but only
|
||||
// uses stable API types (such as `TfLiteOpaqueContext`). The purpose of each
|
||||
// field is the exactly the same as with `TfLiteRegistration`.
|
||||
typedef struct TfLiteRegistrationExternal {
|
||||
// Custom op name.
|
||||
const char* custom_name;
|
||||
|
||||
// The version of the op. The verion should be higher than 0.
|
||||
const int version;
|
||||
|
||||
// Initializes the op from serialized data.
|
||||
void* (*init)(TfLiteOpaqueContext* context, const char* buffer,
|
||||
size_t length);
|
||||
|
||||
// The pointer `buffer` is the data previously returned by an init invocation.
|
||||
void (*free)(TfLiteOpaqueContext* context, void* buffer);
|
||||
|
||||
// Called when the inputs that this node depends on have been resized.
|
||||
TfLiteStatus (*prepare)(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node);
|
||||
|
||||
// Called when the node is executed. (should read node->inputs and output to
|
||||
// node->outputs).
|
||||
TfLiteStatus (*invoke)(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node);
|
||||
} TfLiteRegistrationExternal;
|
||||
|
||||
typedef struct TfLiteRegistration {
|
||||
// Initializes the op from serialized data.
|
||||
// Called only *once* for the lifetime of the op, so any one-time allocations
|
||||
@@ -929,31 +903,8 @@ typedef struct TfLiteRegistration {
|
||||
// Note: It is the responsibility of the registration binder to set this
|
||||
// properly.
|
||||
int version;
|
||||
|
||||
// The external version of `TfLiteRegistration`. Since we can't use internal
|
||||
// types (such as `TfLiteContext`) for C API to maintain ABI stability.
|
||||
// C API user will provide `TfLiteRegistrationExternal` to implement custom
|
||||
// ops. We keep it inside of `TfLiteRegistration` and use it to route
|
||||
// callbacks properly.
|
||||
TfLiteRegistrationExternal* registration_external;
|
||||
} TfLiteRegistration;
|
||||
|
||||
// Old version of `TfLiteRegistration` to maintain binary backward
|
||||
// compatibility.
|
||||
// WARNING: This structure is deprecated / not an official part of the API.
|
||||
// It should be only used for binary backward compatibility.
|
||||
typedef struct TfLiteRegistration_V1 {
|
||||
void* (*init)(TfLiteContext* context, const char* buffer, size_t length);
|
||||
void (*free)(TfLiteContext* context, void* buffer);
|
||||
TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node);
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node);
|
||||
const char* (*profiling_string)(const TfLiteContext* context,
|
||||
const TfLiteNode* node);
|
||||
int32_t builtin_code;
|
||||
const char* custom_name;
|
||||
int version;
|
||||
} TfLiteRegistration_V1;
|
||||
|
||||
// The flags used in `TfLiteDelegate`. Note that this is a bitmask, so the
|
||||
// values should be 1, 2, 4, 8, ...etc.
|
||||
typedef enum TfLiteDelegateFlags {
|
||||
|
||||
@@ -836,16 +836,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_UNSORTED_SEGMENT_PROD: {
|
||||
auto params = safe_allocator.Allocate<TfLiteUnsortedSegmentProdParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* unsorted_segment_prod_params =
|
||||
op->builtin_options_as_UnsortedSegmentProdOptions()) {
|
||||
params->num_segments = unsorted_segment_prod_params->num_segments();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
// Below are the ops with no builtin_data structure.
|
||||
// TODO(aselle): Implement call in BuiltinOptions, but nullptrs are
|
||||
// ok for now, since there is no call implementation either.
|
||||
@@ -858,7 +848,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
case BuiltinOperator_MATRIX_DIAG:
|
||||
case BuiltinOperator_MATRIX_SET_DIAG:
|
||||
case BuiltinOperator_RELU_N1_TO_1:
|
||||
case BuiltinOperator_RELU_0_TO_1:
|
||||
case BuiltinOperator_SELECT:
|
||||
case BuiltinOperator_SELECT_V2:
|
||||
case BuiltinOperator_SLICE:
|
||||
|
||||
@@ -23,16 +23,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
// Opaque type similar to TfLiteDelegate / TfLiteOpaqueDelegate.
|
||||
// This is used for cases (e.g. when using "TF Lite with Google Play Services")
|
||||
// where the TF Lite runtime might be built using a newer (or older)
|
||||
// version of the TF Lite sources than the app, and hence might have a
|
||||
// different definition of the TfLiteDelegate type. TF Lite APIs use
|
||||
// TfLiteOpaqueDelegate rather than TfLiteDelegate when they want to
|
||||
// refer to a delegate defined with that potentially different version
|
||||
// of the TfLiteDelegate type.
|
||||
struct TfLiteOpaqueDelegateStruct;
|
||||
|
||||
namespace tflite {
|
||||
|
||||
/// Abstract interface that returns TfLiteRegistrations given op codes or custom
|
||||
@@ -47,10 +37,8 @@ class OpResolver {
|
||||
virtual const TfLiteRegistration* FindOp(const char* op,
|
||||
int version) const = 0;
|
||||
|
||||
// Represents a sequence of delegates.
|
||||
using TfLiteDelegatePtrVector =
|
||||
std::vector<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>>;
|
||||
|
||||
// Returns optional delegates for resolving and handling ops in the flatbuffer
|
||||
// model. This may be used in addition to the standard TfLiteRegistration
|
||||
// lookup for graph resolution.
|
||||
@@ -59,55 +47,16 @@ class OpResolver {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Represents a function that creates a TfLite delegate instance.
|
||||
// Represent a function that creates a TfLite delegate instance.
|
||||
using TfLiteDelegateCreator =
|
||||
std::function<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>(
|
||||
int /*num_threads*/)>;
|
||||
|
||||
// Represents a sequence of delegate creator functions.
|
||||
using TfLiteDelegateCreators = std::vector<TfLiteDelegateCreator>;
|
||||
|
||||
// Returns a vector of delegate creators to create optional delegates for
|
||||
// resolving and handling ops in the flatbuffer model. This may be used in
|
||||
// addition to the standard TfLiteRegistration lookup for graph resolution.
|
||||
//
|
||||
// Note that this method is not used (will not be called) if you are using
|
||||
// TF Lite in Google Play Services; the GetOpaqueDelegateCreators method
|
||||
// (see below) is used for that case.
|
||||
virtual TfLiteDelegateCreators GetDelegateCreators() const { return {}; }
|
||||
|
||||
// TODO(b/202712825): it would be nice if we could avoid the need for separate
|
||||
// "opaque" types & methods for use only with TF Lite in Google Play Services.
|
||||
|
||||
// Represents an opaque delegate instance.
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
using TfLiteOpaqueDelegatePtr =
|
||||
std::unique_ptr<TfLiteOpaqueDelegateStruct,
|
||||
void (*)(TfLiteOpaqueDelegateStruct*)>;
|
||||
|
||||
// Represents a function that creates an opaque delegate instance.
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
using TfLiteOpaqueDelegateCreator =
|
||||
std::function<TfLiteOpaqueDelegatePtr(int /*num_threads*/)>;
|
||||
|
||||
// Represents a sequence of opaque delegate creator functions.
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
using TfLiteOpaqueDelegateCreators = std::vector<TfLiteOpaqueDelegateCreator>;
|
||||
|
||||
// Returns a vector of opaque delegate creators to create optional opaque
|
||||
// delegates for resolving and handling ops in the flatbuffer model. This may
|
||||
// be used in addition to the standard TfLiteRegistration lookup for graph
|
||||
// resolution.
|
||||
//
|
||||
// Note that this method will be called only if you are using TF Lite in
|
||||
// Google Play Services; if you are using regular TF Lite, GetDelegateCreators
|
||||
// (see above) is used instead.
|
||||
//
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
virtual TfLiteOpaqueDelegateCreators GetOpaqueDelegateCreators() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual ~OpResolver() {}
|
||||
|
||||
private:
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace tflite {
|
||||
namespace reference_ops {
|
||||
|
||||
inline int16_t SaturatingLeftShift(int16_t value, int amount) {
|
||||
int64_t result = static_cast<int64_t>(value) * (1 << amount);
|
||||
result = std::min<int64_t>(result, std::numeric_limits<int16_t>::max());
|
||||
result = std::max<int64_t>(result, std::numeric_limits<int16_t>::min());
|
||||
int32_t result = static_cast<int32_t>(value) * (1 << amount);
|
||||
result = std::min<int32_t>(result, std::numeric_limits<int16_t>::max());
|
||||
result = std::max<int32_t>(result, std::numeric_limits<int16_t>::min());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,6 @@ class RuntimeShape {
|
||||
public:
|
||||
RuntimeShape& operator=(RuntimeShape const&) = delete;
|
||||
|
||||
// RuntimeShape in TFLM supports up to 5 dimensions.
|
||||
// The name kMaxSmallSize comes from the same file of the upstream
|
||||
// tensorflow lite repo and need to be kept the same for max reuse.
|
||||
static constexpr int kMaxSmallSize = 5;
|
||||
|
||||
RuntimeShape() : size_(0) {}
|
||||
|
||||
explicit RuntimeShape(int dimensions_count) : size_(dimensions_count) {}
|
||||
@@ -109,9 +104,11 @@ class RuntimeShape {
|
||||
sizeof(int32_t) * shape.DimensionsCount());
|
||||
}
|
||||
|
||||
// A maximum of 4 dimensions are supported on TFLM.
|
||||
static constexpr int kMaxSize = 5;
|
||||
int32_t size_;
|
||||
union {
|
||||
int32_t dims_[kMaxSmallSize];
|
||||
int32_t dims_[kMaxSize];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -974,11 +974,11 @@ struct StridedSliceParams {
|
||||
int8_t strides_count;
|
||||
int32_t strides[5];
|
||||
|
||||
uint16_t begin_mask;
|
||||
uint16_t ellipsis_mask;
|
||||
uint16_t end_mask;
|
||||
uint16_t new_axis_mask;
|
||||
uint16_t shrink_axis_mask;
|
||||
int16_t begin_mask;
|
||||
int16_t ellipsis_mask;
|
||||
int16_t end_mask;
|
||||
int16_t new_axis_mask;
|
||||
int16_t shrink_axis_mask;
|
||||
};
|
||||
|
||||
struct TanhParams {
|
||||
|
||||
@@ -308,7 +308,7 @@ TfLiteStatus CalculateShapeForBroadcast(TfLiteContext* context,
|
||||
const TfLiteTensor* input3,
|
||||
TfLiteIntArray** output_shape);
|
||||
|
||||
// Return the size of given type in bytes. Return 0 in case of string.
|
||||
// Return the size of given type in bytes. Return 0 in in case of string.
|
||||
int TfLiteTypeGetSize(TfLiteType type);
|
||||
|
||||
// Whether the current platform is mobile (Android or iOS).
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#include "tensorflow/lite/micro/arena_allocator/non_persistent_arena_buffer_allocator.h"
|
||||
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
NonPersistentArenaBufferAllocator::NonPersistentArenaBufferAllocator(
|
||||
uint8_t* buffer, size_t buffer_size)
|
||||
: buffer_head_(buffer),
|
||||
buffer_tail_(buffer + buffer_size),
|
||||
head_temp_(buffer),
|
||||
next_temp_(buffer) {}
|
||||
|
||||
NonPersistentArenaBufferAllocator::~NonPersistentArenaBufferAllocator() {}
|
||||
|
||||
// Allocates a temporary buffer. This buffer is not resizable.
|
||||
uint8_t* NonPersistentArenaBufferAllocator::AllocateTemp(size_t size,
|
||||
size_t alignment) {
|
||||
uint8_t* const aligned_result = AlignPointerUp(next_temp_, alignment);
|
||||
const size_t available_memory = buffer_tail_ - aligned_result;
|
||||
if (available_memory < size) {
|
||||
MicroPrintf(
|
||||
"Failed to allocate temp memory. Requested: %u, "
|
||||
"available %u, missing: %u",
|
||||
size, available_memory, size - available_memory);
|
||||
return nullptr;
|
||||
}
|
||||
next_temp_ = aligned_result + size;
|
||||
temp_buffer_ptr_check_sum_ ^= reinterpret_cast<intptr_t>(aligned_result);
|
||||
temp_buffer_count_++;
|
||||
return aligned_result;
|
||||
}
|
||||
|
||||
// Signals that a temporary buffer is no longer needed.
|
||||
void NonPersistentArenaBufferAllocator::DeallocateTemp(uint8_t* temp_buf) {
|
||||
temp_buffer_ptr_check_sum_ ^= reinterpret_cast<intptr_t>(temp_buf);
|
||||
temp_buffer_count_--;
|
||||
}
|
||||
|
||||
// Returns true if all temporary buffers are already deallocated.
|
||||
bool NonPersistentArenaBufferAllocator::IsAllTempDeallocated() {
|
||||
if (temp_buffer_count_ != 0 || temp_buffer_ptr_check_sum_ != 0) {
|
||||
MicroPrintf(
|
||||
"Number of allocated temp buffers: %d. Checksum passing status: %d",
|
||||
temp_buffer_count_, !temp_buffer_ptr_check_sum_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Signals that all temporary allocations can be reclaimed. TFLM calls this
|
||||
// API when it knows that all temporary buffers that it requested has been
|
||||
// deallocated. The goal of API is to facilitate implementations of
|
||||
// INonPersistentBufferAllocator can reuse buffer with some reasonable
|
||||
// complexity.
|
||||
TfLiteStatus NonPersistentArenaBufferAllocator::ResetTempAllocations() {
|
||||
if (!IsAllTempDeallocated()) {
|
||||
MicroPrintf(
|
||||
"All temp buffers must be freed before calling ResetTempAllocations()");
|
||||
return kTfLiteError;
|
||||
}
|
||||
next_temp_ = head_temp_;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Returns a buffer that is resizable viable ResizeBuffer().
|
||||
uint8_t* NonPersistentArenaBufferAllocator::AllocateResizableBuffer(
|
||||
size_t size, size_t alignment) {
|
||||
// Only supports one resizable buffer, which starts at the buffer head.
|
||||
uint8_t* expected_resizable_buf = AlignPointerUp(buffer_head_, alignment);
|
||||
|
||||
if (head_temp_ != expected_resizable_buf) {
|
||||
MicroPrintf(
|
||||
"Cannot allocate a new resizable buffer when one is already allocated");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ResizeBuffer(expected_resizable_buf, size, alignment) == kTfLiteOk) {
|
||||
return expected_resizable_buf;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Resizes a buffer that is previously returned by the AllocateResizableBuffer.
|
||||
// Note that ResizeBuffer(old_resizable_buf, 0, 1) effectively deallocates
|
||||
// a previous allocated resizable buffer.
|
||||
TfLiteStatus NonPersistentArenaBufferAllocator::ResizeBuffer(
|
||||
uint8_t* resizable_buf, size_t size, size_t alignment) {
|
||||
// Only supports one resizable buffer, which starts at the buffer head.
|
||||
uint8_t* expect_resizable_buf = AlignPointerUp(buffer_head_, alignment);
|
||||
if (resizable_buf != expect_resizable_buf) {
|
||||
MicroPrintf("Internal error: buffer is not resizable");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (head_temp_ != next_temp_) {
|
||||
MicroPrintf("ResetTempAllocations() is not called before ResizeBuffer().");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
const size_t available_memory = buffer_tail_ - expect_resizable_buf;
|
||||
if (available_memory < size) {
|
||||
MicroPrintf(
|
||||
"Failed to resize buffer. Requested: %u, available %u, missing: %u",
|
||||
size, available_memory, size - available_memory);
|
||||
return kTfLiteError;
|
||||
}
|
||||
head_temp_ = expect_resizable_buf + size;
|
||||
next_temp_ = head_temp_;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Frees up the memory occupied by the resizable buffer.
|
||||
TfLiteStatus NonPersistentArenaBufferAllocator::DeallocateResizableBuffer(
|
||||
uint8_t* resizable_buf) {
|
||||
return ResizeBuffer(resizable_buf, 0, 1);
|
||||
}
|
||||
|
||||
// Returns a pointer pointing to the start of the overlay memory, which is
|
||||
// used for activation tensors and scratch buffers by kernels at Invoke stage.
|
||||
uint8_t* NonPersistentArenaBufferAllocator::GetOverlayMemoryAddress() const {
|
||||
return buffer_head_;
|
||||
}
|
||||
|
||||
// Reserves the size of the overlay memory. This overlay is reserved for the
|
||||
// kernels at Invoke stage. This is referred to as the overlay because before
|
||||
// Invoket state, the same memory can be used for temp buffers. The layout of
|
||||
// the memory is planned by the memory planner separately at Invoke stage.
|
||||
TfLiteStatus
|
||||
NonPersistentArenaBufferAllocator::ReserveNonPersistentOverlayMemory(
|
||||
size_t size, size_t alignment) {
|
||||
uint8_t* expect_resizable_buf = AlignPointerUp(buffer_head_, alignment);
|
||||
return ResizeBuffer(expect_resizable_buf, size, alignment);
|
||||
}
|
||||
|
||||
// Returns the size of non-persistent buffer in use.
|
||||
size_t NonPersistentArenaBufferAllocator::GetNonPersistentUsedBytes() const {
|
||||
return (next_temp_ - buffer_head_);
|
||||
}
|
||||
|
||||
// Returns the number of bytes available with a given alignment. This number
|
||||
// takes in account any temporary allocations.
|
||||
size_t NonPersistentArenaBufferAllocator::GetAvailableMemory(
|
||||
size_t alignment) const {
|
||||
uint8_t* const aligned_temp = AlignPointerUp(next_temp_, alignment);
|
||||
uint8_t* const aligned_tail = AlignPointerDown(buffer_tail_, alignment);
|
||||
return aligned_tail - aligned_temp;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,104 +0,0 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_NON_PERSISTENT_ARENA_BUFFER_ALLOCATOR_H_
|
||||
#define TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_NON_PERSISTENT_ARENA_BUFFER_ALLOCATOR_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/ibuffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// Implement INonPersistentBufferAllocator on an arena that is dedicated for
|
||||
// non-persistent buffers.
|
||||
class NonPersistentArenaBufferAllocator : public INonPersistentBufferAllocator {
|
||||
public:
|
||||
NonPersistentArenaBufferAllocator(uint8_t* buffer, size_t buffer_size);
|
||||
virtual ~NonPersistentArenaBufferAllocator();
|
||||
|
||||
// Allocates a temporary buffer. This buffer is not resizable.
|
||||
uint8_t* AllocateTemp(size_t size, size_t alignment) override;
|
||||
|
||||
// Signals that a temporary buffer is no longer needed.
|
||||
void DeallocateTemp(uint8_t* buf) override;
|
||||
|
||||
// Returns true if all temporary buffers are already deallocated.
|
||||
bool IsAllTempDeallocated() override;
|
||||
|
||||
// Signals that all temporary allocations can be reclaimed. TFLM calls this
|
||||
// API when it knows that all temporary buffers that it requested has been
|
||||
// deallocated.
|
||||
TfLiteStatus ResetTempAllocations() override;
|
||||
|
||||
// Returns a buffer that is resizable viable ResizeBuffer().
|
||||
uint8_t* AllocateResizableBuffer(size_t size, size_t alignment) override;
|
||||
|
||||
// Resizes a buffer that is previously returned by the
|
||||
// AllocateResizableBuffer.
|
||||
TfLiteStatus ResizeBuffer(uint8_t* resizable_buf, size_t size,
|
||||
size_t alignment) override;
|
||||
|
||||
// Frees up the memory occupied by the resizable buffer.
|
||||
TfLiteStatus DeallocateResizableBuffer(uint8_t* resizable_buf) override;
|
||||
|
||||
// Returns a pointer pointing to the start of the overlay memory, which is
|
||||
// used for activation tensors and scratch buffers by kernels at Invoke stage.
|
||||
uint8_t* GetOverlayMemoryAddress() const override;
|
||||
|
||||
// Reserves the size of the overlay memory. This overlay is reserved for the
|
||||
// kernels at Invoke stage. This is referred to as the overlay because before
|
||||
// Invoket state, the same memory can be used for temp buffers. The layout of
|
||||
// the memory is planned by the memory planner separately at Invoke stage.
|
||||
TfLiteStatus ReserveNonPersistentOverlayMemory(size_t size,
|
||||
size_t alignment) override;
|
||||
|
||||
// Returns the size of non-persistent buffer in use.
|
||||
size_t GetNonPersistentUsedBytes() const override;
|
||||
|
||||
// Returns the number of bytes available with a given alignment. This number
|
||||
// takes in account any temporary allocations.
|
||||
size_t GetAvailableMemory(size_t alignment) const override;
|
||||
|
||||
TF_LITE_REMOVE_VIRTUAL_DELETE
|
||||
|
||||
private:
|
||||
// The memory arena that this allocator manages.
|
||||
uint8_t* const buffer_head_;
|
||||
uint8_t* const buffer_tail_;
|
||||
|
||||
// The whole region is split into two parts:
|
||||
// buffer_head_ to head_temp_ - 1 belongs to the only resizable buffer.
|
||||
// head_temp_ to buffer_tail_ can be used for (non-resizable) temp buffers.
|
||||
uint8_t* head_temp_;
|
||||
|
||||
// next_temp_ points to the next available temp buffer allocation address and
|
||||
// its range is between head_temp_ and buffer_tail_
|
||||
uint8_t* next_temp_;
|
||||
|
||||
// XOR Check sum for outstanding temp buffers.
|
||||
// If all temp buffers are deallocated OR no temp buffers are allocated,
|
||||
// temp_buffer_ptr_check_sum_ == nullptr.
|
||||
intptr_t temp_buffer_ptr_check_sum_ = 0;
|
||||
// Count of outstanding temp buffers.
|
||||
int temp_buffer_count_ = 0;
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_NON_PERSISTENT_ARENA_BUFFER_ALLOCATOR_H_
|
||||
@@ -1,52 +0,0 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#include "tensorflow/lite/micro/arena_allocator/persistent_arena_buffer_allocator.h"
|
||||
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
PersistentArenaBufferAllocator::PersistentArenaBufferAllocator(
|
||||
uint8_t* buffer, size_t buffer_size)
|
||||
: buffer_head_(buffer),
|
||||
buffer_tail_(buffer + buffer_size),
|
||||
tail_temp_(buffer_tail_) {}
|
||||
|
||||
PersistentArenaBufferAllocator::~PersistentArenaBufferAllocator() {}
|
||||
|
||||
uint8_t* PersistentArenaBufferAllocator::AllocatePersistentBuffer(
|
||||
size_t size, size_t alignment) {
|
||||
uint8_t* const aligned_result =
|
||||
AlignPointerDown(tail_temp_ - size, alignment);
|
||||
if (aligned_result < buffer_head_) {
|
||||
#ifndef TF_LITE_STRIP_ERROR_STRINGS
|
||||
const size_t missing_memory = buffer_head_ - aligned_result;
|
||||
MicroPrintf(
|
||||
"Failed to allocate tail memory. Requested: %u, "
|
||||
"available %u, missing: %u",
|
||||
size, size - missing_memory, missing_memory);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
tail_temp_ = aligned_result;
|
||||
return aligned_result;
|
||||
}
|
||||
|
||||
size_t PersistentArenaBufferAllocator::GetPersistentUsedBytes() const {
|
||||
return buffer_tail_ - tail_temp_;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,59 +0,0 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_PERSISTENT_ARENA_BUFFER_ALLOCATOR_H_
|
||||
#define TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_PERSISTENT_ARENA_BUFFER_ALLOCATOR_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/ibuffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// PersistentArenaBufferAllocator is an implementatation of
|
||||
// IPersistentBufferAllocator interface on an arena that is dedicated for
|
||||
// persistent buffers.
|
||||
class PersistentArenaBufferAllocator : public IPersistentBufferAllocator {
|
||||
public:
|
||||
PersistentArenaBufferAllocator(uint8_t* buffer, size_t buffer_size);
|
||||
virtual ~PersistentArenaBufferAllocator();
|
||||
|
||||
// Allocates persistent memory. The persistent buffer is never freed.
|
||||
// Returns nullptr if errors occured.
|
||||
uint8_t* AllocatePersistentBuffer(size_t size, size_t alignment) override;
|
||||
|
||||
// Returns the size of all persistent allocations in bytes.
|
||||
size_t GetPersistentUsedBytes() const override;
|
||||
|
||||
TF_LITE_REMOVE_VIRTUAL_DELETE
|
||||
private:
|
||||
// The memory arena that this allocator manages.
|
||||
uint8_t* const buffer_head_;
|
||||
uint8_t* const buffer_tail_;
|
||||
|
||||
// The whole region is split into two parts:
|
||||
// tail_temp_ to buffer_tail_ contains allocated buffers;
|
||||
// buffer_head_ to tail_temp_ - 1 belongs to still available spaces.
|
||||
// So in essence, the allocated region grows from the bottom and emulates
|
||||
// SimpleMemoryAllocator's persistent part.
|
||||
uint8_t* tail_temp_;
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_PERSISTENT_ARENA_BUFFER_ALLOCATOR_H_
|
||||
@@ -16,10 +16,10 @@ limitations under the License.
|
||||
#include "tensorflow/lite/micro/fake_micro_context.h"
|
||||
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/simple_memory_allocator.h"
|
||||
#include "tensorflow/lite/micro/micro_allocator.h"
|
||||
#include "tensorflow/lite/micro/micro_arena_constants.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/simple_memory_allocator.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
@@ -12,8 +12,8 @@ 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 TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_IBUFFER_ALLOCATOR_H_
|
||||
#define TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_IBUFFER_ALLOCATOR_H_
|
||||
#ifndef TENSORFLOW_LITE_MICRO_IBUFFER_ALLOCATOR_H_
|
||||
#define TENSORFLOW_LITE_MICRO_IBUFFER_ALLOCATOR_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -97,4 +97,4 @@ class INonPersistentBufferAllocator {
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_ARENA_ALLOCATOR_IBUFFER_ALLOCATOR_H_
|
||||
#endif // TENSORFLOW_LITE_MICRO_IBUFFER_ALLOCATOR_H_
|
||||
@@ -24,7 +24,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
@@ -61,8 +60,8 @@ TfLiteStatus ReluEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default: {
|
||||
MicroPrintf("Only float32 is supported currently, got %s",
|
||||
TfLiteTypeGetName(input->type));
|
||||
TF_LITE_KERNEL_LOG(context, "Only float32 is supported currently, got %s",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
@@ -100,8 +99,8 @@ TfLiteStatus Relu6Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default: {
|
||||
MicroPrintf("Only float32 is supported currently, got %s",
|
||||
TfLiteTypeGetName(input->type));
|
||||
TF_LITE_KERNEL_LOG(context, "Only float32 is supported currently, got %s",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
@@ -110,11 +109,25 @@ TfLiteStatus Relu6Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_RELU() {
|
||||
return tflite::micro::RegisterOp(ReluInit, ReluPrepare, ReluEval);
|
||||
return {/*init=*/ReluInit,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/ReluPrepare,
|
||||
/*invoke=*/ReluEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_RELU6() {
|
||||
return tflite::micro::RegisterOp(Relu6Init, Relu6Prepare, Relu6Eval);
|
||||
return {/*init=*/Relu6Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Relu6Prepare,
|
||||
/*invoke=*/Relu6Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -159,7 +159,14 @@ TfLiteStatus AddEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_ADD() {
|
||||
return tflite::micro::RegisterOp(AddInit, AddPrepare, AddEval);
|
||||
return {/*init=*/AddInit,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/AddPrepare,
|
||||
/*invoke=*/AddEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -208,7 +208,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_ADD_N() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -104,11 +104,25 @@ TfLiteStatus ArgMaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace arg_min_max
|
||||
|
||||
TfLiteRegistration Register_ARG_MAX() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, arg_min_max::ArgMaxEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/nullptr,
|
||||
/*invoke=*/arg_min_max::ArgMaxEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_ARG_MIN() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, arg_min_max::ArgMinEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/nullptr,
|
||||
/*invoke=*/arg_min_max::ArgMinEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
|
||||
@@ -95,7 +95,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace.
|
||||
|
||||
TfLiteRegistration Register_ASSIGN_VARIABLE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -105,7 +105,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace.
|
||||
|
||||
TfLiteRegistration Register_BATCH_TO_SPACE_ND() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -84,8 +84,14 @@ TfLiteStatus BroadcastArgsEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_BROADCAST_ARGS() {
|
||||
return tflite::micro::RegisterOp(nullptr, BroadcastArgsPrepare,
|
||||
BroadcastArgsEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/BroadcastArgsPrepare,
|
||||
/*invoke=*/BroadcastArgsEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
} // namespace tflite
|
||||
@@ -116,8 +116,14 @@ TfLiteStatus BroadcastToEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_BROADCAST_TO() {
|
||||
return tflite::micro::RegisterOp(nullptr, BroadcastToPrepare,
|
||||
BroadcastToEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/BroadcastToPrepare,
|
||||
/*invoke=*/BroadcastToEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
} // namespace tflite
|
||||
@@ -82,7 +82,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace.
|
||||
|
||||
TfLiteRegistration Register_CALL_ONCE() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
return {/*init=*/Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -108,7 +108,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_CAST() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -67,7 +67,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace ceil
|
||||
|
||||
TfLiteRegistration Register_CEIL() {
|
||||
return tflite::micro::RegisterOp(nullptr, ceil::Prepare, ceil::Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/ceil::Prepare,
|
||||
/*invoke=*/ceil::Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
|
||||
@@ -108,7 +108,14 @@ TfLiteStatus CircularBufferEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
TfLiteRegistration* Register_CIRCULAR_BUFFER() {
|
||||
static TfLiteRegistration r = tflite::micro::RegisterOp(CircularBufferInit, CircularBufferPrepare, CircularBufferEval);
|
||||
static TfLiteRegistration r = {/*init=*/CircularBufferInit,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/CircularBufferPrepare,
|
||||
/*invoke=*/CircularBufferEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
return &r;
|
||||
}
|
||||
|
||||
|
||||
@@ -583,33 +583,69 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace comparisons
|
||||
|
||||
TfLiteRegistration Register_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::EqualEval);
|
||||
return {/*init=*/comparisons::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/comparisons::Prepare,
|
||||
/*invoke=*/comparisons::EqualEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_NOT_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::NotEqualEval);
|
||||
return {/*init=*/comparisons::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/comparisons::Prepare,
|
||||
/*invoke=*/comparisons::NotEqualEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_GREATER() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::GreaterEval);
|
||||
return {/*init=*/comparisons::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/comparisons::Prepare,
|
||||
/*invoke=*/comparisons::GreaterEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_GREATER_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::GreaterEqualEval);
|
||||
return {/*init=*/comparisons::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/comparisons::Prepare,
|
||||
/*invoke=*/comparisons::GreaterEqualEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LESS() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::LessEval);
|
||||
return {/*init=*/comparisons::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/comparisons::Prepare,
|
||||
/*invoke=*/comparisons::LessEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LESS_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::LessEqualEval);
|
||||
return {/*init=*/comparisons::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/comparisons::Prepare,
|
||||
/*invoke=*/comparisons::LessEqualEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
|
||||
@@ -148,12 +148,12 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
int num_dimensions = NumDimensions(input);
|
||||
|
||||
if (num_dimensions > RuntimeShape::kMaxSmallSize) {
|
||||
if (num_dimensions > 4) {
|
||||
TF_LITE_KERNEL_LOG(
|
||||
context,
|
||||
"Op Concatenation does not currently support num dimensions > %d "
|
||||
"Op Concatenation does not currently support num dimensions >4 "
|
||||
"Tensor has %d dimensions.",
|
||||
RuntimeShape::kMaxSmallSize, num_dimensions);
|
||||
num_dimensions);
|
||||
return kTfLiteError;
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
@@ -252,8 +252,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace concatenation
|
||||
|
||||
TfLiteRegistration Register_CONCATENATION() {
|
||||
return tflite::micro::RegisterOp(concatenation::Init, concatenation::Prepare,
|
||||
concatenation::Eval);
|
||||
return {/*init=*/concatenation::Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/concatenation::Prepare,
|
||||
/*invoke=*/concatenation::Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
|
||||
@@ -25,7 +25,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
@@ -68,47 +67,23 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<float>(bias),
|
||||
tflite::micro::GetTensorData<float>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr);
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt16: {
|
||||
switch (bias->type) {
|
||||
case kTfLiteInt32: {
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, data),
|
||||
data.per_channel_output_multiplier, data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<std::int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt64: {
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, data),
|
||||
data.per_channel_output_multiplier, data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<std::int64_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Bias type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(bias->type), bias->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, data), data.per_channel_output_multiplier,
|
||||
data.per_channel_output_shift, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<std::int64_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
@@ -119,14 +94,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
@@ -135,7 +110,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_CONV_2D() {
|
||||
return tflite::micro::RegisterOp(Init, ConvPrepare, Eval);
|
||||
return {/*init=*/Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/ConvPrepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -97,16 +97,6 @@ TfLiteStatus TestConvQuantizedPerChannel(
|
||||
float output_scale, int output_zero_point, TfLiteConvParams* conv_params,
|
||||
TfLiteRegistration registration, int16_t* output_data);
|
||||
|
||||
TfLiteStatus TestConvQuantizedPerChannel(
|
||||
int* input_dims_data, const float* input_data, int16_t* input_quantized,
|
||||
float input_scale, int input_zero_point, int* filter_dims_data,
|
||||
const float* filter_data, int8_t* filter_data_quantized,
|
||||
int* bias_dims_data, const float* bias_data, int32_t* bias_data_quantized,
|
||||
float* bias_scales, int* bias_zero_points, int* output_dims_data,
|
||||
const float* expected_output_data, int16_t* expected_output_data_quantized,
|
||||
float output_scale, int output_zero_point, TfLiteConvParams* conv_params,
|
||||
TfLiteRegistration registration, int16_t* output_data);
|
||||
|
||||
} // namespace testing
|
||||
} // namespace tflite
|
||||
|
||||
|
||||
@@ -169,7 +169,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_CUMSUM() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -136,7 +136,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_DEPTH_TO_SPACE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -62,7 +62,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<float>(bias),
|
||||
tflite::micro::GetTensorData<float>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
@@ -76,7 +76,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
@@ -92,7 +92,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D() {
|
||||
return tflite::micro::RegisterOp(Init, DepthwiseConvPrepare, Eval);
|
||||
return {/*init=*/Init,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/DepthwiseConvPrepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -49,32 +49,6 @@ TfLiteStatus CalculateOpDataDepthwiseConv(
|
||||
|
||||
TfLiteStatus DepthwiseConvPrepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
// This is the most generic TfLiteRegistration. The actual supported types may
|
||||
// still be target dependent. The only requirement is that every implementation
|
||||
// (reference or optimized) must define this function.
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D();
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
// Returns a TfLiteRegistration struct for kernel variant that only supports
|
||||
// int8 activations and int8 weights and uses the latency optimized
|
||||
// implementations.
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D_INT8();
|
||||
|
||||
// Returns a TfLiteRegistration struct for kernel variant that only supports
|
||||
// int16 activations and int8 weights and uses the latency optimized
|
||||
// implementations.
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D_INT16();
|
||||
|
||||
#else
|
||||
inline TfLiteRegistration Register_DEPTHWISE_CONV_2D_INT8() {
|
||||
return Register_DEPTHWISE_CONV_2D();
|
||||
}
|
||||
|
||||
inline TfLiteRegistration Register_DEPTHWISE_CONV_2D_INT16() {
|
||||
return Register_DEPTHWISE_CONV_2D();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_DEPTHWISE_CONV_H_
|
||||
|
||||
@@ -57,13 +57,6 @@ TfLiteStatus DequantizeEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
reference_ops::Dequantize(data->quantization_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<uint8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Input %s, output %s not supported.",
|
||||
TfLiteTypeGetName(input->type),
|
||||
@@ -81,8 +74,14 @@ TfLiteStatus DequantizeEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_DEQUANTIZE() {
|
||||
return tflite::micro::RegisterOp(DequantizeInit, DequantizePrepare,
|
||||
DequantizeEval);
|
||||
return {/*init=*/DequantizeInit,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/DequantizePrepare,
|
||||
/*invoke=*/DequantizeEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -41,9 +41,8 @@ TfLiteStatus DequantizePrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(context, input->type == kTfLiteInt8 ||
|
||||
input->type == kTfLiteInt16 ||
|
||||
input->type == kTfLiteUInt8);
|
||||
TF_LITE_ENSURE(context,
|
||||
input->type == kTfLiteInt8 || input->type == kTfLiteInt16);
|
||||
TF_LITE_ENSURE(context, output->type == kTfLiteFloat32);
|
||||
|
||||
if (output->type == kTfLiteInt32) {
|
||||
|
||||
@@ -149,6 +149,8 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
return op_data;
|
||||
}
|
||||
|
||||
void Free(TfLiteContext* context, void* buffer) {}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* op_data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
@@ -800,7 +802,14 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration* Register_DETECTION_POSTPROCESS() {
|
||||
static TfLiteRegistration r = tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
static TfLiteRegistration r = {/*init=*/Init,
|
||||
/*free=*/Free,
|
||||
/*prepare=*/Prepare,
|
||||
/*invoke=*/Eval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
return &r;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,6 @@ limitations under the License.
|
||||
#include <cmath>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
@@ -29,22 +27,6 @@ namespace micro {
|
||||
namespace elementwise {
|
||||
namespace {
|
||||
|
||||
constexpr int kAbsNameId = 0;
|
||||
constexpr int kRsrqtNameId = 1;
|
||||
|
||||
const int kElementwiseInputTensor = 0;
|
||||
const int kElementwiseOutputTensor = 0;
|
||||
|
||||
struct OpDataAbsRsqrt {
|
||||
int32_t multiplier;
|
||||
int shift;
|
||||
int input_offset;
|
||||
int output_offset;
|
||||
bool needs_rescale;
|
||||
TfLiteQuantizationType input_quantization_type;
|
||||
TfLiteType input_type;
|
||||
};
|
||||
|
||||
bool IsNumericSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteFloat32;
|
||||
}
|
||||
@@ -53,57 +35,11 @@ bool IsLogicalSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteBool;
|
||||
}
|
||||
|
||||
bool IsAbsSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteFloat32 || type == kTfLiteInt8 || type == kTfLiteInt16;
|
||||
}
|
||||
|
||||
bool IsRsqrtSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteFloat32 || type == kTfLiteInt8;
|
||||
}
|
||||
|
||||
inline void SetAbsOutputMultiplier(const float input_scale,
|
||||
const float output_scale,
|
||||
int32_t* multiplier, int* shift) {
|
||||
QuantizeMultiplier(static_cast<double>(input_scale / output_scale),
|
||||
multiplier, shift);
|
||||
}
|
||||
|
||||
inline void SetRsqrtOutputMultiplier(const float input_scale,
|
||||
const float output_scale,
|
||||
int32_t* multiplier, int* shift) {
|
||||
const double scale =
|
||||
1. / static_cast<double>((std::sqrt(input_scale) * output_scale));
|
||||
QuantizeMultiplier(scale, multiplier, shift);
|
||||
}
|
||||
|
||||
typedef bool (*IsSupportedType)(TfLiteType);
|
||||
template <IsSupportedType>
|
||||
TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kElementwiseInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kElementwiseOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
if (!IsSupportedType(input->type)) {
|
||||
TF_LITE_KERNEL_LOG(context, "Input data type %s (%d) is not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
typedef bool (*IsSupportedType)(TfLiteType);
|
||||
template <IsSupportedType, const int op_nameid>
|
||||
TfLiteStatus PrepareAbsRsqrt(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
@@ -117,87 +53,14 @@ TfLiteStatus PrepareAbsRsqrt(TfLiteContext* context, TfLiteNode* node) {
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
auto* op_data = static_cast<OpDataAbsRsqrt*>(node->user_data);
|
||||
op_data->input_type = input->type;
|
||||
|
||||
// For int16 type input, we support both quantized and non-quantized
|
||||
// evaluation.
|
||||
if (op_nameid == kAbsNameId) {
|
||||
op_data->input_quantization_type = input->quantization.type;
|
||||
}
|
||||
|
||||
if (input->type == kTfLiteInt8 ||
|
||||
(input->type == kTfLiteInt16 &&
|
||||
input->quantization.type != kTfLiteNoQuantization)) {
|
||||
TF_LITE_ENSURE_EQ(context, input->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
TF_LITE_ENSURE_EQ(context, output->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
const auto* input_params =
|
||||
reinterpret_cast<TfLiteAffineQuantization*>(input->quantization.params);
|
||||
const auto* output_params = reinterpret_cast<TfLiteAffineQuantization*>(
|
||||
output->quantization.params);
|
||||
TF_LITE_ENSURE(context, input_params != nullptr);
|
||||
TF_LITE_ENSURE(context, input_params->scale != nullptr);
|
||||
TF_LITE_ENSURE(context, input_params->scale->size > 0);
|
||||
TF_LITE_ENSURE(context, input_params->zero_point->size > 0);
|
||||
TF_LITE_ENSURE(context, output_params != nullptr);
|
||||
TF_LITE_ENSURE(context, output_params->scale != nullptr);
|
||||
TF_LITE_ENSURE(context, output_params->scale->size > 0);
|
||||
TF_LITE_ENSURE(context, output_params->zero_point->size > 0);
|
||||
op_data->input_offset = input_params->zero_point->data[0];
|
||||
op_data->output_offset = output_params->zero_point->data[0];
|
||||
if (input->type == kTfLiteInt16) {
|
||||
TF_LITE_ENSURE_EQ(context, op_data->input_offset, 0);
|
||||
TF_LITE_ENSURE_EQ(context, op_data->output_offset, 0);
|
||||
}
|
||||
const float input_scale = input_params->scale->data[0];
|
||||
const float output_scale = output_params->scale->data[0];
|
||||
op_data->needs_rescale = input_scale != output_scale;
|
||||
if (op_nameid == kAbsNameId && op_data->needs_rescale) {
|
||||
SetAbsOutputMultiplier(input_scale, output_scale, &op_data->multiplier,
|
||||
&op_data->shift);
|
||||
} else if (op_nameid == kRsrqtNameId) {
|
||||
SetRsqrtOutputMultiplier(input_scale, output_scale, &op_data->multiplier,
|
||||
&op_data->shift);
|
||||
}
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TfLiteStatus EvalImplQuantized(
|
||||
TfLiteContext* context, TfLiteNode* node,
|
||||
T func(TfLiteContext*, TfLiteNode*, T),
|
||||
TfLiteStatus validate_input_func(TfLiteContext*, TfLiteNode*, T),
|
||||
TfLiteType expected_type) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
|
||||
const size_t num_elements = ElementCount(*input->dims);
|
||||
const T* in_data = tflite::micro::GetTensorData<T>(input);
|
||||
T* out_data = tflite::micro::GetTensorData<T>(output);
|
||||
for (size_t i = 0; i < num_elements; ++i) {
|
||||
if (validate_input_func) {
|
||||
TF_LITE_ENSURE_OK(context,
|
||||
validate_input_func(context, node, in_data[i]));
|
||||
}
|
||||
out_data[i] = func(context, node, in_data[i]);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T AbsHelper(T i) {
|
||||
return std::abs(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
T func(T), TfLiteStatus validate_input_func(T),
|
||||
TfLiteType expected_type) {
|
||||
T func(T), TfLiteType expected_type) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
|
||||
@@ -205,9 +68,6 @@ inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
const T* in_data = tflite::micro::GetTensorData<T>(input);
|
||||
T* out_data = tflite::micro::GetTensorData<T>(output);
|
||||
for (size_t i = 0; i < num_elements; ++i) {
|
||||
if (validate_input_func) {
|
||||
TF_LITE_ENSURE_OK(context, validate_input_func(in_data[i]));
|
||||
}
|
||||
out_data[i] = func(in_data[i]);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
@@ -215,114 +75,16 @@ inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
|
||||
inline TfLiteStatus EvalNumeric(TfLiteContext* context, TfLiteNode* node,
|
||||
float float_func(float)) {
|
||||
return EvalImpl<float>(context, node, float_func,
|
||||
/*validate_input_func=*/nullptr, kTfLiteFloat32);
|
||||
return EvalImpl<float>(context, node, float_func, kTfLiteFloat32);
|
||||
}
|
||||
|
||||
inline TfLiteStatus EvalLogical(TfLiteContext* context, TfLiteNode* node,
|
||||
|
||||
bool bool_func(bool)) {
|
||||
return EvalImpl<bool>(context, node, bool_func,
|
||||
/*validate_input_func=*/nullptr, kTfLiteBool);
|
||||
}
|
||||
|
||||
void* ElementWiseAbsRsqrtInit(TfLiteContext* context, const char* buffer,
|
||||
size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataAbsRsqrt));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T AbsEvalQuantized(TfLiteContext* context, TfLiteNode* node, T i) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
const int kMin = std::numeric_limits<T>::min();
|
||||
const int kMax = std::numeric_limits<T>::max();
|
||||
|
||||
const int32_t value = std::abs(i - op_data->input_offset);
|
||||
if (!op_data->needs_rescale) {
|
||||
return static_cast<T>(
|
||||
std::min(std::max(static_cast<long int>(value + op_data->output_offset),
|
||||
static_cast<long int>(kMin)),
|
||||
static_cast<long int>(kMax)));
|
||||
}
|
||||
|
||||
const int32_t output = tflite::MultiplyByQuantizedMultiplier(
|
||||
value, op_data->multiplier, op_data->shift) +
|
||||
op_data->output_offset;
|
||||
return static_cast<T>(std::min(
|
||||
std::max(static_cast<long int>(output), static_cast<long int>(kMin)),
|
||||
static_cast<long int>(kMax)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T RsqrtEvalQuantized(TfLiteContext* context, TfLiteNode* node, T i) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
const int kMin = std::numeric_limits<T>::min();
|
||||
const int kMax = std::numeric_limits<T>::max();
|
||||
|
||||
const int32_t value = (i - op_data->input_offset);
|
||||
const int32_t kShift = 20; // Shift to keep value integer.
|
||||
if (value == 0) {
|
||||
// Assume that any value close to 0 represents the max output value.
|
||||
return static_cast<T>(kMax);
|
||||
}
|
||||
int32_t inv_sqrt_multiplier;
|
||||
int inv_sqrt_shift;
|
||||
GetInvSqrtQuantizedMultiplierExp(value, kReverseShift, &inv_sqrt_multiplier,
|
||||
&inv_sqrt_shift);
|
||||
const int32_t data = tflite::MultiplyByQuantizedMultiplier(
|
||||
static_cast<int32_t>(1), inv_sqrt_multiplier, inv_sqrt_shift + kShift);
|
||||
const int32_t output =
|
||||
tflite::MultiplyByQuantizedMultiplier(data, op_data->multiplier,
|
||||
op_data->shift - kShift) +
|
||||
op_data->output_offset;
|
||||
return static_cast<T>(std::min(
|
||||
std::max(static_cast<long int>(output), static_cast<long int>(kMin)),
|
||||
static_cast<long int>(kMax)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus validate_input_func(TfLiteContext* context, TfLiteNode* node,
|
||||
T i) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
|
||||
TF_LITE_ENSURE_MSG(context, i >= op_data->input_offset,
|
||||
"Rsqrt is only defined for positive values");
|
||||
return static_cast<TfLiteStatus>(kTfLiteOk);
|
||||
return EvalImpl<bool>(context, node, bool_func, kTfLiteBool);
|
||||
}
|
||||
|
||||
TfLiteStatus AbsEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpDataAbsRsqrt* op_data = reinterpret_cast<OpDataAbsRsqrt*>(node->user_data);
|
||||
TfLiteType type = op_data->input_type;
|
||||
TfLiteQuantizationType input_quantization_type =
|
||||
op_data->input_quantization_type;
|
||||
TfLiteStatus eval_result;
|
||||
|
||||
switch (type) {
|
||||
case kTfLiteFloat32:
|
||||
eval_result = EvalNumeric(context, node, std::abs);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
eval_result =
|
||||
EvalImplQuantized<int8_t>(context, node, AbsEvalQuantized,
|
||||
/*validate_input_func=*/nullptr, type);
|
||||
break;
|
||||
case kTfLiteInt16:
|
||||
eval_result =
|
||||
input_quantization_type == kTfLiteNoQuantization
|
||||
? EvalImpl<int16_t>(context, node, AbsHelper,
|
||||
/*validate_input_func=*/nullptr, type)
|
||||
: EvalImplQuantized<int16_t>(context, node, AbsEvalQuantized,
|
||||
/*validate_input_func=*/nullptr,
|
||||
type);
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
|
||||
TfLiteTypeGetName(type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
}
|
||||
return eval_result;
|
||||
return EvalNumeric(context, node, std::abs);
|
||||
}
|
||||
|
||||
TfLiteStatus SinEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
@@ -342,23 +104,7 @@ TfLiteStatus SqrtEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
TfLiteStatus RsqrtEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
TfLiteType type = op_data->input_type;
|
||||
switch (type) {
|
||||
case kTfLiteFloat32:
|
||||
return EvalImpl<float>(
|
||||
context, node, [](float f) { return 1.f / std::sqrt(f); },
|
||||
/*validate_input_func=*/nullptr, type);
|
||||
case kTfLiteInt8:
|
||||
return EvalImplQuantized<int8_t>(context, node,
|
||||
elementwise::RsqrtEvalQuantized,
|
||||
elementwise::validate_input_func, type);
|
||||
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
|
||||
TfLiteTypeGetName(type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return EvalNumeric(context, node, [](float f) { return 1.f / std::sqrt(f); });
|
||||
}
|
||||
|
||||
TfLiteStatus SquareEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
@@ -373,57 +119,101 @@ TfLiteStatus LogicalNotEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace elementwise
|
||||
|
||||
TfLiteRegistration Register_ABS() {
|
||||
return tflite::micro::RegisterOp(
|
||||
elementwise::ElementWiseAbsRsqrtInit,
|
||||
elementwise::PrepareAbsRsqrt<elementwise::IsAbsSupportedType,
|
||||
elementwise::kAbsNameId>,
|
||||
elementwise::AbsEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::AbsEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_SIN() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::SinEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::SinEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_COS() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::CosEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::CosEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LOG() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::LogEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::LogEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_SQRT() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::SqrtEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::SqrtEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_RSQRT() {
|
||||
return tflite::micro::RegisterOp(
|
||||
elementwise::ElementWiseAbsRsqrtInit,
|
||||
elementwise::PrepareAbsRsqrt<elementwise::IsRsqrtSupportedType,
|
||||
elementwise::kRsrqtNameId>,
|
||||
elementwise::RsqrtEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::RsqrtEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_SQUARE() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::SquareEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
/*invoke=*/elementwise::SquareEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LOGICAL_NOT() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsLogicalSupportedType>,
|
||||
elementwise::LogicalNotEval);
|
||||
return {/*init=*/nullptr,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/
|
||||
elementwise::GenericPrepare<elementwise::IsLogicalSupportedType>,
|
||||
/*invoke=*/elementwise::LogicalNotEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
} // namespace tflite
|
||||
|
||||
@@ -146,7 +146,14 @@ TfLiteStatus EluEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_ELU() {
|
||||
return tflite::micro::RegisterOp(EluInit, EluPrepare, EluEval);
|
||||
return {/*init=*/EluInit,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/EluPrepare,
|
||||
/*invoke=*/EluEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
@@ -196,7 +196,14 @@ TfLiteStatus AddEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_ADD() {
|
||||
return tflite::micro::RegisterOp(AddInit, AddPrepare, AddEval);
|
||||
return {/*init=*/AddInit,
|
||||
/*free=*/nullptr,
|
||||
/*prepare=*/AddPrepare,
|
||||
/*invoke=*/AddEval,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0};
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user