blob: 3cbc8e0d3d7d7b669d59ef5b56cb5de831c31a8c [file] [log] [blame]
Kristofer Jonsson641c0912020-08-31 11:34:14 +02001/*
Ledion Dajaf1dada32023-02-17 09:58:00 +01002 * SPDX-FileCopyrightText: Copyright 2019-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
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
Ledion Dajaa7d025a2022-11-21 09:23:50 +010019#ifndef INFERENCE_PROCESS_OPS_RESOLVER
Kristofer Jonsson641c0912020-08-31 11:34:14 +020020#include "tensorflow/lite/micro/all_ops_resolver.h"
Ledion Dajaa7d025a2022-11-21 09:23:50 +010021#else
22#define _STRINGIFY(a) #a
23#define STRINGIFY(a) _STRINGIFY(a)
24#include STRINGIFY(INFERENCE_PROCESS_OPS_RESOLVER)
25#endif
Måns Nilsson231e1d92020-11-05 12:19:34 +010026#include "tensorflow/lite/micro/cortex_m_generic/debug_log_callback.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020027#include "tensorflow/lite/micro/micro_interpreter.h"
Jonny Svärd2ebaac72022-05-10 17:29:30 +020028#include "tensorflow/lite/micro/micro_time.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020029#include "tensorflow/lite/schema/schema_generated.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020030
Jens Elofsson955288a2021-04-22 20:57:15 +020031#include "arm_profiler.hpp"
Per Åstrandd9afc082020-10-06 13:25:08 +020032#include "cmsis_compiler.h"
Kristofer Jonsson1fed1d52022-11-21 13:39:45 +010033#include "crc.hpp"
34#include "ethosu_log.h"
35#include "inference_process.hpp"
Per Åstrandd9afc082020-10-06 13:25:08 +020036
Per Åstrand91a91732020-09-25 15:04:26 +020037#include <inttypes.h>
38
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020039using namespace std;
40
Fredrik Svedberg39a03242022-06-30 10:57:57 +020041namespace {
42const char *BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
43
44void printBase64(const uint8_t *data, size_t len) {
45 size_t count = len / 3;
46 size_t remainder = len % 3;
47 char buf[] = "====";
48 while (count--) {
49 buf[0] = BASE64[data[0] >> 2];
50 buf[1] = BASE64[(data[0] & 3) << 4 | data[1] >> 4];
51 buf[2] = BASE64[(data[1] & 0xf) << 2 | data[2] >> 6];
52 buf[3] = BASE64[data[2] & 0x3f];
53 LOG("%s", buf);
54 data += 3;
55 }
56 if (remainder) {
57 uint8_t b2 = remainder > 1 ? data[1] : 0;
58 buf[0] = BASE64[data[0] >> 2];
59 buf[1] = BASE64[(data[0] & 3) << 4 | b2 >> 4];
60 buf[2] = remainder > 1 ? BASE64[(b2 & 0xf) << 2] : '=';
61 buf[3] = '=';
62 LOG("%s", buf);
63 }
64}
65
66} // namespace
67
Kristofer Jonsson641c0912020-08-31 11:34:14 +020068namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020069DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020070
Kristofer Jonsson34e24962020-11-23 16:22:10 +010071void DataPtr::invalidate() {
72#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010073 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
74#endif
75}
76
77void DataPtr::clean() {
78#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010079 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
80#endif
81}
82
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +010083char *DataPtr::begin() const {
84 return static_cast<char *>(data);
85}
86
87char *DataPtr::end() const {
88 return static_cast<char *>(data) + size;
89}
90
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010091InferenceJob::InferenceJob() : numBytesToPrint(0), externalContext(nullptr) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020092
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020093InferenceJob::InferenceJob(const string &_name,
94 const DataPtr &_networkModel,
95 const vector<DataPtr> &_input,
96 const vector<DataPtr> &_output,
97 const vector<DataPtr> &_expectedOutput,
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010098 const size_t _numBytesToPrint,
99 void *_externalContext) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200100 name(_name),
101 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100102 numBytesToPrint(_numBytesToPrint), externalContext(_externalContext) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200103
Kristofer Jonsson34e24962020-11-23 16:22:10 +0100104void InferenceJob::invalidate() {
105 networkModel.invalidate();
106
107 for (auto &it : input) {
108 it.invalidate();
109 }
110
111 for (auto &it : output) {
112 it.invalidate();
113 }
114
115 for (auto &it : expectedOutput) {
116 it.invalidate();
117 }
118}
119
120void InferenceJob::clean() {
121 networkModel.clean();
122
123 for (auto &it : input) {
124 it.clean();
125 }
126
127 for (auto &it : output) {
128 it.clean();
129 }
130
131 for (auto &it : expectedOutput) {
132 it.clean();
133 }
134}
135
Kristofer Jonsson40d886e2021-12-15 11:16:26 +0100136InferenceProcess::InferenceProcess(uint8_t *_tensorArena, size_t _tensorArenaSize) :
137 tensorArena(_tensorArena), tensorArenaSize(_tensorArenaSize) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200138
139bool InferenceProcess::runJob(InferenceJob &job) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100140 LOG_INFO("Running inference job: %s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200141
Bhavik Patelffe845d2020-11-16 12:13:56 +0100142 // Register debug log callback for profiling
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100143 RegisterDebugLogCallback(tfluDebugLog);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200144
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200145 // Get model handle and verify that the version is correct
Davide Grohmann30b17b92022-06-14 15:17:18 +0200146 const tflite::Model *model = parser.getModel(job.networkModel.data, job.networkModel.size);
147 if (model == nullptr) {
148 LOG_ERR("Invalid model");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200149 return true;
150 }
151
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200152 // Create the TFL micro interpreter
Ledion Dajaa7d025a2022-11-21 09:23:50 +0100153#ifndef INFERENCE_PROCESS_OPS_RESOLVER
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200154 tflite::AllOpsResolver resolver;
Ledion Dajaa7d025a2022-11-21 09:23:50 +0100155#else
156 tflite::MicroMutableOpResolver<kNumberOperators> resolver = get_resolver();
157#endif
Jens Elofsson955288a2021-04-22 20:57:15 +0200158 tflite::ArmProfiler profiler;
Måns Nilssoneee7fc62022-10-20 09:33:40 +0200159 tflite::MicroInterpreter interpreter(model, resolver, tensorArena, tensorArenaSize, nullptr, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200160
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200161 // Allocate tensors
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100162 TfLiteStatus status = interpreter.AllocateTensors();
163 if (status != kTfLiteOk) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100164 LOG_ERR("Failed to allocate tensors for inference: job=%s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200165 return true;
166 }
167
Måns Nilssonc23c4e12023-04-04 16:39:42 +0200168 // Set external context
169 if (job.externalContext != nullptr) {
170 interpreter.SetMicroExternalContext(job.externalContext);
171 }
172
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100173 // Copy IFM data from job descriptor to TFLu arena
174 if (copyIfm(job, interpreter)) {
175 return true;
176 }
177
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200178 // Get the current cycle counter value
179 uint32_t cpuCyclesBegin = tflite::GetCurrentTimeTicks();
180
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100181 // Run the inference
182 status = interpreter.Invoke();
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200183
184 // Calculate nbr of CPU cycles for the Invoke call
185 job.cpuCycles = tflite::GetCurrentTimeTicks() - cpuCyclesBegin;
186
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100187 if (status != kTfLiteOk) {
188 LOG_ERR("Invoke failed for inference: job=%s", job.name.c_str());
189 return true;
190 }
191
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100192 // Copy output data from TFLu arena to job descriptor
193 if (copyOfm(job, interpreter)) {
194 return true;
195 }
196
197 printJob(job, interpreter);
198
199 // Compare the OFM with the expected reference data
200 if (compareOfm(job, interpreter)) {
201 return true;
202 }
203
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200204 LOG_INFO("\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100205 LOG_INFO("Finished running job: %s", job.name.c_str());
206
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200207 profiler.ReportResults();
208
209 LOG("\n");
210 LOG("Operator(s) total: %" PRIu64 " CPU cycles\n\n", profiler.GetTotalTicks());
211
212 LOG("Inference runtime: %" PRIu64 " CPU cycles total\n\n", job.cpuCycles);
213
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100214 return false;
215}
216
217bool InferenceProcess::copyIfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200218 // Create a filtered list of non empty input tensors
219 vector<TfLiteTensor *> inputTensors;
220 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
221 TfLiteTensor *tensor = interpreter.input(i);
222
Ledion Dajaf1dada32023-02-17 09:58:00 +0100223 if (tensor != nullptr && tensor->bytes > 0) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200224 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200225 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200226 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100227
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200228 if (job.input.size() != inputTensors.size()) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100229 LOG_ERR("Number of input buffers does not match number of non empty network tensors: input=%zu, network=%zu",
Anton Moberg07cf70b2021-07-07 11:08:17 +0200230 job.input.size(),
231 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200232 return true;
233 }
234
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100235 // Copy input data from job to TFLu arena
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200236 for (size_t i = 0; i < inputTensors.size(); ++i) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100237 DataPtr &input = job.input[i];
238 TfLiteTensor *tensor = inputTensors[i];
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200239
240 if (input.size != tensor->bytes) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100241 LOG_ERR("Job input size does not match network input size: job=%s, index=%zu, input=%zu, network=%u",
Anton Moberg07cf70b2021-07-07 11:08:17 +0200242 job.name.c_str(),
243 i,
244 input.size,
245 tensor->bytes);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200246 return true;
247 }
248
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100249 copy(input.begin(), input.end(), tensor->data.uint8);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200250 }
251
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100252 return false;
253}
254
255bool InferenceProcess::copyOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
256 // Skip copy if output is empty
257 if (job.output.empty()) {
258 return false;
259 }
260
261 if (interpreter.outputs_size() != job.output.size()) {
262 LOG_ERR("Output size mismatch: job=%zu, network=%u", job.output.size(), interpreter.outputs_size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200263 return true;
264 }
265
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100266 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
267 DataPtr &output = job.output[i];
268 TfLiteTensor *tensor = interpreter.output(i);
Bhavik Patelffe845d2020-11-16 12:13:56 +0100269
Ledion Dajaf1dada32023-02-17 09:58:00 +0100270 if (tensor == nullptr) {
271 return true;
272 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100273 if (tensor->bytes > output.size) {
Ledion Dajaf1dada32023-02-17 09:58:00 +0100274 LOG_ERR("Tensor size mismatch: tensor=%u, expected=%u", tensor->bytes, output.size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200275 return true;
276 }
277
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100278 copy(tensor->data.uint8, tensor->data.uint8 + tensor->bytes, output.begin());
279 }
280
281 return false;
282}
283
284bool InferenceProcess::compareOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
285 // Skip verification if expected output is empty
286 if (job.expectedOutput.empty()) {
287 return false;
288 }
289
290 if (job.expectedOutput.size() != interpreter.outputs_size()) {
291 LOG_ERR("Expected number of output tensors mismatch: job=%s, expected=%zu, network=%zu",
292 job.name.c_str(),
293 job.expectedOutput.size(),
294 interpreter.outputs_size());
295 return true;
296 }
297
298 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
299 const DataPtr &expected = job.expectedOutput[i];
300 const TfLiteTensor *output = interpreter.output(i);
301
Ledion Dajaf1dada32023-02-17 09:58:00 +0100302 if (output == nullptr) {
303 return true;
304 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100305 if (expected.size != output->bytes) {
306 LOG_ERR("Expected output tensor size mismatch: job=%s, index=%u, expected=%zu, network=%zu",
307 job.name.c_str(),
308 i,
309 expected.size,
310 output->bytes);
311 return true;
312 }
313
314 const char *exp = expected.begin();
315 for (unsigned int j = 0; j < output->bytes; ++j) {
316 if (output->data.uint8[j] != exp[j]) {
317 LOG_ERR("Expected output tensor data mismatch: job=%s, index=%u, offset=%u, "
318 "expected=%02x, network=%02x\n",
319 job.name.c_str(),
320 i,
321 j,
322 exp[j],
323 output->data.uint8[j]);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200324 return true;
325 }
326 }
327 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200328
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100329 return false;
330}
331
332void InferenceProcess::printJob(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100333 LOG("arena_used_bytes : %zu\n", interpreter.arena_used_bytes());
334
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100335 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
336 // whichever comes first as well as the output shape.
Ledion Dajaf1dada32023-02-17 09:58:00 +0100337 LOG("num_of_outputs: %u\n", interpreter.outputs_size());
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100338 LOG("output_begin\n");
339 LOG("[\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100340
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100341 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100342 printOutputTensor(interpreter.output(i), job.numBytesToPrint);
343
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100344 if (i != interpreter.outputs_size() - 1) {
345 LOG(",\n");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200346 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200347 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100348
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100349 LOG("]\n");
350 LOG("output_end\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100351}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200352
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100353void InferenceProcess::printOutputTensor(TfLiteTensor *output, size_t bytesToPrint) {
Ledion Dajaf1dada32023-02-17 09:58:00 +0100354 constexpr auto crc = Crc();
355 const uint32_t crc32 = crc.crc32(output->data.data, output->bytes);
356 const size_t numBytesToPrint = min(output->bytes, bytesToPrint);
357 int dims_size = output->dims->size;
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200358
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100359 LOG("{\n");
360 LOG("\"dims\": [%d,", dims_size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200361
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100362 for (int i = 0; i < output->dims->size - 1; ++i) {
363 LOG("%d,", output->dims->data[i]);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200364 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200365
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100366 LOG("%d],\n", output->dims->data[dims_size - 1]);
367 LOG("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Henrik Hoglind59096ef2022-03-18 08:58:11 +0100368 LOG("\"data_bytes\": %d,\n", output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200369
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100370 if (numBytesToPrint) {
371 LOG("\"crc32\": \"%08" PRIx32 "\",\n", crc32);
372 LOG("\"data\":\"");
Fredrik Svedberg39a03242022-06-30 10:57:57 +0200373 printBase64(output->data.uint8, numBytesToPrint);
374 LOG("\"\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100375 } else {
376 LOG("\"crc32\": \"%08" PRIx32 "\"\n", crc32);
377 }
378
379 LOG("}");
380}
381
382void InferenceProcess::tfluDebugLog(const char *s) {
383 LOG("%s", s);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200384}
385
386} // namespace InferenceProcess