Add 16bit xtensa depthwise conv kernel support (#3481)

* Added 16x8 xtensa depthwise conv kernel

* Added int16 support only for HIFI5

* Code formatting
This commit is contained in:
Nicolás Arrieta Larraza 2026-03-18 17:23:13 +01:00 committed by GitHub
parent f2b2b3f51c
commit f5302ed4fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 164 additions and 21 deletions

View file

@ -52,13 +52,14 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
TfLiteTensor* input =
micro_context->AllocateTempInputTensor(node, kConvInputTensor);
TF_LITE_ENSURE(context, input != nullptr);
// For int16 input, only fallback to the reference kernel is used
// so there is no need to prepare the Hifi/Vision kernel.
#ifndef HIFI5
// Int16 input is only supported by HIFI5 for now.
// So there is no need to prepare the Hifi/Vision kernel for other targets.
if (input->type == kTfLiteInt16) {
micro_context->DeallocateTempTfLiteTensor(input);
return kTfLiteOk;
}
#endif
micro_context->DeallocateTempTfLiteTensor(input);
#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5)
@ -110,8 +111,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
switch (filter_int8.type) {
case kTfLiteInt8: {
#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5)
DepthwiseConvEvalHifi(context, node, params, op_data, input,
&filter_int8, bias, output);
DepthwiseConvEvalHifiInt8(context, node, params, op_data, input,
&filter_int8, bias, output);
#elif defined(VISION_P6)
DepthwiseConvEvalVision(context, node, params, op_data, input,
&filter_int8, bias, output);
@ -151,6 +152,10 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
case kTfLiteInt16: {
switch (filter->type) {
case kTfLiteInt8: {
#if defined(HIFI5)
DepthwiseConvEvalHifiInt16(context, node, params, op_data, input,
filter, bias, output);
#else
reference_integer_ops::DepthwiseConvPerChannel(
DepthwiseConvParamsQuantized(params, op_data.reference_op_data),
op_data.reference_op_data.per_channel_output_multiplier,
@ -173,6 +178,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
#endif // USE_TFLM_COMPRESSION
tflite::micro::GetTensorShape(output),
tflite::micro::GetTensorData<int16_t>(output));
#endif // defined(HIFI5)
break;
}
default:

View file

@ -50,8 +50,6 @@ TfLiteStatus DepthwiseConvPrepareHifi(TfLiteContext* context,
micro_context->AllocateTempInputTensor(node, kConvWeightsTensor);
TF_LITE_ENSURE(context, filter != nullptr);
TF_LITE_ENSURE_EQ(context, input->type, kTfLiteInt8);
const RuntimeShape& input_shape = GetTensorShape(input);
const RuntimeShape& filter_shape = GetTensorShape(filter);
const RuntimeShape& output_shape = GetTensorShape(output);
@ -90,13 +88,13 @@ TfLiteStatus DepthwiseConvPrepareHifi(TfLiteContext* context,
return kTfLiteOk;
}
TfLiteStatus DepthwiseConvEvalHifi(TfLiteContext* context, TfLiteNode* node,
const TfLiteDepthwiseConvParams& params,
const XtensaDepthwiseConvOpData& data,
const TfLiteEvalTensor* input,
const TfLiteEvalTensor* filter,
const TfLiteEvalTensor* bias,
TfLiteEvalTensor* output) {
TfLiteStatus DepthwiseConvEvalHifiInt8(TfLiteContext* context, TfLiteNode* node,
const TfLiteDepthwiseConvParams& params,
const XtensaDepthwiseConvOpData& data,
const TfLiteEvalTensor* input,
const TfLiteEvalTensor* filter,
const TfLiteEvalTensor* bias,
TfLiteEvalTensor* output) {
#ifdef USE_TFLM_COMPRESSION
MicroContext* micro_context = GetMicroContext(context);
@ -217,5 +215,135 @@ TfLiteStatus DepthwiseConvEvalHifi(TfLiteContext* context, TfLiteNode* node,
return kTfLiteOk;
}
TfLiteStatus DepthwiseConvEvalHifiInt16(TfLiteContext* context,
TfLiteNode* node,
const TfLiteDepthwiseConvParams& params,
const XtensaDepthwiseConvOpData& data,
const TfLiteEvalTensor* input,
const TfLiteEvalTensor* filter,
const TfLiteEvalTensor* bias,
TfLiteEvalTensor* output) {
#ifdef USE_TFLM_COMPRESSION
MicroContext* micro_context = GetMicroContext(context);
const CompressionTensorData* filter_comp_td =
micro_context->GetTensorCompressionData(node,
kDepthwiseConvWeightsTensor);
const CompressionTensorData* bias_comp_td =
micro_context->GetTensorCompressionData(node, kDepthwiseConvBiasTensor);
#endif // USE_TFLM_COMPRESSION
// If dilation is not required use the optimized NN Library kernel.
// Otherwise call the reference implementation.
if ((params.dilation_width_factor == 1) &&
(params.dilation_height_factor == 1) && bias != nullptr) {
const int stride_width = params.stride_width;
const int stride_height = params.stride_height;
const int pad_width = data.reference_op_data.padding.width;
const int pad_height = data.reference_op_data.padding.height;
const int depth_multiplier = params.depth_multiplier;
const int32_t output_activation_min =
data.reference_op_data.output_activation_min;
const int32_t output_activation_max =
data.reference_op_data.output_activation_max;
TFLITE_DCHECK_LE(output_activation_min, output_activation_max);
const RuntimeShape& input_shape = tflite::micro::GetTensorShape(input);
const RuntimeShape& filter_shape = tflite::micro::GetTensorShape(filter);
const RuntimeShape& output_shape = tflite::micro::GetTensorShape(output);
const RuntimeShape& bias_shape = tflite::micro::GetTensorShape(bias);
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
const int batches = MatchingDim(input_shape, 0, output_shape, 0);
const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3);
const int input_height = input_shape.Dims(1);
const int input_width = input_shape.Dims(2);
const int input_depth = input_shape.Dims(3);
const int filter_height = filter_shape.Dims(1);
const int filter_width = filter_shape.Dims(2);
const int output_height = output_shape.Dims(1);
const int output_width = output_shape.Dims(2);
TFLITE_DCHECK_EQ(output_depth, input_depth * depth_multiplier);
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
const int16_t* input_data = tflite::micro::GetTensorData<int16_t>(input);
#ifdef USE_TFLM_COMPRESSION
const int8_t* filter_data = tflite::micro::GetTensorData<int8_t>(
micro_context, filter, filter_comp_td,
data.reference_op_data.weights_scratch_index);
const int64_t* bias_data = tflite::micro::GetTensorData<int64_t>(
micro_context, bias, bias_comp_td,
data.reference_op_data.bias_scratch_index);
#else // USE_TFLM_COMPRESSION
const int8_t* filter_data = tflite::micro::GetTensorData<int8_t>(filter);
const int64_t* bias_data = tflite::micro::GetTensorData<int64_t>(bias);
#endif // USE_TFLM_COMPRESSION
int16_t* output_data = tflite::micro::GetTensorData<int16_t>(output);
int32_t input_data_format = 0;
int32_t output_data_format = 0;
uint8_t* p_scratch = static_cast<uint8_t*>(
context->GetScratchBuffer(context, data.scratch_tensor_index));
for (int i = 0; i < batches; i++) {
TF_LITE_ENSURE_EQ(
context,
xa_nn_conv2d_depthwise_per_chan_sym8sxsym16s(
&output_data[i * output_height * output_width * output_depth],
filter_data,
&input_data[i * input_height * input_width * input_depth],
bias_data, input_height, input_width, input_depth, filter_height,
filter_width, depth_multiplier, stride_width, stride_height,
pad_width, pad_height, output_height, output_width,
-data.reference_op_data.input_zero_point,
data.reference_op_data.per_channel_output_multiplier,
data.reference_op_data.per_channel_output_shift,
data.reference_op_data.output_zero_point, input_data_format,
output_data_format, p_scratch),
0);
}
int out_length = batches * output_height * output_width * output_depth;
TF_LITE_ENSURE_EQ(context,
xa_nn_vec_activation_min_max_16_16(
output_data, output_data, output_activation_min,
output_activation_max, out_length),
0);
return kTfLiteOk;
}
reference_integer_ops::DepthwiseConvPerChannel(
DepthwiseConvParamsQuantized(params, data.reference_op_data),
data.reference_op_data.per_channel_output_multiplier,
data.reference_op_data.per_channel_output_shift,
tflite::micro::GetTensorShape(input),
tflite::micro::GetTensorData<int16_t>(input),
tflite::micro::GetTensorShape(filter),
#ifdef USE_TFLM_COMPRESSION
tflite::micro::GetTensorData<int8_t>(
micro_context, filter, filter_comp_td,
data.reference_op_data.weights_scratch_index),
tflite::micro::GetTensorShape(bias),
tflite::micro::GetOptionalTensorData<int64_t>(
micro_context, bias, bias_comp_td,
data.reference_op_data.bias_scratch_index),
#else // USE_TFLM_COMPRESSION
tflite::micro::GetTensorData<int8_t>(filter),
tflite::micro::GetTensorShape(bias),
tflite::micro::GetOptionalTensorData<int64_t>(bias),
#endif // USE_TFLM_COMPRESSION
tflite::micro::GetTensorShape(output),
tflite::micro::GetTensorData<int16_t>(output));
return kTfLiteOk;
}
} // namespace tflite
#endif // defined(HIFI3) ||defined(HIFI4) || defined(HIFI5)

View file

@ -42,13 +42,22 @@ struct XtensaDepthwiseConvOpData {
#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5)
TfLiteStatus DepthwiseConvPrepareHifi(TfLiteContext* context, TfLiteNode* node);
TfLiteStatus DepthwiseConvEvalHifi(TfLiteContext* context, TfLiteNode* node,
const TfLiteDepthwiseConvParams& params,
const XtensaDepthwiseConvOpData& data,
const TfLiteEvalTensor* input,
const TfLiteEvalTensor* filter,
const TfLiteEvalTensor* bias,
TfLiteEvalTensor* output);
TfLiteStatus DepthwiseConvEvalHifiInt8(TfLiteContext* context, TfLiteNode* node,
const TfLiteDepthwiseConvParams& params,
const XtensaDepthwiseConvOpData& data,
const TfLiteEvalTensor* input,
const TfLiteEvalTensor* filter,
const TfLiteEvalTensor* bias,
TfLiteEvalTensor* output);
TfLiteStatus DepthwiseConvEvalHifiInt16(TfLiteContext* context,
TfLiteNode* node,
const TfLiteDepthwiseConvParams& params,
const XtensaDepthwiseConvOpData& data,
const TfLiteEvalTensor* input,
const TfLiteEvalTensor* filter,
const TfLiteEvalTensor* bias,
TfLiteEvalTensor* output);
TfLiteStatus DepthwiseConvReferenceEvalInt8(TfLiteContext* context,
TfLiteNode* node);