blob: 743ed6444ee064fea1c3e6d7bce6fd5b5d94b4cb [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
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 Jonsson72fa50b2020-09-10 13:26:41 +020033using namespace std;
34
Kristofer Jonsson641c0912020-08-31 11:34:14 +020035namespace {
Måns Nilsson231e1d92020-11-05 12:19:34 +010036
37void tflu_debug_log(const char *s) {
38 fprintf(stderr, "%s", s);
39}
40
Kristofer Jonsson641c0912020-08-31 11:34:14 +020041void print_output_data(TfLiteTensor *output, size_t bytesToPrint) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020042 const int numBytesToPrint = min(output->bytes, bytesToPrint);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020043
44 int dims_size = output->dims->size;
45 printf("{\n");
46 printf("\"dims\": [%d,", dims_size);
47 for (int i = 0; i < output->dims->size - 1; ++i) {
48 printf("%d,", output->dims->data[i]);
49 }
50 printf("%d],\n", output->dims->data[dims_size - 1]);
51
Per Åstrand91a91732020-09-25 15:04:26 +020052 printf("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020053 printf("\"data\":\"");
54 for (int i = 0; i < numBytesToPrint - 1; ++i) {
55 if (i % 16 == 0 && i != 0) {
56 printf("\n");
57 }
58 printf("0x%02x,", output->data.uint8[i]);
59 }
60 printf("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
61 printf("}");
62}
63
64bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst) {
65 if (dst.data == nullptr) {
66 return false;
67 }
68
69 if (src.bytes > dst.size) {
70 printf("Tensor size %d does not match output size %d.\n", src.bytes, dst.size);
71 return true;
72 }
73
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020074 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
Kristofer Jonsson641c0912020-08-31 11:34:14 +020075 dst.size = src.bytes;
76
77 return false;
78}
79
80} // namespace
81
82namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020083DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020084
Kristofer Jonsson34e24962020-11-23 16:22:10 +010085void DataPtr::invalidate() {
86#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010087 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
88#endif
89}
90
91void DataPtr::clean() {
92#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010093 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
94#endif
95}
96
Kristofer Jonsson641c0912020-08-31 11:34:14 +020097InferenceJob::InferenceJob() : numBytesToPrint(0) {}
98
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020099InferenceJob::InferenceJob(const string &_name,
100 const DataPtr &_networkModel,
101 const vector<DataPtr> &_input,
102 const vector<DataPtr> &_output,
103 const vector<DataPtr> &_expectedOutput,
Bhavik Patelffe845d2020-11-16 12:13:56 +0100104 size_t _numBytesToPrint,
105 const vector<uint8_t> &_pmuEventConfig,
Bhavik Patel97906eb2020-12-17 15:32:16 +0100106 const uint32_t _pmuCycleCounterEnable) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200107 name(_name),
108 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Bhavik Patel97906eb2020-12-17 15:32:16 +0100109 numBytesToPrint(_numBytesToPrint), pmuEventConfig(_pmuEventConfig), pmuCycleCounterEnable(_pmuCycleCounterEnable),
Bhavik Patelffe845d2020-11-16 12:13:56 +0100110 pmuEventCount(), pmuCycleCounterCount(0) {
111#if defined(INFERENCE_PROC_TFLU_PROFILER) && defined(ETHOSU)
112 pmuEventCount = vector<uint32_t>(ETHOSU_PMU_NCOUNTERS, 0);
113#endif
114}
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 +0200148// NOTE: Adding code for get_lock & free_lock with some corrections from
149// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
150// TODO: check correctness?
151void InferenceProcess::getLock() {
152 int status = 0;
153
154 do {
155 // Wait until lock_var is free
156 while (__LDREXW(&lock) != 0)
157 ;
158
159 // Try to set lock_var
160 status = __STREXW(1, &lock);
161 } while (status != 0);
162
163 // Do not start any other memory access until memory barrier is completed
164 __DMB();
165}
166
167// TODO: check correctness?
168void InferenceProcess::freeLock() {
169 // Ensure memory operations completed before releasing lock
170 __DMB();
171
172 lock = 0;
173}
174
175bool InferenceProcess::push(const InferenceJob &job) {
176 getLock();
177 inferenceJobQueue.push(job);
178 freeLock();
179
180 return true;
181}
182
183bool InferenceProcess::runJob(InferenceJob &job) {
184 printf("Running inference job: %s\n", job.name.c_str());
185
Bhavik Patelffe845d2020-11-16 12:13:56 +0100186 // Register debug log callback for profiling
187 RegisterDebugLogCallback(tflu_debug_log);
188
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200189 tflite::MicroErrorReporter microErrorReporter;
190 tflite::ErrorReporter *reporter = &microErrorReporter;
191
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200192 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200193 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
194 if (model->version() != TFLITE_SCHEMA_VERSION) {
Per Åstrand91a91732020-09-25 15:04:26 +0200195 printf("Model provided is schema version %" PRIu32 " not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200196 model->version(),
197 TFLITE_SCHEMA_VERSION);
198 return true;
199 }
200
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200201 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200202 tflite::AllOpsResolver resolver;
Kristofer Jonsson91f600c2021-02-10 11:29:52 +0100203 tflite::MicroProfiler profiler;
Bhavik Patelffe845d2020-11-16 12:13:56 +0100204
205#if defined(INFERENCE_PROC_TFLU_PROFILER) && defined(ETHOSU)
Per Åstrand2572fe92021-03-23 13:57:14 +0100206 vector<ethosu_pmu_event_type> pmu_events(ETHOSU_PMU_NCOUNTERS, ETHOSU_PMU_NO_EVENT);
207
208 for (size_t i = 0; i < job.pmuEventConfig.size(); i++) {
209 pmu_events[i] = ethosu_pmu_event_type(job.pmuEventConfig[i]);
210 }
211 profiler.MonitorEthosuPMUEvents(pmu_events[0], pmu_events[1], pmu_events[2], pmu_events[3]);
Bhavik Patelffe845d2020-11-16 12:13:56 +0100212#endif
Anton Moberg66ed1822021-02-10 08:49:28 +0100213 tflite::MicroInterpreter interpreter(model, resolver, tensorArena, tensorArenaSize, reporter, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200214
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200215 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200216 TfLiteStatus allocate_status = interpreter.AllocateTensors();
217 if (allocate_status != kTfLiteOk) {
218 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
219 return true;
220 }
221
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200222 // Create a filtered list of non empty input tensors
223 vector<TfLiteTensor *> inputTensors;
224 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
225 TfLiteTensor *tensor = interpreter.input(i);
226
227 if (tensor->bytes > 0) {
228 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200229 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200230 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200231 if (job.input.size() != inputTensors.size()) {
232 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
233 job.input.size(),
234 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200235 return true;
236 }
237
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200238 // Copy input data
239 for (size_t i = 0; i < inputTensors.size(); ++i) {
240 const DataPtr &input = job.input[i];
241 const TfLiteTensor *tensor = inputTensors[i];
242
243 if (input.size != tensor->bytes) {
244 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
245 job.name.c_str(),
246 i,
247 input.size,
248 tensor->bytes);
249 return true;
250 }
251
252 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
253 }
254
255 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200256 TfLiteStatus invoke_status = interpreter.Invoke();
257 if (invoke_status != kTfLiteOk) {
258 printf("Invoke failed for inference job: %s\n", job.name.c_str());
259 return true;
260 }
261
Bhavik Patelffe845d2020-11-16 12:13:56 +0100262 printf("%s : %zu\r\n", "arena_used_bytes", interpreter.arena_used_bytes());
263
264#ifdef INFERENCE_PROC_TFLU_PROFILER
Kristofer Jonsson91f600c2021-02-10 11:29:52 +0100265 printf("Inference runtime: %u cycles\r\n", (unsigned int)profiler.GetTotalTicks());
Bhavik Patelffe845d2020-11-16 12:13:56 +0100266
267 if (job.pmuCycleCounterEnable != 0) {
Kristofer Jonsson91f600c2021-02-10 11:29:52 +0100268 job.pmuCycleCounterCount = profiler.GetTotalTicks();
Bhavik Patelffe845d2020-11-16 12:13:56 +0100269 }
270
271#ifdef ETHOSU
272 for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) {
273 job.pmuEventCount[i] = profiler.GetEthosuPMUCounter(i);
274 }
275#endif
276#endif
277
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200278 // Copy output data
279 if (job.output.size() > 0) {
280 if (interpreter.outputs_size() != job.output.size()) {
281 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
282 return true;
283 }
284
285 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
286 if (copyOutput(*interpreter.output(i), job.output[i])) {
287 return true;
288 }
289 }
290 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200291
292 if (job.numBytesToPrint > 0) {
293 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
294 // whichever comes first as well as the output shape.
295 printf("num_of_outputs: %d\n", interpreter.outputs_size());
296 printf("output_begin\n");
297 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200298
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200299 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
300 TfLiteTensor *output = interpreter.output(i);
301 print_output_data(output, job.numBytesToPrint);
302 if (i != interpreter.outputs_size() - 1) {
303 printf(",\n");
304 }
305 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200306
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200307 printf("]\n");
308 printf("output_end\n");
309 }
310
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200311 if (job.expectedOutput.size() > 0) {
312 if (job.expectedOutput.size() != interpreter.outputs_size()) {
Bhavik Patelffe845d2020-11-16 12:13:56 +0100313 printf("Expected number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200314 job.name.c_str(),
315 job.expectedOutput.size(),
316 interpreter.outputs_size());
317 return true;
318 }
319
320 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
321 const DataPtr &expected = job.expectedOutput[i];
322 const TfLiteTensor *output = interpreter.output(i);
323
324 if (expected.size != output->bytes) {
Per Åstrand90455452021-02-25 11:10:08 +0100325 printf("Expected tensor size does not match output size. job=%s, index=%u, expected=%zu, network=%zu\n",
326 job.name.c_str(),
327 i,
328 expected.size,
329 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200330 return true;
331 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200332
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200333 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200334 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
Per Åstrand90455452021-02-25 11:10:08 +0100335 printf("Expected data does not match output data. job=%s, index=%u, offset=%u, "
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200336 "expected=%02x, network=%02x\n",
337 job.name.c_str(),
338 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200339 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200340 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200341 output->data.uint8[j]);
Per Åstrand90455452021-02-25 11:10:08 +0100342 return true;
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200343 }
344 }
345 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200346 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200347
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200348 printf("Finished running job: %s\n", job.name.c_str());
349
350 return false;
351}
352
353bool InferenceProcess::run(bool exitOnEmpty) {
354 bool anyJobFailed = false;
355
356 while (true) {
357 getLock();
358 bool empty = inferenceJobQueue.empty();
359 freeLock();
360
361 if (empty) {
362 if (exitOnEmpty) {
363 printf("Exit from InferenceProcess::run() on empty job queue!\n");
364 break;
365 }
366
367 continue;
368 }
369
370 getLock();
371 InferenceJob job = inferenceJobQueue.front();
372 inferenceJobQueue.pop();
373 freeLock();
374
375 if (runJob(job)) {
376 anyJobFailed = true;
377 continue;
378 }
379 }
380
381 return anyJobFailed;
382}
383
384} // namespace InferenceProcess