Thanks @sterretje,
I have made a bit progress. I can import the libs without errors now.
Here is the full code on Nicla:
#include <Arduino.h>
#include <Nicla_System.h>
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/micro/examples/hello_world/models/model.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_profiler.h"
#include "tensorflow/lite/micro/recording_micro_interpreter.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace {
// new alias.
using HelloWorldOpResolver = tflite::MicroMutableOpResolver<1>;
TfLiteStatus RegisterOps(HelloWorldOpResolver& op_resolver) {
TF_LITE_ENSURE_STATUS(op_resolver.AddFullyConnected());
return kTfLiteOk;
}
} // namespace
TfLiteStatus ProfileMemoryAndLatency() {
tflite::MicroProfiler profiler;
HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));
// Arena size just a round number. The exact arena usage can be determined
// using the RecordingMicroInterpreter.
constexpr int kTensorArenaSize = 3000;
uint8_t tensor_arena[kTensorArenaSize];
constexpr int kNumResourceVariables = 24;
tflite::RecordingMicroAllocator* allocator(
tflite::RecordingMicroAllocator::Create(tensor_arena, kTensorArenaSize));
tflite::RecordingMicroInterpreter interpreter(
tflite::GetModel(g_model), op_resolver, allocator,
tflite::MicroResourceVariables::Create(allocator, kNumResourceVariables),
&profiler);
TF_LITE_ENSURE_STATUS(interpreter.AllocateTensors());
TFLITE_CHECK_EQ(interpreter.inputs_size(), 1);
interpreter.input(0)->data.f[0] = 1.f;
TF_LITE_ENSURE_STATUS(interpreter.Invoke());
//MicroPrintf(""); // Print an empty new line
profiler.LogTicksPerTagCsv();
//MicroPrintf(""); // Print an empty new line
interpreter.GetMicroAllocator().PrintAllocations();
return kTfLiteOk;
}
TfLiteStatus LoadFloatModelAndPerformInference() {
const tflite::Model* model =
::tflite::GetModel(g_model);
TFLITE_CHECK_EQ(model->version(), TFLITE_SCHEMA_VERSION);
HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));
// Arena size just a round number. The exact arena usage can be determined
// using the RecordingMicroInterpreter.
constexpr int kTensorArenaSize = 3000;
uint8_t tensor_arena[kTensorArenaSize];
tflite::MicroInterpreter interpreter(model, op_resolver, tensor_arena,
kTensorArenaSize);
TF_LITE_ENSURE_STATUS(interpreter.AllocateTensors());
// Check if the predicted output is within a small range of the
// expected output
float epsilon = 0.05f;
constexpr int kNumTestValues = 4;
float golden_inputs[kNumTestValues] = {0.f, 1.f, 3.f, 5.f};
for (int i = 0; i < kNumTestValues; ++i) {
interpreter.input(0)->data.f[0] = golden_inputs[i];
TF_LITE_ENSURE_STATUS(interpreter.Invoke());
float y_pred = interpreter.output(0)->data.f[0];
TFLITE_CHECK_LE(abs(sin(golden_inputs[i]) - y_pred), epsilon);
}
return kTfLiteOk;
}
TfLiteStatus initializeModelAndPerformInference() {
tflite::InitializeTarget();
TF_LITE_ENSURE_STATUS(ProfileMemoryAndLatency());
TF_LITE_ENSURE_STATUS(LoadFloatModelAndPerformInference());
//MicroPrintf("~~~ALL TESTS PASSED~~~\n");
return kTfLiteOk;
}
void setup() {
nicla::begin();
nicla::leds.begin();
initializeModelAndPerformInference();
}
void loop() {
nicla::leds.setColor(blue);
delay(1000);
nicla::leds.setColor(red);
delay(1000);
}
Now, I am getting following errors:
main.cpp:(.text._Z23ProfileMemoryAndLatencyv+0x6e): undefined reference to `tflite::MicroInterpreter::AllocateTensors()'
main.cpp:(.text._Z23ProfileMemoryAndLatencyv+0xbe): undefined reference to `tflite::MicroInterpreter::input(unsigned int)'
main.cpp:(.text._Z23ProfileMemoryAndLatencyv+0xcc): undefined reference to `tflite::MicroInterpreter::Invoke()'
main.cpp:(.text._Z23ProfileMemoryAndLatencyv+0xd8): undefined reference to `tflite::MicroProfiler::LogTicksPerTagCsv()'
main.cpp:(.text._Z23ProfileMemoryAndLatencyv+0xde): undefined reference to `tflite::RecordingMicroAllocator::PrintAllocations() const'
main.cpp:(.text._Z23ProfileMemoryAndLatencyv+0xe4): undefined reference to `tflite::MicroInterpreter::~MicroInterpreter()'
So I believe the linker is unable to find the definitions for various symbols related to the TensorFlow Lite library. I have found the linker script:
MEMORY
{
FLASH (rx) : ORIGIN = 0x10000, LENGTH = 0x70000
RAM_NVIC (rwx) : ORIGIN = 0x20000000, LENGTH = 0xE0
RAM (rwx) : ORIGIN = (0x20000000 + 0xE0), LENGTH = (0x10000 - 0xE0)
}
OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
KEEP(*(.Vectors))
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > FLASH
.sdh_soc_observers :
{
PROVIDE(__start_sdh_soc_observers = .);
KEEP(*(SORT(.sdh_soc_observers*)))
PROVIDE(__stop_sdh_soc_observers = .);
} > FLASH
.sdh_stack_observers :
{
PROVIDE(__start_sdh_stack_observers = .);
KEEP(*(SORT(.sdh_stack_observers*)))
PROVIDE(__stop_sdh_stack_observers = .);
} > FLASH
.sdh_req_observers :
{
PROVIDE(__start_sdh_req_observers = .);
KEEP(*(SORT(.sdh_req_observers*)))
PROVIDE(__stop_sdh_req_observers = .);
} > FLASH
.sdh_state_observers :
{
PROVIDE(__start_sdh_state_observers = .);
KEEP(*(SORT(.sdh_state_observers*)))
PROVIDE(__stop_sdh_state_observers = .);
} > FLASH
.sdh_ble_observers :
{
PROVIDE(__start_sdh_ble_observers = .);
KEEP(*(SORT(.sdh_ble_observers*)))
PROVIDE(__stop_sdh_ble_observers = .);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
. = ALIGN(8);
} > FLASH
__exidx_end = .;
__etext = .;
.data : AT (__etext)
{
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
PROVIDE(__start_fs_data = .);
KEEP(*(.fs_data))
PROVIDE(__stop_fs_data = .);
*(.jcr)
. = ALIGN(8);
__data_end__ = .;
} > RAM
__edata = .;
.nvictable (NOLOAD) :
{
PROVIDE(__start_nvictable = .);
KEEP(*(.nvictable))
PROVIDE(__stop_nvictable = .);
} > RAM_NVIC
.noinit (NOLOAD) :
{
PROVIDE(__start_noinit = .);
KEEP(*(.noinit))
PROVIDE(__stop_noinit = .);
} > RAM
.bss :
{
. = ALIGN(8);
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(8);
__bss_end__ = .;
} > RAM
.heap (NOLOAD):
{
__end__ = .;
end = __end__;
*(.heap*);
ASSERT(. <= (ORIGIN(RAM) + LENGTH(RAM) - 0x400), "heap region overflowed into stack");
. = ORIGIN(RAM) + LENGTH(RAM) - 0x400;
__HeapLimit = .;
} > RAM
PROVIDE(__heap_start = ADDR(.heap));
PROVIDE(__heap_size = SIZEOF(.heap));
PROVIDE(__mbed_sbrk_start = ADDR(.heap));
PROVIDE(__mbed_krbs_start = ADDR(.heap) + SIZEOF(.heap));
.stack (NOLOAD):
{
__StackLimit = .;
*(.stack*)
. = ORIGIN(RAM) + LENGTH(RAM);
} > RAM
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - 0x400;
PROVIDE(__stack = __StackTop);
}
Now trying to figure out what is going on there. I think it makes sense to change the title to for example "undefined reference to tflite::MicroInterpreter::AllocateTensors()", since I believe, we can def. run ML on Nicla.