blob: 61db73deb7ab1b179bcd4b75653b8c3b9995423b [file] [log] [blame]
Kristofer Jonsson641c0912020-08-31 11:34:14 +02001/*
2 * Copyright (c) 2019-2020 Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include "tensorflow/lite/micro/all_ops_resolver.h"
Måns Nilsson231e1d92020-11-05 12:19:34 +010020#include "tensorflow/lite/micro/cortex_m_generic/debug_log_callback.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020021#include "tensorflow/lite/micro/micro_error_reporter.h"
22#include "tensorflow/lite/micro/micro_interpreter.h"
23#include "tensorflow/lite/schema/schema_generated.h"
24#include "tensorflow/lite/version.h"
25
26#include "inference_process.hpp"
27
Per Åstrandd9afc082020-10-06 13:25:08 +020028#include "cmsis_compiler.h"
29
Per Åstrand91a91732020-09-25 15:04:26 +020030#include <inttypes.h>
31
Kristofer Jonsson641c0912020-08-31 11:34:14 +020032#ifndef TENSOR_ARENA_SIZE
33#define TENSOR_ARENA_SIZE (1024)
34#endif
35
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020036using namespace std;
37
Kristofer Jonsson641c0912020-08-31 11:34:14 +020038__attribute__((section(".bss.NoInit"), aligned(16))) uint8_t inferenceProcessTensorArena[TENSOR_ARENA_SIZE];
39
40namespace {
Måns Nilsson231e1d92020-11-05 12:19:34 +010041
42void tflu_debug_log(const char *s) {
43 fprintf(stderr, "%s", s);
44}
45
Kristofer Jonsson641c0912020-08-31 11:34:14 +020046void print_output_data(TfLiteTensor *output, size_t bytesToPrint) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020047 const int numBytesToPrint = min(output->bytes, bytesToPrint);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020048
49 int dims_size = output->dims->size;
50 printf("{\n");
51 printf("\"dims\": [%d,", dims_size);
52 for (int i = 0; i < output->dims->size - 1; ++i) {
53 printf("%d,", output->dims->data[i]);
54 }
55 printf("%d],\n", output->dims->data[dims_size - 1]);
56
Per Åstrand91a91732020-09-25 15:04:26 +020057 printf("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020058 printf("\"data\":\"");
59 for (int i = 0; i < numBytesToPrint - 1; ++i) {
60 if (i % 16 == 0 && i != 0) {
61 printf("\n");
62 }
63 printf("0x%02x,", output->data.uint8[i]);
64 }
65 printf("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
66 printf("}");
67}
68
69bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst) {
70 if (dst.data == nullptr) {
71 return false;
72 }
73
74 if (src.bytes > dst.size) {
75 printf("Tensor size %d does not match output size %d.\n", src.bytes, dst.size);
76 return true;
77 }
78
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020079 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
Kristofer Jonsson641c0912020-08-31 11:34:14 +020080 dst.size = src.bytes;
81
82 return false;
83}
84
85} // namespace
86
87namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020088DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020089
Kristofer Jonsson34e24962020-11-23 16:22:10 +010090void DataPtr::invalidate() {
91#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
92 printf("Invalidate. data=%p, size=%zu\n", data, size);
93 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
94#endif
95}
96
97void DataPtr::clean() {
98#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
99 printf("Clean. data=%p, size=%zu\n", data, size);
100 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
101#endif
102}
103
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200104InferenceJob::InferenceJob() : numBytesToPrint(0) {}
105
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200106InferenceJob::InferenceJob(const string &_name,
107 const DataPtr &_networkModel,
108 const vector<DataPtr> &_input,
109 const vector<DataPtr> &_output,
110 const vector<DataPtr> &_expectedOutput,
111 size_t _numBytesToPrint) :
112 name(_name),
113 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
114 numBytesToPrint(_numBytesToPrint) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200115
Kristofer Jonsson34e24962020-11-23 16:22:10 +0100116void InferenceJob::invalidate() {
117 networkModel.invalidate();
118
119 for (auto &it : input) {
120 it.invalidate();
121 }
122
123 for (auto &it : output) {
124 it.invalidate();
125 }
126
127 for (auto &it : expectedOutput) {
128 it.invalidate();
129 }
130}
131
132void InferenceJob::clean() {
133 networkModel.clean();
134
135 for (auto &it : input) {
136 it.clean();
137 }
138
139 for (auto &it : output) {
140 it.clean();
141 }
142
143 for (auto &it : expectedOutput) {
144 it.clean();
145 }
146}
147
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200148InferenceProcess::InferenceProcess() : lock(0) {}
149
150// NOTE: Adding code for get_lock & free_lock with some corrections from
151// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
152// TODO: check correctness?
153void InferenceProcess::getLock() {
154 int status = 0;
155
156 do {
157 // Wait until lock_var is free
158 while (__LDREXW(&lock) != 0)
159 ;
160
161 // Try to set lock_var
162 status = __STREXW(1, &lock);
163 } while (status != 0);
164
165 // Do not start any other memory access until memory barrier is completed
166 __DMB();
167}
168
169// TODO: check correctness?
170void InferenceProcess::freeLock() {
171 // Ensure memory operations completed before releasing lock
172 __DMB();
173
174 lock = 0;
175}
176
177bool InferenceProcess::push(const InferenceJob &job) {
178 getLock();
179 inferenceJobQueue.push(job);
180 freeLock();
181
182 return true;
183}
184
185bool InferenceProcess::runJob(InferenceJob &job) {
186 printf("Running inference job: %s\n", job.name.c_str());
187
188 tflite::MicroErrorReporter microErrorReporter;
189 tflite::ErrorReporter *reporter = &microErrorReporter;
190
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200191 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200192 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
193 if (model->version() != TFLITE_SCHEMA_VERSION) {
Per Åstrand91a91732020-09-25 15:04:26 +0200194 printf("Model provided is schema version %" PRIu32 " not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200195 model->version(),
196 TFLITE_SCHEMA_VERSION);
197 return true;
198 }
199
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200200 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200201 tflite::AllOpsResolver resolver;
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200202 tflite::MicroInterpreter interpreter(model, resolver, inferenceProcessTensorArena, TENSOR_ARENA_SIZE, reporter);
203
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200204 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200205 TfLiteStatus allocate_status = interpreter.AllocateTensors();
206 if (allocate_status != kTfLiteOk) {
207 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
208 return true;
209 }
210
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200211 // Create a filtered list of non empty input tensors
212 vector<TfLiteTensor *> inputTensors;
213 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
214 TfLiteTensor *tensor = interpreter.input(i);
215
216 if (tensor->bytes > 0) {
217 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200218 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200219 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200220
221 if (job.input.size() != inputTensors.size()) {
222 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
223 job.input.size(),
224 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200225 return true;
226 }
227
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200228 // Copy input data
229 for (size_t i = 0; i < inputTensors.size(); ++i) {
230 const DataPtr &input = job.input[i];
231 const TfLiteTensor *tensor = inputTensors[i];
232
233 if (input.size != tensor->bytes) {
234 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
235 job.name.c_str(),
236 i,
237 input.size,
238 tensor->bytes);
239 return true;
240 }
241
242 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
243 }
244
Måns Nilsson231e1d92020-11-05 12:19:34 +0100245 // Register debug log callback for profiling
246 RegisterDebugLogCallback(tflu_debug_log);
247
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200248 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200249 TfLiteStatus invoke_status = interpreter.Invoke();
250 if (invoke_status != kTfLiteOk) {
251 printf("Invoke failed for inference job: %s\n", job.name.c_str());
252 return true;
253 }
254
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200255 // Copy output data
256 if (job.output.size() > 0) {
257 if (interpreter.outputs_size() != job.output.size()) {
258 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
259 return true;
260 }
261
262 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
263 if (copyOutput(*interpreter.output(i), job.output[i])) {
264 return true;
265 }
266 }
267 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200268
269 if (job.numBytesToPrint > 0) {
270 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
271 // whichever comes first as well as the output shape.
272 printf("num_of_outputs: %d\n", interpreter.outputs_size());
273 printf("output_begin\n");
274 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200275
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200276 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
277 TfLiteTensor *output = interpreter.output(i);
278 print_output_data(output, job.numBytesToPrint);
279 if (i != interpreter.outputs_size() - 1) {
280 printf(",\n");
281 }
282 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200283
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200284 printf("]\n");
285 printf("output_end\n");
286 }
287
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200288 if (job.expectedOutput.size() > 0) {
289 if (job.expectedOutput.size() != interpreter.outputs_size()) {
290 printf("Expeded number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
291 job.name.c_str(),
292 job.expectedOutput.size(),
293 interpreter.outputs_size());
294 return true;
295 }
296
297 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
298 const DataPtr &expected = job.expectedOutput[i];
299 const TfLiteTensor *output = interpreter.output(i);
300
301 if (expected.size != output->bytes) {
302 printf(
303 "Expected tensor size does not match network size. job=%s, index=%u, expected=%zu, network=%zu\n",
304 job.name.c_str(),
305 i,
306 expected.size,
307 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200308 return true;
309 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200310
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200311 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200312 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
313 printf("Expected tensor size does not match network size. job=%s, index=%u, offset=%u, "
314 "expected=%02x, network=%02x\n",
315 job.name.c_str(),
316 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200317 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200318 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200319 output->data.uint8[j]);
320 }
321 }
322 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200323 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200324
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200325 printf("Finished running job: %s\n", job.name.c_str());
326
327 return false;
328}
329
330bool InferenceProcess::run(bool exitOnEmpty) {
331 bool anyJobFailed = false;
332
333 while (true) {
334 getLock();
335 bool empty = inferenceJobQueue.empty();
336 freeLock();
337
338 if (empty) {
339 if (exitOnEmpty) {
340 printf("Exit from InferenceProcess::run() on empty job queue!\n");
341 break;
342 }
343
344 continue;
345 }
346
347 getLock();
348 InferenceJob job = inferenceJobQueue.front();
349 inferenceJobQueue.pop();
350 freeLock();
351
352 if (runJob(job)) {
353 anyJobFailed = true;
354 continue;
355 }
356 }
357
358 return anyJobFailed;
359}
360
361} // namespace InferenceProcess