blob: d2c716708f5373b131a8bc81e25161d6d5b6148b [file] [log] [blame]
alexander3c798932021-03-26 21:42:19 +00001/*
2 * Copyright (c) 2021 Arm Limited. All rights reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#include "Model.hpp"
alexander31ae9f02022-02-10 16:15:54 +000018#include "log_macros.h"
alexander3c798932021-03-26 21:42:19 +000019
alexander31ae9f02022-02-10 16:15:54 +000020#include <cinttypes>
alexander3c798932021-03-26 21:42:19 +000021
22/* Initialise the model */
23arm::app::Model::~Model()
24{
alexander31ae9f02022-02-10 16:15:54 +000025 delete this->m_pInterpreter;
alexander3c798932021-03-26 21:42:19 +000026 /**
27 * No clean-up function available for allocator in TensorFlow Lite Micro yet.
28 **/
29}
30
31arm::app::Model::Model() :
Isabella Gottardi56ee6202021-05-12 08:27:15 +010032 m_inited (false),
33 m_type(kTfLiteNoType)
alexander3c798932021-03-26 21:42:19 +000034{
Kshitij Sisodiadd6d07b2022-05-03 10:10:14 +010035 this->m_pErrorReporter = tflite::GetMicroErrorReporter();
alexander3c798932021-03-26 21:42:19 +000036}
37
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +010038bool arm::app::Model::Init(uint8_t* tensorArenaAddr,
39 uint32_t tensorArenaSize,
Kshitij Sisodia937052d2022-05-13 16:44:16 +010040 const uint8_t* nnModelAddr,
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +010041 uint32_t nnModelSize,
42 tflite::MicroAllocator* allocator)
alexander3c798932021-03-26 21:42:19 +000043{
44 /* Following tf lite micro example:
45 * Map the model into a usable data structure. This doesn't involve any
46 * copying or parsing, it's a very lightweight operation. */
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +010047 debug("loading model from @ 0x%p\n", nnModelAddr);
48 debug("model size: %" PRIu32 " bytes.\n", nnModelSize);
49
50 this->m_pModel = ::tflite::GetModel(nnModelAddr);
alexander3c798932021-03-26 21:42:19 +000051
Isabella Gottardi56ee6202021-05-12 08:27:15 +010052 if (this->m_pModel->version() != TFLITE_SCHEMA_VERSION) {
53 this->m_pErrorReporter->Report(
alexander3c798932021-03-26 21:42:19 +000054 "[ERROR] model's schema version %d is not equal "
55 "to supported version %d.",
Isabella Gottardi56ee6202021-05-12 08:27:15 +010056 this->m_pModel->version(), TFLITE_SCHEMA_VERSION);
alexander3c798932021-03-26 21:42:19 +000057 return false;
58 }
59
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +010060 this->m_modelAddr = nnModelAddr;
61 this->m_modelSize = nnModelSize;
62
alexander3c798932021-03-26 21:42:19 +000063 /* Pull in only the operation implementations we need.
64 * This relies on a complete list of all the ops needed by this graph.
65 * An easier approach is to just use the AllOpsResolver, but this will
66 * incur some penalty in code space for op implementations that are not
67 * needed by this graph.
68 * static ::tflite::ops::micro::AllOpsResolver resolver; */
69 /* NOLINTNEXTLINE(runtime-global-variables) */
70 debug("loading op resolver\n");
71
72 this->EnlistOperations();
73
74 /* Create allocator instance, if it doesn't exist */
Isabella Gottardi56ee6202021-05-12 08:27:15 +010075 this->m_pAllocator = allocator;
76 if (!this->m_pAllocator) {
alexander3c798932021-03-26 21:42:19 +000077 /* Create an allocator instance */
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +010078 info("Creating allocator using tensor arena at 0x%p\n", tensorArenaAddr);
alexander3c798932021-03-26 21:42:19 +000079
Isabella Gottardi56ee6202021-05-12 08:27:15 +010080 this->m_pAllocator = tflite::MicroAllocator::Create(
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +010081 tensorArenaAddr,
82 tensorArenaSize,
Isabella Gottardi56ee6202021-05-12 08:27:15 +010083 this->m_pErrorReporter);
alexander3c798932021-03-26 21:42:19 +000084
Isabella Gottardi56ee6202021-05-12 08:27:15 +010085 if (!this->m_pAllocator) {
alexander3c798932021-03-26 21:42:19 +000086 printf_err("Failed to create allocator\n");
87 return false;
88 }
Isabella Gottardi56ee6202021-05-12 08:27:15 +010089 debug("Created new allocator @ 0x%p\n", this->m_pAllocator);
alexander3c798932021-03-26 21:42:19 +000090 } else {
Isabella Gottardi56ee6202021-05-12 08:27:15 +010091 debug("Using existing allocator @ 0x%p\n", this->m_pAllocator);
alexander3c798932021-03-26 21:42:19 +000092 }
93
Isabella Gottardi56ee6202021-05-12 08:27:15 +010094 this->m_pInterpreter = new ::tflite::MicroInterpreter(
95 this->m_pModel, this->GetOpResolver(),
96 this->m_pAllocator, this->m_pErrorReporter);
alexander3c798932021-03-26 21:42:19 +000097
Isabella Gottardi56ee6202021-05-12 08:27:15 +010098 if (!this->m_pInterpreter) {
alexander3c798932021-03-26 21:42:19 +000099 printf_err("Failed to allocate interpreter\n");
100 return false;
101 }
102
103 /* Allocate memory from the tensor_arena for the model's tensors. */
104 info("Allocating tensors\n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100105 TfLiteStatus allocate_status = this->m_pInterpreter->AllocateTensors();
alexander3c798932021-03-26 21:42:19 +0000106
107 if (allocate_status != kTfLiteOk) {
alexander3c798932021-03-26 21:42:19 +0000108 printf_err("tensor allocation failed!\n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100109 delete this->m_pInterpreter;
alexander3c798932021-03-26 21:42:19 +0000110 return false;
111 }
112
113 /* Get information about the memory area to use for the model's input. */
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100114 this->m_input.resize(this->GetNumInputs());
alexander3c798932021-03-26 21:42:19 +0000115 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++)
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100116 this->m_input[inIndex] = this->m_pInterpreter->input(inIndex);
alexander3c798932021-03-26 21:42:19 +0000117
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100118 this->m_output.resize(this->GetNumOutputs());
alexander3c798932021-03-26 21:42:19 +0000119 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++)
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100120 this->m_output[outIndex] = this->m_pInterpreter->output(outIndex);
alexander3c798932021-03-26 21:42:19 +0000121
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100122 if (this->m_input.empty() || this->m_output.empty()) {
alexander3c798932021-03-26 21:42:19 +0000123 printf_err("failed to get tensors\n");
124 return false;
125 } else {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100126 this->m_type = this->m_input[0]->type; /* Input 0 should be the main input */
alexander3c798932021-03-26 21:42:19 +0000127
128 /* Clear the input & output tensors */
129 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100130 std::memset(this->m_input[inIndex]->data.data, 0, this->m_input[inIndex]->bytes);
alexander3c798932021-03-26 21:42:19 +0000131 }
132 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100133 std::memset(this->m_output[outIndex]->data.data, 0, this->m_output[outIndex]->bytes);
alexander3c798932021-03-26 21:42:19 +0000134 }
135
136 this->LogInterpreterInfo();
137 }
138
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100139 this->m_inited = true;
alexander3c798932021-03-26 21:42:19 +0000140 return true;
141}
142
143tflite::MicroAllocator* arm::app::Model::GetAllocator()
144{
145 if (this->IsInited()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100146 return this->m_pAllocator;
alexander3c798932021-03-26 21:42:19 +0000147 }
148 return nullptr;
149}
150
151void arm::app::Model::LogTensorInfo(TfLiteTensor* tensor)
152{
153 if (!tensor) {
154 printf_err("Invalid tensor\n");
155 assert(tensor);
156 return;
157 }
158
159 debug("\ttensor is assigned to 0x%p\n", tensor);
160 info("\ttensor type is %s\n", TfLiteTypeGetName(tensor->type));
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100161 info("\ttensor occupies %zu bytes with dimensions\n",
162 tensor->bytes);
alexander3c798932021-03-26 21:42:19 +0000163 for (int i = 0 ; i < tensor->dims->size; ++i) {
164 info ("\t\t%d: %3d\n", i, tensor->dims->data[i]);
165 }
166
167 TfLiteQuantization quant = tensor->quantization;
168 if (kTfLiteAffineQuantization == quant.type) {
169 auto* quantParams = (TfLiteAffineQuantization*)quant.params;
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100170 info("Quant dimension: %" PRIi32 "\n", quantParams->quantized_dimension);
alexander3c798932021-03-26 21:42:19 +0000171 for (int i = 0; i < quantParams->scale->size; ++i) {
172 info("Scale[%d] = %f\n", i, quantParams->scale->data[i]);
173 }
174 for (int i = 0; i < quantParams->zero_point->size; ++i) {
175 info("ZeroPoint[%d] = %d\n", i, quantParams->zero_point->data[i]);
176 }
177 }
178}
179
180void arm::app::Model::LogInterpreterInfo()
181{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100182 if (!this->m_pInterpreter) {
alexander3c798932021-03-26 21:42:19 +0000183 printf_err("Invalid interpreter\n");
184 return;
185 }
186
187 info("Model INPUT tensors: \n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100188 for (auto input : this->m_input) {
alexander3c798932021-03-26 21:42:19 +0000189 this->LogTensorInfo(input);
190 }
191
192 info("Model OUTPUT tensors: \n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100193 for (auto output : this->m_output) {
alexander3c798932021-03-26 21:42:19 +0000194 this->LogTensorInfo(output);
195 }
196
197 info("Activation buffer (a.k.a tensor arena) size used: %zu\n",
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100198 this->m_pInterpreter->arena_used_bytes());
alexander3c798932021-03-26 21:42:19 +0000199
Richard Burton0d110592021-08-12 17:26:30 +0100200 /* We expect there to be only one subgraph. */
201 const uint32_t nOperators = tflite::NumSubgraphOperators(this->m_pModel, 0);
202 info("Number of operators: %" PRIu32 "\n", nOperators);
alexander3c798932021-03-26 21:42:19 +0000203
Richard Burton0d110592021-08-12 17:26:30 +0100204 const tflite::SubGraph* subgraph = this->m_pModel->subgraphs()->Get(0);
205
206 auto* opcodes = this->m_pModel->operator_codes();
207
208 /* For each operator, display registration information. */
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100209 for (size_t i = 0 ; i < nOperators; ++i) {
Richard Burton0d110592021-08-12 17:26:30 +0100210 const tflite::Operator* op = subgraph->operators()->Get(i);
211 const tflite::OperatorCode* opcode = opcodes->Get(op->opcode_index());
212 const TfLiteRegistration* reg = nullptr;
213
214 tflite::GetRegistrationFromOpCode(opcode, this->GetOpResolver(),
215 this->m_pErrorReporter, &reg);
alexander31ae9f02022-02-10 16:15:54 +0000216 std::string opName;
alexander3c798932021-03-26 21:42:19 +0000217
218 if (reg) {
219 if (tflite::BuiltinOperator_CUSTOM == reg->builtin_code) {
220 opName = std::string(reg->custom_name);
221 } else {
222 opName = std::string(EnumNameBuiltinOperator(
223 tflite::BuiltinOperator(reg->builtin_code)));
224 }
225 }
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100226 info("\tOperator %zu: %s\n", i, opName.c_str());
alexander3c798932021-03-26 21:42:19 +0000227 }
228}
229
230bool arm::app::Model::IsInited() const
231{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100232 return this->m_inited;
alexander3c798932021-03-26 21:42:19 +0000233}
234
235bool arm::app::Model::IsDataSigned() const
236{
237 return this->GetType() == kTfLiteInt8;
238}
239
Cisco Cervellera02101092021-09-07 11:34:43 +0100240bool arm::app::Model::ContainsEthosUOperator() const
241{
242 /* We expect there to be only one subgraph. */
243 const uint32_t nOperators = tflite::NumSubgraphOperators(this->m_pModel, 0);
244 const tflite::SubGraph* subgraph = this->m_pModel->subgraphs()->Get(0);
245 const auto* opcodes = this->m_pModel->operator_codes();
246
247 /* check for custom operators */
248 for (size_t i = 0; (i < nOperators); ++i)
249 {
250 const tflite::Operator* op = subgraph->operators()->Get(i);
251 const tflite::OperatorCode* opcode = opcodes->Get(op->opcode_index());
252
253 auto builtin_code = tflite::GetBuiltinCode(opcode);
254 if ((builtin_code == tflite::BuiltinOperator_CUSTOM) &&
255 ( nullptr != opcode->custom_code()) &&
alexander31ae9f02022-02-10 16:15:54 +0000256 ( "ethos-u" == std::string(opcode->custom_code()->c_str())))
Cisco Cervellera02101092021-09-07 11:34:43 +0100257 {
258 return true;
259 }
260 }
261 return false;
262}
263
alexander3c798932021-03-26 21:42:19 +0000264bool arm::app::Model::RunInference()
265{
266 bool inference_state = false;
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100267 if (this->m_pModel && this->m_pInterpreter) {
268 if (kTfLiteOk != this->m_pInterpreter->Invoke()) {
alexander3c798932021-03-26 21:42:19 +0000269 printf_err("Invoke failed.\n");
270 } else {
271 inference_state = true;
272 }
273 } else {
274 printf_err("Error: No interpreter!\n");
275 }
276 return inference_state;
277}
278
279TfLiteTensor* arm::app::Model::GetInputTensor(size_t index) const
280{
281 if (index < this->GetNumInputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100282 return this->m_input.at(index);
alexander3c798932021-03-26 21:42:19 +0000283 }
284 return nullptr;
285}
286
287TfLiteTensor* arm::app::Model::GetOutputTensor(size_t index) const
288{
289 if (index < this->GetNumOutputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100290 return this->m_output.at(index);
alexander3c798932021-03-26 21:42:19 +0000291 }
292 return nullptr;
293}
294
295size_t arm::app::Model::GetNumInputs() const
296{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100297 if (this->m_pModel && this->m_pInterpreter) {
298 return this->m_pInterpreter->inputs_size();
alexander3c798932021-03-26 21:42:19 +0000299 }
300 return 0;
301}
302
303size_t arm::app::Model::GetNumOutputs() const
304{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100305 if (this->m_pModel && this->m_pInterpreter) {
306 return this->m_pInterpreter->outputs_size();
alexander3c798932021-03-26 21:42:19 +0000307 }
308 return 0;
309}
310
311
312TfLiteType arm::app::Model::GetType() const
313{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100314 return this->m_type;
alexander3c798932021-03-26 21:42:19 +0000315}
316
317TfLiteIntArray* arm::app::Model::GetInputShape(size_t index) const
318{
319 if (index < this->GetNumInputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100320 return this->m_input.at(index)->dims;
alexander3c798932021-03-26 21:42:19 +0000321 }
322 return nullptr;
323}
324
325TfLiteIntArray* arm::app::Model::GetOutputShape(size_t index) const
326{
327 if (index < this->GetNumOutputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100328 return this->m_output.at(index)->dims;
alexander3c798932021-03-26 21:42:19 +0000329 }
330 return nullptr;
331}
332
333bool arm::app::Model::ShowModelInfoHandler()
334{
335 if (!this->IsInited()) {
336 printf_err("Model is not initialised! Terminating processing.\n");
337 return false;
338 }
339
340 PrintTensorFlowVersion();
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +0100341 info("Model address: 0x%p", this->ModelPointer());
342 info("Model size: %" PRIu32 " bytes.", this->ModelSize());
alexander3c798932021-03-26 21:42:19 +0000343 info("Model info:\n");
344 this->LogInterpreterInfo();
345
alexander31ae9f02022-02-10 16:15:54 +0000346 info("The model is optimised for Ethos-U NPU: %s.\n", this->ContainsEthosUOperator()? "yes": "no");
alexander3c798932021-03-26 21:42:19 +0000347
348 return true;
349}
alexander3c798932021-03-26 21:42:19 +0000350
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +0100351const uint8_t* arm::app::Model::ModelPointer()
alexander3c798932021-03-26 21:42:19 +0000352{
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +0100353 return this->m_modelAddr;
alexander3c798932021-03-26 21:42:19 +0000354}
355
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +0100356uint32_t arm::app::Model::ModelSize()
alexander3c798932021-03-26 21:42:19 +0000357{
Kshitij Sisodiaaa4bcb12022-05-06 09:13:03 +0100358 return this->m_modelSize;
359}