blob: 759617abc18be51a4a1a04657e8c5657cf7bde9c [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"
20#include "tensorflow/lite/micro/micro_error_reporter.h"
21#include "tensorflow/lite/micro/micro_interpreter.h"
22#include "tensorflow/lite/schema/schema_generated.h"
23#include "tensorflow/lite/version.h"
24
25#include "inference_process.hpp"
26
Per Åstrandd9afc082020-10-06 13:25:08 +020027#include "cmsis_compiler.h"
28
Per Åstrand91a91732020-09-25 15:04:26 +020029#include <inttypes.h>
30
Kristofer Jonsson641c0912020-08-31 11:34:14 +020031#ifndef TENSOR_ARENA_SIZE
32#define TENSOR_ARENA_SIZE (1024)
33#endif
34
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020035using namespace std;
36
Kristofer Jonsson641c0912020-08-31 11:34:14 +020037__attribute__((section(".bss.NoInit"), aligned(16))) uint8_t inferenceProcessTensorArena[TENSOR_ARENA_SIZE];
38
39namespace {
40void print_output_data(TfLiteTensor *output, size_t bytesToPrint) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020041 const int numBytesToPrint = min(output->bytes, bytesToPrint);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020042
43 int dims_size = output->dims->size;
44 printf("{\n");
45 printf("\"dims\": [%d,", dims_size);
46 for (int i = 0; i < output->dims->size - 1; ++i) {
47 printf("%d,", output->dims->data[i]);
48 }
49 printf("%d],\n", output->dims->data[dims_size - 1]);
50
Per Åstrand91a91732020-09-25 15:04:26 +020051 printf("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020052 printf("\"data\":\"");
53 for (int i = 0; i < numBytesToPrint - 1; ++i) {
54 if (i % 16 == 0 && i != 0) {
55 printf("\n");
56 }
57 printf("0x%02x,", output->data.uint8[i]);
58 }
59 printf("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
60 printf("}");
61}
62
63bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst) {
64 if (dst.data == nullptr) {
65 return false;
66 }
67
68 if (src.bytes > dst.size) {
69 printf("Tensor size %d does not match output size %d.\n", src.bytes, dst.size);
70 return true;
71 }
72
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020073 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
Kristofer Jonsson641c0912020-08-31 11:34:14 +020074 dst.size = src.bytes;
75
76 return false;
77}
78
79} // namespace
80
81namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020082DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020083
84InferenceJob::InferenceJob() : numBytesToPrint(0) {}
85
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020086InferenceJob::InferenceJob(const string &_name,
87 const DataPtr &_networkModel,
88 const vector<DataPtr> &_input,
89 const vector<DataPtr> &_output,
90 const vector<DataPtr> &_expectedOutput,
91 size_t _numBytesToPrint) :
92 name(_name),
93 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
94 numBytesToPrint(_numBytesToPrint) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020095
96InferenceProcess::InferenceProcess() : lock(0) {}
97
98// NOTE: Adding code for get_lock & free_lock with some corrections from
99// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
100// TODO: check correctness?
101void InferenceProcess::getLock() {
102 int status = 0;
103
104 do {
105 // Wait until lock_var is free
106 while (__LDREXW(&lock) != 0)
107 ;
108
109 // Try to set lock_var
110 status = __STREXW(1, &lock);
111 } while (status != 0);
112
113 // Do not start any other memory access until memory barrier is completed
114 __DMB();
115}
116
117// TODO: check correctness?
118void InferenceProcess::freeLock() {
119 // Ensure memory operations completed before releasing lock
120 __DMB();
121
122 lock = 0;
123}
124
125bool InferenceProcess::push(const InferenceJob &job) {
126 getLock();
127 inferenceJobQueue.push(job);
128 freeLock();
129
130 return true;
131}
132
133bool InferenceProcess::runJob(InferenceJob &job) {
134 printf("Running inference job: %s\n", job.name.c_str());
135
136 tflite::MicroErrorReporter microErrorReporter;
137 tflite::ErrorReporter *reporter = &microErrorReporter;
138
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200139 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200140 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
141 if (model->version() != TFLITE_SCHEMA_VERSION) {
Per Åstrand91a91732020-09-25 15:04:26 +0200142 printf("Model provided is schema version %" PRIu32 " not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200143 model->version(),
144 TFLITE_SCHEMA_VERSION);
145 return true;
146 }
147
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200148 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200149 tflite::AllOpsResolver resolver;
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200150 tflite::MicroInterpreter interpreter(model, resolver, inferenceProcessTensorArena, TENSOR_ARENA_SIZE, reporter);
151
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200152 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200153 TfLiteStatus allocate_status = interpreter.AllocateTensors();
154 if (allocate_status != kTfLiteOk) {
155 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
156 return true;
157 }
158
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200159 // Create a filtered list of non empty input tensors
160 vector<TfLiteTensor *> inputTensors;
161 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
162 TfLiteTensor *tensor = interpreter.input(i);
163
164 if (tensor->bytes > 0) {
165 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200166 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200167 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200168
169 if (job.input.size() != inputTensors.size()) {
170 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
171 job.input.size(),
172 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200173 return true;
174 }
175
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200176 // Copy input data
177 for (size_t i = 0; i < inputTensors.size(); ++i) {
178 const DataPtr &input = job.input[i];
179 const TfLiteTensor *tensor = inputTensors[i];
180
181 if (input.size != tensor->bytes) {
182 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
183 job.name.c_str(),
184 i,
185 input.size,
186 tensor->bytes);
187 return true;
188 }
189
190 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
191 }
192
193 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200194 TfLiteStatus invoke_status = interpreter.Invoke();
195 if (invoke_status != kTfLiteOk) {
196 printf("Invoke failed for inference job: %s\n", job.name.c_str());
197 return true;
198 }
199
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200200 // Copy output data
201 if (job.output.size() > 0) {
202 if (interpreter.outputs_size() != job.output.size()) {
203 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
204 return true;
205 }
206
207 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
208 if (copyOutput(*interpreter.output(i), job.output[i])) {
209 return true;
210 }
211 }
212 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200213
214 if (job.numBytesToPrint > 0) {
215 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
216 // whichever comes first as well as the output shape.
217 printf("num_of_outputs: %d\n", interpreter.outputs_size());
218 printf("output_begin\n");
219 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200220
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200221 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
222 TfLiteTensor *output = interpreter.output(i);
223 print_output_data(output, job.numBytesToPrint);
224 if (i != interpreter.outputs_size() - 1) {
225 printf(",\n");
226 }
227 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200228
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200229 printf("]\n");
230 printf("output_end\n");
231 }
232
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200233 if (job.expectedOutput.size() > 0) {
234 if (job.expectedOutput.size() != interpreter.outputs_size()) {
235 printf("Expeded number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
236 job.name.c_str(),
237 job.expectedOutput.size(),
238 interpreter.outputs_size());
239 return true;
240 }
241
242 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
243 const DataPtr &expected = job.expectedOutput[i];
244 const TfLiteTensor *output = interpreter.output(i);
245
246 if (expected.size != output->bytes) {
247 printf(
248 "Expected tensor size does not match network size. job=%s, index=%u, expected=%zu, network=%zu\n",
249 job.name.c_str(),
250 i,
251 expected.size,
252 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200253 return true;
254 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200255
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200256 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200257 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
258 printf("Expected tensor size does not match network size. job=%s, index=%u, offset=%u, "
259 "expected=%02x, network=%02x\n",
260 job.name.c_str(),
261 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200262 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200263 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200264 output->data.uint8[j]);
265 }
266 }
267 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200268 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200269
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200270 printf("Finished running job: %s\n", job.name.c_str());
271
272 return false;
273}
274
275bool InferenceProcess::run(bool exitOnEmpty) {
276 bool anyJobFailed = false;
277
278 while (true) {
279 getLock();
280 bool empty = inferenceJobQueue.empty();
281 freeLock();
282
283 if (empty) {
284 if (exitOnEmpty) {
285 printf("Exit from InferenceProcess::run() on empty job queue!\n");
286 break;
287 }
288
289 continue;
290 }
291
292 getLock();
293 InferenceJob job = inferenceJobQueue.front();
294 inferenceJobQueue.pop();
295 freeLock();
296
297 if (runJob(job)) {
298 anyJobFailed = true;
299 continue;
300 }
301 }
302
303 return anyJobFailed;
304}
305
306} // namespace InferenceProcess