Standalone encoder and decoder for Soundstream - Extract setup code from the existing soundstream application into a library target. - Rename the existing soundstream application to soundstream_e2e. - Encoder and decoder applications for Soundstream, which run a single invocation of their model, and populate the `output_header` section so that the results can be retrieved. - Enable c++17 to use std::optional - Remove the TF_LITE_STATIC_MEMORY define (as we are using the heap in the soundstream library now) - Fix the definition of operator delete so the linker resolves it properly - Add a definition of operator new (which calls malloc) Change-Id: I446f135825f77744bbe561105b5e156c51b3bc0c
diff --git a/.bazelrc b/.bazelrc index 5dd1c3b..8c3f5e9 100644 --- a/.bazelrc +++ b/.bazelrc
@@ -1,6 +1,6 @@ -build --action_env=BAZEL_CXXOPTS="-std=gnu++14" -build --cxxopt='-std=gnu++14' -build --conlyopt='-std=gnu11' +build --action_env=BAZEL_CXXOPTS="-std=c++17" +build --cxxopt='-std=c++17' +build --conlyopt='-std=c11' # Enable toolchain resolution with cc build --incompatible_enable_cc_toolchain_resolution @@ -15,5 +15,4 @@ build --copt=-DTF_LITE_USE_GLOBAL_MIN build --copt=-DTF_LITE_USE_GLOBAL_MAX build --copt=-DTF_LITE_MCU_DEBUG_LOG -build --copt=-DTF_LITE_STATIC_MEMORY build:opt --copt=-DTF_LITE_STRIP_ERROR_STRINGS
diff --git a/crt/kelvin_gloss.cc b/crt/kelvin_gloss.cc index 9d13823..07e8b8d 100644 --- a/crt/kelvin_gloss.cc +++ b/crt/kelvin_gloss.cc
@@ -136,8 +136,8 @@ return reinterpret_cast<void*>(prev_heap_end); } +void* operator new(size_t n) { return malloc(n); } + void operator delete(void* p) noexcept { free(p); } -extern "C" void operator delete(void* p, uint32_t c) noexcept { - operator delete(p); -} +void operator delete(void* p, size_t c) noexcept { operator delete(p); }
diff --git a/examples/hello_world/hello_world.c b/examples/hello_world/hello_world.c index e47bd5e..aa96624 100644 --- a/examples/hello_world/hello_world.c +++ b/examples/hello_world/hello_world.c
@@ -46,7 +46,5 @@ output = data[kDataSize - 1]; output_header.output_ptr = (uint32_t)&output; - // invalidate all cache. - asm volatile("flushall"); return 0; }
diff --git a/examples/tflm/soundstream/BUILD b/examples/tflm/soundstream/BUILD index 0b79a83..b3a41f3 100644 --- a/examples/tflm/soundstream/BUILD +++ b/examples/tflm/soundstream/BUILD
@@ -2,25 +2,70 @@ package(default_visibility = ["//visibility:public"]) -kelvin_binary( +cc_library( name = "soundstream", srcs = [ + "decoder.cc", + "encoder.cc", + "decoder_non_stream_q16x8_b64_io_int16_tflite.cc", + "encoder_non_stream_q16x8_b64_io_int16_tflite.cc", + "decoder_non_stream_q16x8_b64_io_int16_tflite.h", + "encoder_non_stream_q16x8_b64_io_int16_tflite.h", + ], + hdrs = [ + "decoder.h", + "encoder.h", + ], + tags = ["manual"], + deps = [ + "@tflite-micro//tensorflow/lite/micro:micro_framework", + ], +) + +kelvin_binary( + name = "soundstream_decoder", + srcs = [ + "soundstream_decoder.cc", + ], + tags = ["manual"], + deps = [ + ":soundstream", + "//crt:crt_header", + "@tflite-micro//tensorflow/lite/micro:micro_framework", + "@tflite-micro//tensorflow/lite/micro:system_setup", + ], +) + +kelvin_binary( + name = "soundstream_encoder", + srcs = [ + "soundstream_encoder.cc", + ], + tags = ["manual"], + deps = [ + ":soundstream", + "//crt:crt_header", + "@tflite-micro//tensorflow/lite/micro:micro_framework", + "@tflite-micro//tensorflow/lite/micro:system_setup", + ], +) + +kelvin_binary( + name = "soundstream_e2e", + srcs = [ "best_of_times_s16_decoded.cc", "best_of_times_s16_encoded.cc", "best_of_times_s16_wav.cc", - "decoder_non_stream_q16x8_b64_io_int16_tflite.cc", - "encoder_non_stream_q16x8_b64_io_int16_tflite.cc", - "soundstream.cc", + "soundstream_e2e.cc", ], hdrs = [ "best_of_times_s16_decoded.h", "best_of_times_s16_encoded.h", "best_of_times_s16_wav.h", - "decoder_non_stream_q16x8_b64_io_int16_tflite.h", - "encoder_non_stream_q16x8_b64_io_int16_tflite.h", ], tags = ["manual"], deps = [ + ":soundstream", "//crt:crt_header", "@tflite-micro//tensorflow/lite/micro:micro_framework", "@tflite-micro//tensorflow/lite/micro:system_setup",
diff --git a/examples/tflm/soundstream/decoder.cc b/examples/tflm/soundstream/decoder.cc new file mode 100644 index 0000000..2a0fc4c --- /dev/null +++ b/examples/tflm/soundstream/decoder.cc
@@ -0,0 +1,37 @@ +#include "examples/tflm/soundstream/decoder.h" + +#include "examples/tflm/soundstream/decoder_non_stream_q16x8_b64_io_int16_tflite.h" + +namespace kelvin::soundstream::decoder { +std::optional<Decoder> Setup(uint8_t* tensor_arena) { + auto* model = + tflite::GetModel(g__decoder_non_stream_q16x8_b64_io_int16_model_data); + if (model->version() != TFLITE_SCHEMA_VERSION) { + return {}; + } + + Decoder d; + d.resolver = std::make_unique<tflite::MicroMutableOpResolver<11>>(); + d.resolver->AddReshape(); + d.resolver->AddPad(); + d.resolver->AddConv2D(); + d.resolver->AddLeakyRelu(); + d.resolver->AddSplit(); + d.resolver->AddTransposeConv(); + d.resolver->AddStridedSlice(); + d.resolver->AddConcatenation(); + d.resolver->AddDepthwiseConv2D(); + d.resolver->AddAdd(); + d.resolver->AddQuantize(); + + d.interpreter = std::make_unique<tflite::MicroInterpreter>( + model, *d.resolver, tensor_arena, kTensorArenaSizeBytes); + + TfLiteStatus allocate_status = d.interpreter->AllocateTensors(); + if (allocate_status != kTfLiteOk) { + MicroPrintf("Failed to allocate decoder's tensors"); + return {}; + } + return d; +} +} // namespace kelvin::soundstream::decoder
diff --git a/examples/tflm/soundstream/decoder.h b/examples/tflm/soundstream/decoder.h new file mode 100644 index 0000000..b21c93d --- /dev/null +++ b/examples/tflm/soundstream/decoder.h
@@ -0,0 +1,20 @@ +#ifndef EXAMPLES_TFLM_SOUNDSTREAM_DECODER_H_ +#define EXAMPLES_TFLM_SOUNDSTREAM_DECODER_H_ + +#include <cstddef> +#include <memory> +#include <optional> + +#include "tensorflow/lite/micro/micro_interpreter.h" +#include "tensorflow/lite/micro/micro_mutable_op_resolver.h" + +namespace kelvin::soundstream::decoder { +constexpr size_t kTensorArenaSizeBytes = 96 * 1024; +struct Decoder { + std::unique_ptr<tflite::MicroInterpreter> interpreter; + std::unique_ptr<tflite::MicroMutableOpResolver<11>> resolver; +}; +std::optional<Decoder> Setup(uint8_t* tensor_arena); +} // namespace kelvin::soundstream::decoder + +#endif // EXAMPLES_TFLM_SOUNDSTREAM_DECODER_H_
diff --git a/examples/tflm/soundstream/encoder.cc b/examples/tflm/soundstream/encoder.cc new file mode 100644 index 0000000..d31d517 --- /dev/null +++ b/examples/tflm/soundstream/encoder.cc
@@ -0,0 +1,32 @@ +#include "examples/tflm/soundstream/encoder.h" + +#include "examples/tflm/soundstream/encoder_non_stream_q16x8_b64_io_int16_tflite.h" + +namespace kelvin::soundstream::encoder { +std::optional<Encoder> Setup(uint8_t* tensor_arena) { + auto* model = + tflite::GetModel(g__encoder_non_stream_q16x8_b64_io_int16_model_data); + if (model->version() != TFLITE_SCHEMA_VERSION) { + return {}; + } + + Encoder e; + e.resolver = std::make_unique<tflite::MicroMutableOpResolver<6>>(); + e.resolver->AddReshape(); + e.resolver->AddPad(); + e.resolver->AddConv2D(); + e.resolver->AddLeakyRelu(); + e.resolver->AddDepthwiseConv2D(); + e.resolver->AddAdd(); + + e.interpreter = std::make_unique<tflite::MicroInterpreter>( + model, *e.resolver, tensor_arena, kTensorArenaSizeBytes); + + TfLiteStatus allocate_status = e.interpreter->AllocateTensors(); + if (allocate_status != kTfLiteOk) { + MicroPrintf("Failed to allocate encoder's tensors"); + return {}; + } + return e; +} +} // namespace kelvin::soundstream::encoder
diff --git a/examples/tflm/soundstream/encoder.h b/examples/tflm/soundstream/encoder.h new file mode 100644 index 0000000..3cb74f9 --- /dev/null +++ b/examples/tflm/soundstream/encoder.h
@@ -0,0 +1,20 @@ +#ifndef EXAMPLES_TFLM_SOUNDSTREAM_ENCODER_H_ +#define EXAMPLES_TFLM_SOUNDSTREAM_ENCODER_H_ + +#include <cstddef> +#include <memory> +#include <optional> + +#include "tensorflow/lite/micro/micro_interpreter.h" +#include "tensorflow/lite/micro/micro_mutable_op_resolver.h" + +namespace kelvin::soundstream::encoder { +constexpr size_t kTensorArenaSizeBytes = 96 * 1024; +struct Encoder { + std::unique_ptr<tflite::MicroInterpreter> interpreter; + std::unique_ptr<tflite::MicroMutableOpResolver<6>> resolver; +}; +std::optional<Encoder> Setup(uint8_t* tensor_arena); +} // namespace kelvin::soundstream::encoder + +#endif // EXAMPLES_TFLM_SOUNDSTREAM_ENCODER_H_
diff --git a/examples/tflm/soundstream/soundstream.cc b/examples/tflm/soundstream/soundstream.cc deleted file mode 100644 index 7f500a9..0000000 --- a/examples/tflm/soundstream/soundstream.cc +++ /dev/null
@@ -1,118 +0,0 @@ -// Copyright 2023 Google LLC -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - -#include "examples/tflm/soundstream/best_of_times_s16_decoded.h" -#include "examples/tflm/soundstream/best_of_times_s16_encoded.h" -#include "examples/tflm/soundstream/best_of_times_s16_wav.h" -#include "examples/tflm/soundstream/decoder_non_stream_q16x8_b64_io_int16_tflite.h" -#include "examples/tflm/soundstream/encoder_non_stream_q16x8_b64_io_int16_tflite.h" -#include "tensorflow/lite/micro/micro_interpreter.h" -#include "tensorflow/lite/micro/micro_mutable_op_resolver.h" - -namespace { -const tflite::Model *encoder_model = nullptr; -const tflite::Model *decoder_model = nullptr; -tflite::MicroInterpreter *encoder_interpreter = nullptr; -tflite::MicroInterpreter *decoder_interpreter = nullptr; -constexpr int kTensorArenaSize = - 96 * 1024; -uint8_t encoder_tensor_arena[kTensorArenaSize] __attribute__((aligned(16))); -uint8_t decoder_tensor_arena[kTensorArenaSize] __attribute__((aligned(16))); -} // namespace - -int main(int argc, char **argv) { - encoder_model = - tflite::GetModel(g__encoder_non_stream_q16x8_b64_io_int16_model_data); - if (encoder_model->version() != TFLITE_SCHEMA_VERSION) { - return 1; - } - decoder_model = - tflite::GetModel(g__decoder_non_stream_q16x8_b64_io_int16_model_data); - if (decoder_model->version() != TFLITE_SCHEMA_VERSION) { - return 1; - } - - static tflite::MicroMutableOpResolver<6> encoder_resolver{}; - encoder_resolver.AddReshape(); - encoder_resolver.AddPad(); - encoder_resolver.AddConv2D(); - encoder_resolver.AddLeakyRelu(); - encoder_resolver.AddDepthwiseConv2D(); - encoder_resolver.AddAdd(); - - static tflite::MicroMutableOpResolver<11> decoder_resolver{}; - decoder_resolver.AddReshape(); - decoder_resolver.AddPad(); - decoder_resolver.AddConv2D(); - decoder_resolver.AddLeakyRelu(); - decoder_resolver.AddSplit(); - decoder_resolver.AddTransposeConv(); - decoder_resolver.AddStridedSlice(); - decoder_resolver.AddConcatenation(); - decoder_resolver.AddDepthwiseConv2D(); - decoder_resolver.AddAdd(); - decoder_resolver.AddQuantize(); - - static tflite::MicroInterpreter encoder_static_interpreter( - encoder_model, encoder_resolver, encoder_tensor_arena, kTensorArenaSize); - encoder_interpreter = &encoder_static_interpreter; - - static tflite::MicroInterpreter decoder_static_interpreter( - decoder_model, decoder_resolver, decoder_tensor_arena, kTensorArenaSize); - decoder_interpreter = &decoder_static_interpreter; - - TfLiteStatus allocate_status = encoder_interpreter->AllocateTensors(); - if (allocate_status != kTfLiteOk) { - MicroPrintf("Failed to allocate encoder's tensors"); - return -1; - } - allocate_status = decoder_interpreter->AllocateTensors(); - if (allocate_status != kTfLiteOk) { - MicroPrintf("Failed to allocate decoder's tensors"); - return -1; - } - - TfLiteTensor *encoder_input = encoder_interpreter->input(0); - TfLiteTensor *encoder_output = encoder_interpreter->output(0); - TfLiteTensor *decoder_input = decoder_interpreter->input(0); - TfLiteTensor *decoder_output = decoder_interpreter->output(0); - - int invocation_count = - (g_best_of_times_s16_audio_data_size * sizeof(int16_t)) / - encoder_input->bytes; - for (int i = 0; i < invocation_count; ++i) { - MicroPrintf("Invocation %d of %d", i, invocation_count); - memcpy(encoder_input->data.uint8, - g_best_of_times_s16_audio_data + - ((i * encoder_input->bytes) / sizeof(int16_t)), - encoder_input->bytes); - TfLiteStatus invoke_status = encoder_interpreter->Invoke(); - if (invoke_status != kTfLiteOk) { - MicroPrintf("Failed to invoke encoder"); - return -1; - } - if (memcmp(encoder_output->data.uint8, - g_best_of_times_s16_encoded + (i * encoder_output->bytes), - encoder_output->bytes)) { - MicroPrintf("Encoder output mismatches reference"); - return -1; - } - - memcpy(decoder_input->data.uint8, encoder_output->data.uint8, - decoder_input->bytes); - invoke_status = decoder_interpreter->Invoke(); - if (invoke_status != kTfLiteOk) { - MicroPrintf("Failed to invoke decoder"); - return -1; - } - if (memcmp(decoder_output->data.uint8, - g_best_of_times_s16_decoded + (i * decoder_output->bytes), - decoder_output->bytes)) { - MicroPrintf("Decoder output mismatches reference"); - return -1; - } - } - - return 0; -}
diff --git a/examples/tflm/soundstream/soundstream_decoder.cc b/examples/tflm/soundstream/soundstream_decoder.cc new file mode 100644 index 0000000..77f0f94 --- /dev/null +++ b/examples/tflm/soundstream/soundstream_decoder.cc
@@ -0,0 +1,45 @@ +// Copyright 2023 Google LLC +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "examples/tflm/soundstream/decoder.h" + +typedef struct { + uint32_t return_code; // Populated in kelvin_start + uint32_t output_ptr; + uint32_t length; +} OutputHeader; + +__attribute__((section(".model_output_header"))) OutputHeader output_header = { + .output_ptr = 0, + .length = 0, +}; + +namespace { +uint8_t + decoder_tensor_arena[kelvin::soundstream::decoder::kTensorArenaSizeBytes] + __attribute__((aligned(64))); +} // namespace + +int main(int argc, char **argv) { + auto decoder = kelvin::soundstream::decoder::Setup(decoder_tensor_arena); + if (!decoder) { + MicroPrintf("Unable to construct decoder"); + return -1; + } + + TfLiteTensor *decoder_input = decoder->interpreter->input(0); + TfLiteTensor *decoder_output = decoder->interpreter->output(0); + + memset(decoder_input->data.uint8, 0, decoder_input->bytes); + TfLiteStatus invoke_status = decoder->interpreter->Invoke(); + if (invoke_status != kTfLiteOk) { + MicroPrintf("Failed to invoke decoder"); + return -1; + } + + output_header.length = decoder_output->bytes; + output_header.output_ptr = + reinterpret_cast<uint32_t>(decoder_output->data.uint8); + return 0; +}
diff --git a/examples/tflm/soundstream/soundstream_e2e.cc b/examples/tflm/soundstream/soundstream_e2e.cc new file mode 100644 index 0000000..4c43060 --- /dev/null +++ b/examples/tflm/soundstream/soundstream_e2e.cc
@@ -0,0 +1,75 @@ +// Copyright 2023 Google LLC +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "examples/tflm/soundstream/best_of_times_s16_decoded.h" +#include "examples/tflm/soundstream/best_of_times_s16_encoded.h" +#include "examples/tflm/soundstream/best_of_times_s16_wav.h" +#include "examples/tflm/soundstream/decoder.h" +#include "examples/tflm/soundstream/encoder.h" + +namespace { +uint8_t + encoder_tensor_arena[kelvin::soundstream::encoder::kTensorArenaSizeBytes] + __attribute__((aligned(64))); +uint8_t + decoder_tensor_arena[kelvin::soundstream::decoder::kTensorArenaSizeBytes] + __attribute__((aligned(64))); +} // namespace + +int main(int argc, char **argv) { + auto encoder = kelvin::soundstream::encoder::Setup(encoder_tensor_arena); + if (!encoder) { + MicroPrintf("Unable to construct encoder"); + return -1; + } + + auto decoder = kelvin::soundstream::decoder::Setup(decoder_tensor_arena); + if (!decoder) { + MicroPrintf("Unable to construct decoder"); + return -1; + } + + TfLiteTensor *encoder_input = encoder->interpreter->input(0); + TfLiteTensor *encoder_output = encoder->interpreter->output(0); + TfLiteTensor *decoder_input = decoder->interpreter->input(0); + TfLiteTensor *decoder_output = decoder->interpreter->output(0); + + int invocation_count = + (g_best_of_times_s16_audio_data_size * sizeof(int16_t)) / + encoder_input->bytes; + for (int i = 0; i < invocation_count; ++i) { + MicroPrintf("Invocation %d of %d", i, invocation_count); + memcpy(encoder_input->data.uint8, + g_best_of_times_s16_audio_data + + ((i * encoder_input->bytes) / sizeof(int16_t)), + encoder_input->bytes); + TfLiteStatus invoke_status = encoder->interpreter->Invoke(); + if (invoke_status != kTfLiteOk) { + MicroPrintf("Failed to invoke encoder"); + return -1; + } + if (memcmp(encoder_output->data.uint8, + g_best_of_times_s16_encoded + (i * encoder_output->bytes), + encoder_output->bytes)) { + MicroPrintf("Encoder output mismatches reference"); + return -1; + } + + memcpy(decoder_input->data.uint8, encoder_output->data.uint8, + decoder_input->bytes); + invoke_status = decoder->interpreter->Invoke(); + if (invoke_status != kTfLiteOk) { + MicroPrintf("Failed to invoke decoder"); + return -1; + } + if (memcmp(decoder_output->data.uint8, + g_best_of_times_s16_decoded + (i * decoder_output->bytes), + decoder_output->bytes)) { + MicroPrintf("Decoder output mismatches reference"); + return -1; + } + } + + return 0; +}
diff --git a/examples/tflm/soundstream/soundstream_encoder.cc b/examples/tflm/soundstream/soundstream_encoder.cc new file mode 100644 index 0000000..bc7fc25 --- /dev/null +++ b/examples/tflm/soundstream/soundstream_encoder.cc
@@ -0,0 +1,45 @@ +// Copyright 2023 Google LLC +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "examples/tflm/soundstream/encoder.h" + +typedef struct { + uint32_t return_code; // Populated in kelvin_start + uint32_t output_ptr; + uint32_t length; +} OutputHeader; + +__attribute__((section(".model_output_header"))) OutputHeader output_header = { + .output_ptr = 0, + .length = 0, +}; + +namespace { +uint8_t + encoder_tensor_arena[kelvin::soundstream::encoder::kTensorArenaSizeBytes] + __attribute__((aligned(64))); +} // namespace + +int main(int argc, char **argv) { + auto encoder = kelvin::soundstream::encoder::Setup(encoder_tensor_arena); + if (!encoder) { + MicroPrintf("Unable to construct encoder"); + return -1; + } + + TfLiteTensor *encoder_input = encoder->interpreter->input(0); + TfLiteTensor *encoder_output = encoder->interpreter->output(0); + + memset(encoder_input->data.uint8, 0, encoder_input->bytes); + TfLiteStatus invoke_status = encoder->interpreter->Invoke(); + if (invoke_status != kTfLiteOk) { + MicroPrintf("Failed to invoke encoder"); + return -1; + } + + output_header.length = encoder_output->bytes; + output_header.output_ptr = + reinterpret_cast<uint32_t>(encoder_output->data.uint8); + return 0; +}