| /* 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 |