blob: 4a7f0a48d201cca7b471cdf4ac395ad347c853d3 [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{
27 if (this->_m_pInterpreter) {
28 delete this->_m_pInterpreter;
29 }
30
31 /**
32 * No clean-up function available for allocator in TensorFlow Lite Micro yet.
33 **/
34}
35
36arm::app::Model::Model() :
37 _m_inited (false),
38 _m_type(kTfLiteNoType)
39{
40 this->_m_pErrorReporter = &this->_m_uErrorReporter;
41}
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);
50 this->_m_pModel = ::tflite::GetModel(model_addr);
51
52 if (this->_m_pModel->version() != TFLITE_SCHEMA_VERSION) {
53 this->_m_pErrorReporter->Report(
54 "[ERROR] model's schema version %d is not equal "
55 "to supported version %d.",
56 this->_m_pModel->version(), TFLITE_SCHEMA_VERSION);
57 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();
70
71 /* Create allocator instance, if it doesn't exist */
72 this->_m_pAllocator = allocator;
73 if (!this->_m_pAllocator) {
74 /* Create an allocator instance */
75 info("Creating allocator using tensor arena in %s\n",
76 ACTIVATION_BUF_SECTION_NAME);
77
78 this->_m_pAllocator = tflite::MicroAllocator::Create(
79 this->GetTensorArena(),
80 this->GetActivationBufferSize(),
81 this->_m_pErrorReporter);
82
83 if (!this->_m_pAllocator) {
84 printf_err("Failed to create allocator\n");
85 return false;
86 }
87 debug("Created new allocator @ 0x%p\n", this->_m_pAllocator);
88 } else {
89 debug("Using existing allocator @ 0x%p\n", this->_m_pAllocator);
90 }
91
92 this->_m_pInterpreter = new ::tflite::MicroInterpreter(
93 this->_m_pModel, this->GetOpResolver(),
94 this->_m_pAllocator, this->_m_pErrorReporter);
95
96 if (!this->_m_pInterpreter) {
97 printf_err("Failed to allocate interpreter\n");
98 return false;
99 }
100
101 /* Allocate memory from the tensor_arena for the model's tensors. */
102 info("Allocating tensors\n");
103 TfLiteStatus allocate_status = this->_m_pInterpreter->AllocateTensors();
104
105 if (allocate_status != kTfLiteOk) {
106 this->_m_pErrorReporter->Report("[ERROR] allocateTensors() failed");
107 printf_err("tensor allocation failed!\n");
108 delete this->_m_pInterpreter;
109 return false;
110 }
111
112 /* Get information about the memory area to use for the model's input. */
113 this->_m_input.resize(this->GetNumInputs());
114 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++)
115 this->_m_input[inIndex] = this->_m_pInterpreter->input(inIndex);
116
117 this->_m_output.resize(this->GetNumOutputs());
118 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++)
119 this->_m_output[outIndex] = this->_m_pInterpreter->output(outIndex);
120
121 if (this->_m_input.empty() || this->_m_output.empty()) {
122 printf_err("failed to get tensors\n");
123 return false;
124 } else {
125 this->_m_type = this->_m_input[0]->type; /* Input 0 should be the main input */
126
127 /* Clear the input & output tensors */
128 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++) {
129 std::memset(this->_m_input[inIndex]->data.data, 0, this->_m_input[inIndex]->bytes);
130 }
131 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++) {
132 std::memset(this->_m_output[outIndex]->data.data, 0, this->_m_output[outIndex]->bytes);
133 }
134
135 this->LogInterpreterInfo();
136 }
137
138 this->_m_inited = true;
139 return true;
140}
141
142tflite::MicroAllocator* arm::app::Model::GetAllocator()
143{
144 if (this->IsInited()) {
145 return this->_m_pAllocator;
146 }
147 return nullptr;
148}
149
150void arm::app::Model::LogTensorInfo(TfLiteTensor* tensor)
151{
152 if (!tensor) {
153 printf_err("Invalid tensor\n");
154 assert(tensor);
155 return;
156 }
157
158 debug("\ttensor is assigned to 0x%p\n", tensor);
159 info("\ttensor type is %s\n", TfLiteTypeGetName(tensor->type));
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100160 info("\ttensor occupies %zu bytes with dimensions\n",
161 tensor->bytes);
alexander3c798932021-03-26 21:42:19 +0000162 for (int i = 0 ; i < tensor->dims->size; ++i) {
163 info ("\t\t%d: %3d\n", i, tensor->dims->data[i]);
164 }
165
166 TfLiteQuantization quant = tensor->quantization;
167 if (kTfLiteAffineQuantization == quant.type) {
168 auto* quantParams = (TfLiteAffineQuantization*)quant.params;
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100169 info("Quant dimension: %" PRIi32 "\n", quantParams->quantized_dimension);
alexander3c798932021-03-26 21:42:19 +0000170 for (int i = 0; i < quantParams->scale->size; ++i) {
171 info("Scale[%d] = %f\n", i, quantParams->scale->data[i]);
172 }
173 for (int i = 0; i < quantParams->zero_point->size; ++i) {
174 info("ZeroPoint[%d] = %d\n", i, quantParams->zero_point->data[i]);
175 }
176 }
177}
178
179void arm::app::Model::LogInterpreterInfo()
180{
181 if (!this->_m_pInterpreter) {
182 printf_err("Invalid interpreter\n");
183 return;
184 }
185
186 info("Model INPUT tensors: \n");
187 for (auto input : this->_m_input) {
188 this->LogTensorInfo(input);
189 }
190
191 info("Model OUTPUT tensors: \n");
192 for (auto output : this->_m_output) {
193 this->LogTensorInfo(output);
194 }
195
196 info("Activation buffer (a.k.a tensor arena) size used: %zu\n",
197 this->_m_pInterpreter->arena_used_bytes());
198
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100199 const size_t nOperators = this->_m_pInterpreter->operators_size();
200 info("Number of operators: %zu\n", nOperators);
alexander3c798932021-03-26 21:42:19 +0000201
202 /* For each operator, display registration information */
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100203 for (size_t i = 0 ; i < nOperators; ++i) {
alexander3c798932021-03-26 21:42:19 +0000204 const tflite::NodeAndRegistration nodeReg =
205 this->_m_pInterpreter->node_and_registration(i);
206 const TfLiteRegistration* reg = nodeReg.registration;
207 std::string opName{""};
208
209 if (reg) {
210 if (tflite::BuiltinOperator_CUSTOM == reg->builtin_code) {
211 opName = std::string(reg->custom_name);
212 } else {
213 opName = std::string(EnumNameBuiltinOperator(
214 tflite::BuiltinOperator(reg->builtin_code)));
215 }
216 }
Kshitij Sisodiaf9c19ea2021-05-07 16:08:14 +0100217 info("\tOperator %zu: %s\n", i, opName.c_str());
alexander3c798932021-03-26 21:42:19 +0000218 }
219}
220
221bool arm::app::Model::IsInited() const
222{
223 return this->_m_inited;
224}
225
226bool arm::app::Model::IsDataSigned() const
227{
228 return this->GetType() == kTfLiteInt8;
229}
230
231bool arm::app::Model::RunInference()
232{
233 bool inference_state = false;
234 if (this->_m_pModel && this->_m_pInterpreter) {
235 if (kTfLiteOk != this->_m_pInterpreter->Invoke()) {
236 printf_err("Invoke failed.\n");
237 } else {
238 inference_state = true;
239 }
240 } else {
241 printf_err("Error: No interpreter!\n");
242 }
243 return inference_state;
244}
245
246TfLiteTensor* arm::app::Model::GetInputTensor(size_t index) const
247{
248 if (index < this->GetNumInputs()) {
249 return this->_m_input.at(index);
250 }
251 return nullptr;
252}
253
254TfLiteTensor* arm::app::Model::GetOutputTensor(size_t index) const
255{
256 if (index < this->GetNumOutputs()) {
257 return this->_m_output.at(index);
258 }
259 return nullptr;
260}
261
262size_t arm::app::Model::GetNumInputs() const
263{
264 if (this->_m_pModel && this->_m_pInterpreter) {
265 return this->_m_pInterpreter->inputs_size();
266 }
267 return 0;
268}
269
270size_t arm::app::Model::GetNumOutputs() const
271{
272 if (this->_m_pModel && this->_m_pInterpreter) {
273 return this->_m_pInterpreter->outputs_size();
274 }
275 return 0;
276}
277
278
279TfLiteType arm::app::Model::GetType() const
280{
281 return this->_m_type;
282}
283
284TfLiteIntArray* arm::app::Model::GetInputShape(size_t index) const
285{
286 if (index < this->GetNumInputs()) {
287 return this->_m_input.at(index)->dims;
288 }
289 return nullptr;
290}
291
292TfLiteIntArray* arm::app::Model::GetOutputShape(size_t index) const
293{
294 if (index < this->GetNumOutputs()) {
295 return this->_m_output.at(index)->dims;
296 }
297 return nullptr;
298}
299
300bool arm::app::Model::ShowModelInfoHandler()
301{
302 if (!this->IsInited()) {
303 printf_err("Model is not initialised! Terminating processing.\n");
304 return false;
305 }
306
307 PrintTensorFlowVersion();
308 info("Model info:\n");
309 this->LogInterpreterInfo();
310
311#if defined(ARM_NPU)
312 info("Use of Arm uNPU is enabled\n");
313#else /* ARM_NPU */
314 info("Use of Arm uNPU is disabled\n");
315#endif /* ARM_NPU */
316
317 return true;
318}
319namespace arm {
320namespace app {
alexanderc350cdc2021-04-29 20:36:09 +0100321 static uint8_t tensor_arena[ACTIVATION_BUF_SZ] ACTIVATION_BUF_ATTRIBUTE;
alexander3c798932021-03-26 21:42:19 +0000322} /* namespace app */
323} /* namespace arm */
324
325size_t arm::app::Model::GetActivationBufferSize()
326{
327 return ACTIVATION_BUF_SZ;
328}
329
330uint8_t *arm::app::Model::GetTensorArena()
331{
alexanderc350cdc2021-04-29 20:36:09 +0100332 return tensor_arena;
alexander3c798932021-03-26 21:42:19 +0000333}