blob: 009706eba79dab4ea28aa10b5eef4a8cc017d5f0 [file] [log] [blame]
Kristofer Jonsson641c0912020-08-31 11:34:14 +02001/*
Per Åstrand90455452021-02-25 11:10:08 +01002 * Copyright (c) 2019-2021 Arm Limited. All rights reserved.
Kristofer Jonsson641c0912020-08-31 11:34:14 +02003 *
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"
Bhavik Patelffe845d2020-11-16 12:13:56 +010023#include "tensorflow/lite/micro/micro_profiler.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020024#include "tensorflow/lite/schema/schema_generated.h"
25#include "tensorflow/lite/version.h"
26
Jens Elofsson955288a2021-04-22 20:57:15 +020027#include "arm_profiler.hpp"
28#ifdef ETHOSU
29#include "ethosu_profiler.hpp"
30#endif
31
Kristofer Jonsson641c0912020-08-31 11:34:14 +020032#include "inference_process.hpp"
33
Per Åstrandd9afc082020-10-06 13:25:08 +020034#include "cmsis_compiler.h"
35
Per Åstrand91a91732020-09-25 15:04:26 +020036#include <inttypes.h>
37
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020038using namespace std;
39
Kristofer Jonsson641c0912020-08-31 11:34:14 +020040namespace {
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)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010092 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
93#endif
94}
95
96void DataPtr::clean() {
97#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010098 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
99#endif
100}
101
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200102InferenceJob::InferenceJob() : numBytesToPrint(0) {}
103
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200104InferenceJob::InferenceJob(const string &_name,
105 const DataPtr &_networkModel,
106 const vector<DataPtr> &_input,
107 const vector<DataPtr> &_output,
108 const vector<DataPtr> &_expectedOutput,
Bhavik Patelffe845d2020-11-16 12:13:56 +0100109 size_t _numBytesToPrint,
110 const vector<uint8_t> &_pmuEventConfig,
Bhavik Patel97906eb2020-12-17 15:32:16 +0100111 const uint32_t _pmuCycleCounterEnable) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200112 name(_name),
113 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Bhavik Patel97906eb2020-12-17 15:32:16 +0100114 numBytesToPrint(_numBytesToPrint), pmuEventConfig(_pmuEventConfig), pmuCycleCounterEnable(_pmuCycleCounterEnable),
Jens Elofssonde044c32021-05-06 16:21:29 +0200115 pmuEventCount(), pmuCycleCounterCount(0) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200116
Kristofer Jonsson34e24962020-11-23 16:22:10 +0100117void InferenceJob::invalidate() {
118 networkModel.invalidate();
119
120 for (auto &it : input) {
121 it.invalidate();
122 }
123
124 for (auto &it : output) {
125 it.invalidate();
126 }
127
128 for (auto &it : expectedOutput) {
129 it.invalidate();
130 }
131}
132
133void InferenceJob::clean() {
134 networkModel.clean();
135
136 for (auto &it : input) {
137 it.clean();
138 }
139
140 for (auto &it : output) {
141 it.clean();
142 }
143
144 for (auto &it : expectedOutput) {
145 it.clean();
146 }
147}
148
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200149// NOTE: Adding code for get_lock & free_lock with some corrections from
150// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
151// TODO: check correctness?
152void InferenceProcess::getLock() {
153 int status = 0;
154
155 do {
156 // Wait until lock_var is free
157 while (__LDREXW(&lock) != 0)
158 ;
159
160 // Try to set lock_var
161 status = __STREXW(1, &lock);
162 } while (status != 0);
163
164 // Do not start any other memory access until memory barrier is completed
165 __DMB();
166}
167
168// TODO: check correctness?
169void InferenceProcess::freeLock() {
170 // Ensure memory operations completed before releasing lock
171 __DMB();
172
173 lock = 0;
174}
175
176bool InferenceProcess::push(const InferenceJob &job) {
177 getLock();
178 inferenceJobQueue.push(job);
179 freeLock();
180
181 return true;
182}
183
184bool InferenceProcess::runJob(InferenceJob &job) {
185 printf("Running inference job: %s\n", job.name.c_str());
186
Bhavik Patelffe845d2020-11-16 12:13:56 +0100187 // Register debug log callback for profiling
188 RegisterDebugLogCallback(tflu_debug_log);
189
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200190 tflite::MicroErrorReporter microErrorReporter;
191 tflite::ErrorReporter *reporter = &microErrorReporter;
192
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200193 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200194 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
195 if (model->version() != TFLITE_SCHEMA_VERSION) {
Per Åstrand91a91732020-09-25 15:04:26 +0200196 printf("Model provided is schema version %" PRIu32 " not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200197 model->version(),
198 TFLITE_SCHEMA_VERSION);
199 return true;
200 }
201
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200202 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200203 tflite::AllOpsResolver resolver;
Jens Elofsson955288a2021-04-22 20:57:15 +0200204#ifdef ETHOSU
Jens Elofssonde044c32021-05-06 16:21:29 +0200205 tflite::EthosUProfiler profiler;
Jens Elofsson955288a2021-04-22 20:57:15 +0200206#else
207 tflite::ArmProfiler profiler;
Bhavik Patelffe845d2020-11-16 12:13:56 +0100208#endif
Jens Elofssonde044c32021-05-06 16:21:29 +0200209
Anton Moberg66ed1822021-02-10 08:49:28 +0100210 tflite::MicroInterpreter interpreter(model, resolver, tensorArena, tensorArenaSize, reporter, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200211
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200212 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200213 TfLiteStatus allocate_status = interpreter.AllocateTensors();
214 if (allocate_status != kTfLiteOk) {
215 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
216 return true;
217 }
218
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200219 // Create a filtered list of non empty input tensors
220 vector<TfLiteTensor *> inputTensors;
221 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
222 TfLiteTensor *tensor = interpreter.input(i);
223
224 if (tensor->bytes > 0) {
225 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200226 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200227 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200228 if (job.input.size() != inputTensors.size()) {
229 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
230 job.input.size(),
231 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200232 return true;
233 }
234
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200235 // Copy input data
236 for (size_t i = 0; i < inputTensors.size(); ++i) {
237 const DataPtr &input = job.input[i];
238 const TfLiteTensor *tensor = inputTensors[i];
239
240 if (input.size != tensor->bytes) {
241 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
242 job.name.c_str(),
243 i,
244 input.size,
245 tensor->bytes);
246 return true;
247 }
248
249 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
250 }
251
252 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200253 TfLiteStatus invoke_status = interpreter.Invoke();
254 if (invoke_status != kTfLiteOk) {
255 printf("Invoke failed for inference job: %s\n", job.name.c_str());
256 return true;
257 }
258
Bhavik Patelffe845d2020-11-16 12:13:56 +0100259 printf("%s : %zu\r\n", "arena_used_bytes", interpreter.arena_used_bytes());
260
261#ifdef INFERENCE_PROC_TFLU_PROFILER
Kristofer Jonsson91f600c2021-02-10 11:29:52 +0100262 printf("Inference runtime: %u cycles\r\n", (unsigned int)profiler.GetTotalTicks());
Bhavik Patelffe845d2020-11-16 12:13:56 +0100263
264 if (job.pmuCycleCounterEnable != 0) {
Kristofer Jonsson91f600c2021-02-10 11:29:52 +0100265 job.pmuCycleCounterCount = profiler.GetTotalTicks();
Bhavik Patelffe845d2020-11-16 12:13:56 +0100266 }
267
Bhavik Patelffe845d2020-11-16 12:13:56 +0100268#endif
269
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200270 // Copy output data
271 if (job.output.size() > 0) {
272 if (interpreter.outputs_size() != job.output.size()) {
273 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
274 return true;
275 }
276
277 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
278 if (copyOutput(*interpreter.output(i), job.output[i])) {
279 return true;
280 }
281 }
282 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200283
284 if (job.numBytesToPrint > 0) {
285 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
286 // whichever comes first as well as the output shape.
287 printf("num_of_outputs: %d\n", interpreter.outputs_size());
288 printf("output_begin\n");
289 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200290
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200291 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
292 TfLiteTensor *output = interpreter.output(i);
293 print_output_data(output, job.numBytesToPrint);
294 if (i != interpreter.outputs_size() - 1) {
295 printf(",\n");
296 }
297 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200298
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200299 printf("]\n");
300 printf("output_end\n");
301 }
302
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200303 if (job.expectedOutput.size() > 0) {
304 if (job.expectedOutput.size() != interpreter.outputs_size()) {
Bhavik Patelffe845d2020-11-16 12:13:56 +0100305 printf("Expected number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200306 job.name.c_str(),
307 job.expectedOutput.size(),
308 interpreter.outputs_size());
309 return true;
310 }
311
312 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
313 const DataPtr &expected = job.expectedOutput[i];
314 const TfLiteTensor *output = interpreter.output(i);
315
316 if (expected.size != output->bytes) {
Per Åstrand90455452021-02-25 11:10:08 +0100317 printf("Expected tensor size does not match output size. job=%s, index=%u, expected=%zu, network=%zu\n",
318 job.name.c_str(),
319 i,
320 expected.size,
321 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200322 return true;
323 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200324
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200325 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200326 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
Per Åstrand90455452021-02-25 11:10:08 +0100327 printf("Expected data does not match output data. job=%s, index=%u, offset=%u, "
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200328 "expected=%02x, network=%02x\n",
329 job.name.c_str(),
330 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200331 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200332 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200333 output->data.uint8[j]);
Per Åstrand90455452021-02-25 11:10:08 +0100334 return true;
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200335 }
336 }
337 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200338 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200339
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200340 printf("Finished running job: %s\n", job.name.c_str());
341
342 return false;
343}
344
345bool InferenceProcess::run(bool exitOnEmpty) {
346 bool anyJobFailed = false;
347
348 while (true) {
349 getLock();
350 bool empty = inferenceJobQueue.empty();
351 freeLock();
352
353 if (empty) {
354 if (exitOnEmpty) {
355 printf("Exit from InferenceProcess::run() on empty job queue!\n");
356 break;
357 }
358
359 continue;
360 }
361
362 getLock();
363 InferenceJob job = inferenceJobQueue.front();
364 inferenceJobQueue.pop();
365 freeLock();
366
367 if (runJob(job)) {
368 anyJobFailed = true;
369 continue;
370 }
371 }
372
373 return anyJobFailed;
374}
375
376} // namespace InferenceProcess