blob: acc2f0e8fc8c0b3b139a4ac25712865b03f3e5f0 [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"
18
19#include "hal.h"
20
21#include <cstdint>
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +010022#include <inttypes.h>
alexander3c798932021-03-26 21:42:19 +000023
24/* Initialise the model */
25arm::app::Model::~Model()
26{
Isabella Gottardi56ee6202021-05-12 08:27:15 +010027 if (this->m_pInterpreter) {
28 delete this->m_pInterpreter;
alexander3c798932021-03-26 21:42:19 +000029 }
30
31 /**
32 * No clean-up function available for allocator in TensorFlow Lite Micro yet.
33 **/
34}
35
36arm::app::Model::Model() :
Isabella Gottardi56ee6202021-05-12 08:27:15 +010037 m_inited (false),
38 m_type(kTfLiteNoType)
alexander3c798932021-03-26 21:42:19 +000039{
Isabella Gottardi56ee6202021-05-12 08:27:15 +010040 this->m_pErrorReporter = &this->m_uErrorReporter;
alexander3c798932021-03-26 21:42:19 +000041}
42
43bool arm::app::Model::Init(tflite::MicroAllocator* allocator)
44{
45 /* Following tf lite micro example:
46 * Map the model into a usable data structure. This doesn't involve any
47 * copying or parsing, it's a very lightweight operation. */
48 const uint8_t* model_addr = ModelPointer();
49 debug("loading model from @ 0x%p\n", model_addr);
Isabella Gottardi56ee6202021-05-12 08:27:15 +010050 this->m_pModel = ::tflite::GetModel(model_addr);
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
60 /* Pull in only the operation implementations we need.
61 * This relies on a complete list of all the ops needed by this graph.
62 * An easier approach is to just use the AllOpsResolver, but this will
63 * incur some penalty in code space for op implementations that are not
64 * needed by this graph.
65 * static ::tflite::ops::micro::AllOpsResolver resolver; */
66 /* NOLINTNEXTLINE(runtime-global-variables) */
67 debug("loading op resolver\n");
68
69 this->EnlistOperations();
Cisco Cervellera02101092021-09-07 11:34:43 +010070
71#if !defined(ARM_NPU)
72 /* If it is not a NPU build check if the model contains a NPU operator */
73 bool contains_ethosu_operator = this->ContainsEthosUOperator();
74 if (contains_ethosu_operator)
75 {
76 printf_err("Ethos-U operator present in the model but this build does not include Ethos-U drivers\n");
77 return false;
78 }
79#endif /* ARM_NPU */
alexander3c798932021-03-26 21:42:19 +000080
81 /* Create allocator instance, if it doesn't exist */
Isabella Gottardi56ee6202021-05-12 08:27:15 +010082 this->m_pAllocator = allocator;
83 if (!this->m_pAllocator) {
alexander3c798932021-03-26 21:42:19 +000084 /* Create an allocator instance */
85 info("Creating allocator using tensor arena in %s\n",
86 ACTIVATION_BUF_SECTION_NAME);
87
Isabella Gottardi56ee6202021-05-12 08:27:15 +010088 this->m_pAllocator = tflite::MicroAllocator::Create(
alexander3c798932021-03-26 21:42:19 +000089 this->GetTensorArena(),
90 this->GetActivationBufferSize(),
Isabella Gottardi56ee6202021-05-12 08:27:15 +010091 this->m_pErrorReporter);
alexander3c798932021-03-26 21:42:19 +000092
Isabella Gottardi56ee6202021-05-12 08:27:15 +010093 if (!this->m_pAllocator) {
alexander3c798932021-03-26 21:42:19 +000094 printf_err("Failed to create allocator\n");
95 return false;
96 }
Isabella Gottardi56ee6202021-05-12 08:27:15 +010097 debug("Created new allocator @ 0x%p\n", this->m_pAllocator);
alexander3c798932021-03-26 21:42:19 +000098 } else {
Isabella Gottardi56ee6202021-05-12 08:27:15 +010099 debug("Using existing allocator @ 0x%p\n", this->m_pAllocator);
alexander3c798932021-03-26 21:42:19 +0000100 }
101
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100102 this->m_pInterpreter = new ::tflite::MicroInterpreter(
103 this->m_pModel, this->GetOpResolver(),
104 this->m_pAllocator, this->m_pErrorReporter);
alexander3c798932021-03-26 21:42:19 +0000105
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100106 if (!this->m_pInterpreter) {
alexander3c798932021-03-26 21:42:19 +0000107 printf_err("Failed to allocate interpreter\n");
108 return false;
109 }
110
111 /* Allocate memory from the tensor_arena for the model's tensors. */
112 info("Allocating tensors\n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100113 TfLiteStatus allocate_status = this->m_pInterpreter->AllocateTensors();
alexander3c798932021-03-26 21:42:19 +0000114
115 if (allocate_status != kTfLiteOk) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100116 this->m_pErrorReporter->Report("[ERROR] allocateTensors() failed");
alexander3c798932021-03-26 21:42:19 +0000117 printf_err("tensor allocation failed!\n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100118 delete this->m_pInterpreter;
alexander3c798932021-03-26 21:42:19 +0000119 return false;
120 }
121
122 /* Get information about the memory area to use for the model's input. */
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100123 this->m_input.resize(this->GetNumInputs());
alexander3c798932021-03-26 21:42:19 +0000124 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++)
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100125 this->m_input[inIndex] = this->m_pInterpreter->input(inIndex);
alexander3c798932021-03-26 21:42:19 +0000126
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100127 this->m_output.resize(this->GetNumOutputs());
alexander3c798932021-03-26 21:42:19 +0000128 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++)
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100129 this->m_output[outIndex] = this->m_pInterpreter->output(outIndex);
alexander3c798932021-03-26 21:42:19 +0000130
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100131 if (this->m_input.empty() || this->m_output.empty()) {
alexander3c798932021-03-26 21:42:19 +0000132 printf_err("failed to get tensors\n");
133 return false;
134 } else {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100135 this->m_type = this->m_input[0]->type; /* Input 0 should be the main input */
alexander3c798932021-03-26 21:42:19 +0000136
137 /* Clear the input & output tensors */
138 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100139 std::memset(this->m_input[inIndex]->data.data, 0, this->m_input[inIndex]->bytes);
alexander3c798932021-03-26 21:42:19 +0000140 }
141 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100142 std::memset(this->m_output[outIndex]->data.data, 0, this->m_output[outIndex]->bytes);
alexander3c798932021-03-26 21:42:19 +0000143 }
144
145 this->LogInterpreterInfo();
146 }
147
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100148 this->m_inited = true;
alexander3c798932021-03-26 21:42:19 +0000149 return true;
150}
151
152tflite::MicroAllocator* arm::app::Model::GetAllocator()
153{
154 if (this->IsInited()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100155 return this->m_pAllocator;
alexander3c798932021-03-26 21:42:19 +0000156 }
157 return nullptr;
158}
159
160void arm::app::Model::LogTensorInfo(TfLiteTensor* tensor)
161{
162 if (!tensor) {
163 printf_err("Invalid tensor\n");
164 assert(tensor);
165 return;
166 }
167
168 debug("\ttensor is assigned to 0x%p\n", tensor);
169 info("\ttensor type is %s\n", TfLiteTypeGetName(tensor->type));
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100170 info("\ttensor occupies %zu bytes with dimensions\n",
171 tensor->bytes);
alexander3c798932021-03-26 21:42:19 +0000172 for (int i = 0 ; i < tensor->dims->size; ++i) {
173 info ("\t\t%d: %3d\n", i, tensor->dims->data[i]);
174 }
175
176 TfLiteQuantization quant = tensor->quantization;
177 if (kTfLiteAffineQuantization == quant.type) {
178 auto* quantParams = (TfLiteAffineQuantization*)quant.params;
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100179 info("Quant dimension: %" PRIi32 "\n", quantParams->quantized_dimension);
alexander3c798932021-03-26 21:42:19 +0000180 for (int i = 0; i < quantParams->scale->size; ++i) {
181 info("Scale[%d] = %f\n", i, quantParams->scale->data[i]);
182 }
183 for (int i = 0; i < quantParams->zero_point->size; ++i) {
184 info("ZeroPoint[%d] = %d\n", i, quantParams->zero_point->data[i]);
185 }
186 }
187}
188
189void arm::app::Model::LogInterpreterInfo()
190{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100191 if (!this->m_pInterpreter) {
alexander3c798932021-03-26 21:42:19 +0000192 printf_err("Invalid interpreter\n");
193 return;
194 }
195
196 info("Model INPUT tensors: \n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100197 for (auto input : this->m_input) {
alexander3c798932021-03-26 21:42:19 +0000198 this->LogTensorInfo(input);
199 }
200
201 info("Model OUTPUT tensors: \n");
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100202 for (auto output : this->m_output) {
alexander3c798932021-03-26 21:42:19 +0000203 this->LogTensorInfo(output);
204 }
205
206 info("Activation buffer (a.k.a tensor arena) size used: %zu\n",
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100207 this->m_pInterpreter->arena_used_bytes());
alexander3c798932021-03-26 21:42:19 +0000208
Richard Burton0d110592021-08-12 17:26:30 +0100209 /* We expect there to be only one subgraph. */
210 const uint32_t nOperators = tflite::NumSubgraphOperators(this->m_pModel, 0);
211 info("Number of operators: %" PRIu32 "\n", nOperators);
alexander3c798932021-03-26 21:42:19 +0000212
Richard Burton0d110592021-08-12 17:26:30 +0100213 const tflite::SubGraph* subgraph = this->m_pModel->subgraphs()->Get(0);
214
215 auto* opcodes = this->m_pModel->operator_codes();
216
217 /* For each operator, display registration information. */
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100218 for (size_t i = 0 ; i < nOperators; ++i) {
Richard Burton0d110592021-08-12 17:26:30 +0100219 const tflite::Operator* op = subgraph->operators()->Get(i);
220 const tflite::OperatorCode* opcode = opcodes->Get(op->opcode_index());
221 const TfLiteRegistration* reg = nullptr;
222
223 tflite::GetRegistrationFromOpCode(opcode, this->GetOpResolver(),
224 this->m_pErrorReporter, &reg);
alexander3c798932021-03-26 21:42:19 +0000225 std::string opName{""};
226
227 if (reg) {
228 if (tflite::BuiltinOperator_CUSTOM == reg->builtin_code) {
229 opName = std::string(reg->custom_name);
230 } else {
231 opName = std::string(EnumNameBuiltinOperator(
232 tflite::BuiltinOperator(reg->builtin_code)));
233 }
234 }
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100235 info("\tOperator %zu: %s\n", i, opName.c_str());
alexander3c798932021-03-26 21:42:19 +0000236 }
237}
238
239bool arm::app::Model::IsInited() const
240{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100241 return this->m_inited;
alexander3c798932021-03-26 21:42:19 +0000242}
243
244bool arm::app::Model::IsDataSigned() const
245{
246 return this->GetType() == kTfLiteInt8;
247}
248
Cisco Cervellera02101092021-09-07 11:34:43 +0100249bool arm::app::Model::ContainsEthosUOperator() const
250{
251 /* We expect there to be only one subgraph. */
252 const uint32_t nOperators = tflite::NumSubgraphOperators(this->m_pModel, 0);
253 const tflite::SubGraph* subgraph = this->m_pModel->subgraphs()->Get(0);
254 const auto* opcodes = this->m_pModel->operator_codes();
255
256 /* check for custom operators */
257 for (size_t i = 0; (i < nOperators); ++i)
258 {
259 const tflite::Operator* op = subgraph->operators()->Get(i);
260 const tflite::OperatorCode* opcode = opcodes->Get(op->opcode_index());
261
262 auto builtin_code = tflite::GetBuiltinCode(opcode);
263 if ((builtin_code == tflite::BuiltinOperator_CUSTOM) &&
264 ( nullptr != opcode->custom_code()) &&
265 ( 0 == std::string(opcode->custom_code()->c_str()).compare("ethos-u")))
266 {
267 return true;
268 }
269 }
270 return false;
271}
272
alexander3c798932021-03-26 21:42:19 +0000273bool arm::app::Model::RunInference()
274{
275 bool inference_state = false;
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100276 if (this->m_pModel && this->m_pInterpreter) {
277 if (kTfLiteOk != this->m_pInterpreter->Invoke()) {
alexander3c798932021-03-26 21:42:19 +0000278 printf_err("Invoke failed.\n");
279 } else {
280 inference_state = true;
281 }
282 } else {
283 printf_err("Error: No interpreter!\n");
284 }
285 return inference_state;
286}
287
288TfLiteTensor* arm::app::Model::GetInputTensor(size_t index) const
289{
290 if (index < this->GetNumInputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100291 return this->m_input.at(index);
alexander3c798932021-03-26 21:42:19 +0000292 }
293 return nullptr;
294}
295
296TfLiteTensor* arm::app::Model::GetOutputTensor(size_t index) const
297{
298 if (index < this->GetNumOutputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100299 return this->m_output.at(index);
alexander3c798932021-03-26 21:42:19 +0000300 }
301 return nullptr;
302}
303
304size_t arm::app::Model::GetNumInputs() const
305{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100306 if (this->m_pModel && this->m_pInterpreter) {
307 return this->m_pInterpreter->inputs_size();
alexander3c798932021-03-26 21:42:19 +0000308 }
309 return 0;
310}
311
312size_t arm::app::Model::GetNumOutputs() const
313{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100314 if (this->m_pModel && this->m_pInterpreter) {
315 return this->m_pInterpreter->outputs_size();
alexander3c798932021-03-26 21:42:19 +0000316 }
317 return 0;
318}
319
320
321TfLiteType arm::app::Model::GetType() const
322{
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100323 return this->m_type;
alexander3c798932021-03-26 21:42:19 +0000324}
325
326TfLiteIntArray* arm::app::Model::GetInputShape(size_t index) const
327{
328 if (index < this->GetNumInputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100329 return this->m_input.at(index)->dims;
alexander3c798932021-03-26 21:42:19 +0000330 }
331 return nullptr;
332}
333
334TfLiteIntArray* arm::app::Model::GetOutputShape(size_t index) const
335{
336 if (index < this->GetNumOutputs()) {
Isabella Gottardi56ee6202021-05-12 08:27:15 +0100337 return this->m_output.at(index)->dims;
alexander3c798932021-03-26 21:42:19 +0000338 }
339 return nullptr;
340}
341
342bool arm::app::Model::ShowModelInfoHandler()
343{
344 if (!this->IsInited()) {
345 printf_err("Model is not initialised! Terminating processing.\n");
346 return false;
347 }
348
349 PrintTensorFlowVersion();
350 info("Model info:\n");
351 this->LogInterpreterInfo();
352
353#if defined(ARM_NPU)
354 info("Use of Arm uNPU is enabled\n");
355#else /* ARM_NPU */
356 info("Use of Arm uNPU is disabled\n");
357#endif /* ARM_NPU */
358
359 return true;
360}
361namespace arm {
362namespace app {
alexanderc350cdc2021-04-29 20:36:09 +0100363 static uint8_t tensor_arena[ACTIVATION_BUF_SZ] ACTIVATION_BUF_ATTRIBUTE;
alexander3c798932021-03-26 21:42:19 +0000364} /* namespace app */
365} /* namespace arm */
366
367size_t arm::app::Model::GetActivationBufferSize()
368{
369 return ACTIVATION_BUF_SZ;
370}
371
372uint8_t *arm::app::Model::GetTensorArena()
373{
alexanderc350cdc2021-04-29 20:36:09 +0100374 return tensor_arena;
alexander3c798932021-03-26 21:42:19 +0000375}