blob: 7f4a30905f56187037ae849a26b94e0e2f9e708f [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 Åstrand91a91732020-09-25 15:04:26 +020027#include <inttypes.h>
28
Kristofer Jonsson641c0912020-08-31 11:34:14 +020029#ifndef TENSOR_ARENA_SIZE
30#define TENSOR_ARENA_SIZE (1024)
31#endif
32
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020033using namespace std;
34
Kristofer Jonsson641c0912020-08-31 11:34:14 +020035__attribute__((section(".bss.NoInit"), aligned(16))) uint8_t inferenceProcessTensorArena[TENSOR_ARENA_SIZE];
36
37namespace {
38void print_output_data(TfLiteTensor *output, size_t bytesToPrint) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020039 const int numBytesToPrint = min(output->bytes, bytesToPrint);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020040
41 int dims_size = output->dims->size;
42 printf("{\n");
43 printf("\"dims\": [%d,", dims_size);
44 for (int i = 0; i < output->dims->size - 1; ++i) {
45 printf("%d,", output->dims->data[i]);
46 }
47 printf("%d],\n", output->dims->data[dims_size - 1]);
48
Per Åstrand91a91732020-09-25 15:04:26 +020049 printf("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020050 printf("\"data\":\"");
51 for (int i = 0; i < numBytesToPrint - 1; ++i) {
52 if (i % 16 == 0 && i != 0) {
53 printf("\n");
54 }
55 printf("0x%02x,", output->data.uint8[i]);
56 }
57 printf("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
58 printf("}");
59}
60
61bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst) {
62 if (dst.data == nullptr) {
63 return false;
64 }
65
66 if (src.bytes > dst.size) {
67 printf("Tensor size %d does not match output size %d.\n", src.bytes, dst.size);
68 return true;
69 }
70
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020071 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
Kristofer Jonsson641c0912020-08-31 11:34:14 +020072 dst.size = src.bytes;
73
74 return false;
75}
76
77} // namespace
78
79namespace InferenceProcess {
80DataPtr::DataPtr(void *data, size_t size) : data(data), size(size) {}
81
82InferenceJob::InferenceJob() : numBytesToPrint(0) {}
83
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020084InferenceJob::InferenceJob(const string &name,
Kristofer Jonsson641c0912020-08-31 11:34:14 +020085 const DataPtr &networkModel,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020086 const vector<DataPtr> &input,
87 const vector<DataPtr> &output,
88 const vector<DataPtr> &expectedOutput,
Kristofer Jonsson641c0912020-08-31 11:34:14 +020089 size_t numBytesToPrint) :
90 name(name),
91 networkModel(networkModel), input(input), output(output), expectedOutput(expectedOutput),
92 numBytesToPrint(numBytesToPrint) {}
93
94InferenceProcess::InferenceProcess() : lock(0) {}
95
96// NOTE: Adding code for get_lock & free_lock with some corrections from
97// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
98// TODO: check correctness?
99void InferenceProcess::getLock() {
100 int status = 0;
101
102 do {
103 // Wait until lock_var is free
104 while (__LDREXW(&lock) != 0)
105 ;
106
107 // Try to set lock_var
108 status = __STREXW(1, &lock);
109 } while (status != 0);
110
111 // Do not start any other memory access until memory barrier is completed
112 __DMB();
113}
114
115// TODO: check correctness?
116void InferenceProcess::freeLock() {
117 // Ensure memory operations completed before releasing lock
118 __DMB();
119
120 lock = 0;
121}
122
123bool InferenceProcess::push(const InferenceJob &job) {
124 getLock();
125 inferenceJobQueue.push(job);
126 freeLock();
127
128 return true;
129}
130
131bool InferenceProcess::runJob(InferenceJob &job) {
132 printf("Running inference job: %s\n", job.name.c_str());
133
134 tflite::MicroErrorReporter microErrorReporter;
135 tflite::ErrorReporter *reporter = &microErrorReporter;
136
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200137 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200138 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
139 if (model->version() != TFLITE_SCHEMA_VERSION) {
Per Åstrand91a91732020-09-25 15:04:26 +0200140 printf("Model provided is schema version %" PRIu32 " not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200141 model->version(),
142 TFLITE_SCHEMA_VERSION);
143 return true;
144 }
145
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200146 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200147 tflite::AllOpsResolver resolver;
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200148 tflite::MicroInterpreter interpreter(model, resolver, inferenceProcessTensorArena, TENSOR_ARENA_SIZE, reporter);
149
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200150 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200151 TfLiteStatus allocate_status = interpreter.AllocateTensors();
152 if (allocate_status != kTfLiteOk) {
153 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
154 return true;
155 }
156
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200157 // Create a filtered list of non empty input tensors
158 vector<TfLiteTensor *> inputTensors;
159 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
160 TfLiteTensor *tensor = interpreter.input(i);
161
162 if (tensor->bytes > 0) {
163 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200164 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200165 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200166
167 if (job.input.size() != inputTensors.size()) {
168 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
169 job.input.size(),
170 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200171 return true;
172 }
173
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200174 // Copy input data
175 for (size_t i = 0; i < inputTensors.size(); ++i) {
176 const DataPtr &input = job.input[i];
177 const TfLiteTensor *tensor = inputTensors[i];
178
179 if (input.size != tensor->bytes) {
180 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
181 job.name.c_str(),
182 i,
183 input.size,
184 tensor->bytes);
185 return true;
186 }
187
188 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
189 }
190
191 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200192 TfLiteStatus invoke_status = interpreter.Invoke();
193 if (invoke_status != kTfLiteOk) {
194 printf("Invoke failed for inference job: %s\n", job.name.c_str());
195 return true;
196 }
197
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200198 // Copy output data
199 if (job.output.size() > 0) {
200 if (interpreter.outputs_size() != job.output.size()) {
201 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
202 return true;
203 }
204
205 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
206 if (copyOutput(*interpreter.output(i), job.output[i])) {
207 return true;
208 }
209 }
210 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200211
212 if (job.numBytesToPrint > 0) {
213 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
214 // whichever comes first as well as the output shape.
215 printf("num_of_outputs: %d\n", interpreter.outputs_size());
216 printf("output_begin\n");
217 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200218
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200219 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
220 TfLiteTensor *output = interpreter.output(i);
221 print_output_data(output, job.numBytesToPrint);
222 if (i != interpreter.outputs_size() - 1) {
223 printf(",\n");
224 }
225 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200226
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200227 printf("]\n");
228 printf("output_end\n");
229 }
230
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200231 if (job.expectedOutput.size() > 0) {
232 if (job.expectedOutput.size() != interpreter.outputs_size()) {
233 printf("Expeded number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
234 job.name.c_str(),
235 job.expectedOutput.size(),
236 interpreter.outputs_size());
237 return true;
238 }
239
240 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
241 const DataPtr &expected = job.expectedOutput[i];
242 const TfLiteTensor *output = interpreter.output(i);
243
244 if (expected.size != output->bytes) {
245 printf(
246 "Expected tensor size does not match network size. job=%s, index=%u, expected=%zu, network=%zu\n",
247 job.name.c_str(),
248 i,
249 expected.size,
250 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200251 return true;
252 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200253
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200254 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200255 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
256 printf("Expected tensor size does not match network size. job=%s, index=%u, offset=%u, "
257 "expected=%02x, network=%02x\n",
258 job.name.c_str(),
259 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200260 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200261 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200262 output->data.uint8[j]);
263 }
264 }
265 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200266 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200267
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200268 printf("Finished running job: %s\n", job.name.c_str());
269
270 return false;
271}
272
273bool InferenceProcess::run(bool exitOnEmpty) {
274 bool anyJobFailed = false;
275
276 while (true) {
277 getLock();
278 bool empty = inferenceJobQueue.empty();
279 freeLock();
280
281 if (empty) {
282 if (exitOnEmpty) {
283 printf("Exit from InferenceProcess::run() on empty job queue!\n");
284 break;
285 }
286
287 continue;
288 }
289
290 getLock();
291 InferenceJob job = inferenceJobQueue.front();
292 inferenceJobQueue.pop();
293 freeLock();
294
295 if (runJob(job)) {
296 anyJobFailed = true;
297 continue;
298 }
299 }
300
301 return anyJobFailed;
302}
303
304} // namespace InferenceProcess