blob: f6cc3a036b279581126e0b8aeea3f8a280ecd353 [file] [log] [blame]
/* 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.
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 <cstdint>
#include <cstring>
#include "signal/micro/kernels/framer_flexbuffers_generated_data.h"
#include "tensorflow/lite/micro/kernels/kernel_runner.h"
#include "tensorflow/lite/micro/test_helpers.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace tflite {
namespace {
constexpr int kFrameSizeIndex = 0; // 'frame_size'
constexpr int kFrameStepIndex = 1; // 'frame_step'
constexpr int kPrefillIndex = 2; // 'prefill'
constexpr int kInputsSize = 1;
constexpr int kOutputsSize = 2;
constexpr int kTensorsSize = kInputsSize + kOutputsSize;
class FramerKernelRunner {
public:
FramerKernelRunner(int* input_dims_data, int16_t* input_data,
int* output_dims_data, int16_t* output_data,
int* output_ready_dims_data, bool* output_ready)
: inputs_array_{testing::IntArrayFromInts(inputs_array_data_)},
outputs_array_{testing::IntArrayFromInts(outputs_array_data_)} {
tensors_[0] = testing::CreateTensor(
input_data, testing::IntArrayFromInts(input_dims_data));
tensors_[1] = testing::CreateTensor(
output_data, testing::IntArrayFromInts(output_dims_data));
tensors_[2] = testing::CreateTensor(
output_ready, testing::IntArrayFromInts(output_ready_dims_data));
// go/tflm-static-cleanups for reasoning new is being used like this
kernel_runner_ = new (kernel_runner_buffer) micro::KernelRunner(
*registration_, tensors_, kTensorsSize, inputs_array_, outputs_array_,
/*builtin_data=*/nullptr);
}
micro::KernelRunner& kernel_runner() { return *kernel_runner_; }
private:
uint8_t kernel_runner_buffer[sizeof(micro::KernelRunner)];
int inputs_array_data_[kInputsSize + 1] = {kInputsSize, 0};
int outputs_array_data_[kOutputsSize + 1] = {kOutputsSize, 1, 2};
TfLiteTensor tensors_[kTensorsSize] = {};
TfLiteIntArray* inputs_array_ = nullptr;
TfLiteIntArray* outputs_array_ = nullptr;
TFLMRegistration* registration_ = tflm_signal::Register_FRAMER();
micro::KernelRunner* kernel_runner_ = nullptr;
};
alignas(alignof(FramerKernelRunner)) uint8_t
framer_kernel_runner_buffer[sizeof(FramerKernelRunner)];
void TestFramerInvoke(int* input_dims_data, int16_t* input_data,
int* output_dims_data, const int16_t* golden,
int golden_len, int* output_ready_dims_data,
const unsigned char* flexbuffers_data,
const unsigned int flexbuffers_data_size,
int16_t* output_data, bool* output_ready,
micro::KernelRunner* runner) {
FlexbufferWrapper fbw(flexbuffers_data, flexbuffers_data_size);
int frame_size = fbw.ElementAsInt32(kFrameSizeIndex);
int frame_step = fbw.ElementAsInt32(kFrameStepIndex);
bool prefill = fbw.ElementAsBool(kPrefillIndex);
int latency_samples = frame_size - frame_step;
int input_size = input_dims_data[input_dims_data[0]];
int outer_dims = 1;
for (int i = 1; i < input_dims_data[0]; i++) {
outer_dims *= input_dims_data[i];
}
int n_frames = output_dims_data[output_dims_data[0] - 1];
TF_LITE_MICRO_EXPECT_EQ(frame_size, output_dims_data[output_dims_data[0]]);
for (int i = 0; i < golden_len - latency_samples; i += input_size) {
for (int outer_dim = 0; outer_dim < outer_dims; outer_dim++) {
memcpy(&input_data[outer_dim * input_size], &golden[latency_samples + i],
input_size * sizeof(int16_t));
}
TF_LITE_MICRO_EXPECT_EQ(runner->Invoke(), kTfLiteOk);
TF_LITE_MICRO_EXPECT_EQ(*output_ready, (i >= latency_samples) || prefill);
if (*output_ready == true) {
for (int outer_dim = 0; outer_dim < outer_dims; outer_dim++) {
for (int frame = 0; frame < n_frames; frame++) {
int output_idx =
outer_dim * frame_size * n_frames + frame * frame_size;
int golden_idx = i + frame * frame_step;
TF_LITE_MICRO_EXPECT_EQ(
0, memcmp(&golden[golden_idx], &output_data[output_idx],
frame_size * sizeof(int16_t)));
}
}
}
}
}
void TestFramer(int* input_dims_data, int16_t* input_data,
int* output_dims_data, const int16_t* golden, int golden_len,
int* output_ready_dims_data,
const unsigned char* flexbuffers_data,
const unsigned int flexbuffers_data_size,
int16_t* output_data) {
bool output_ready = false;
FramerKernelRunner* framer_runner = new (framer_kernel_runner_buffer)
FramerKernelRunner(input_dims_data, input_data, output_dims_data,
output_data, output_ready_dims_data, &output_ready);
// TfLite uses a char* for the raw bytes whereas flexbuffers use an unsigned
// char*. This small discrepancy results in compiler warnings unless we
// reinterpret_cast right before passing in the flexbuffer bytes to the
// KernelRunner.
TF_LITE_MICRO_EXPECT_EQ(framer_runner->kernel_runner().InitAndPrepare(
reinterpret_cast<const char*>(flexbuffers_data),
flexbuffers_data_size),
kTfLiteOk);
TestFramerInvoke(input_dims_data, input_data, output_dims_data, golden,
golden_len, output_ready_dims_data, flexbuffers_data,
flexbuffers_data_size, output_data, &output_ready,
&framer_runner->kernel_runner());
}
void TestFramerReset(int* input_dims_data, int16_t* input_data,
int* output_dims_data, const int16_t* golden,
int golden_len, int* output_ready_dims_data,
const unsigned char* flexbuffers_data,
const unsigned int flexbuffers_data_size,
int16_t* output_data) {
bool output_ready = false;
FramerKernelRunner* framer_runner = new (framer_kernel_runner_buffer)
FramerKernelRunner(input_dims_data, input_data, output_dims_data,
output_data, output_ready_dims_data, &output_ready);
// TfLite uses a char* for the raw bytes whereas flexbuffers use an unsigned
// char*. This small discrepancy results in compiler warnings unless we
// reinterpret_cast right before passing in the flexbuffer bytes to the
// KernelRunner.
TF_LITE_MICRO_EXPECT_EQ(framer_runner->kernel_runner().InitAndPrepare(
reinterpret_cast<const char*>(flexbuffers_data),
flexbuffers_data_size),
kTfLiteOk);
TestFramerInvoke(input_dims_data, input_data, output_dims_data, golden,
golden_len, output_ready_dims_data, flexbuffers_data,
flexbuffers_data_size, output_data, &output_ready,
&framer_runner->kernel_runner());
framer_runner->kernel_runner().Reset();
TestFramerInvoke(input_dims_data, input_data, output_dims_data, golden,
golden_len, output_ready_dims_data, flexbuffers_data,
flexbuffers_data_size, output_data, &output_ready,
&framer_runner->kernel_runner());
}
} // namespace
} // namespace tflite
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(FramerTest_3_1_0) {
const int kInputSize = 1;
const int kOutputSize = 3;
int input_dims_data[] = {1, kInputSize};
int output_dims_data[] = {2, 1, kOutputSize};
int output_ready_dims_data[] = {0};
const int16_t golden[] = {0x0, 0x0, 0x1234, 0x5678, 0x4321, 0x7777};
int16_t input_data;
int16_t output_data[kOutputSize];
tflite::TestFramer(input_dims_data, &input_data, output_dims_data, golden,
sizeof(golden) / sizeof(int16_t), output_ready_dims_data,
g_gen_data_3_1_0_framer, g_gen_data_size_3_1_0_framer,
output_data);
}
TF_LITE_MICRO_TEST(FramerTest_5_2_1) {
const int kInputSize = 2;
const int kOutputSize = 5;
int input_dims_data[] = {1, kInputSize};
int output_dims_data[] = {2, 1, kOutputSize};
int output_ready_dims_data[] = {0};
const int16_t golden[] = {0x0, 0x0, 0x0, 0x1010, 0x0202, 0x7070, 0x0606};
int16_t input_data[kInputSize];
int16_t output_data[kOutputSize];
tflite::TestFramer(input_dims_data, input_data, output_dims_data, golden,
sizeof(golden) / sizeof(int16_t), output_ready_dims_data,
g_gen_data_5_2_1_framer, g_gen_data_size_5_2_1_framer,
output_data);
}
TF_LITE_MICRO_TEST(FramerTest_5_2_1_NFrames2) {
const int kInputSize = 4;
const int kOutputSize = 5;
const int kNFrames = 2;
int input_dims_data[] = {1, kInputSize};
int output_dims_data[] = {2, kNFrames, kOutputSize};
int output_ready_dims_data[] = {0};
const int16_t golden[] = {0x0, 0x0, 0x0, 0x1010, 0x0202, 0x7070, 0x0606};
int16_t input_data[kInputSize];
int16_t output_data[kNFrames * kOutputSize];
tflite::TestFramer(input_dims_data, input_data, output_dims_data, golden,
sizeof(golden) / sizeof(int16_t), output_ready_dims_data,
g_gen_data_5_2_1_framer, g_gen_data_size_5_2_1_framer,
output_data);
}
TF_LITE_MICRO_TEST(FramerTest_5_2_1_NFrames2OuterDims4) {
const int kInputSize = 4;
const int kOutputSize = 5;
int input_dims_data[] = {3, 2, 2, kInputSize};
int output_dims_data[] = {4, 2, 2, 2, kOutputSize};
int output_ready_dims_data[] = {0};
const int16_t golden[] = {0x0, 0x0, 0x0, 0x1010, 0x0202, 0x7070, 0x0606};
int16_t input_data[2 * 2 * kInputSize];
int16_t output_data[2 * 2 * 2 * kOutputSize];
tflite::TestFramer(input_dims_data, input_data, output_dims_data, golden,
sizeof(golden) / sizeof(int16_t), output_ready_dims_data,
g_gen_data_5_2_1_framer, g_gen_data_size_5_2_1_framer,
output_data);
}
TF_LITE_MICRO_TEST(TestReset) {
const int kInputSize = 1;
const int kOutputSize = 3;
int input_dims_data[] = {1, kInputSize};
int output_dims_data[] = {2, 1, kOutputSize};
int output_ready_dims_data[] = {0};
const int16_t golden[] = {0x0, 0x0, 0x1234, 0x5678, 0x4321, 0x7777};
int16_t input_data;
int16_t output_data[kOutputSize];
tflite::TestFramerReset(input_dims_data, &input_data, output_dims_data,
golden, sizeof(golden) / sizeof(int16_t),
output_ready_dims_data, g_gen_data_3_1_0_framer,
g_gen_data_size_3_1_0_framer, output_data);
}
TF_LITE_MICRO_TESTS_END