blob: f3d2da88ebf8bdd0668057f0d87de655efdd62b9 [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"
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
27#include "inference_process.hpp"
28
Per Åstrandd9afc082020-10-06 13:25:08 +020029#include "cmsis_compiler.h"
30
Per Åstrand91a91732020-09-25 15:04:26 +020031#include <inttypes.h>
32
Kristofer Jonsson641c0912020-08-31 11:34:14 +020033#ifndef TENSOR_ARENA_SIZE
34#define TENSOR_ARENA_SIZE (1024)
35#endif
36
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020037using namespace std;
38
Kristofer Jonsson641c0912020-08-31 11:34:14 +020039__attribute__((section(".bss.NoInit"), aligned(16))) uint8_t inferenceProcessTensorArena[TENSOR_ARENA_SIZE];
40
41namespace {
Måns Nilsson231e1d92020-11-05 12:19:34 +010042
43void tflu_debug_log(const char *s) {
44 fprintf(stderr, "%s", s);
45}
46
Kristofer Jonsson641c0912020-08-31 11:34:14 +020047void print_output_data(TfLiteTensor *output, size_t bytesToPrint) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020048 const int numBytesToPrint = min(output->bytes, bytesToPrint);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020049
50 int dims_size = output->dims->size;
51 printf("{\n");
52 printf("\"dims\": [%d,", dims_size);
53 for (int i = 0; i < output->dims->size - 1; ++i) {
54 printf("%d,", output->dims->data[i]);
55 }
56 printf("%d],\n", output->dims->data[dims_size - 1]);
57
Per Åstrand91a91732020-09-25 15:04:26 +020058 printf("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020059 printf("\"data\":\"");
60 for (int i = 0; i < numBytesToPrint - 1; ++i) {
61 if (i % 16 == 0 && i != 0) {
62 printf("\n");
63 }
64 printf("0x%02x,", output->data.uint8[i]);
65 }
66 printf("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
67 printf("}");
68}
69
70bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst) {
71 if (dst.data == nullptr) {
72 return false;
73 }
74
75 if (src.bytes > dst.size) {
76 printf("Tensor size %d does not match output size %d.\n", src.bytes, dst.size);
77 return true;
78 }
79
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020080 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
Kristofer Jonsson641c0912020-08-31 11:34:14 +020081 dst.size = src.bytes;
82
83 return false;
84}
85
86} // namespace
87
88namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020089DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020090
Kristofer Jonsson34e24962020-11-23 16:22:10 +010091void DataPtr::invalidate() {
92#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010093 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)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010099 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
100#endif
101}
102
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200103InferenceJob::InferenceJob() : numBytesToPrint(0) {}
104
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200105InferenceJob::InferenceJob(const string &_name,
106 const DataPtr &_networkModel,
107 const vector<DataPtr> &_input,
108 const vector<DataPtr> &_output,
109 const vector<DataPtr> &_expectedOutput,
Bhavik Patelffe845d2020-11-16 12:13:56 +0100110 size_t _numBytesToPrint,
111 const vector<uint8_t> &_pmuEventConfig,
112 const uint32_t pmuCycleCounterEnable) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200113 name(_name),
114 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Bhavik Patelffe845d2020-11-16 12:13:56 +0100115 numBytesToPrint(_numBytesToPrint), pmuEventConfig(_pmuEventConfig), pmuCycleCounterEnable(pmuCycleCounterEnable),
116 pmuEventCount(), pmuCycleCounterCount(0) {
117#if defined(INFERENCE_PROC_TFLU_PROFILER) && defined(ETHOSU)
118 pmuEventCount = vector<uint32_t>(ETHOSU_PMU_NCOUNTERS, 0);
119#endif
120}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200121
Kristofer Jonsson34e24962020-11-23 16:22:10 +0100122void InferenceJob::invalidate() {
123 networkModel.invalidate();
124
125 for (auto &it : input) {
126 it.invalidate();
127 }
128
129 for (auto &it : output) {
130 it.invalidate();
131 }
132
133 for (auto &it : expectedOutput) {
134 it.invalidate();
135 }
136}
137
138void InferenceJob::clean() {
139 networkModel.clean();
140
141 for (auto &it : input) {
142 it.clean();
143 }
144
145 for (auto &it : output) {
146 it.clean();
147 }
148
149 for (auto &it : expectedOutput) {
150 it.clean();
151 }
152}
153
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200154InferenceProcess::InferenceProcess() : lock(0) {}
155
156// NOTE: Adding code for get_lock & free_lock with some corrections from
157// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
158// TODO: check correctness?
159void InferenceProcess::getLock() {
160 int status = 0;
161
162 do {
163 // Wait until lock_var is free
164 while (__LDREXW(&lock) != 0)
165 ;
166
167 // Try to set lock_var
168 status = __STREXW(1, &lock);
169 } while (status != 0);
170
171 // Do not start any other memory access until memory barrier is completed
172 __DMB();
173}
174
175// TODO: check correctness?
176void InferenceProcess::freeLock() {
177 // Ensure memory operations completed before releasing lock
178 __DMB();
179
180 lock = 0;
181}
182
183bool InferenceProcess::push(const InferenceJob &job) {
184 getLock();
185 inferenceJobQueue.push(job);
186 freeLock();
187
188 return true;
189}
190
191bool InferenceProcess::runJob(InferenceJob &job) {
192 printf("Running inference job: %s\n", job.name.c_str());
193
Bhavik Patelffe845d2020-11-16 12:13:56 +0100194 // Register debug log callback for profiling
195 RegisterDebugLogCallback(tflu_debug_log);
196
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200197 tflite::MicroErrorReporter microErrorReporter;
198 tflite::ErrorReporter *reporter = &microErrorReporter;
199
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200200 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200201 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
202 if (model->version() != TFLITE_SCHEMA_VERSION) {
Per Åstrand91a91732020-09-25 15:04:26 +0200203 printf("Model provided is schema version %" PRIu32 " not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200204 model->version(),
205 TFLITE_SCHEMA_VERSION);
206 return true;
207 }
208
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200209 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200210 tflite::AllOpsResolver resolver;
Bhavik Patelffe845d2020-11-16 12:13:56 +0100211 tflite::MicroProfiler profiler(reporter);
212
213#if defined(INFERENCE_PROC_TFLU_PROFILER) && defined(ETHOSU)
214 profiler.MonitorEthosuPMUEvents(ethosu_pmu_event_type(job.pmuEventConfig[0]),
215 ethosu_pmu_event_type(job.pmuEventConfig[1]),
216 ethosu_pmu_event_type(job.pmuEventConfig[2]),
217 ethosu_pmu_event_type(job.pmuEventConfig[3]));
218#endif
219
220 tflite::MicroInterpreter interpreter(
221 model, resolver, inferenceProcessTensorArena, TENSOR_ARENA_SIZE, reporter, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200222
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200223 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200224 TfLiteStatus allocate_status = interpreter.AllocateTensors();
225 if (allocate_status != kTfLiteOk) {
226 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
227 return true;
228 }
229
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200230 // Create a filtered list of non empty input tensors
231 vector<TfLiteTensor *> inputTensors;
232 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
233 TfLiteTensor *tensor = interpreter.input(i);
234
235 if (tensor->bytes > 0) {
236 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200237 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200238 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200239
240 if (job.input.size() != inputTensors.size()) {
241 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
242 job.input.size(),
243 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200244 return true;
245 }
246
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200247 // Copy input data
248 for (size_t i = 0; i < inputTensors.size(); ++i) {
249 const DataPtr &input = job.input[i];
250 const TfLiteTensor *tensor = inputTensors[i];
251
252 if (input.size != tensor->bytes) {
253 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
254 job.name.c_str(),
255 i,
256 input.size,
257 tensor->bytes);
258 return true;
259 }
260
261 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
262 }
263
264 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200265 TfLiteStatus invoke_status = interpreter.Invoke();
266 if (invoke_status != kTfLiteOk) {
267 printf("Invoke failed for inference job: %s\n", job.name.c_str());
268 return true;
269 }
270
Bhavik Patelffe845d2020-11-16 12:13:56 +0100271 printf("%s : %zu\r\n", "arena_used_bytes", interpreter.arena_used_bytes());
272
273#ifdef INFERENCE_PROC_TFLU_PROFILER
274 printf("Inference runtime: %u cycles\r\n", (unsigned int)profiler.TotalInferenceTime());
275
276 if (job.pmuCycleCounterEnable != 0) {
277 job.pmuCycleCounterCount = profiler.TotalInferenceTime();
278 }
279
280#ifdef ETHOSU
281 for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) {
282 job.pmuEventCount[i] = profiler.GetEthosuPMUCounter(i);
283 }
284#endif
285#endif
286
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200287 // Copy output data
288 if (job.output.size() > 0) {
289 if (interpreter.outputs_size() != job.output.size()) {
290 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
291 return true;
292 }
293
294 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
295 if (copyOutput(*interpreter.output(i), job.output[i])) {
296 return true;
297 }
298 }
299 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200300
301 if (job.numBytesToPrint > 0) {
302 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
303 // whichever comes first as well as the output shape.
304 printf("num_of_outputs: %d\n", interpreter.outputs_size());
305 printf("output_begin\n");
306 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200307
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200308 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
309 TfLiteTensor *output = interpreter.output(i);
310 print_output_data(output, job.numBytesToPrint);
311 if (i != interpreter.outputs_size() - 1) {
312 printf(",\n");
313 }
314 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200315
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200316 printf("]\n");
317 printf("output_end\n");
318 }
319
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200320 if (job.expectedOutput.size() > 0) {
321 if (job.expectedOutput.size() != interpreter.outputs_size()) {
Bhavik Patelffe845d2020-11-16 12:13:56 +0100322 printf("Expected number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200323 job.name.c_str(),
324 job.expectedOutput.size(),
325 interpreter.outputs_size());
326 return true;
327 }
328
329 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
330 const DataPtr &expected = job.expectedOutput[i];
331 const TfLiteTensor *output = interpreter.output(i);
332
333 if (expected.size != output->bytes) {
334 printf(
335 "Expected tensor size does not match network size. job=%s, index=%u, expected=%zu, network=%zu\n",
336 job.name.c_str(),
337 i,
338 expected.size,
339 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200340 return true;
341 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200342
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200343 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200344 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
345 printf("Expected tensor size does not match network size. job=%s, index=%u, offset=%u, "
346 "expected=%02x, network=%02x\n",
347 job.name.c_str(),
348 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200349 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200350 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200351 output->data.uint8[j]);
352 }
353 }
354 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200355 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200356
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200357 printf("Finished running job: %s\n", job.name.c_str());
358
359 return false;
360}
361
362bool InferenceProcess::run(bool exitOnEmpty) {
363 bool anyJobFailed = false;
364
365 while (true) {
366 getLock();
367 bool empty = inferenceJobQueue.empty();
368 freeLock();
369
370 if (empty) {
371 if (exitOnEmpty) {
372 printf("Exit from InferenceProcess::run() on empty job queue!\n");
373 break;
374 }
375
376 continue;
377 }
378
379 getLock();
380 InferenceJob job = inferenceJobQueue.front();
381 inferenceJobQueue.pop();
382 freeLock();
383
384 if (runJob(job)) {
385 anyJobFailed = true;
386 continue;
387 }
388 }
389
390 return anyJobFailed;
391}
392
393} // namespace InferenceProcess