blob: 0c47d1e3729152f0a15771b78d266e2a7f4c7f6c [file] [log] [blame]
Kristofer Jonsson641c0912020-08-31 11:34:14 +02001/*
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +01002 * Copyright (c) 2019-2022 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"
Jonny Svärd2ebaac72022-05-10 17:29:30 +020024#include "tensorflow/lite/micro/micro_time.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020025#include "tensorflow/lite/schema/schema_generated.h"
Kristofer Jonsson641c0912020-08-31 11:34:14 +020026
Jens Elofsson955288a2021-04-22 20:57:15 +020027#include "arm_profiler.hpp"
Kristofer Jonsson3bd34232021-08-30 13:55:55 +020028#ifdef LAYER_BY_LAYER_PROFILER
Jens Elofsson701a63b2021-05-23 17:37:07 +020029#include "layer_by_layer_profiler.hpp"
Jens Elofsson955288a2021-04-22 20:57:15 +020030#endif
Jonny Svärd5adf5a62022-02-09 16:42:10 +010031
32#include "crc.hpp"
33
Anton Moberg07cf70b2021-07-07 11:08:17 +020034#include "ethosu_log.h"
Jens Elofsson955288a2021-04-22 20:57:15 +020035
Kristofer Jonsson641c0912020-08-31 11:34:14 +020036#include "inference_process.hpp"
37
Per Åstrandd9afc082020-10-06 13:25:08 +020038#include "cmsis_compiler.h"
39
Per Åstrand91a91732020-09-25 15:04:26 +020040#include <inttypes.h>
41
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +020042using namespace std;
43
Fredrik Svedberg39a03242022-06-30 10:57:57 +020044namespace {
45const char *BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
46
47void printBase64(const uint8_t *data, size_t len) {
48 size_t count = len / 3;
49 size_t remainder = len % 3;
50 char buf[] = "====";
51 while (count--) {
52 buf[0] = BASE64[data[0] >> 2];
53 buf[1] = BASE64[(data[0] & 3) << 4 | data[1] >> 4];
54 buf[2] = BASE64[(data[1] & 0xf) << 2 | data[2] >> 6];
55 buf[3] = BASE64[data[2] & 0x3f];
56 LOG("%s", buf);
57 data += 3;
58 }
59 if (remainder) {
60 uint8_t b2 = remainder > 1 ? data[1] : 0;
61 buf[0] = BASE64[data[0] >> 2];
62 buf[1] = BASE64[(data[0] & 3) << 4 | b2 >> 4];
63 buf[2] = remainder > 1 ? BASE64[(b2 & 0xf) << 2] : '=';
64 buf[3] = '=';
65 LOG("%s", buf);
66 }
67}
68
69} // namespace
70
Kristofer Jonsson641c0912020-08-31 11:34:14 +020071namespace InferenceProcess {
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020072DataPtr::DataPtr(void *_data, size_t _size) : data(_data), size(_size) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020073
Kristofer Jonsson34e24962020-11-23 16:22:10 +010074void DataPtr::invalidate() {
75#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010076 SCB_InvalidateDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
77#endif
78}
79
80void DataPtr::clean() {
81#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
Kristofer Jonsson34e24962020-11-23 16:22:10 +010082 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t *>(data), size);
83#endif
84}
85
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +010086char *DataPtr::begin() const {
87 return static_cast<char *>(data);
88}
89
90char *DataPtr::end() const {
91 return static_cast<char *>(data) + size;
92}
93
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +010094InferenceJob::InferenceJob() : numBytesToPrint(0), externalContext(nullptr) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +020095
Per Åstrandbbd9c8f2020-09-25 15:07:35 +020096InferenceJob::InferenceJob(const string &_name,
97 const DataPtr &_networkModel,
98 const vector<DataPtr> &_input,
99 const vector<DataPtr> &_output,
100 const vector<DataPtr> &_expectedOutput,
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100101 const size_t _numBytesToPrint,
102 void *_externalContext) :
Per Åstrandbbd9c8f2020-09-25 15:07:35 +0200103 name(_name),
104 networkModel(_networkModel), input(_input), output(_output), expectedOutput(_expectedOutput),
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100105 numBytesToPrint(_numBytesToPrint), externalContext(_externalContext) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200106
Kristofer Jonsson34e24962020-11-23 16:22:10 +0100107void InferenceJob::invalidate() {
108 networkModel.invalidate();
109
110 for (auto &it : input) {
111 it.invalidate();
112 }
113
114 for (auto &it : output) {
115 it.invalidate();
116 }
117
118 for (auto &it : expectedOutput) {
119 it.invalidate();
120 }
121}
122
123void InferenceJob::clean() {
124 networkModel.clean();
125
126 for (auto &it : input) {
127 it.clean();
128 }
129
130 for (auto &it : output) {
131 it.clean();
132 }
133
134 for (auto &it : expectedOutput) {
135 it.clean();
136 }
137}
138
Kristofer Jonsson40d886e2021-12-15 11:16:26 +0100139InferenceProcess::InferenceProcess(uint8_t *_tensorArena, size_t _tensorArenaSize) :
140 tensorArena(_tensorArena), tensorArenaSize(_tensorArenaSize) {}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200141
142bool InferenceProcess::runJob(InferenceJob &job) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100143 LOG_INFO("Running inference job: %s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200144
Bhavik Patelffe845d2020-11-16 12:13:56 +0100145 // Register debug log callback for profiling
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100146 RegisterDebugLogCallback(tfluDebugLog);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200147
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200148 // Get model handle and verify that the version is correct
Davide Grohmann30b17b92022-06-14 15:17:18 +0200149 const tflite::Model *model = parser.getModel(job.networkModel.data, job.networkModel.size);
150 if (model == nullptr) {
151 LOG_ERR("Invalid model");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200152 return true;
153 }
154
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200155 // Create the TFL micro interpreter
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200156 tflite::AllOpsResolver resolver;
Jens Elofsson955288a2021-04-22 20:57:15 +0200157 tflite::ArmProfiler profiler;
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100158 tflite::MicroErrorReporter errorReporter;
159 tflite::MicroInterpreter interpreter(
160 model, resolver, tensorArena, tensorArenaSize, &errorReporter, nullptr, &profiler);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200161
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100162 // Set external context
Davide Grohmann165f00a2022-02-09 14:53:58 +0100163 if (job.externalContext != nullptr) {
164 interpreter.SetMicroExternalContext(job.externalContext);
165 }
Kristofer Jonsson5a15bf42022-01-27 17:36:55 +0100166
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200167 // Allocate tensors
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100168 TfLiteStatus status = interpreter.AllocateTensors();
169 if (status != kTfLiteOk) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100170 LOG_ERR("Failed to allocate tensors for inference: job=%s", job.name.c_str());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200171 return true;
172 }
173
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100174 // Copy IFM data from job descriptor to TFLu arena
175 if (copyIfm(job, interpreter)) {
176 return true;
177 }
178
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200179 // Get the current cycle counter value
180 uint32_t cpuCyclesBegin = tflite::GetCurrentTimeTicks();
181
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100182 // Run the inference
183 status = interpreter.Invoke();
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200184
185 // Calculate nbr of CPU cycles for the Invoke call
186 job.cpuCycles = tflite::GetCurrentTimeTicks() - cpuCyclesBegin;
187
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100188 if (status != kTfLiteOk) {
189 LOG_ERR("Invoke failed for inference: job=%s", job.name.c_str());
190 return true;
191 }
192
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100193 // Copy output data from TFLu arena to job descriptor
194 if (copyOfm(job, interpreter)) {
195 return true;
196 }
197
198 printJob(job, interpreter);
199
200 // Compare the OFM with the expected reference data
201 if (compareOfm(job, interpreter)) {
202 return true;
203 }
204
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200205 LOG_INFO("\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100206 LOG_INFO("Finished running job: %s", job.name.c_str());
207
Jonny Svärd2ebaac72022-05-10 17:29:30 +0200208 profiler.ReportResults();
209
210 LOG("\n");
211 LOG("Operator(s) total: %" PRIu64 " CPU cycles\n\n", profiler.GetTotalTicks());
212
213 LOG("Inference runtime: %" PRIu64 " CPU cycles total\n\n", job.cpuCycles);
214
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100215 return false;
216}
217
218bool InferenceProcess::copyIfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200219 // Create a filtered list of non empty input tensors
220 vector<TfLiteTensor *> inputTensors;
221 for (size_t i = 0; i < interpreter.inputs_size(); ++i) {
222 TfLiteTensor *tensor = interpreter.input(i);
223
224 if (tensor->bytes > 0) {
225 inputTensors.push_back(tensor);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200226 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200227 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100228
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200229 if (job.input.size() != inputTensors.size()) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100230 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 +0200231 job.input.size(),
232 inputTensors.size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200233 return true;
234 }
235
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100236 // Copy input data from job to TFLu arena
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200237 for (size_t i = 0; i < inputTensors.size(); ++i) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100238 DataPtr &input = job.input[i];
239 TfLiteTensor *tensor = inputTensors[i];
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200240
241 if (input.size != tensor->bytes) {
Kristofer Jonssoneb912392021-11-12 12:51:27 +0100242 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 +0200243 job.name.c_str(),
244 i,
245 input.size,
246 tensor->bytes);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200247 return true;
248 }
249
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100250 copy(input.begin(), input.end(), tensor->data.uint8);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200251 }
252
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100253 return false;
254}
255
256bool InferenceProcess::copyOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
257 // Skip copy if output is empty
258 if (job.output.empty()) {
259 return false;
260 }
261
262 if (interpreter.outputs_size() != job.output.size()) {
263 LOG_ERR("Output size mismatch: job=%zu, network=%u", job.output.size(), interpreter.outputs_size());
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200264 return true;
265 }
266
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100267 for (unsigned i = 0; i < interpreter.outputs_size(); ++i) {
268 DataPtr &output = job.output[i];
269 TfLiteTensor *tensor = interpreter.output(i);
Bhavik Patelffe845d2020-11-16 12:13:56 +0100270
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100271 if (tensor->bytes > output.size) {
272 LOG_ERR("Tensor size mismatch: tensor=%d, expected=%d", tensor->bytes, output.size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200273 return true;
274 }
275
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100276 copy(tensor->data.uint8, tensor->data.uint8 + tensor->bytes, output.begin());
277 }
278
279 return false;
280}
281
282bool InferenceProcess::compareOfm(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
283 // Skip verification if expected output is empty
284 if (job.expectedOutput.empty()) {
285 return false;
286 }
287
288 if (job.expectedOutput.size() != interpreter.outputs_size()) {
289 LOG_ERR("Expected number of output tensors mismatch: job=%s, expected=%zu, network=%zu",
290 job.name.c_str(),
291 job.expectedOutput.size(),
292 interpreter.outputs_size());
293 return true;
294 }
295
296 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
297 const DataPtr &expected = job.expectedOutput[i];
298 const TfLiteTensor *output = interpreter.output(i);
299
300 if (expected.size != output->bytes) {
301 LOG_ERR("Expected output tensor size mismatch: job=%s, index=%u, expected=%zu, network=%zu",
302 job.name.c_str(),
303 i,
304 expected.size,
305 output->bytes);
306 return true;
307 }
308
309 const char *exp = expected.begin();
310 for (unsigned int j = 0; j < output->bytes; ++j) {
311 if (output->data.uint8[j] != exp[j]) {
312 LOG_ERR("Expected output tensor data mismatch: job=%s, index=%u, offset=%u, "
313 "expected=%02x, network=%02x\n",
314 job.name.c_str(),
315 i,
316 j,
317 exp[j],
318 output->data.uint8[j]);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200319 return true;
320 }
321 }
322 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200323
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100324 return false;
325}
326
327void InferenceProcess::printJob(InferenceJob &job, tflite::MicroInterpreter &interpreter) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100328 LOG("arena_used_bytes : %zu\n", interpreter.arena_used_bytes());
329
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100330 // Print all of the output data, or the first NUM_BYTES_TO_PRINT bytes,
331 // whichever comes first as well as the output shape.
332 LOG("num_of_outputs: %d\n", interpreter.outputs_size());
333 LOG("output_begin\n");
334 LOG("[\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100335
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100336 for (unsigned int i = 0; i < interpreter.outputs_size(); i++) {
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100337 printOutputTensor(interpreter.output(i), job.numBytesToPrint);
338
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100339 if (i != interpreter.outputs_size() - 1) {
340 LOG(",\n");
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200341 }
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200342 }
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100343
Henrik Hoglindae4d8302021-12-08 15:06:02 +0100344 LOG("]\n");
345 LOG("output_end\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100346}
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200347
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100348void InferenceProcess::printOutputTensor(TfLiteTensor *output, size_t bytesToPrint) {
349 constexpr auto crc = Crc();
350 const uint32_t crc32 = crc.crc32(output->data.data, output->bytes);
351 const int numBytesToPrint = min(output->bytes, bytesToPrint);
352 int dims_size = output->dims->size;
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200353
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100354 LOG("{\n");
355 LOG("\"dims\": [%d,", dims_size);
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200356
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100357 for (int i = 0; i < output->dims->size - 1; ++i) {
358 LOG("%d,", output->dims->data[i]);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200359 }
Kristofer Jonsson72fa50b2020-09-10 13:26:41 +0200360
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100361 LOG("%d],\n", output->dims->data[dims_size - 1]);
362 LOG("\"data_address\": \"%08" PRIx32 "\",\n", (uint32_t)output->data.data);
Henrik Hoglind59096ef2022-03-18 08:58:11 +0100363 LOG("\"data_bytes\": %d,\n", output->bytes);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200364
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100365 if (numBytesToPrint) {
366 LOG("\"crc32\": \"%08" PRIx32 "\",\n", crc32);
367 LOG("\"data\":\"");
Fredrik Svedberg39a03242022-06-30 10:57:57 +0200368 printBase64(output->data.uint8, numBytesToPrint);
369 LOG("\"\n");
Kristofer Jonssondcc1ce02021-12-21 16:25:19 +0100370 } else {
371 LOG("\"crc32\": \"%08" PRIx32 "\"\n", crc32);
372 }
373
374 LOG("}");
375}
376
377void InferenceProcess::tfluDebugLog(const char *s) {
378 LOG("%s", s);
Kristofer Jonsson641c0912020-08-31 11:34:14 +0200379}
380
381} // namespace InferenceProcess