blob: 58075307127c88b64c9e121b82807c38e1add1e4 [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
27#ifndef TENSOR_ARENA_SIZE
28#define TENSOR_ARENA_SIZE (1024)
29#endif
30
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020031using namespace std;
32
Kristofer Jonsson641c0912020-08-31 11:34:14 +020033__attribute__((section(".bss.NoInit"), aligned(16))) uint8_t inferenceProcessTensorArena[TENSOR_ARENA_SIZE];
34
35namespace {
36void print_output_data(TfLiteTensor *output, size_t bytesToPrint) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020037 const int numBytesToPrint = min(output->bytes, bytesToPrint);
Kristofer Jonsson641c0912020-08-31 11:34:14 +020038
39 int dims_size = output->dims->size;
40 printf("{\n");
41 printf("\"dims\": [%d,", dims_size);
42 for (int i = 0; i < output->dims->size - 1; ++i) {
43 printf("%d,", output->dims->data[i]);
44 }
45 printf("%d],\n", output->dims->data[dims_size - 1]);
46
47 printf("\"data_address\": \"%08x\",\n", (uint32_t)output->data.data);
48 printf("\"data\":\"");
49 for (int i = 0; i < numBytesToPrint - 1; ++i) {
50 if (i % 16 == 0 && i != 0) {
51 printf("\n");
52 }
53 printf("0x%02x,", output->data.uint8[i]);
54 }
55 printf("0x%02x\"\n", output->data.uint8[numBytesToPrint - 1]);
56 printf("}");
57}
58
59bool copyOutput(const TfLiteTensor &src, InferenceProcess::DataPtr &dst) {
60 if (dst.data == nullptr) {
61 return false;
62 }
63
64 if (src.bytes > dst.size) {
65 printf("Tensor size %d does not match output size %d.\n", src.bytes, dst.size);
66 return true;
67 }
68
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020069 copy(src.data.uint8, src.data.uint8 + src.bytes, static_cast<uint8_t *>(dst.data));
Kristofer Jonsson641c0912020-08-31 11:34:14 +020070 dst.size = src.bytes;
71
72 return false;
73}
74
75} // namespace
76
77namespace InferenceProcess {
78DataPtr::DataPtr(void *data, size_t size) : data(data), size(size) {}
79
80InferenceJob::InferenceJob() : numBytesToPrint(0) {}
81
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020082InferenceJob::InferenceJob(const string &name,
Kristofer Jonsson641c0912020-08-31 11:34:14 +020083 const DataPtr &networkModel,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020084 const vector<DataPtr> &input,
85 const vector<DataPtr> &output,
86 const vector<DataPtr> &expectedOutput,
Kristofer Jonsson641c0912020-08-31 11:34:14 +020087 size_t numBytesToPrint) :
88 name(name),
89 networkModel(networkModel), input(input), output(output), expectedOutput(expectedOutput),
90 numBytesToPrint(numBytesToPrint) {}
91
92InferenceProcess::InferenceProcess() : lock(0) {}
93
94// NOTE: Adding code for get_lock & free_lock with some corrections from
95// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
96// TODO: check correctness?
97void InferenceProcess::getLock() {
98 int status = 0;
99
100 do {
101 // Wait until lock_var is free
102 while (__LDREXW(&lock) != 0)
103 ;
104
105 // Try to set lock_var
106 status = __STREXW(1, &lock);
107 } while (status != 0);
108
109 // Do not start any other memory access until memory barrier is completed
110 __DMB();
111}
112
113// TODO: check correctness?
114void InferenceProcess::freeLock() {
115 // Ensure memory operations completed before releasing lock
116 __DMB();
117
118 lock = 0;
119}
120
121bool InferenceProcess::push(const InferenceJob &job) {
122 getLock();
123 inferenceJobQueue.push(job);
124 freeLock();
125
126 return true;
127}
128
129bool InferenceProcess::runJob(InferenceJob &job) {
130 printf("Running inference job: %s\n", job.name.c_str());
131
132 tflite::MicroErrorReporter microErrorReporter;
133 tflite::ErrorReporter *reporter = &microErrorReporter;
134
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200135 // Get model handle and verify that the version is correct
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200136 const tflite::Model *model = ::tflite::GetModel(job.networkModel.data);
137 if (model->version() != TFLITE_SCHEMA_VERSION) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200138 printf("Model provided is schema version %d not equal to supported version %d.\n",
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200139 model->version(),
140 TFLITE_SCHEMA_VERSION);
141 return true;
142 }
143
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200144 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200145 tflite::AllOpsResolver resolver;
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200146 tflite::MicroInterpreter interpreter(model, resolver, inferenceProcessTensorArena, TENSOR_ARENA_SIZE, reporter);
147
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200148 // Allocate tensors
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200149 TfLiteStatus allocate_status = interpreter.AllocateTensors();
150 if (allocate_status != kTfLiteOk) {
151 printf("AllocateTensors failed for inference job: %s\n", job.name.c_str());
152 return true;
153 }
154
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200155 // Create a filtered list of non empty input tensors
156 vector<TfLiteTensor *> inputTensors;
157 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
158 TfLiteTensor *tensor = interpreter.input(i);
159
160 if (tensor->bytes > 0) {
161 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200162 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200163 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200164
165 if (job.input.size() != inputTensors.size()) {
166 printf("Number of input buffers does not match number of non empty network tensors. input=%zu, network=%zu\n",
167 job.input.size(),
168 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200169 return true;
170 }
171
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200172 // Copy input data
173 for (size_t i = 0; i < inputTensors.size(); ++i) {
174 const DataPtr &input = job.input[i];
175 const TfLiteTensor *tensor = inputTensors[i];
176
177 if (input.size != tensor->bytes) {
178 printf("Input size does not match network size. job=%s, index=%zu, input=%zu, network=%u\n",
179 job.name.c_str(),
180 i,
181 input.size,
182 tensor->bytes);
183 return true;
184 }
185
186 copy(static_cast<char *>(input.data), static_cast<char *>(input.data) + input.size, tensor->data.uint8);
187 }
188
189 // Run the inference
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200190 TfLiteStatus invoke_status = interpreter.Invoke();
191 if (invoke_status != kTfLiteOk) {
192 printf("Invoke failed for inference job: %s\n", job.name.c_str());
193 return true;
194 }
195
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200196 // Copy output data
197 if (job.output.size() > 0) {
198 if (interpreter.outputs_size() != job.output.size()) {
199 printf("Number of outputs mismatch. job=%zu, network=%u\n", job.output.size(), interpreter.outputs_size());
200 return true;
201 }
202
203 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
204 if (copyOutput(*interpreter.output(i), job.output[i])) {
205 return true;
206 }
207 }
208 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200209
210 if (job.numBytesToPrint > 0) {
211 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
212 // whichever comes first as well as the output shape.
213 printf("num_of_outputs: %d\n", interpreter.outputs_size());
214 printf("output_begin\n");
215 printf("[\n");
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200216
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200217 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
218 TfLiteTensor *output = interpreter.output(i);
219 print_output_data(output, job.numBytesToPrint);
220 if (i != interpreter.outputs_size() - 1) {
221 printf(",\n");
222 }
223 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200224
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200225 printf("]\n");
226 printf("output_end\n");
227 }
228
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200229 if (job.expectedOutput.size() > 0) {
230 if (job.expectedOutput.size() != interpreter.outputs_size()) {
231 printf("Expeded number of output tensors does not match network. job=%s, expected=%zu, network=%zu\n",
232 job.name.c_str(),
233 job.expectedOutput.size(),
234 interpreter.outputs_size());
235 return true;
236 }
237
238 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
239 const DataPtr &expected = job.expectedOutput[i];
240 const TfLiteTensor *output = interpreter.output(i);
241
242 if (expected.size != output->bytes) {
243 printf(
244 "Expected tensor size does not match network size. job=%s, index=%u, expected=%zu, network=%zu\n",
245 job.name.c_str(),
246 i,
247 expected.size,
248 output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200249 return true;
250 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200251
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200252 for (unsigned int j = 0; j < output->bytes; ++j) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200253 if (output->data.uint8[j] != static_cast<uint8_t *>(expected.data)[j]) {
254 printf("Expected tensor size does not match network size. job=%s, index=%u, offset=%u, "
255 "expected=%02x, network=%02x\n",
256 job.name.c_str(),
257 i,
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200258 j,
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200259 static_cast<uint8_t *>(expected.data)[j],
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200260 output->data.uint8[j]);
261 }
262 }
263 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200264 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200265
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200266 printf("Finished running job: %s\n", job.name.c_str());
267
268 return false;
269}
270
271bool InferenceProcess::run(bool exitOnEmpty) {
272 bool anyJobFailed = false;
273
274 while (true) {
275 getLock();
276 bool empty = inferenceJobQueue.empty();
277 freeLock();
278
279 if (empty) {
280 if (exitOnEmpty) {
281 printf("Exit from InferenceProcess::run() on empty job queue!\n");
282 break;
283 }
284
285 continue;
286 }
287
288 getLock();
289 InferenceJob job = inferenceJobQueue.front();
290 inferenceJobQueue.pop();
291 freeLock();
292
293 if (runJob(job)) {
294 anyJobFailed = true;
295 continue;
296 }
297 }
298
299 return anyJobFailed;
300}
301
302} // namespace InferenceProcess